这是有效的:

[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.FreeHGlobalMarshal.FreeCoTaskMemMarshal.FreeBSTR释放字符指针——它们都崩溃了。没有其他释放内存的方法,所以我猜内存是通过newmalloc()分配的。那现在呢,我被套住了?我的程序有永久性的内存泄漏吗?
我查了来源。字符串是通过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/

10-13 04:15