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

跟踪非托管C#代码中的访问冲突源 我目前正在研究一些与C++ DLL对话的C代码。这不是我——或我公司的任何其他人——有任何经验的领域。至少可以说,这让人大开眼界,c#,c++,unmanaged,C#,C++,Unmanaged,经过大量的阅读、反复尝试和挫折,我终于解决了大部分问题,得到了一些基本上实用的东西。然而,时不时地,它仍然把这个扔给我 System.AccessViolationException:尝试读取或写入受保护内存。这通常表示其他内存已损坏 。。然后就死了。只有在并行线程上运行调用时才会出现此错误-这是单线程的。这个dll应该是线程安全的,如果处理正确,我们有充分的理由相信它应该是线程安全的 此错误的原因始终是对同一函数的调用: [DllImport(DLL, SetLastError = true,

经过大量的阅读、反复尝试和挫折,我终于解决了大部分问题,得到了一些基本上实用的东西。然而,时不时地,它仍然把这个扔给我

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++函数时可能出错吗?< /P> < P>查看下面的代码,我用它来调用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;

现在,我在本例中使用的类型与我在代码中使用的类型不完全匹配,并且我还没有测试此代码。所以可能会有输入错误之类的,但是“原则”是可以的。

看看下面的代码,我用它来调用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;

现在,我在本例中使用的类型与我在代码中使用的类型不完全匹配,并且我还没有测试此代码。所以可能会有拼写错误之类的,但是“原则”是可以的。

首先,您不需要在这里设置字符集,因为这里没有字符串

第二,cpp中的函数应该声明为导出而不是导入,因此它应该如下所示:

__declspec(dllimport) int __stdcall QABatchWV_Close(int);
接下来,您应该将C#代码中的调用约定设置为stdcall:

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

接下来,在C代码中应该使用int而不是IntPtr。我几乎可以肯定这个函数(C++中的DLL)的名称被损坏,不是QabATCHWVY关闭,而是像QabATCHWVY之类的东西。Close@32. 您应该使用“dll导出查看器”进行检查。

首先,您不需要在这里设置字符集,因为这里没有字符串

第二,cpp中的函数应该声明为导出而不是导入,因此它应该如下所示:

__declspec(dllimport) int __stdcall QABatchWV_Close(int);
接下来,您应该将C#代码中的调用约定设置为stdcall:

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

接下来,在C代码中应该使用int而不是IntPtr。我几乎可以肯定这个函数(C++中的DLL)的名称被损坏,不是QabATCHWVY关闭,而是像QabATCHWVY之类的东西。Close@32. 您应该使用“dll导出查看器”进行检查。

您决定将参数用作
IntPtr
而不是返回值中的
int
是否有原因?@nvoigt正在尝试使其正常工作。我试过两端都是int,两端都是IntPtr,每个都是一个,如图所示。我很高兴承认我在这方面已经不太清楚了,只是在绝望中尝试着。当编译C++库时,你需要找出<<代码> int >代码的大小。然后你需要在C#中选择正确的东西
IntPtr
是32位还是64位取决于执行代码的位置。在C++中,C++库不能决定在运行时,它会在编译时被固定。@ Noigigt OK,谢谢,我想我需要回到供应商那里去做这个了吗?另外,当我在问题中编辑时,我只在并行线程上运行应用程序时才会出现此错误。这种情况永远不会发生在单线程上。这是否仍然可能是由于int大小不同造成的?是否有原因决定使用参数作为
IntPtr
,而不是返回值中的
int
。@nvoigt正在尝试使其正常工作。我试过两端都是int,两端都是IntPtr,每个都是一个,如图所示。我很高兴承认我在这方面已经不太清楚了,只是在绝望中尝试着。当编译C++库时,你需要找出<<代码> int >代码的大小。然后你需要在C#中选择正确的东西
IntPtr
是32位还是64位取决于执行代码的位置。在C++中,C++库不能决定在运行时,它会在编译时被固定。@ Noigigt OK,谢谢,我想我需要回到供应商那里去做这个了吗?另外,当我在问题中编辑时,我只在并行线程上运行应用程序时才会出现此错误。这种情况永远不会发生在单线程上。这仍然可能是由于int大小不同造成的吗?