2011-01-31 46 views
13

我是一个应用程序的子类。我的子类窗口过程是在一个DLL中。我在DLL中的子类代码看起来有点像这样(剥离下来,删除了其他不相关的部分)。CallbackOnCollectedDelegate检测到

class FooBar 
{ 
    private delegate int WndProcDelegateType(IntPtr hWnd, int uMsg, 
              int wParam, int lParam); 

    private const int GWL_WNDPROC = (-4); 
    private static IntPtr oldWndProc = IntPtr.Zero; 
    private static WndProcDelegateType newWndProc = new 
                WndProcDelegateType(MyWndProc); 

    internal static bool bHooked = false; 

    [DllImport("user32.dll")] 
    private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, 
              WndProcDelegateType dwNewLong); 

    [DllImport("user32.dll")] 
    private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, 
              IntPtr dwNewLong); 


    [DllImport("user32")] 
    private static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, 
              int Msg, int wParam, int lParam); 

    private static int MyWndProc(IntPtr lhWnd, int Msg, int wParam, int lParam) 
    { 
    switch (Msg) 
    { 
     // the usual stuff 


     // finally 
     return CallWindowProc(oldWndProc, lhWnd, Msg, wParam, lParam); 
    } 


    internal static void Hook() 
    { 
    oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc); 
    bHooked = oldWndProc != IntPtr.Zero; 
    } 

    internal static void Unhook() 
    { 
    if (bHooked) SetWindowLong(hWnd, GWL_WNDPROC, oldWndProc); 
    } 
} 

现在,即使我在委托的类级静态实例变量持有的强引用WndProc中,我得到这个错误。

CallbackOnCollectedDelegate检测

消息:回调被上型的 垃圾收集代表 '!PowerPointAddIn1 FooBar的+ WndProcDelegateType ::调用' 制成。 这可能会导致应用程序崩溃, 损坏和数据丢失。当通过 代表非托管代码,他们必须保持 活着的托管应用程序,直到 它保证,他们将永远不会调用 。

我在做什么错?

回答

25
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, MyWndProc); 

这就使得C#创建的即时委托对象。它将代码翻译为:

oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, new WndProcDelegateType(MyWndProc)); 

这是一个问题,委托对象没有被引用到任何地方。接下来的垃圾收集将会破坏它,从非托管代码下拉出地毯。你已经在你的代码中做了正确的事情,你忘记了使用它。修复:

oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc); 

从NativeWindow的派生自己的类,并使用其AssignHandle()方法是更好的捕鼠器BTW。当看到WM_DESTROY消息时调用ReleaseHandle()。

+1

谢谢你的帮助,你的回答是正确的,但我仍然得到例外。错误的代码在发布这个问题之前,我已经做了修改,我在两个地方发布了代码,并发布了以前的代码,我没有改变,但我仍然得到这个例外 – 2011-01-31 20:37:39

9

叫我疯了,但存储参考应解决此问题:

private static readonly WndProcDelegateType _reference = MyWndProc; 
+2

谢谢。我以为我已经有了一个参考。无论如何,你的声明和我的唯一区别是我使用了新的操作符,而你没有。我按照你的建议尝试了它,但它仍然抛出相同的异常。 :-( – 2011-01-31 20:40:21

+1

)如果是这么简单,我不会在搜索结果中使用谷歌搜索... – BrainSlugs83 2014-04-26 07:43:30

2

可以在调用返回后调用回调函数,受管理的调用方必须采取措施确保委托保持未收集状态,直到回调函数结束。有关防止垃圾收集的详细信息,请参阅使用平台调用进行互操作封送。

http://msdn.microsoft.com/en-us/library/eaw10et3.aspx