2

我正在尝试使用此签名围绕方法编写一个包装器:

[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对象;当我取消引用它时,它是否被复制回托管值类型对象?


我惊讶地发现我可以传递引用来代替指针,因为这非常有效:

[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_DestroyWindow")]
public static extern void DestroyWindow(ref Window window);

但是对于返回类型我不能这样做。无论如何,这样做是否安全?我想通过取消引用然后重新引用它,Window当我将它传递回时DestroyWindow,DLL 将无法找到要销毁的正确对象,因为内存会被转移;但是看到代码运行完美,我想这很好吗?

(实际上,在查看结构的定义方式后,我发现它有一个“id”,我假设它用作句柄,而不是指针本身)

4

1 回答 1

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结构的托管副本,因此即使您将其传回,您也不会传回相同的对象。这有一些严重的问题:

  1. 该库可能将指针值用作标记​​,并且不会将复制的 Window 对象识别为同一个窗口。
  2. 这将导致内存泄漏,因为SDL_CreateWindow返回的指针永远不会在调用DestroyWindow.
  3. 事实上,调用DestroyWindow很可能会导致堆崩溃或损坏,因为它会尝试释放它没有分配的内存。

这意味着您需要将创建的相同IntPtr传递SDL_CreateWindowDestoryWindow

为了能够同时保留IntPtr和 Window 类,请创建一个 WindowClass 包装器来存储这两个Window结构,以便您可以访问其属性并存储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))
    }
}
于 2013-07-09T02:48:02.000 回答