2010-05-21 6 views
16

Je suis en train d'implémenter une petite application (observateur) qui doit "se lier" au bas d'une autre fenêtre (observée). Ce dernier n'est pas une fenêtre à l'intérieur de l'application.Écoute d'une autre fenêtre redimensionner les événements en C#

À ce moment, j'ai résolu en obtenant le hWnd de la fenêtre et en interrogeant périodiquement dans un fil l'emplacement de la fenêtre observée, en déplaçant la fenêtre d'observation en conséquence.

Cependant, il s'agit d'une solution très inélégante. Ce que je voudrais faire est d'écouter l'événement de redimensionnement de la fenêtre observée afin que l'observateur ne réagisse que lorsque c'est nécessaire.

Je suppose que je devrais utiliser un crochet, et j'ai trouvé beaucoup de façons de le faire, mais mon manque de connaissance du C WinAPI me bloque dans la compréhension de quel crochet j'ai besoin de créer et comment (pinvoke/parameters/etc).

Je suis sûr que cela est tout à fait banal, et certains d'entre vous familiariser avec C/C++ et WinAPI aura la réponse prête à la main;)

Merci

+0

Seule mention de C# est dans le titre et les balises. Utilisez-vous C# ou C? – Yuvi

+0

@Yuvi, Ma conjecture est qu'il veut écouter un "événement" en C# qui est tiré dans une autre application pas dans son "domaine". –

+0

@Yuvi Je mentionne C# car mon programme de base est écrit en C#. Pas vraiment à l'aise d'utiliser C++ pour les applications d'affaires;) –

Répondre

7

Extension de la réponse de Chris Taylor: Au lieu de faire l'interopérabilité native, vous pouvez utiliser ManagedWinApi, qui contient une classe Hook.

EDIT: Pour utiliser ManagedWinApi. Quelque part dans votre code:

Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false); 
MyHook.Callback += MyHookCallback; 
MyHook StartHook(); 

Pour le rappel, la référence CallWndProc et CWPSTRUCT:

private static int MyHookCallback(int code, IntPtr wParam, IntPtr lParam, ref bool callNext) 
{ 
    if (code >= 0) 
    { 
     // You will need to define the struct 
     var message = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT)); 
     // Do something with the data 
    } 
    return 0; // Return value is ignored unless you set callNext to false 
} 
+1

En effet, je suis tombé sur ManagedWinAPi, mais les docs ne sont pas aussi explicite que j'aimerais aussi, vu mon incompétence dans le WinAPI. Je regarde dans les exemples pour l'obtenir. Pour autant que je l'ai compris, je n'ai pas vraiment besoin d'un système global car un accrochage direct à la fenêtre est suffisant. J'ai juste besoin d'être notifié au sujet des événements de redimensionnement/déplacement. Idéalement, cela devrait se produire dans un événement C#. Ce que je dois comprendre est comment connecter les points, donc toute suggestion est plus que bienvenue, en particulier de personnes familières avec le ManagedWinApi. –

+0

Merci! C'est très clair. Je vais le tester dès que possible. Pour référence, CWPSTRUCT est ici http://www.pinvoke.net/default.aspx/Structures/CWPSTRUCT.html –

+0

le code que vous avez suggéré ne se connecte qu'aux fenêtres du projet C#. Je dois accrocher à une fenêtre externe. Googling autour j'ai compris que cela ne semble pas possible en C#, sauf si Magadewinapi a un moyen de le faire. J'ai essayé de placer le param global dans le constructeur de Hook à vrai, mais j'ai frappé ma session. Toute suggestion? –

4

Un crochet WH_CALLWNDPROC suffirait probablement, Cela vous permettra de surveiller tous les messages destinés à la fenêtre d'intérêt. Demandez-vous comment créer un hook global en utilisant C# ou êtes-vous heureux de créer le hook en C++, puis d'interopérer avec celui de .NET? La deuxième option est la route que j'irais.

Au fond du haut de ma tête, ce que je ferais est la suivante

1- Créer hook global en C, et les fonctions d'exportation à InstallHook et UninstallHook, qui peut être appelé à partir de votre C# application en utilisant Interop. InstallHook prend un hwnd de la fenêtre dans votre application C#.

