Comportamiento extraño de los ganchos de Windows

I was looking for a possibility to be notified in a .NET windows application when any window is activated in the OS (Windows XP 32-bit). On CodeProject I have found a solution by using global system hooks.

http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H .

Here is a short summary of this procedure:

In an unmanaged assembly (written in C++) a method is implemented which installs the WH_CBT gancho.

bool InitializeCbtHook(int threadID, HWND destination) 
{ 
    if (g_appInstance == NULL) 
    { 
       return false; 
    }  

    if (GetProp(GetDesktopWindow(), " HOOK_HWND_CBT") != NULL) 
    { 
        SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "HOOK_HWND_CBT"), 
            RegisterWindowMessage("HOOK_CBT_REPLACED"),  0, 0); 
    } 

    SetProp(GetDesktopWindow(), " HOOK_HWND_CBT", destination); 


    hookCbt = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtHookCallback,     g_appInstance, threadID); 

    return hookCbt != NULL; 

} 

In the callback method (filter function) depending on the hook type windows messages are sent to a destination window.

static LRESULT CALLBACK CbtHookCallback(int code, WPARAM wparam, LPARAM lparam) 
{ 
    if (code >= 0) 
    { 
        UINT msg = 0; 

        if (code == HCBT_ACTIVATE) 
            msg = RegisterWindowMessage("HOOK_HCBT_ACTIVATE"); 
        else if (code == HCBT_CREATEWND) 
            msg = RegisterWindowMessage("HOOK_HCBT_CREATEWND"); 
        else if (code == HCBT_DESTROYWND) 
            msg = RegisterWindowMessage("HOOK_HCBT_DESTROYWND"); 
        else if (code == HCBT_MINMAX) 
            msg = RegisterWindowMessage("HOOK_HCBT_MINMAX"); 
        else if (code == HCBT_MOVESIZE) 
            msg = RegisterWindowMessage("HOOK_HCBT_MOVESIZE"); 
        else if (code == HCBT_SETFOCUS) 
            msg = RegisterWindowMessage("HOOK_HCBT_SETFOCUS"); 
        else if (code == HCBT_SYSCOMMAND) 
            msg = RegisterWindowMessage("HOOK_HCBT_SYSCOMMAND"); 

        HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), HOOK_HWND_CBT"); 

        if (msg != 0) 
            SendNotifyMessage(dstWnd, msg, wparam, lparam); 
    } 

    return CallNextHookEx(hookCbt, code, wparam, lparam); 
} 

To use this assembly in a .NET Windows Application the following method has to be imported:

[DllImport("GlobalCbtHook.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern bool InitializeCbtHook (int threadID, IntPtr DestWindow);

[DllImport("GlobalCbtHook.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UninitializeCbtHook(int hookType);

Después de llamar InitializeCbtHook the messages received from GlobalCbtHook.dll can be processed in:

protected override void WndProc(ref Message msg) 

The messages have to be registered in both the assembly and the application by calling RegisterWindowMessage.

[DllImport("user32.dll")]
private static extern int RegisterWindowMessage(string lpString);

This implementation works fine. But in most cases when I activate Microsoft Office Outlook my .NET Application receives the activate-event after I minimize Outlook or activate an other window. At first I thought that my .NET wrapper is the cause of the problem. But after I used the sources from the above link I could recognized the same behaviour. My actually workaround is to use WH_SHELL hook. I know that one difference between WH_CBT y WH_SHELL hook is when using WH_CBT hook it is possible to interrupt the filter function chain by not calling the CallNextHookEx method. Could this play a role in my problem? Please provide help.

preguntado el 24 de agosto de 12 a las 20:08

¿Por qué no usar el System.Windows.UIAutomation namespace? That was written specifically for what you're trying to do! -

Thank you for your reply, but according to my information, you can use the System.Windows.UIAutomation namespace only in WPF-Applications. -

Strange, because I've used it from console applications! -

So I'll try it out and inform you of the results. Thanks! -

1 Respuestas

obviously the hooking does not work in cases of outlook - what about other microsoft products (word, power point ...)??

but, why hooking? this little class will work even if outlook is activated

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsMonitor
{
    public class ActiveWindowChangedEventArgs : EventArgs
    {
        public IntPtr CurrentActiveWindow { get; private set; }
        public IntPtr LastActiveWindow { get; private set; }

        public ActiveWindowChangedEventArgs(IntPtr lastActiveWindow, IntPtr currentActiveWindow)
        {
            this.LastActiveWindow = lastActiveWindow;
            this.CurrentActiveWindow = currentActiveWindow;
        }
    }

    public delegate void ActiveWindowChangedEventHandler(object sender, ActiveWindowChangedEventArgs e);

    public class ActiveWindowMonitor
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        private Timer monitorTimer;

        public IntPtr ActiveWindow { get; private set; }
        public event ActiveWindowChangedEventHandler ActiveWindowChanged;

        public ActiveWindowMonitor()
        {
            this.monitorTimer = new Timer();
            this.monitorTimer.Tick += new EventHandler(monitorTimer_Tick);
            this.monitorTimer.Interval = 10;
            this.monitorTimer.Start();
        }

        private void monitorTimer_Tick(object sender, EventArgs e)
        {
            CheckActiveWindow();
        }

        private void CheckActiveWindow()
        {
            IntPtr currentActiveWindow = GetForegroundWindow();
            if (this.ActiveWindow != currentActiveWindow)
            {
                IntPtr lastActiveWindow = this.ActiveWindow;
                this.ActiveWindow = currentActiveWindow;

                OnActiveWindowChanged(lastActiveWindow, this.ActiveWindow);
            }
        }

        protected virtual void OnActiveWindowChanged(IntPtr lastActiveWindow, IntPtr currentActiveWindow)
        {
            ActiveWindowChangedEventHandler temp = ActiveWindowChanged;
            if (temp != null)
            {
                temp.Invoke(this, new ActiveWindowChangedEventArgs(lastActiveWindow, currentActiveWindow));
            }
        }
    }
}

personal

    public void InitActiveWindowMonitor()
    {
        WindowsMonitor.ActiveWindowMonitor monitor = new WindowsMonitor.ActiveWindowMonitor();
        monitor.ActiveWindowChanged += new WindowsMonitor.ActiveWindowChangedEventHandler(monitor_ActiveWindowChanged);
    }

    private void monitor_ActiveWindowChanged(object sender, WindowsMonitor.ActiveWindowChangedEventArgs e)
    {
        //ouh a window got activated
    }

Respondido 29 ago 12, 13:08

Hi, thanks for your reply. I tried out your WindowsMonitor class and it works fine. But I can not understand the implementation of the OnActiveWindowChanged method. You raise there an event in a seperate thread. Is this thread safe ? And what about performance. I think detecting the active window in a loop is higly time-consuming. - Adrian

nice to hear from you :) yeah, it should be thread safe as far as you initialize the monitor on your main thread... but, i didn't test it. Performance problem? Hm, i guess no - because the GetActiveWindow() method isn't slow and it's not a 'loop' but rather a timed-event (timer) which fires each 10ms (of course you can increase the interval, but you're going to loose fast window switches...). I run this code in some applications by myself and i didn't recognice any performance problems yet. - codeteq

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.