2012-03-12 13 views
19

Sto provando a creare un'applicazione che notificherà all'utente il nome e l'artista attuale della traccia in riproduzione per il quale devo monitorare lo track change event.Configurazione Hook su messaggi Windows

Ho utilizzato Winspector e ho scoperto che ogni volta che c'è un cambio di traccia in spotify WM_SETTEXT viene inviato il messaggio.

enter image description here

Per questo credo di avere per impostare una HOOK attraverso la mia applicazione per cercare WM_SETTEXT messaggio inviato dal l'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 sfondo di C# e gestione di messaggi/eventi di Windows.

Quindi, se voi ragazzi potete fornirmi un piccolo codice di lavoro per avvolgere la mia testa su setting up hook su un'altra applicazione o se potete indirizzarmi ad un bell'articolo su come ottenere questo.

+0

@squelos: grazie che aiuterà anche. – RanRag

+2

Il tipo di hook che si desidera utilizzare richiede una DLL che può essere iniettata nell'altro processo. Non è possibile scrivere una DLL di questo tipo nel linguaggio C#, il processo non avrà il CLR caricato e inizializzato. È richiesto un linguaggio non gestito, C, C++ o Delphi sono le scelte tipiche. –

+0

@HansPassant: Quindi cosa suggerito da 'zabulus' non funzionerà. – RanRag

risposta

42

Ecco un approccio diverso: salta l'API SetWindowsHook, e di utilizzare invece WinEvents, che utilizzano SetWinEventHook invece. 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#: è possibile specificare che WinEvents viene consegnato "out context", ovvero gli eventi vengono pubblicati torna al tuo processo, quindi non hai bisogno di una DLL separata. (Il tuo codice ha bisogno di eseguire un loop di messaggi sullo stesso thread che ha chiamato SetWinEventHook, tuttavia.)

Si scopre che uno dei tipi di eventi supportati da WinEvent è un evento di "cambio di nome", che viene attivato 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 di messa a fuoco e vari tipi di modifiche dello stato; per ulteriori informazioni.) Viene attivato anche da altri controlli quando la loro interfaccia utente interna cambia, ad esempio da una casella di riepilogo quando il testo di una voce di elenco cambia, quindi devo fare un po 'di filtraggio.

Ecco un esempio di codice che stampa le modifiche del titolo su qualsiasi HWND sul desktop: lo vedrai stampare 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 dal 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()); 
    } 
} 
+0

L'unica risposta finora che merita un upvote ... E sì, questa è un'opzione molto migliore di un hook globale comunque. –

+0

@Brendan: grazie proverò il tuo codice e ti risponderò. – RanRag

+1

O semplicemente utilizzare lo spazio dei nomi 'System.Windows.Automation'. –

2

Per consigli su come utilizzare SetWindowHookEx, vedere SO question 214022. Per il codice di lavoro in C#, vedere SO question 1811383.

In generale, se si vuole accedere alle funzioni WinAPI da C#, è necessario fare un Platform Invoke chiamate (breve PInvoke). pinvoke.net è una buona risorsa per le firme che la tua fonte Coede 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 un codice di esempio qui: http://en.serialcoder.net/Winforms/527/533/Interoperability%20Win32/How%20can%20I%20use%20%20Hooks%20%20in%20.NET.aspx Spero che questo aiuti.

+0

grazie li guarderò e tornerò da te. – RanRag

+0

Gli ** soli ** hook che è possibile installare da un'applicazione gestita (C# /. NET) sono un hook per tastiera di basso livello ('WH_LL_KEYBOARD') e un hook del mouse di basso livello (' WH_LL_MOUSE'). Poiché nessuno dei due farà ciò che @Noob è in cerca di qui, dovrà chiamare 'SetWindowsHookEx' da un'applicazione non gestita, scritta in un linguaggio come C o C++. –

3

Si può cercare di ignorare WndProc nel modulo principale, qualcosa di simile:

protected override void WndProc(ref Message m) 
{ 
    base.WndProc(ref m); 

    if (m.Msg == WM_SETTEXT) 
    { 
      // Call to your logic here 
    } 
} 
+0

Ma come collegherò questo ad un particolare processo. Intendo dove passo il parametro 'hwnd' che è specifico per la mia applicazione. – RanRag

+0

Avete le vostre fonti di applicazione? In caso affermativo, trova la classe del modulo passata al metodo Application.Run. E incolla il mio codice sul suo corpo – zabulus

+0

Questo è lo stato corrente del mio codice http://pastebin.com/sVKm2Y5a – RanRag

Problemi correlati