在Windows消息中设置Hook

我正在尝试制作一个应用程序,通知当前播放曲目的名称和艺术家,以便我需要监视track change event

我用了Winspector ,发现每当有一个跟踪发生改变时,发送WM_SETTEXT消息。

在这里输入图像说明

为此,我相信我必须通过我的应用程序来设置HOOK来查找其他应用程序发送的WM_SETTEXT消息。

现在,我面临的问题是我无法得到任何工作示例代码来处理。 我读了setwindowshookex的文档,也做了一些谷歌search,但我真的失去了,因为我没有C#的背景和处理Windows消息/事件。

所以,如果你们可以给我一个小的工作代码来包装我的头在另一个应用程序setting up hook ,或者如果你可以指示我一些很好的文章,如何实现这一目标。

这里有一个不同的方法:跳过SetWindowsHook API,而是使用WinEvents ,而使用SetWinEventHook 。 这些有点类似于windows的钩子,因为它们都涉及一个在特定事件中被调用的回调函数,但是WinEvents在C#中使用起来要容易得多:你可以指定WinEvents是“out context”,这意味着事件被发布回到你自己的进程,所以你不需要单独的DLL。 (但是,您的代码确实需要在调用SetWinEventHook的同一个线程上运行消息循环。

事实证明,WinEvent支持的事件类型之一是“名称更改”事件,当HWND的标题文本发生更改时,USER32会自动触发此事件,这似乎是您正在寻找的事件。 (WinEvents也可用于跟踪焦点更改和各种类型的状态更改;有关详细信息, 请参阅MSDN 。)当内部UI发生更改时,也可以使用其他控件触发它们 – 例如,当列表项的文本发生更改时,我们必须做一些过滤。

以下是一些示例代码,可以在桌面上的任意HWND上打印标题更改 – 例如,当任务栏中的时钟文本发生更改时,您会看到它会打印出通知。 你需要修改这段代码来过滤你在Spotify中跟踪的HWND。 而且,这个代码监听所有进程/线程上的名称更改; 你应该使用GetWindowThreadProcessId从目标HWND获得threadID,并且只能监听来自该线程的事件。

还要注意,这是一个脆弱的方法。 如果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() { // listn 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()); } } 

你可以尝试重写你的主窗体中的WndProc,如下所示:

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

有关如何使用SetWindowHookEx的建议,请参阅SO问题214022 。 在C#中使用代码见SO问题1811383 。

一般来说,如果你想从C#访问WinAPI函数,你需要做一个平台调用调用 (简称PInvoke )。 pinvoke.net是一个很好的资源,你的源系统需要签名才能做到这一点,但是这已经在问题1811383中得到了解决。

由于我从来没有理解过整个Windows消息队列,所以我不知道zabulus提出的方法是否会在消息来自不同的进程时起作用。 但我在这里找到了一些示例代码: http : //en.serialcoder.net/Winforms/527/533/Interoperability%20Win32/How%20can%20I%20use%20%20Hooks%20%20in%20.NET.aspx希望这有帮助。