窗口钩子的奇怪行为 [英] Strange behaviour of windows hooks
问题描述
当在操作系统中激活任何窗口时(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;
命名空间WindowsMonitor
{
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()
{
WindowsMonitor.ActiveWindowMonitor monitor = new WindowsMonitor.ActiveWindowMonitor
monitor.ActiveWindowChanged + = new WindowsMonitor.ActiveWindowChangedEventHandler(monitor_ActiveWindowChanged);
}
private void monitor_ActiveWindowChanged(object sender,WindowsMonitor.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 fromGlobalCbtHook.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 betweenWH_CBT
andWH_SHELL
hook is when usingWH_CBT
hook it is possible to interrupt the filter function chain by not calling theCallNextHookEx
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屋!