2011-01-31 3 views
13

Sono sottoclasse di un'applicazione. La mia procedura Window sottoclasse si trova all'interno di una DLL. Il mio codice di sottoclassi all'interno della DLL sembra un po 'come questo (rimosso, rimosso altre parti non correlate).CallbackOnCollectedDelegate è stato rilevato

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); 
    } 
} 

Ora, anche se ho in mano un forte riferimento alla WndProc in una a livello di classe un'istanza statica variabile del delegato, ottengo questo errore.

CallbackOnCollectedDelegate è stato rilevato

Messaggio: Una callback è stato effettuato su un garbage delegato raccolta di tipo 'PowerPointAddIn1 FooBar + WndProcDelegateType :: Invoke!'. Ciò potrebbe causare arresti anomali dell'applicazione, corruzione e perdita di dati. Quando si passano i delegati a codice non gestito, devono essere mantenuti in memoria in attesa dall'applicazione gestita fino a , in modo che non vengano mai chiamati .

Cosa sto sbagliando?

risposta

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

che le forze di C# per creare un oggetto delegato on-the-fly. Si traduce il codice in questo:

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

che è un problema, che delegare l'oggetto non è referenziato da nessuna parte. La prossima garbage collection lo distruggerà, estraendo il tappeto da un codice non gestito. Hai già fatto la cosa giusta nel tuo codice, hai semplicemente dimenticato di usarlo. Fissare:

oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc); 

Derivando la propria classe da NativeWindow e usa il suo metodo AssignHandle() è la migliore trappola per topi btw. Chiama ReleaseHandle() quando viene visualizzato il messaggio WM_DESTROY.

+1

Grazie per il tuo aiuto. La tua risposta è corretta ma ottengo comunque l'eccezione Sono terribilmente dispiaciuto di aver postato il codice errato. Avevo già apportato tale modifica prima di pubblicare questa domanda: avevo il codice in due punti e ho pubblicato il codice precedente, che non avevo modificato, ma ottengo comunque l'eccezione. –

+0

Ehm, che cosa dovrei guardare? –

+0

@Hans: Scusa, non ti ho preso. Ho lasciato qualcosa di inspiegabile? –

9

Call me pazzo, ma la memorizzazione di un riferimento dovrebbe risolvere questo:

private static readonly WndProcDelegateType _reference = MyWndProc; 
+2

Grazie. Pensavo di avere già un riferimento. Ad ogni modo, l'unica differenza tra la tua dichiarazione e la mia era che usavo il nuovo operatore e tu no. L'ho provato nel modo che hai suggerito ma getta comunque la stessa eccezione. :-( –

+1

Se fosse così semplice, non lo farei a Google per ore e ore ... – BrainSlugs83

2

la funzione di richiamata può essere richiamata dopo la restituzione della chiamata, il chiamante gestito deve adottare misure per garantire che il delegato rimanga non ritirato fino al termine della funzione di callback. Per informazioni dettagliate sulla prevenzione della garbage collection, vedere Interoperare il marshaling con Platform Invoke.

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

+1

Grazie. Sono nel mezzo di qualcosa. Studierò il link che menzioni quando ho una possibilità. Molte grazie per il vostro aiuto. –