Подтвердить что ты не робот

Настройка Hook на сообщениях Windows

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

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

enter image description here

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

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

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

4b9b3361

Ответ 1

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

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

Вот пример кода, который печатает изменения названия на любом HWND на рабочем столе - вы увидите, что оно распечатывает уведомление, поскольку текст в часах на панели задач изменяется, например. Вы захотите изменить этот код, чтобы фильтровать только HWND, который вы отслеживаете в Spotify. Кроме того, этот код прослушивает имена изменений для всех процессов/потоков; вы должны получить threadID из целевого 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

За информацией о том, как использовать SetWindowHookEx, см. SO вопрос 214022. Для рабочего кода в С# см. SO вопрос 1811383.

В общем случае, если вы хотите получить доступ к функциям WinAPI из С#, вам необходимо выполнить вызов 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 Надеюсь, что это поможет.

Ответ 3

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

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

     if (m.Msg == WM_SETTEXT)
     {
           // Call to your logic here
     }
}