这是有效的:
[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetError")]
private static extern IntPtr SDL_GetError();
public static string GetError()
{
return Marshal.PtrToStringAnsi(SDL_GetError());
}
这个崩溃:
[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_GetError")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetError();
This article表明return属性本质上类似于调用
Marshal.PtrToStringAnsi
,那么该怎么办呢?正如danielpointed out,它可能崩溃了,因为marshaller试图释放内存。文章还指出,
注意:非托管端不能使用“new”关键字或“malloc()”c函数分配内存。在这些情况下,interop封送拆收器将无法释放内存。这是因为“new”关键字依赖于编译器,“malloc”函数依赖于c库。
我试过用
Marshal.FreeHGlobal
,Marshal.FreeCoTaskMem
和Marshal.FreeBSTR
释放字符指针——它们都崩溃了。没有其他释放内存的方法,所以我猜内存是通过new
或malloc()
分配的。那现在呢,我被套住了?我的程序有永久性的内存泄漏吗?我查了来源。字符串是通过
static char errmsg[SDL_ERRBUFIZE]
创建的。我的c已经生锈了,但我猜它被声明为static
,这样当它超出函数范围时就不会被释放。不过,我不记得静态数组在内存中的位置;有什么方法可以释放它们吗?编辑:等等…它是静态的。这意味着每次出现新的错误时,它都会覆盖旧的错误消息,因此为什么
SDL_GetError()
只返回最新的错误消息。所以,我不必担心释放它。因此,如果所有
return: MarshalAs...
选项都试图释放内存,那么唯一的解决方案就是我当前的解决方案。这毕竟是最理想的。 最佳答案
如链接文章中所述,当使用[return: MarshalAs(UnmanagedType.LPStr)]
时,clr使用FreeCoTaskMem()
释放本机字符串的内存。如果通过Marshal.PtrToStringAnsi()
手动创建托管字符串对象,则根本不会释放内存。
如果它崩溃,那么字符串可能不是通过CoTaskMemAlloc()
在非托管端创建的,而是通过new()或malloc()创建的(例如)。SDL_GetError()
的api应该说明谁的工作是释放本机字符串以及如何释放。
关于c# - 如何通过属性编码(marshal)至ANSI字符串?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17667611/