如何处理返回结构指针的方法?

时间:2013-07-09 01:54:54

标签: c# interop

我正在尝试使用此签名编写一个包装器的包装器:

[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”,我认为它用作句柄,而不是指针本身)

1 个答案:

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

  1. 库可能使用指针值作为标记,并且不会将复制的Window对象识别为同一窗口。
  2. 它会导致内存泄漏,因为SDL_CreateWindow返回的指针永远不会在调用DestroyWindow时被释放。
  3. 事实上,对DestroyWindow的调用很可能会崩溃或损坏堆,因为它会尝试释放它没有分配的内存。
  4. 这意味着您需要将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))
        }
    }