這裏有一個不同的方法:跳過SetWindowsHook API,而是使用WinEvents,它使用SetWinEventHook代替。這些有點類似於windows鉤子,因爲它們都涉及在特定事件中調用的回調函數,但是WinEvents在C#中使用起來要容易得多:您可以指定WinEvent是「out context」,這意味着事件已發佈回到你自己的過程,所以你不需要單獨的DLL。 (然而,你的代碼確實需要在調用SetWinEventHook的同一線程上運行消息循環。)
事實證明,WinEvent支持的事件類型之一是「名稱更改」事件,該事件被自動觸發每當HWND的標題文本改變時,都由USER32執行,這看起來就是你正在尋找的東西。 (WinEvents也可用於跟蹤焦點更改和各種類型的狀態更改;有關更多信息,請參閱)。當內部UI發生更改時,它也會被其他控件觸發 - 例如,當列表項的文本發生更改時,通過列表框,我們必須做一些過濾。
下面是一些示例代碼,可以在桌面上的任何HWND上打印標題更改 - 例如,您會看到它會在任務欄上的時鐘文本發生更改時打印出通知。你需要修改這段代碼來過濾你在Spotify中跟蹤的HWND。此外,這段代碼監聽所有進程/線程上的名稱更改;您應該使用GetWindowThreadProcessId從目標HWND獲取線程ID,並只偵聽來自該線程的事件。
還要注意這是一種脆弱的方法;如果Spotify改變其顯示文本的方式或更改其格式,則需要修改代碼以跟上其更改。
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:謝謝,這也會有所幫助。 – RanRag 2012-03-12 11:09:09
你想要使用的鉤子類型需要一個可以注入其他進程的DLL。你不能用C#語言編寫這樣一個DLL,該進程將不會加載和初始化CLR。一個非託管語言是必需的,C,C++或Delphi是典型的選擇。 – 2012-03-12 13:42:54
@HansPassant:那麼'zabulus'建議不起作用。 – RanRag 2012-03-12 13:45:21