Настройка перехвата сообщений Windows - PullRequest
21 голосов
/ 12 марта 2012

Я пытаюсь создать приложение, которое будет уведомлять текущий игровой трек имя и имя исполнителя для этого мне нужно контролировать track change event.

Я использовал Winspector и обнаружил, что при изменении дорожки spotify WM_SETTEXT сообщение отправлено.

enter image description here

Для этого я считаю, что мне нужно настроить HOOK через мое приложение для поиска WM_SETTEXT сообщения, отправленного другим приложением.

Теперь проблема, с которой я сталкиваюсь, заключается в том, что я не могу заставить работать любой пример кода. Я прочитал документацию setwindowshookex , а также немного погуглил, но на самом деле растерялся, поскольку у меня нет опыта работы с C # и обработки сообщений / событий Windows.

Итак, если вы, ребята, можете предоставить мне небольшой рабочий код, чтобы обернуть мою голову вокруг setting up hook в другом приложении, или если вы можете направить меня к какой-нибудь хорошей статье о том, как этого добиться.

Ответы [ 3 ]

49 голосов
/ 13 марта 2012

Вот другой подход: пропустите API SetWindowsHook и вместо этого используйте WinEvents , в котором вместо этого используется SetWinEventHook . Они в некоторой степени похожи на хуки окон, в том и том, что оба включают функцию обратного вызова, которая вызывается при определенных событиях, но WinEvents намного проще использовать из C #: вы можете указать, что WinEvents доставляются «вне контекста», то есть события публикуются вернуться к вашему собственному процессу, поэтому вам не нужна отдельная DLL. (Однако ваш код должен запускать цикл сообщений в том же потоке, который вызвал SetWinEventHook.)

Оказывается, что одним из типов событий, поддерживаемых WinEvent, является событие «изменение имени», которое автоматически запускается пользователем USER32 при изменении текста заголовка HWND, что, по-видимому, является тем, что вы ищете. (WinEvents также можно использовать для отслеживания изменений фокуса и различных типов состояний; см. MSDN для получения дополнительной информации.) Он также запускается другими элементами управления при изменении их внутреннего пользовательского интерфейса - например, списком, когда текст элемент списка изменяется, поэтому мы должны выполнить некоторую фильтрацию.

Вот пример кода, который распечатывает изменения заголовков на любом HWND на рабочем столе - вы увидите, как он распечатывает уведомление, например, при изменении текста на часах на панели задач. Вы захотите изменить этот код для фильтрации только того HWND, который вы отслеживаете в Spotify. Кроме того, этот код прослушивает изменения имен во всех процессах / потоках; вы должны получить идентификатор потока из целевого HWND с помощью GetWindowThreadProcessId и прослушивать только события из этого потока.

Обратите внимание, что это хрупкий подход; если 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()); 
    }
}
2 голосов
/ 12 марта 2012

Вы можете попробовать переопределить WndProc в своей основной форме, примерно так:

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

     if (m.Msg == WM_SETTEXT)
     {
           // Call to your logic here
     }
}
2 голосов
/ 12 марта 2012

Чтобы узнать, как использовать SetWindowHookEx, см. SO вопрос 214022 . Для рабочего кода в C # см. SO вопрос 1811383 .

Как правило, если вы хотите получить доступ к функциям WinAPI из C #, вам необходимо выполнить Платформа Invoke Call (короткий 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 Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...