web-dev-qa-db-fra.com

Configuration des messages Hook on Windows

J'essaie de créer une application qui notifiera le nom et l'artiste de la piste en cours de lecture à l'utilisateur, car je dois surveiller le track change event.

J'ai utilisé Winspector et j'ai découvert que chaque fois qu'il y avait un changement de piste dans spotify WM_SETTEXT le message est envoyé.

enter image description here

Pour cela, je crois que je dois configurer un HOOK via mon application pour rechercher WM_SETTEXT message envoyé par l'autre application.

Maintenant, le problème auquel je suis confronté est que je ne suis pas en mesure d'obtenir un exemple de code de travail avec lequel travailler. J'ai lu la documentation de setwindowshookex et j'ai également fait quelques recherches sur Google, mais je suis vraiment perdu car je n'ai aucune expérience en C # et ne gère pas les messages/événements Windows.

Donc, si vous pouvez me fournir un petit code de travail pour faire le tour de ma tête setting up hook sur une autre application ou si vous pouvez me diriger vers un article sympa sur la façon d'y parvenir.

21
RanRag

Voici une approche différente: ignorez l'API SetWindowsHook et utilisez plutôt WinEvents , qui utilise SetWinEventHook à la place. Ceux-ci sont quelque peu similaires aux crochets Windows, dans la mesure où ils impliquent tous deux une fonction de rappel qui est appelée lors d'événements spécifiques, mais WinEvents est beaucoup plus facile à utiliser à partir de C #: vous pouvez spécifier que WinEvents sont livrés "hors contexte", ce qui signifie que les événements sont publiés revenir à votre propre processus, vous n'avez donc pas besoin d'une DLL distincte. (Cependant, votre code doit exécuter une boucle de message sur le même thread qui a appelé SetWinEventHook.)

Il s'avère que l'un des types d'événements pris en charge par WinEvent est un événement de `` changement de nom '', qui est automatiquement déclenché par USER32 chaque fois que le texte du titre d'un HWND change, ce qui semble être ce que vous recherchez. (WinEvents peut également être utilisé pour suivre les changements de focus et divers types de changements d'état; voir MSDN pour plus d'informations.) Il est également déclenché par d'autres contrôles lorsque leur interface utilisateur interne change - par exemple par une zone de liste lorsque le texte d'un élément de liste change, nous devons donc filtrer.

Voici un exemple de code qui imprime les modifications de titre sur n'importe quel HWND sur le bureau - vous le verrez imprimer une notification lorsque le texte de l'horloge de la barre des tâches change, par exemple. Vous voudrez modifier ce code pour filtrer uniquement le HWND que vous suivez dans Spotify. En outre, ce code écoute les changements de nom sur tous les processus/threads; vous devez obtenir le threadID du HWND cible en utilisant GetWindowThreadProcessId et écouter uniquement les événements de ce thread.

Notez également qu'il s'agit d'une approche fragile; si Spotify change la façon dont il affiche le texte, ou change le format de celui-ci, vous devrez modifier votre code pour suivre ses changements.

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

Pour des conseils sur l'utilisation de SetWindowHookEx, voir SO question 214022 . Pour le code de travail en C #, voir SO question 181138 .

En général, si vous souhaitez accéder aux fonctions WinAPI à partir de C #, vous devez effectuer un appel d'appel de plate-forme (court PInvoke ). pinvoke.net est une bonne ressource sur les signatures dont votre source a besoin pour le faire, mais cela a déjà été couvert dans la question 1811383.

Comme je n'ai jamais compris toute la file d'attente de messagerie Windows, je ne sais pas si la méthode proposée par zabulus fonctionnera lorsque le message provient d'un processus différent. Mais j'ai trouvé un exemple de code ici: http://en.serialcoder.net/Winforms/527/533/Interoperability%20Win32/How%20can%20I%20use%20%20Hooks%20%20in%20. NET.aspx J'espère que cela vous aidera.

2
Treb

Vous pouvez essayer de remplacer WndProc dans votre formulaire principal, quelque chose comme ceci:

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

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