2- Avoir la fonction de crochet installé poster un message personnalisé à la fenêtre C# fournie dans l'appel à InstallHook quand jamais il y a un message qui vous intéresse comme WM_SIZE dans votre cas.

3- Dans l'application C#, votre fenêtre qui reçoit les messages postés à partir du crochet remplacera WndProc pour gérer le message personnalisé.

Ceci est un aperçu d'une approche.

4

Je vous suggère d'utiliser WinEvents:

public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 

    [DllImport("user32.dll")] 
    public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

Voir aussi: event hooks

+0

Je suis sûr que cela fonctionne. Je viens d'écrire un programme prenant les avantages de SetWinEventHook. –

1

I couru dans la même chose dans un code que je travaillais avec et découvert que je ne pouvais pas injecter un .DLL géré dans le processus.

Ne voulant pas écrire un C++ DLL non géré qui a utilisé SetWindowsHook, je suis allé avec une combinaison de SetWinEventHook, qui signale quand une fenêtre commence et se termine un événement de déplacement et un Timer au sondage la fenêtre alors qu'il se déplace pour lui donner l'apparence de suivre la fenêtre pendant qu'elle bouge.

Voici une version (très simplifiée) d'une classe qui peut le faire (il suffit de remplacer les TODO par du code pour déplacer la fenêtre ou déclencher un événement).

public class DockingHelper 
{ 
    private readonly uint m_processId, m_threadId; 

    private readonly IntPtr m_target; 

    // Needed to prevent the GC from sweeping up our callback 
    private readonly WinEventDelegate m_winEventDelegate; 
    private IntPtr m_hook; 

    private Timer m_timer; 

    public DockingHelper(string windowName, string className) 
    { 
     if (windowName == null && className == null) throw new ArgumentException("Either windowName or className must have a value"); 

     m_target = FindWindow(className, windowName); 
     ThrowOnWin32Error("Failed to get target window"); 

     m_threadId = GetWindowThreadProcessId(m_target, out m_processId); 
     ThrowOnWin32Error("Failed to get process id"); 

     m_winEventDelegate = WhenWindowMoveStartsOrEnds; 
    } 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern bool UnhookWinEvent(IntPtr hWinEventHook); 

    [DllImport("user32.dll")] 
    private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); 

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 

    private void ThrowOnWin32Error(string message) 
    { 
     int err = Marshal.GetLastWin32Error(); 
     if (err != 0) 
      throw new Win32Exception(err, message); 
    } 

    private RECT GetWindowLocation() 
    { 
     RECT loc; 
     GetWindowRect(m_target, out loc); 
     if (Marshal.GetLastWin32Error() != 0) 
     { 
      // Do something useful with this to handle if the target window closes, etc. 
     } 
     return loc; 
    } 

    public void Subscribe() 
    { 
     // 10 = window move start, 11 = window move end, 0 = fire out of context 
     m_hook = SetWinEventHook(10, 11, m_target, m_winEventDelegate, m_processId, m_threadId, 0); 
    } 

    private void PollWindowLocation(object state) 
    { 
     var location = GetWindowLocation(); 
     // TODO: Reposition your window with the values from location (or fire an event with it attached) 
    } 

    public void Unsubscribe() 
    { 
     UnhookWinEvent(m_hook); 
    } 

    private void WhenWindowMoveStartsOrEnds(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) 
    { 
     if (hwnd != m_target) // We only want events from our target window, not other windows owned by the thread. 
      return; 

     if (eventType == 10) // Starts 
     { 
      m_timer = new Timer(PollWindowLocation, null, 10, Timeout.Infinite); 
      // This is always the original position of the window, so we don't need to do anything, yet. 
     } 
     else if (eventType == 11) 
     { 
      m_timer.Dispose(); 
      m_timer = null; 
      var location = GetWindowLocation(); 
      // TODO: Reposition your window with the values from location (or fire an event with it attached) 
     } 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct RECT 
    { 
     public int Left, Top, Right, Bottom; 
    } 

    private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); 
} 
Questions connexes