2009-06-08 17 views
5

Ho provato un sacco di cose ma non riesco a farlo funzionare in modo coerente tra la mia barra delle applicazioni e altri effetti soprannaturali sulla mia interfaccia utente desktop.Come aggiungere un "hook di Windows" di sistema in modo da essere avvisati della creazione o dell'attivazione di Windows?

Provato prima usando una libreria aperta http://mwinapi.sourceforge.net/. Anche se ha funzionato bene come un livello OO per enumerare finestre e cose. Non è stato possibile eseguire correttamente i ganci

La tappa successiva era Dino E.'s post on Windows Hooks in the .Net framework. Ho finito per scrivere il mio tipo mentre stavo capendo il testo e cercando di farlo funzionare.

La mia intenzione è quella di avere questa applicazione in esecuzione e di essere in grado di registrare tutte le finestre create mentre è in esecuzione. Chiamare tutti i bulbi oculari ...

Aggiornamento: snipped poiché a quanto pare si can't write global windows hooks in .Net/codice gestito (ad eccezione di alcuni bassi del mouse o la tastiera livello ganci)

Quindi sono passato a C++. Ancora tutte le chiamate WinAPI restituiscono gli handle validi ma non vedo la mia funzione filtro chiamata - non sembrano ricevere notifiche. Ancora non funziona ... Qualcuno può individuare l'errore.

void CWinHookFacade::Hook() 
{ 
    HMODULE hCurrentDll = LoadLibrary(_T("[Path to my hook dll]")); 
    m_HookHandle = SetWindowsHookEx(WH_CBT, 
     FilterFunctionForHook, 
     hCurrentDll, 
     0); 
    if (m_HookHandle == NULL) 
    { 
     throw new std::exception("Unable to hook"); 
    } 

} 
void CWinHookFacade::Unhook() 
{ 
    if (!UnhookWindowsHookEx(m_HookHandle)) 
    { 
     throw new std::exception("Unhook failed!"); 
    } 
    m_HookHandle = NULL; 
} 

LRESULT CWinHookFacade::FilterFunctionForHook(int code, WPARAM wParam, LPARAM lParam) 
{ 
    if (code >= 0) 
    { 
     switch(code) 
     { 
     case HCBT_CREATEWND: 
      wprintf(_T("Created Window")); 
      break; 
     case HCBT_ACTIVATE: 
      wprintf(_T("Activated Window")); 
      break; 
     case HCBT_DESTROYWND: 
      wprintf(_T("Destroy Window")); 
      break; 
     } 
    } 

    return CallNextHookEx(m_HookHandle, code, wParam, lParam); 
} 

exe ​​client chiama il Hook_DLL come questo

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    CWinHookFacade::Hook(); 
    getchar(); 
    CWinHookFacade::Unhook(); 
} 
+0

Per chiarire: SetWindowsHookEx restituisce un handle valido ogni volta. Inoltre, nel caso qualcuno volesse provare questo, il codice sopra riportato non ha altre dipendenze. Incolla in un file .cs in un progetto vuoto ed esegui. – Gishu

risposta

5

Penso che i problemi si hanno sono perché si sta cercando di implementare una funzione di hook in C#. Basato su pinvoke.net's documentation su SetWindowsHookEx(), si dice che non è possibile farlo: la procedura di hook deve trovarsi in una DLL non gestita. In caso contrario, la DLL verrà caricata in tutti i processi in esecuzione con un loop di messaggi, che a sua volta causerà il caricamento e l'avvio del CLR in ogni processo. Non solo ciò richiederebbe molto tempo, ma l'inserimento del CLR in tutti i processi probabilmente non è l'idea migliore. Inoltre, cosa succede se un processo ha già un CLR in esecuzione diverso da quello contro cui è stata costruita la tua DLL?

L'approccio migliore sarebbe spostare questo codice in una DLL C++ non gestita e utilizzare una sorta di comunicazione tra processi per inviare i dati intercettati dalla procedura di hook all'applicazione.

Aggiornamento

Prima (e questo non è probabilmente causando il problema), il motivo per cui stai chiamando LoadLibrary() per ottenere il HINSTANCE della DLL? Probabilmente sarebbe meglio chiamare GetModuleHandle() dato che la DLL è già stata caricata.

Per quanto riguarda il motivo per cui la procedura di hook non viene mai chiamata, come è stato verificato? Poiché si stanno agganciati tutti i thread della GUI nel sistema, ciò significa che la DLL deve essere caricata in tutti i processi della GUI. È probabile che non vedrai i risultati della chiamata allo wprintf() perché gli altri processi non dispongono di una finestra della console per mostrare l'output.

Per verificare che la DLL sia caricata correttamente, utilizzare un programma che elenca le DLL caricate da un processo (mi piace Process Explorer). Puoi usare Trova | Trova la voce di menu Handle o DLL per cercare il nome della tua DLL - dovrebbe apparire in tutti i processi con un loop di messaggi.

Dopo aver verificato che la DLL sia caricata, per vedere se il tuo hook è chiamato puoi collegare un debugger a un altro processo (come Blocco note) e quindi impostare un punto di interruzione nella funzione di hook. Dovrebbe sparire ogni volta che un messaggio viene inviato al gancio CBT. Se non si desidera utilizzare un debugger, è possibile modificare le chiamate su wprintf() su OutputDebugString() ed eseguire un'utilità come DebugView per monitorare i risultati.

Infine, poiché la funzione di hook viene chiamata nel contesto di un altro processo, la variabile m_HookHandle non sarà valida lì. È necessario archiviarlo in un shared data segment in modo che tutte le istanze caricate della DLL abbiano lo stesso valore.

+0

Sapevo della parte di iniezione - aveva il mio codice in una DLL. Ma non sapeva che è impossibile fare in C#. Ci proveremo domani in CPP e pubblicheremo come funziona ... Ho perso la lettura della sezione delle note sulla pagina di Pinvoke a mio rischio e pericolo. (Ho trovato che ManagedThreadID non funziona troppo) – Gishu

+0

Un'altra cosa che mi ha buttato fuori era che la classe Hook dalla http://mwinapi.sourceforge.net/ library ha un parametro ctor - bGlobal ed è completamente gestita. Strano ... – Gishu

+0

Passato a non gestito troppo - ancora non sembra funzionare. Aggiornamento della domanda originale ... – Gishu

3

Finalmente inchiodato questo. Scriverlo come blog post nel caso in cui qualcuno abbia bisogno di questo in futuro.