SafeHandle用于包装非拥有指针

时间:2015-07-29 04:23:11

标签: c# winapi

我正在尝试使用System.Runtime.InteropServices.SafeHandle的子类来管理指向Win32 GUI对象的指针(即HWNDHMENU)。然后我使用这些句柄在C#中实现基于Win32的GUI库。在此过程中,我创建一个菜单并将其分配给窗口。但是,假设我没有保留包含HMENU的SafeHandle,因此在拥有它的窗口之前它是垃圾收集的。当他们的SafeHandles消失时,我不能简单地销毁所有HMENU指针,因为这样的事情发生了:

  1. 我创建了一个HMENU并将其分配到SafeHandle的子类中,该子类会自动调用DestroyMenu()
  2. 我创建了一个顶级HWND,并使用HMENU Win32 API调用为其分配SetMenu()
  3. 因为HWND现在保留HMENU,所以不应该销毁它(通过DestroyMenu()),直到拥有它的HWND最早被销毁。< / LI>
  4. 但是,因为我将HMENU包装在SafeHandle中,所以GC很快会解除分配SafeHandle,从而销毁HMENU,而HWND仍会引用它。 / LI>
  5. 由于使用了明确删除的句柄,我的程序会崩溃。
  6. 我能做些什么来确保HMENU只要有需要就会保持不变,但是一旦拥有HWND GCed,它仍会被销毁?我查看this question,这几乎是理想的,除了它讨论使用内置引用计数的本机库。 Win32没有 - 句柄存在或被销毁。我考虑使用类似SafeHandle的类添加我自己的引用计数,但由于并非所有引用HMENU的程序的所有部分(即Win32)都可以访问我参考计数班。这是我用例的问题吗?我是否可以/应该实现这样的API包装器?

1 个答案:

答案 0 :(得分:0)

简短的回答是否定的,在不再需要HMENU之前,没有办法让Win32 API保持对.net对象的引用。

你可以做的是用自己的类包装HWND,并在你的SetMenu上保存你对象内的引用 - 但这不会起作用,因为:

  1. 不允许从终结器中销毁GDI对象,因为终结器是从GC中的专用线程调用的,并且只允许从创建它的同一线程触摸GDI对象(它将排序 - 大部分时间都在工作,但是你以不支持的方式使用API​​)

  2. 从终结器中释放HWND(即使它被允许)将使窗口在完全不可预测的时间从屏幕上消失

  3. 这适用于引用计数的本机框架,因为与GC不同,发布引用是100%可预测和可控制的。

    (基本上,WinForms的设计方式是出于某种原因)