非托管DLL导致AccessViolationException

时间:2012-07-26 14:21:15

标签: c# c++ dll interop

这个真的开始让我头疼:(

我有一个非托管的DLL,我正在尝试互操作,但它并不顺利。该应用程序有时会工作......但大多数情况下,随机通过AccessViolationException并崩溃。

我想我已经把它缩小到我对单个DllImport的错误处理:

C ++函数:

HTMLRENDERERDLL_REDIST_API void SetDataBuffer( int windowHandle, unsigned char* dataSource, int format, int stride, int totalBufferSize );

C#DllImport:

[DllImport("MyDll.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
    static private extern unsafe void SetDataBuffer(Int32 windowHandle, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] dataSource, Int32 format, Int32 stride, Int32 totalBufferSize);

致电所说的功能:

var buffer = new byte[windowWidth * windowHeight * bytesPerPixel];
SetDataBuffer(windowHandle, buffer, (Int32)0, (Int32)(windowWidth * bytesPerPixel), (Int32)(windowWidth * windowHeight * bytesPerPixel));

这有什么明显的错误吗?我怀疑dataSource是罪魁祸首,但......不确定如何证明它!

由于

2 个答案:

答案 0 :(得分:2)

您的问题可以从函数名称中推断出来。当您“设置缓冲区”时,本机代码可能稍后将使用该缓冲区。这与垃圾收集器不兼容,它会在压缩堆时移动数组。当本机代码写入缓冲区时,这是一个很大的Kaboom,它将写入不再存在的内存。当垃圾收集器检测到堆完整性受到损害时,最典型的结果是FatalExecutionEngineException。

需要固定数组,这是pinvoke marshaller在调用函数时所做的事情,但是在调用之后它取消数组。

您可以使用GCHandle.Alloc()固定托管数组,但如果您将其固定很长时间,这对垃圾收集器非常不利。到目前为止,最好的解决方案是使用Marshal.AllocHGlobal来分配一大块非托管内存,它永远不会移动。

如果仍有问题,请担心缓冲区的大小。而在本机代码中只是简单的痛苦,它很少需要帮助来解决AccessViolation问题。这是本机代码的标准故障模式。很难诊断,如果你没有它的源代码就不可能。联系代码所有者以获得支持,有一个小的repro代码段可以帮助他找到问题。

答案 1 :(得分:0)

我同意数据源可能是问题所在。

尝试更改dllimport,使其使用IntPtr而不是byte []。

[DllImport("MyDll.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
static private extern unsafe void SetDataBuffer(Int32 windowHandle, IntPtr dataSource, Int32 format, Int32 stride, Int32 totalBufferSize);

然后当你调用它时,显式为缓冲区分配内存,如下所示:

IntPtr buffer = Marshal.AllocHGlobal(windowWidth * windowHeight * bytesPerPixel);
SetDataBuffer(windowHandle, buffer, (Int32)0, (Int32)(windowWidth * bytesPerPixel), (Int32)(windowWidth * windowHeight * bytesPerPixel));

完成后,请不要忘记致电Marshal.FreeHGlobal(buffer)

我认为缓冲区是由Update方法在内部使用的吗?