Обнаружен CallbackOnCollectedDelegate
Я подклассифицирую приложение. Моя подклассовая процедура 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
Сообщение: обратный вызов был выполнен на сборщик мусора собрал тип 'PowerPointAddIn1! FooBar + WndProcDelegateType:: Invoke. Это может привести к сбоям приложений, коррупции и потери данных. При прохождении делегаты неуправляемого кода, они должны быть сохранены живым управляемым приложением до гарантировано, что они никогда не будут называться.
Что я делаю неправильно?
Ответы
Ответ 1
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, MyWndProc);
Это заставляет С# создавать объект делегата "на лету". Он переводит код на это:
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, new WndProcDelegateType(MyWndProc));
что является проблемой, что делегировать объект не упоминается нигде. Следующая сборка мусора собирается уничтожить ее, вытащив ковер из-под неуправляемого кода. Вы уже сделали правильную вещь в своем коде, вы просто забыли использовать ее. Fix:
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc);
Получение собственного класса из NativeWindow и использование его метода AssignHandle() - лучшая мышеловка. Вызовите ReleaseHandle(), когда увидите сообщение WM_DESTROY.
Ответ 2
Назовите меня сумасшедшим, но сохранение ссылки должно решить следующее:
private static readonly WndProcDelegateType _reference = MyWndProc;
Ответ 3
функция обратного вызова может быть вызвана после того, как вызов будет возвращен, управляемый вызывающий должен предпринять шаги, чтобы гарантировать, что делегат остается неподключенным до тех пор, пока функция обратного вызова не завершится. Подробную информацию о предотвращении сбора мусора см. В разделе Interop Marshaling с помощью Platform Invoke.
http://msdn.microsoft.com/en-us/library/eaw10et3.aspx