互操作 C#/C 问题:AccessViolationException

发布于 2024-10-08 06:03:26 字数 819 浏览 0 评论 0原文

并感谢您提供的任何帮助的建议。

我在 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分配的内存。

我做错了什么?

真的很感谢。

and thanks in advice for any help.

i have this trivial function in 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.

I would like calling this function from 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. Watching exception in detail, I found that exception is thrown from Marshal.CoTaskMemFree(IntPtr) method.

It seems that this method is unable to free memory allocated by C malloc.

What am i doing wrong?

Really thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酒儿 2024-10-15 06:03:26

CoTaskMemFree 不能用于释放由 malloc 分配的内存(因为它们使用不同的分配器)。根据 MSDN,“运行时始终使用 CoTaskMemFree 方法来释放内存如果您正在使用的内存不是通过 CoTaskMemAlloc 方法分配的,则必须使用 IntPtr 并使用适当的方法手动释放内存。”

此外,Adam Nathan 指出“UnmanagementType. LPStruct 仅支持一种特定情况:将 System.Guid 值类型视为具有额外间接级别的非托管 GUID...您可能应该远离 UnmanagedType.LPStruct。

有两种可能的解决方案:

  1. 将方法的返回类型声明为 IntPtr 并使用 Marshal.ReadInt32 读取结构体的字段,或使用 Marshal.PtrToStructure 将数据复制到托管结构,或使用不安全代码将 IntPtr 值转换为 Point *。 C 库需要公开一个释放内存的 destroyPoint(Point *) 方法。
  2. 将 C 方法签名更改为 void getPoint(int x, int y, Point *)。这让 C# 分配结构,而 C 方法只需填充数据值。 (大多数 Win32 API 都是这样定义的)。

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

CoTaskMemFree cannot be used to free memory allocated by malloc (because they use different allocators). 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."

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."

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 *. The C library will need to expose a destroyPoint(Point *) method that frees the memory.
  2. Change the C method signature to void getPoint(int x, int y, Point *). This lets C# allocate the struct, and the C method simply fills in the data values. (Most of the Win32 APIs are defined this way).

One final note: Unless your method uses the SetLastError Win32 API, you don't need to specify SetLastError = true on your P/Invoke attribute.

苏辞 2024-10-15 06:03:26

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

无论如何,正确的解决方案是,如果您使用库创建对象,那么也应该使用它来销毁它。实现一个“destroyPoint”函数,该函数释放 C 库中的内存,将其导入到 C# 代码中,然后从那里调用它来销毁 C 库创建的对象。

作为一般设计/编码规则,每个“创建”函数都应该有一个匹配的“释放/销毁/删除”函数。不说别的,它可以轻松确保所有创建的项目都得到正确销毁。

Since you don't have the code that frees "p", it is hard to say. However it is likely that the way malloc() and free() work together is completely different to the way C# manages memory. Since C# has garbage collection (I believe) it is likely that it uses a completely different memory management system.

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.

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.

百思不得你姐 2024-10-15 06:03:26

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

How is the Point type defined on the C# side?
It has to be unsafe, or you need to return a void pointer (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.
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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文