简体   繁体   English

Interop C#/ C问题:AccessViolationException

[英]Problem with Interop C#/C: AccessViolationException

and thanks in advice for any help. 并感谢任何帮助的建议。

i have this trivial function in C: 我在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 is a very simple struct with two int fields, x and y. Point是一个非常简单的结构,有两个int字段,x和y。

I would like calling this function from C#. 我想从C#调用这个函数。

I use this code: 我用这个代码:

[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);

But at runtime I have an AccessViolationException . 但是在运行时我有一个AccessViolationException Watching exception in detail, I found that exception is thrown from Marshal.CoTaskMemFree(IntPtr) method. 详细观察异常,我发现从Marshal.CoTaskMemFree(IntPtr)方法抛出异常。

It seems that this method is unable to free memory allocated by C malloc. 看来这个方法无法释放C malloc分配的内存。

What am i doing wrong? 我究竟做错了什么?

Really thanks. 真的感谢。

CoTaskMemFree cannot be used to free memory allocated by malloc (because they use different allocators). CoTaskMemFree不能用于释放malloc分配的malloc (因为它们使用不同的分配器)。 According to 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." 根据MSDN ,“运行时始终使用CoTaskMemFree方法释放内存。如果您使用的内存未使用CoTaskMemAlloc方法分配,则必须使用IntPtr并使用适当的方法手动释放内存。”

Additionally, Adam Nathan notes that "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." 此外, Adam Nathan指出 “UnmanagedType.LPStruct仅支持一种特定情况:将System.Guid值类型视为具有额外级别间接的非托管GUID。...您应该远离UnmanagedType.LPStruct。 “

There are two possible solutions: 有两种可能的解决方案:

  1. Declare the return type of the method as IntPtr and use Marshal.ReadInt32 to read the fields of the struct, or use Marshal.PtrToStructure to copy the data to a managed struct, or use unsafe code to cast the IntPtr value to a Point * . 将方法的返回类型声明为IntPtr并使用Marshal.ReadInt32读取结构的字段,或使用Marshal.PtrToStructure将数据复制到托管结构,或使用不安全的代码将IntPtr值转换为Point * The C library will need to expose a destroyPoint(Point *) method that frees the memory. C库需要公开一个释放内存的destroyPoint(Point *)方法。
  2. Change the C method signature to void getPoint(int x, int y, Point *) . 将C方法签名更改为void getPoint(int x, int y, Point *) This lets C# allocate the struct, and the C method simply fills in the data values. 这允许C#分配结构,而C方法只是填充数据值。 (Most of the Win32 APIs are defined this way). (大多数Win32 API都是以这种方式定义的)。

One final note: Unless your method uses the SetLastError Win32 API, you don't need to specify SetLastError = true on your P/Invoke attribute. 最后一点:除非您的方法使用SetLastError Win32 API,否则您不需要在P / Invoke属性上指定SetLastError = true

Since you don't have the code that frees "p", it is hard to say. 由于您没有释放“p”的代码,因此很难说。 However it is likely that the way malloc() and free() work together is completely different to the way C# manages memory. 但是,malloc()和free()一起工作的方式可能与C#管理内存的方式完全不同。 Since C# has garbage collection (I believe) it is likely that it uses a completely different memory management system. 由于C#有垃圾收集(我相信),它很可能使用完全不同的内存管理系统。

In any case, the correct solution is that if you use your library to create an object, you should also use it to destroy it. 在任何情况下,正确的解决方案是,如果您使用库创建对象,您还应该使用它来销毁它。 Implement a "destroyPoint" function that frees the memory in your C library, import it to the C# code, and call it from there to destroy the objects created by your C library. 实现一个“destroyPoint”函数,该函数释放C库中的内存,将其导入C#代码,并从那里调用它来销毁C库创建的对象。

As a general design/coding rule, every "create" function should have a matching "free/destroy/delete" function. 作为一般设计/编码规则,每个“创建”函数都应具有匹配的“自由/销毁/删除”功能。 Apart from nothing else, it makes it easy to ensure that all created items get properly destroyed. 除此之外,它可以轻松确保所有创建的项目得到正确销毁。

How is the Point type defined on the C# side? 如何在C#端定义Point类型?
It has to be unsafe, or you need to return a void pointer (IntPtr). 它必须是不安全的,或者你需要返回一个void指针(IntPtr)。 The GC is not able to count references from outside (here the allocated memory), thus your code can not expect to manage externally allocated memory via the GC. GC无法计算来自外部的引用(这里是已分配的内存),因此您的代码无法通过GC管理外部分配的内存。
One alternative is to keep a static reference to avoid a Garbage collection, if you need to keep the object persistently during the runtime of your application. 一种替代方法是保留静态引用以避免垃圾收集,如果您需要在应用程序的运行时期间持久保留对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM