2017-04-21 76 views
0

我目前正在研究一些C++代码,这些代码涉及到C++ dll。这不是我或我公司的任何其他人有任何经验的领域。至少可以说是令人大开眼界。跟踪非托管C#代码中的访问冲突源

经过大量的阅读,反复试验和挫折之后,我设法解决了大部分问题,并获得了很大程度上的功能。然而,不时地,它仍然把这扔给我......

System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏。

..然后死亡。只有在并行线程上运行调用时才会出现此错误 - 这很好。这个DLL应该是线程安全的,我们有充分的理由相信它应该是,如果正确处理。

此错误的原因始终是相同的函数调用:

[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi)] 
public static extern int QABatchWV_Close(IntPtr vi1); 

我对库中的头文件,该函数定义为:

__declspec(dllimport) int __stdcall QABatchWV_Close(int); 

从我了解还有像SafeHandle和MarshalAs这样的其他工具。但坦率地说,我不确定在这种情况下如何最好地部署它们。

这个错误往往需要几个小时的使用时间才能显示出来,所以调整和希望在这里不会是一个有效的方法。任何人都可以指出我在调用C++函数时可能会做错什么?

+0

是否有你决定使用参数作为'IntPtr'而不是'int'作为返回值的原因? – nvoigt

+0

@nvoigt试验,试图让它表现。我已经用int来测试它的两端,IntPtr在两端和其中的一个,如图所示。我很高兴地承认,我对此深表遗憾,只是在绝望中尝试一些事情。 –

+0

你需要找出当C++库被编译时int的大小。然后你需要在C#中选择正确的东西。 “IntPtr”可以是32位或64位,具体取决于您执行代码的位置。您的C++库不能确定在运行时为C#,它将在编译时被修复。 – nvoigt

回答

1

看看下面的代码,我用它来调用c(而不是C++)dll。我知道这不是对你的问题的真正回答,但也许你可以使用其中的一些事情。

请注意dll声明中的“CallingConvention”指示符以及try catch的“finally”部分中的“FreeGlobal”。

public class csInterface 
{ 
    [DllImport(@"myDLL.dll", EntryPoint = "dllFunc", CallingConvention = CallingConvention.StdCall)] 
    private static extern void dllFunc(IntPtr inp, IntPtr outp); 

    public static int myDll(ref MyInput myInput, ref MyOutput myOutput) 
    { 
     int sizeIn, sizeOut; 
     IntPtr ptr_i = IntPtr.Zero, ptr_u = IntPtr.Zero; 

     sizeIn = Marshal.SizeOf(typeof(myInput)); 
     sizeOut = Marshal.SizeOf(typeof(myOutput)); 
     /* Calling C */ 
     try 
     { 
      ptr_i = Marshal.AllocHGlobal(sizeIn); 
      ptr_u = Marshal.AllocHGlobal(sizeOut); 

      Marshal.StructureToPtr(myInput, ptr_i, true); 
      Marshal.StructureToPtr(myOutput, ptr_u, true); 

      dllFunc(ptr_i, ptr_u); 

      myOutput = (MyOutput)(Marshal.PtrToStructure(ptr_u, typeof(MyOutput))); 
     } 
     catch (Exception) 
     { 
      //Return something meaningful (or not) 
      return -999; 
     } 
     finally 
     { 
      //Free memory 
      Marshal.FreeHGlobal(ptr_i); 
      Marshal.FreeHGlobal(ptr_u); 
     } 
     //Return something to indicate it all went well 
     return 0; 
    } 
} 

在C#中,我宣布我的类型

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct MySubType 
    { 
     public int a; 
     public double b; 
    } 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct MyInput 
    { 
     [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)] 
     public string aString; //A string of length 3 
     public bool aBoolean;  
     public int anInt; 
     public char aChar; 
     public double aDouble; 

     [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 12)] 
     public MySubType[] aSubType; //Array of struct of length 12 
    } 

以及输出类似的东西。

用C

现在(它可能是相同的或C++相似),我宣布我的DLL

__declspec(dllexport) void _stdcall dllFunc(MyCInput *myCInput, MyCOutput *myCOutput) 
{ 
    //Code 
} 

和相应的C类型这显然具有镜像C#类型完全相同

typedef struct 
{ 
    int a; 
    double b; 
} MyCSubType; 

typedef struct 
{ 
    char aString[4]; 
    int aBoolean; //This needs to be cast over to your C boolean type 
    int anInt; 
    char aChar; 
    double aDouble; 
    MyCSubType myCSubType[12]; 
} MyCType; 

现在我在这个例子中使用的类型并不完全符合我在代码中使用的类型,并且我没有测试过这些代码。所以可能会有拼写错误等,但“原则”是可以的。

1

那么,首先你不需要在这里设置字符集,因为没有字符串。

所有的二 - 功能CPP应该被宣布为导出不是进口的,所以它应该是这样的:

__declspec(dllimport) int __stdcall QABatchWV_Close(int); 

接下来,你应该设置调用约定在C#代码STDCALL:

[DllImport(DLL, SetLastError = true, CallingConvention=CallingConvention.Stdcall)] 

接下来你应该在int代替intPtr在C#代码。我几乎可以肯定这个函数的名称(在C++ dll中)被破坏,它不是QABatchWV_Close,而是像QABatchWV_Close @ 32之类的东西。你应该使用“dll导出查看器”来检查它。