【问题标题】:Tracing source of access violation in unmanaged C# code跟踪非托管 C# 代码中的访问冲突源
【发布时间】:2017-04-21 09:14:08
【问题描述】:

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

【问题讨论】:

  • 您决定将参数用作IntPtr 而不是返回值中的int 是否有原因?
  • @nvoigt 实验,试图让它正常运行。如图所示,我已经尝试过在两端使用 int,在两端使用 IntPtr,并且每个都使用一个。我很高兴承认我对此一无所知,只是在绝望中尝试。
  • 您需要了解编译 C++ 库时 int 的大小。然后你需要在 C# 中选择正确的东西。 IntPtr 是 32 位还是 64 位,具体取决于您执行代码的位置。您的 C++ 库无法像 C# 那样在运行时决定,它将在编译时修复。
  • @nvoigt 好的,谢谢,我想我需要回到供应商那里吗?此外,当我编辑问题时,我只会在并行线程上运行应用程序时收到此错误。这永远不会发生单线程。这仍然可能是由 int 大小不同引起的吗?

标签: c# c++ unmanaged


【解决方案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 导出查看器”进行检查。

【讨论】:

    【解决方案2】:

    看看下面我用来调用 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;
    

    现在我在此示例中使用的类型与我在代码中使用的类型不完全匹配,并且我还没有测试过这段代码。所以可能会有错别字等,但“原则”是可以的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多