在Windows消息上设置Hook

Ran*_*Rag 21 c# windows winapi

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

我使用Winspector并发现每当Spotify WM_SETTEXT消息中的轨道发生变化时都会发送.

在此输入图像描述

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

现在,我遇到的问题是我无法获得任何可用的示例代码.我阅读了setwindowshookex的文档,也做了一些谷歌搜索,但我真的迷失了,因为我没有C#的背景和处理Windows消息/事件.

所以,如果你们可以为我提供一个小的工作代码来包装我的setting up hook另一个应用程序,或者你可以指导我一些关于如何实现这一目标的好文章.

Bre*_*McK 50

这是一种不同的方法:跳过SetWindowsHook API,而是使用WinEvents,而不是使用SetWinEventHook.这些有点类似于windows钩子,因为它们都涉及在特定事件中调用的回调函数,但是WinEvents更容易在C#中使用:您可以特定地将WinEvent传递给"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()
    {
        // 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()); 
    }
}
Run Code Online (Sandbox Code Playgroud)