【问题标题】:Strange behaviour of windows hookswindows钩子的奇怪行为
【发布时间】:2012-08-20 08:42:32
【问题描述】:

我一直在寻找在操作系统(Windows XP 32 位)中激活任何窗口时在 .NET Windows 应用程序中收到通知的可能性。在 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 方法来中断过滤器函数链。这会在我的问题中发挥作用吗? 请提供帮助。

【问题讨论】:

  • 为什么不直接使用System.Windows.UIAutomation 命名空间?这是专门为你想做的事情而写的!
  • 感谢您的回复,但根据我的信息,您只能在WPF-Applications中使用System.Windows.UIAutomation命名空间。
  • 奇怪,因为我在控制台应用程序中使用过它!
  • 所以我会尝试一下并通知您结果。谢谢!

标签: c# c++ winapi dll hook


【解决方案1】:

显然挂钩在 Outlook 的情况下不起作用 - 其他 microsoft 产品(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
    }

【讨论】:

  • 您好,感谢您的回复。我试用了您的 WindowsMonitor 课程,效果很好。但我无法理解OnActiveWindowChanged 方法的实现。您在单独的线程中引发了一个事件。这个线程安全吗?那么性能呢。我认为在循环中检测活动窗口非常耗时。
  • 很高兴收到您的来信 :) 是的,就您在主线程上初始化监视器而言,它应该是线程安全的……但是,我没有对其进行测试。性能问题?嗯,我想不 - 因为 GetActiveWindow() 方法并不慢而且它不是一个“循环”,而是一个每 10 毫秒触发一次的定时事件(定时器)(当然你可以增加间隔,但你要去松开快速窗口开关...)。我自己在一些应用程序中运行了这段代码,我还没有发现任何性能问题。
猜你喜欢
  • 2021-04-14
  • 2012-08-28
  • 1970-01-01
  • 2020-04-26
  • 2021-03-02
  • 1970-01-01
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多