CallbackOnCollectedDelegate был обнаружен - PullRequest
14 голосов
/ 31 января 2011

Я делю приложение на подклассы. Моя подклассовая процедура 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. Это может вызвать сбой приложения, коррупция и потеря данных. При прохождении делегаты в неуправляемый код, они должны быть сохранены живой управляемым приложением до гарантируется, что они никогда не будут быть вызванным.

Что я делаю не так?

Ответы [ 3 ]

28 голосов
/ 31 января 2011
oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, MyWndProc);

Это заставляет C # создавать объект-делегат на лету.Он переводит код в это:

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

, что является проблемой, так как на объект делегата нигде не ссылаются.Следующая сборка мусора собирается уничтожить его, вытащив коврик из-под неуправляемого кода.Вы уже сделали правильную вещь в своем коде, вы просто забыли его использовать.Исправление:

oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc);

Получение собственного класса из NativeWindow и использование его метода AssignHandle () - лучший способ мышеловки.Вызовите ReleaseHandle (), когда увидите сообщение WM_DESTROY.

10 голосов
/ 31 января 2011

Назовите меня сумасшедшим, но хранение ссылки должно решить эту проблему:

 private static readonly WndProcDelegateType _reference = MyWndProc;  
2 голосов
/ 27 мая 2014

функция обратного вызова может быть вызвана после возврата вызова, управляемый вызывающий должен предпринять шаги, чтобы гарантировать, что делегат останется невыбранным, пока функция обратного вызова не завершится.Для получения подробной информации о предотвращении сбора мусора см. Interf Marshaling с Platform Invoke.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...