窗户挂钩的奇怪行为

Strange behaviour of windows hooks

本文关键字:窗户      更新时间:2023-10-16

当操作系统(windows XP 32位)中的任何窗口被激活时,我正在寻找在.NET窗口应用程序中收到通知的可能性。在CodeProject上,我通过使用全局系统挂钩找到了一个解决方案。

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

以下是该程序的简短总结:

在非托管程序集中(用C++编写),实现了一个安装WH_CBT钩子的方法。

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

在回调方法(过滤函数)中,根据钩子类型,窗口消息被发送到目标窗口。

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

要在.NET Windows应用程序中使用此程序集,必须导入以下方法:

[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);

调用InitializeCbtHook后,从GlobalCbtHook.dll接收的消息可以在中进行处理

protected override void WndProc(ref Message msg) 

必须通过调用在程序集和应用程序中注册消息RegisterWindowMessage

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

此实现效果良好。但在大多数情况下,当我激活Microsoft Office Outlook时在最小化Outlook或激活其他窗口后,我的.NET应用程序收到激活事件。起初,我认为我的.NET包装是问题的原因。但在我使用了上述链接的来源后,我可以识别出同样的行为。我的实际解决方法是使用WH_SHELL钩子。我知道WH_CBTWH_SHELL钩子之间的一个区别是,当使用WH_CBT钩子时,可以通过不调用CallNextHookEx方法来中断过滤器函数链。这会对我的问题起作用吗?请提供帮助。

显然挂钩在outlook的情况下不起作用-其他微软产品(word,power point…)呢??

但是,为什么要勾搭?即使outlook被激活,这个小类也能工作

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

使用

    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
    }