我正在为应用程序创建子类。我的子类Window过程在DLL中。我在DLL中的子类代码看起来有点像这样(剥离,删除其他非相关部分)。
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
消息:对a进行了回调 垃圾收集类型的代表 'PowerPointAddIn1!FooBar的+ WndProcDelegateType ::调用'。 这可能会导致应用程序崩溃, 腐败和数据丢失。路过时 委托非托管代码,必须保留 由托管应用程序活着直到 保证他们永远不会 被称为。
我做错了什么?
答案 0 :(得分:27)
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, MyWndProc);
迫使C#动态创建委托对象。它将代码转换为:
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, new WndProcDelegateType(MyWndProc));
这是一个问题,委托对象不会在任何地方引用。下一个垃圾收集将破坏它,从非托管代码下拉出地毯。你已经在你的代码中做了正确的事情,你只是忘了使用它。修正:
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc);
从NativeWindow派生自己的类并使用其AssignHandle()方法是更好的捕鼠器顺便说一句。当您看到WM_DESTROY消息时调用ReleaseHandle()。
答案 1 :(得分:9)
叫我疯了,但存储引用应该解决这个问题:
private static readonly WndProcDelegateType _reference = MyWndProc;
答案 2 :(得分:2)
可以在调用返回后调用回调函数,受管理的调用者必须采取措施以确保在回调函数完成之前代理保持未收集状态。有关防止垃圾回收的详细信息,请参阅使用平台调用进行Interop Marshaling。