web-dev-qa-db-ja.com

Windowsメッセージでのフックのセットアップ

track change eventを監視する必要があることをユーザーに現在の再生トラックの名前とアーティストに通知するアプリケーションを作成しようとしています。

私はWinspectorを使用しましたが、spotifyでトラックの変更があるたびにWM_SETTEXTメッセージが送信されることがわかりました。

enter image description here

このため、他のアプリケーションから送信されたWM_SETTEXTメッセージを探すために、アプリケーションを介してHOOKを設定する必要があると思います。

今、私が直面している問題は、動作するサンプルコードを処理できないことです。私は setwindowshookex のドキュメントを読み、いくつかのグーグルを行いましたが、C#の背景とWindowsメッセージ/イベントの処理の背景がないため、本当に迷っています。

それで、もしあなたが私に別のアプリケーションでsetting up hookを取り巻く小さな実用的なコードを私に提供できるか、またはこれを達成する方法についてのいくつかの素晴らしい記事に私を導くことができるなら、.

21
RanRag

これは別のアプローチです:SetWindowsHook APIをスキップして、代わりに WinEvents を使用します SetWinEventHook 代わりに。これらはどちらも特定のイベントで呼び出されるコールバック関数を伴うという点でWindowsフックに似ていますが、WinEventはC#からはるかに使いやすくなっています。WinEventは「アウトコンテキスト」で配信されることを指定できます。独自のプロセスに戻るため、個別のDLLは必要ありません。 (ただし、コードはSetWinEventHookを呼び出したのと同じスレッドでメッセージループを実行する必要があります。)

WinEventがサポートするイベントの種類の1つが「名前の変更」イベントであり、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()); 
    }
}
50
BrendanMcK

SetWindowHookExの使用方法については、 SO質問214022 を参照してください。 C#での作業コードについては、 SO質問181138 を参照してください。

一般に、C#からWinAPI関数にアクセスする場合は、Platform Invoke Call(shortを実行する必要があります。 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 お役に立てば幸いです。

2
Treb

次のようなメインフォームでWndProcをオーバーライドしようとすることができます。

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

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