代码之家  ›  专栏  ›  技术社区  ›  John

通过P/Invoke使用回调和堆对象的安全方法

  •  2
  • John  · 技术社区  · 8 年前

    以下示例来自 Microsoft's documentation :

    public delegate bool CallBack(int handle, IntPtr param);
    
    public class LibWrap
    {
      // passing managed object as LPARAM
      // BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
    
      [DllImport("user32.dll")]
      public static extern bool EnumWindows(CallBack cb, IntPtr param);
    }
    
    public class App
    {
      public static void Main()
      {
        Run();
      }
    
      [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
      public static void Run()
      {
        TextWriter tw = System.Console.Out;
        GCHandle gch = GCHandle.Alloc(tw);
    
        CallBack cewp = new CallBack(CaptureEnumWindowsProc);
    
        // platform invoke will prevent delegate to be garbage collected
        // before call ends
    
        LibWrap.EnumWindows(cewp, GCHandle.ToIntPtr(gch));
        gch.Free();
      }
    
      private static bool CaptureEnumWindowsProc(int handle, IntPtr param)
      {
        GCHandle gch = GCHandle.FromIntPtr(param);
        TextWriter tw = (TextWriter)gch.Target;
        tw.WriteLine(handle);
        return true;
      }
    }
    

    有两件事让我感到困惑。

    首先,文件 GCHandle.Alloc 只讨论如何防止对象被垃圾收集。如果这就是全部,你就不需要了 GC句柄分配 :显然在样本中, tw 不会在呼叫期间被收集 EnumWindows -函数范围中有对它的引用。

    这里的问题是人们需要确保它不是 感动的 但是 GC句柄分配

    GCHandle.Alloc(myDelegate) 还是有更多的事情要考虑?

    1 回复  |  直到 8 年前
        1
  •  2
  •   David Heffernan    8 年前

    GCHandle.ToIntPtr 承诺给您一个整数值,您可以在以后传递给 GCHandle.FromIntPtr 以检索原始句柄。这就是全部。如果需要停止对象在内存中移动,则必须将其固定。但实际上并不需要固定对象,只需要停止收集它,并能够在回调中检索它。

    EnumWindows ,如评论中所述。如果将委托传递给非托管代码,而该非托管代码持有对该委托的引用,则需要做一些工作。必须确保委托的生存期超过对它的非托管引用。