GetFunctionPointerForDelegate和pin指针

时间:2014-08-07 14:07:16

标签: delegates c++-cli interop function-pointers unmanaged

嗨,这是关于C ++ CLI i动作中给出的一些代码,我很难理解。代码如下:

        delegate bool EnumWindowsDelegateProc(
            IntPtr hwnd,IntPtr lParam);   
        ref class WindowEnumerator
        {
        private:
            EnumWindowsDelegateProc^ _WindowFound;   
        public:
            WindowEnumerator(EnumWindowsDelegateProc^ handler) 
            {
                _WindowFound = handler; 
            }
            void Init()
            {
                pin_ptr<EnumWindowsDelegateProc^> tmp =  &_WindowFound;
                EnumWindows((WNDENUMPROC)                   
                    Marshal::GetFunctionPointerForDelegate(
                    _WindowFound).ToPointer(), 0);        
            }
        };

在上面的代码_WindowFound已被固定,所以GC不会推动它。问题是

  1. 不是tmp仅在Int()内有效,因此_WindowFound仅固定 在调用Int()期间?
  2. 如果是这样的情况那么代理人的位置是否存在 在EnumWindows将其称为函数时,内存可能会发生变化 指针?

1 个答案:

答案 0 :(得分:1)

pin_ptr&lt;&gt;当代码执行离开声明它的块时,自动取消,RAII样式。因此,它将固定在代码中的Init()方法的整个主体中。所以你的2子弹不适用。

值得注意的是,代码并不是非常正确的。它有效,但偶然。 Marshal.GetFunctionPointerForDelegate()调用存根编译器来自动生成允许本机代码调用委托目标所需的本机代码。该存根的生存期由委托对象的生命周期控制。换句话说,只要委托对象被垃圾收集,存根也将被销毁。

固定委托对象以任何方式影响存根。它已经不可移动,GC从不移动代码。它是偶然的,因为固定一个对象需要为对象创建一个额外的GC句柄(GCHandle :: Alloc),足以防止过早收集。

这种代码并没有太大的区别,无论如何,EnumWindows()很慢。当您调用其他需要回调的本机代码时,不一定是这种情况,避免固定应该始终是一个目标。您所要做的就是让抖动在代码之外看到对委托对象的引用,它仍然可以使用,如下所示:

void Init() {
    EnumWindows((WNDENUMPROC)
                Marshal::GetFunctionPointerForDelegate(
                _WindowFound).ToPointer(), 0);
    GC::KeepAlive(_WindowFound);
}

非常高效,GC :: KeepAlive()不生成任何代码,它只是告诉抖动延长_WIndowFound引用的生命周期,以便在EnumWindows()执行时无法收集它。即使这在特定情况下也是过度的,因为 somebody 将会引用WindowEnumerator对象以便检索_WindowFound,但是比抱歉更安全。