Impostazione di Hook sui messaggi di Windows

Sto cercando di creare un’applicazione che notifichi all’utente il nome e l’artista del brano in riproduzione per quello che ho bisogno di monitorare l’ track change event del track change event .

Ho usato Winspector e ho scoperto che ogni volta che c’è un cambio di traccia in WM_SETTEXT , viene inviato il messaggio WM_SETTEXT .

inserisci la descrizione dell'immagine qui

Per questo credo di dover creare un HOOK attraverso la mia applicazione per cercare il messaggio WM_SETTEXT inviato dall’altra applicazione.

Ora, il problema che sto affrontando è che non sono in grado di ottenere alcun codice di esempio funzionante con cui lavorare. Ho letto la documentazione di setwindowshookex e ho anche fatto qualche ricerca su google, ma sono davvero perso perché non ho background di C # e gestione di messaggi / eventi di Windows.

Quindi, se voi ragazzi potete fornirmi un piccolo codice di lavoro per capire come setting up hook su un’altra applicazione o se potete indirizzarmi ad un bell’articolo su come ottenerlo.

Ecco un approccio diverso: salta l’API SetWindowsHook e usa invece WinEvents , che usa SetWinEventHook . Questi sono in qualche modo simili agli hook di Windows, in quanto entrambi implicano una funzione di callback chiamata a eventi specifici, ma WinEvents è molto più facile da usare da C #: è ansible specificare che WinEvents viene consegnato “out context”, ovvero gli eventi vengono pubblicati torna al tuo processo, quindi non hai bisogno di una DLL separata. (Tuttavia, il codice deve eseguire un loop di messaggi sullo stesso thread che ha chiamato SetWinEventHook).

Si scopre che uno dei tipi di eventi supportati da WinEvent è un evento di ‘cambio di nome’, che viene triggersto automaticamente da USER32 ogni volta che cambia il testo del titolo di un HWND, che sembra quello che stai cercando. (WinEvents può anche essere utilizzato per tenere traccia delle modifiche allo stato attivo e vari tipi di modifiche dello stato, vedere MSDN per ulteriori informazioni.) Viene triggersto anche da altri controlli quando l’interfaccia utente interna cambia, ad esempio da una casella di riepilogo quando il testo di un elemento di elenco cambia, quindi dobbiamo fare un po ‘di filtraggio.

Ecco un esempio di codice che stampa le modifiche del titolo su qualsiasi HWND sul desktop – vedrai che stampa una notifica mentre il testo sull’orologio sulla barra delle applicazioni cambia, per esempio. Dovrai modificare questo codice per filtrare solo l’HWND che stai monitorando in Spotify. Inoltre, questo codice ascolta le modifiche al nome su tutti i processi / thread; dovresti ottenere il threadID dall’HWND di destinazione usando GetWindowThreadProcessId e solo ascoltare gli eventi da quel thread.

Si noti inoltre che si tratta di un approccio fragile; se Spotify cambia il modo in cui visualizza il testo o ne modifica il formato, è necessario modificare il codice per tenere il passo con le sue modifiche.

 using System; using System.Windows; using System.Windows.Forms; using System.Runtime.InteropServices; class NameChangeTracker { delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); [DllImport("user32.dll")] static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); [DllImport("user32.dll")] static extern bool UnhookWinEvent(IntPtr hWinEventHook); const uint EVENT_OBJECT_NAMECHANGE = 0x800C; const uint WINEVENT_OUTOFCONTEXT = 0; // Need to ensure delegate is not collected while we're using it, // storing it in a class field is simplest way to do this. static WinEventDelegate procDelegate = new WinEventDelegate(WinEventProc); public static void Main() { // Listen for name change changes across all processes/threads on current desktop... IntPtr hhook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE, IntPtr.Zero, procDelegate, 0, 0, WINEVENT_OUTOFCONTEXT); // MessageBox provides the necessary mesage loop that SetWinEventHook requires. // In real-world code, use a regular message loop (GetMessage/TranslateMessage/ // DispatchMessage etc or equivalent.) MessageBox.Show("Tracking name changes on HWNDs, close message box to exit."); UnhookWinEvent(hhook); } static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { // filter out non-HWND namechanges... (eg. items within a listbox) if(idObject != 0 || idChild != 0) { return; } Console.WriteLine("Text of hwnd changed {0:x8}", hwnd.ToInt32()); } } 

Puoi provare a sovrascrivere WndProc nel tuo modulo principale, qualcosa del genere:

 protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_SETTEXT) { // Call to your logic here } } 

Per consigli su come usare SetWindowHookEx, vedi domanda SO 214022 . Per il codice di lavoro in C # vedi domanda SO 1811383 .

In generale, se si desidera accedere alle funzioni WinAPI da C #, è necessario eseguire una chiamata di richiamo della piattaforma (breve PInvoke ). pinvoke.net è una buona risorsa sulle firme di cui la tua fonte ha bisogno per farlo, ma che è già stata trattata nella domanda 1811383.

Poiché non ho mai capito l’intera coda di messaggistica di Windows, non so se il metodo proposto da zabulus funzionerà quando il messaggio proviene da un processo diverso. Ma ho trovato qualche codice di esempio qui: http://en.serialcoder.net/Winforms/527/533/Interoperability%20Win32/How%20can%20I%20use%20%20Hooks%20%20in%20.NET.aspx Hope questo aiuta