窗口钩子的奇怪行为 [英] Strange behaviour of windows hooks

查看:200
本文介绍了窗口钩子的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



以下是此过程的简短摘要:



在非托管程序集)实现一个安装 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
我的.NET应用程序收到激活事件后,我最小化Outlook或激活另一个窗口。起初我以为我的.NET包装是问题的原因。但是,在我使用来自上述链接的源,我可以识别相同的行为。
我实际上的解决方法是使用 WH_SHELL 钩子。我知道 WH_CBT WH_SHELL 钩子之间的一个区别是当使用 WH_CBT hook可以通过不调用 CallNextHookEx 方法来中断过滤器函数链。这可能在我的问题中发挥作用吗?
请提供帮助。

解决方案

显然挂钩在前景不起作用 - 微软产品(word,power point ...)



但是,为什么钩?这个小类将工作,即使outlook被激活

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

命名空间WindowsMo​​nitor
{
public class ActiveWindowChangedEventArgs:EventArgs
{
public IntPtr CurrentActiveWindow {get;私人集}
public IntPtr LastActiveWindow {get;私人集}

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;私人集}
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()
{
WindowsMo​​nitor.ActiveWindowMonitor monitor = new WindowsMo​​nitor.ActiveWindowMonitor
monitor.ActiveWindowChanged + = new WindowsMo​​nitor.ActiveWindowChangedEventHandler(monitor_ActiveWindowChanged);
}

private void monitor_ActiveWindowChanged(object sender,WindowsMo​​nitor.ActiveWindowChangedEventArgs e)
{
//一个窗口被激活
}


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 hook.

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

After calling 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 and 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.

解决方案

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

usage

    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
    }

这篇关于窗口钩子的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