编程语言
首页 > 编程语言> > Interop C#/ C的问题:AccessViolationException

Interop C#/ C的问题:AccessViolationException

作者:互联网

感谢您的帮助.

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

我究竟做错了什么?

真的感谢.

解决方法:

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

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

标签:access-violation,c-3,c,pinvoke
来源: https://codeday.me/bug/20191105/1996223.html