拦截另一个窗口的窗口消息
Posted
技术标签:
【中文标题】拦截另一个窗口的窗口消息【英文标题】:Intercepting Window Messages for another Window 【发布时间】:2013-12-19 19:27:07 【问题描述】:我正在使用 CefGlue 制作一个带有嵌入式 webkit 浏览器的应用程序,我需要在浏览器窗口中监听鼠标移动。 winforms 控件不会将鼠标事件传递给控件,因此我无法监听它们。
但是,我发现了一个错误/功能请求,其中包含一个解决方案,但如何实现它超出了我的能力范围,我不熟悉直接在 WinAPI 中工作。开发人员说我需要:
2。特定于操作系统(windows) - 创建浏览器后(CefLifeSpanHandler.OnAfterCreated)获取窗口句柄和子类 它们(窗口子类化技术)。其实现在我们有本地人 具有类 CefBrowserWindow 的窗口(由 CefBrowser.GetHost().GetWindowHandle()),然后是子窗口 Chrome_WidgetWin_0,然后是 Chrome_RenderWidgetHostHWND。为了 拦截 WM_MOUSEMOVE 你对 Chrome_WidgetWin_0 感兴趣 窗口,可以通过 CefBrowserWindow 轻松获取。只是玩 用 Spy++ 精确查看。
https://bitbucket.org/xilium/xilium.cefglue/issue/4/mouse-events-unaccessible
我做了一些谷歌搜索,但我不知道如何挂钩。我的表单上有这个功能:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
base.WndProc(ref m);
switch (m.Msg)
case WM_MOUSEMOVE:
Console.WriteLine("Mouse move!");
break;
default:
Console.WriteLine(m.ToString());
break;
但是当我在控件上方时,我从来没有看到鼠标移动。我怀疑我需要监听CefBrowser.GetHost().GetWindowHandle()
的 WndProc,但我不知道该怎么做。
【问题讨论】:
见NativeWindow Class 【参考方案1】:我找到了解决方案。我在 WebLifeSpanHandler 的 OnAfterCreated 事件中调用此函数。
browser.Created += (sender, eventargs) =>
Console.WriteLine("Created.");
BrowserWindowPointer = browser.CefBrowser.GetHost().GetWindowHandle();
Console.WriteLine("BrowserWindowPointer: " + BrowserWindowPointer);
uint BrowserThreadId = GetWindowThreadProcessId(BrowserWindowPointer, IntPtr.Zero);
Console.WriteLine("Browser PID: " + BrowserThreadId);
MouseHookProcedure = new HookProc(this.MouseHookProc);
hHookMouse = SetWindowsHookEx(WH_MOUSE,
MouseHookProcedure,
(IntPtr)0,
BrowserThreadId);
if (hHookMouse == 0)
Console.WriteLine("MouseHook Failed. Making cursor always visible.");
Cursor.Show();
KeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
hHookKeyboard = SetWindowsHookEx(WH_KEYBOARD,
KeyboardHookProcedure,
(IntPtr)0,
BrowserThreadId);
;
我要找的函数是GetWindowThreadProcessId,我可以使用browser.CefBrowser.GetHost().GetWindowHandle()
提供的窗口句柄
需要注意的一点是,Hook Procedure 需要具有某种更大的范围。 C# 看不到它与本机进程挂钩,如果你让它超出范围,它会被垃圾收集。为了解决这个问题,我将它们设为类属性。
支持文件:
http://support.microsoft.com/kb/318804 如何在 Visual C# .NET 中设置 Windows 挂钩 提供了挂钩鼠标事件的基础。修改为使用浏览器线程 id http://msdn.microsoft.com/en-us/library/ms644988(v=vs.85).aspxMouseProc回调函数如何处理鼠标windows钩子 http://msdn.microsoft.com/en-us/library/ms644984(VS.85).aspx KeyboardProc 回调函数 如何处理键盘窗口挂钩。包括 lParam 的结构。重要提示:wParam 包含输入的键的虚拟键代码。 http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx Virtual-Key Codes 按键列表及其虚拟按键代码,这样我就可以知道用户按下了哪个按钮。我的两个钩子(代码不好,我对封送数据不做任何事情):
private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
//Marshall the data from the callback.
MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (nCode < 0)
return CallNextHookEx(hHookMouse, nCode, wParam, lParam);
else
if (wParam.ToInt32() == WM_MOUSEMOVE)
Screensaver_OnMouseMove(this, null);
return CallNextHookEx(hHookMouse, nCode, wParam, lParam);
private int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
//Marshall the data from the callback.
KeyboardHookStruct keyInfo = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
int VK_ESCAPE = 0x1B;
if (nCode < 0)
return CallNextHookEx(hHookKeyboard, nCode, wParam, lParam);
else
int keyCode = wParam.ToInt32();
if (keyCode == VK_ESCAPE)
Application.Exit();
return CallNextHookEx(hHookKeyboard, nCode, wParam, lParam);
这是暴露钩子所需的外部和代码,这是类级别的范围:
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
//Declare the hook handle as an int.
static int hHookMouse = 0;
static int hHookKeyboard = 0;
//Declare the mouse hook constant.
//For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;
private const int WM_MOUSEMOVE = 0x0200;
//Declare the wrapper managed POINT class.
[StructLayout(LayoutKind.Sequential)]
public class POINT
public int x;
public int y;
//Declare the wrapper managed MouseHookStruct class.
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
public struct KeyboardHookStruct
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
//This is the Import for the SetWindowsHookEx function.
//Use this function to install a thread-specific hook.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, uint threadId);
//This is the Import for the UnhookWindowsHookEx function.
//Call this function to uninstall the hook.
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
//This is the Import for the CallNextHookEx function.
//Use this function to pass the hook information to the next hook procedure in chain.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
public HookProc KeyboardHookProcedure get; set;
public HookProc MouseHookProcedure get; set;
我不确定这是否完全需要,特别是因为被钩住的线程正在关闭,但为了更好地衡量,不要忘记在听完后清理你的钩子。我在表单的 dispose 方法上执行此操作:
protected override void Dispose(bool disposing)
base.Dispose();
if (disposing)
if (hHookKeyboard != 0)
UnhookWindowsHookEx(hHookKeyboard);
if (hHookMouse != 0)
UnhookWindowsHookEx(hHookMouse);
【讨论】:
以上是关于拦截另一个窗口的窗口消息的主要内容,如果未能解决你的问题,请参考以下文章
[QT]MdiArea子窗口的管理,拦截子窗口关闭消息并在父窗口处理
猎豹MFC--拦截消息SetWindowsLong CallWindowsProc控件不能满足我们的需求时 增加修改这些功能