c # - Opsætning af Hook på Windows-meddelelser

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg forsøger at lave en ansøgning, der vil give besked om det nuværende spillerespor
navn og kunstner til brugeren for at jeg skal overvåge track change event.


Jeg brugte Winspector og fandt ud af, at når der er en sporændring i
spotify WM\_SETTEXT beskeden er sendt.


Indtast billedbeskrivelse her


For dette mener jeg, at jeg skal oprette en HOOK gennem min ansøgning for at søge efter WM\_SETTEXT besked sendt af den anden ansøgning.


Nu er det problem, jeg står over for, at jeg ikke kan få nogen arbejdsprøvekode til at arbejde med. Jeg læste dokumentationen af ​​setwindowshookex og gjorde også nogle googling, men jeg er virkelig tabt, da jeg ikke har nogen baggrund for C # og håndtering af Windows-meddelelser/begivenheder. [9]


Så, hvis du kan give mig en lille arbejdskode til at pakke mit hoved omkring setting up hook på en anden applikation, eller hvis du kan henvise mig til en god artikel om, hvordan du opnår dette.

Bedste reference


Her er en anden tilgang: spring over SetWindowsHook API, og brug i stedet WinEvents , som bruger SetWinEventHook i stedet for. Disse ligner noget Windows-kroge, idet begge involverer en tilbagekoblingsfunktion, der kaldes på bestemte begivenheder, men WinEvents er langt lettere at bruge fra C #: Du kan specificere, at WinEvents leveres 'out context', hvilket betyder Hændelser sendes tilbage til din egen proces, så du behøver ikke en separat DLL. (Din kode behøver at køre en meddelelsessløjfe på samme tråd, der hedder SetWinEventHook, dog.)


Det viser sig, at en af ​​de begivenheder, WinEvent støtter, er en 'navneændring' -hændelse, som automatisk afskediges af USER32, når titlen på en HWND ændres, hvilket synes er, hvad du leder efter. (WinEvents kan også bruges til at spore fokusændringer og forskellige typer af tilstandsændringer; se MSDN for yderligere information.) Den er også fyret af andre kontroller, når deres interne brugergrænseflade ændrer sig - f.eks. ved hjælp af en listekasse, når teksten i en liste genstand ændres, så vi skal gøre noget ved at filtrere.


Her er nogle eksempler kode, der udskriver titler ændres på enhver HWND på skrivebordet - du vil se det udskrive en meddelelse, da teksten i uret på proceslinjen ændrer sig. Du vil gerne ændre denne kode for at filtrere efter blot HWND du sporer i Spotify. Også denne kode lytter til navneændringer på alle processer/tråde; du skal få threadID fra målet HWND ved hjælp af GetWindowThreadProcessId og kun lytte til begivenheder fra den tråd.


Bemærk også, at dette er noget af en skrøbelig tilgang; Hvis Spotify ændrer, hvordan det viser teksten, eller ændrer formatet på det, skal du '' ændre din kode for at holde op med ændringerne.


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()); 
    }
}

Andre referencer 1


Du kan forsøge at tilsidesætte WndProc i din hovedformular, noget som dette:


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

     if (m.Msg == WM\_SETTEXT)
     {
           // Call to your logic here
     }
}

Andre referencer 2


For råd om hvordan man bruger SetWindowHookEx, se SO spørgsmål 214022. For arbejdskode i C # se SO spørgsmål 1811383.


Hvis du vil have adgang til WinAPI-funktioner fra C #, skal du generelt lave et Platform Invoke Call (kort PInvoke ). pinvoke.net er en god ressource på underskrifterne, som din kildecoede har brug for for at kunne gøre det, men det er allerede omfattet 1811383. [12]


Da jeg aldrig forstod hele Windows messaging køen, ved jeg ikke, om metoden foreslået af zabulus vil fungere, når meddelelsen stammer fra en anden proces. Men jeg fandt nogle eksempler her:
http://en.serialcoder.net/Winforms/527/533/Interoperability\%20Win32/How\%20can\%20I\%20use\%20\%20Hooks\%20\%20in\%20.NET.aspx Håber det hjælper. [13]