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());
}
}
@squelos: grazie che aiuterà anche. – RanRag
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. –
@HansPassant: Quindi cosa suggerito da 'zabulus' non funzionerà. – RanRag