我正在尝试使用此签名编写一个包装器的包装器:
[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern unsafe Window* SDL_CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
你会注意到它返回一个指向Window
的指针,这是一个结构。
如果我按原样公开此方法,那么使用它的任何代码都必须标记为不安全,我宁愿避免使用它。因此,我编写了一个公开的包装器方法来取消引用它:
public static unsafe Window CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags)
{
return *SDL_CreateWindow(title, x, y, w, h, flags);
}
但我不能100%确定现在正在做什么。 DLL正在创建Window
对象;当我取消引用它时,它是复制回到托管值类型对象吗?
我很惊讶地发现我可以传递参考in place of pointers,因为这完美地运作了:
[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow")]
public static extern void DestroyWindow(ref Window window);
但我can't do that for return types。无论如何,这样做是否安全?我想通过取消引用然后重新引用Window
当我将它传递回DestroyWindow
时,DLL将无法找到要销毁的正确对象,因为内存会被转移周围;但看到代码运行完美,我想这很好吗?
(实际上,在查看结构如何定义之后,我看到它有一个“id”,我认为它用作句柄,而不是指针本身)
答案 0 :(得分:2)
如果您更改SDL_CreateWindow
以返回IntPtr
DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr SDL_CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags);
public static Window CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags) {
IntPtr windowPtr = SDL_CreateWindow(title, x, y, w, h, flags);
return (Window)Marhsal.PtrToStructure(windowPtr, typeof(Window));
}
但这不安全。如上所述,这将创建Window
结构的托管副本,因此即使您将其传回,也不会传回相同的对象。这有一些严重的问题:
SDL_CreateWindow
返回的指针永远不会在调用DestroyWindow
时被释放。DestroyWindow
的调用很可能会崩溃或损坏堆,因为它会尝试释放它没有分配的内存。这意味着您需要将IntPtr
创建的SDL_CreateWindow
传递给DestoryWindow
为了能够保留IntPtr
和Window类,创建一个存储Window
结构的WindowClass包装器,以便您可以访问其属性并存储返回的IntPtr
和在对非托管DLL的子序列调用中使用它。
public class MyWindow {
public Window Window { get; set; }
public IntPtr WindowPtr { get; set; }
}
public static MyWindow CreateWindow(string title, int x, int y, int w, int h, WindowFlags flags) {
IntPtr windowPtr = SDL_CreateWindow(title, x, y, w, h, flags);
return new MyWindow {
WindowPtr = windowPtr,
Window = (Window)Marhsal.PtrToStructure(windowPtr, typeof(Window))
}
}