首页 文章

Interop C#/ C问题:AccessViolationException

提问于
浏览
3

并感谢任何帮助的建议 .

我在C中有这个简单的功能:

__declspec(dllexport)  Point* createPoint (int x, int y) {
    Point *p;

    p = (Point*) malloc(sizeof(Point)); 
    p->x = x;
    p->y=y;

    return p;       
}

Point是一个非常简单的结构,有两个int字段,x和y .

我想从C#调用这个函数 .

我用这个代码:

[DllImport("simpleC.dll", EntryPoint = "createPoint", CallingConvention = CallingConvention.Cdecl, SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.LPStruct)]
public static extern Point createPoint(int x, int y);

Point p = Wrapper.createPoint(1, 2);

但是在运行时我有 AccessViolationException . 详细观察异常,我发现从 Marshal.CoTaskMemFree(IntPtr) 方法抛出了异常 .

看来这个方法无法释放C malloc分配的内存 .

我究竟做错了什么?

真的感谢 .

3 回答

  • 2

    CoTaskMemFree 不能用于释放由 malloc 分配的内存(因为它们使用不同的分配器) . 根据MSDN,"The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method."

    另外,Adam Nathan notes那"UnmanagedType.LPStruct is only supported for one specific case: treating a System.Guid value type as an unmanaged GUID with an extra level of indirection. ... You should probably just stay away from UnmanagedType.LPStruct."

    有两种可能的解决方案:

    • 将方法的返回类型声明为 IntPtr 并使用Marshal.ReadInt32读取结构的字段,或使用Marshal.PtrToStructure将数据复制到托管结构,或使用不安全代码将 IntPtr 值转换为 Point * . C库需要公开一个释放内存的 destroyPoint(Point *) 方法 .

    • 将C方法签名更改为 void getPoint(int x, int y, Point *) . 这允许C#分配结构,而C方法只是填充数据值 . (大多数Win32 API都是以这种方式定义的) .

    最后一点:除非您的方法使用SetLastError Win32 API,否则您无需在P / Invoke属性上指定 SetLastError = true .

  • 1

    由于您没有释放“p”的代码,因此很难说 . 但是,malloc()和free()一起工作的方式可能与C#管理内存的方式完全不同 . 由于C#有垃圾收集(我相信),它很可能使用完全不同的内存管理系统 .

    在任何情况下,正确的解决方案是,如果您使用库创建对象,您还应该使用它来销毁它 . 实现一个“destroyPoint”函数,该函数释放C库中的内存,将其导入C#代码,并从那里调用它来销毁C库创建的对象 .

    作为一般设计/编码规则,每个“创建”函数都应具有匹配的“自由/销毁/删除”功能 . 除此之外,它可以轻松确保所有创建的项目得到正确销毁 .

  • 1

    如何在C#端定义Point类型?
    它必须是不安全的,或者你需要返回一个void指针(IntPtr) . GC无法计算来自外部的引用(这里是已分配的内存),因此您的代码无法通过GC管理外部分配的内存 .
    一种替代方法是保留静态引用以避免垃圾收集,如果您需要在应用程序的运行时期间持久保留对象 .

相关问题