跟踪非托管C#代码中的访问冲突源

时间:2017-04-21 09:14:08

标签: c# c++ unmanaged

我目前正在研究一些与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 ++函数时可能做错了什么?

2 个答案:

答案 0 :(得分:1)

看一下我用来调用c(而不是c ++)dll的以下代码。我知道这不是你的问题的答案,但也许你可以使用其中的一些。

请注意dll声明中的“CallingConvention”-specifier以及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 :(得分:1)

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

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

__declspec(dllimport) int __stdcall QABatchWV_Close(int);

接下来,您应该将C#代码中的调用约定设置为stdcall:

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

接下来你应该在C#代码中使用int而不是IntPtr。我几乎可以肯定这个函数的名称(在C ++ dll中)被破坏了,它不是QABatchWV_Close,而是像QABatchWV_Close @ 32。您应该使用“dll export viewer”进行检查。