1

I'm currently coding an application that needs to catch every action done by the user on his window handle.

I needed the following event to be raised:

  • Window resized, maximized, minimized or moved
  • User changed the active window
  • User changed the window handle's keyboard focused

To do that, I tried a lot of solutions, but in vain. First, I used a Timer that poll every 100ms the foreground window (using GetForegroundWindow() ) and the keyboard focused handle using AttachThreadInput() coupled with GetFocus() function.

But this solution was not very convenient and I prefered a cleaner solution using UIAutomation provided by .NET Framework. But I realized it used a lot of CPU and was too slow for my purpose, and the event was called sometimes 3 or 4 times when I switched to another window handle.

Concerning window resize, maximize, etc. I did a timer as well (but not very truthful), and tried to get working some Hooking technics like CBT hook and Shell hook. Unfortunately, I found that this kind of hook (global hook) isn't supported by C#.

I'm looking for a stable and reliable code for this part of my program. Thanks in advance.

Louisbob
  • 860
  • 3
  • 9
  • 22
  • [WND_PROC](http://msdn.microsoft.com/en-us/library/system.windows.forms.control.wndproc.aspx) - I don't know what it is your using, but this is what windows uses to handle pretty much every message – Sayse Jul 14 '13 at 21:33
  • No, I would like to avoid Winform for the moment. I precise that I want to monitor other window's handle, not the one of my application. Concerning WND_PROC : I guess it is only for the application itself, isn't it? I looked for passing message using AttachThreadInput() to my app, but it needs a Form object – Louisbob Jul 14 '13 at 21:44
  • Yea it is for the application I believe..(window) – Sayse Jul 14 '13 at 22:12
  • So it is not what I'm looking for then... – Louisbob Jul 15 '13 at 08:54
  • If you want "stable and reliable", don't use polling and AttachThreadInput. Use a regular Win32 DLL containing global hooks, and use some basic IPC (WM_COPYDATA) to forward hook events to your C# program. – manuell Jul 15 '13 at 13:54

1 Answers1

1

BrendanMcK wonderfully respond to my question on this post:

Setting up Hook on Windows messages

I copied his answer just below. It is more convenient than Timer as I suggered, and it is less CPU-eater than UIAutomation. Thanks everyone!

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()); 
    }
}
Community
  • 1
  • 1
Louisbob
  • 860
  • 3
  • 9
  • 22