我正在尝试创建一个项目,可以检测任务栏图标何时闪烁.您通常通过在应用程序中创建一个钩子并检查相应的消息来完成此操作.在这种情况下,可以通过在应用程序中创建WH_SHELL挂钩,等待HSHELL_REDRAW消息并检查lParam变量为TRUE来检测消息.
根据文档,如果lParam值为true,则wParam变量中窗口句柄引用的窗口闪烁.大.
问题是我不能,为了我的生活,弄清楚如何实际创建一个WH_SHELL钩子.创建钩子的代码看起来相当简单:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Diagnostics; class Flashy { [DllImport("user32.dll",CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)] protected static extern IntPtr SetWindowsHookEx(int code,HookProc func,IntPtr hInstance,int threadID); [DllImport("user32.dll",CallingConvention = CallingConvention.StdCall)] static extern int CallNextHookEx(IntPtr hhk,int nCode,IntPtr wParam,IntPtr lParam); [DllImport("kernel32.dll",SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); private int WH_SHELL = 10; static IntPtr hHook; public void doStuff() { using (Process process = Process.GetCurrentProcess()) using (ProcessModule module = process.MainModule) { IntPtr hModule = GetModuleHandle(module.ModuleName); MyDLL.Class1.hHook = SetWindowsHookEx(WH_SHELL,MyDLL.Class1.MyHookProc,hModule,0); } } }
现在,从我读过的内容来看,WH_SHELL需要一个dll.所以我看起来像:
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; namespace FlashyDLL { public class Class1 { //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",CallingConvention = CallingConvention.StdCall)] static extern int CallNextHookEx(IntPtr hhk,IntPtr lParam); public static IntPtr hHook; public static int MyHookProc(int nCode,IntPtr lParam) { if (nCode < 0) return CallNextHookEx(hHook,nCode,wParam,lParam); switch (nCode) { case 6: //HSHELL_REDRAW break; } return CallNextHookEx(hHook,lParam); } } }
那么我做错了什么?谢谢!
*编辑:
这是有效的代码的最后几位.它基本上是接受的答案中的代码,只有一些小的补充.我是这样发布的,因为我讨厌尝试从线程的各个部分拼凑一些代码,我也想到其他人也这样做.
请注意,这是一个全局shell挂钩,而不是特定于应用程序的挂钩,因此您必须针对您要查找的句柄检查lParam句柄,以确保Flash消息与正确的窗口有关.
第1步:获取消息的窗口:
using System; using System.Windows.Forms; public class TestAlertForm : Form { private IntPtr myWindow; private int uMsgNotify = 0; readonly IntPtr HSHELL_FLASH = new IntPtr(0x8006); protected override void WndProc(ref Message m) { if (m.Msg == uMsgNotify) { if (m.WParam == HSHELL_FLASH) { if (m.LParam == myWindow) { MessageBox.Show("FLASH!"); } } } base.WndProc(ref m); } }
第2步:PInvokes:
public class Win32 { [DllImport("user32.dll",EntryPoint = "RegisterWindowMessageA",CharSet = CharSet.Ansi,SetLastError = true,ExactSpelling = true)] private static extern int RegisterWindowMessage(string lpString); [DllImport("user32.dll",ExactSpelling = true)] private static extern int RegisterShellHookWindow(IntPtr hWnd); [DllImport("user32.dll",EntryPoint = "FindWindow",SetLastError = true)] static extern System.IntPtr FindWindowByCaption(int ZeroOnly,string lpWindowName); }
第3步:激活钩子:
var taf = new TestAlertForm(); myWindow = FindWindowByCaption(0,"My Window Title"); uMsgNotify = RegisterWindowMessage("SHELLHOOK"); Win32.RegisterShellHookWindow(taf.Handle);
解决方法
好吧,事实证明这在.Net中有点棘手 – 但它有可能.
您可以使用SetWindowsHookEx通过ID直接挂接到单个线程,但这种方式更简单,更容易匹配窗口.
您可以使用SetWindowsHookEx通过ID直接挂接到单个线程,但这种方式更简单,更容易匹配窗口.
警告:这依赖于内部USER32.dll调用.
第1步:获取消息的窗口:
using System; using System.Windows.Forms; public class TestAlertForm : Form { readonly IntPtr HSHELL_FLASH = new IntPtr(0x8006); protected override void WndProc(ref Message m) { if (m.WParam == HSHELL_FLASH) { // TODO: DO WORK HERE // m.LParam <-- A handle to the window that is 'flashing' // You should be able to match this to your target. MessageBox.Show("FLASH!"); } base.WndProc(ref m); } }
第2步:PInvokes:
public class Win32 { [DllImport("user32.dll")] public static extern bool RegisterShellHookWindow(IntPtr handle); }
第3步:激活钩子:
var taf = new TestAlertForm(); Win32.RegisterShellHookWindow(taf.Handle);
毕竟,你应该看到每个闪存的消息框.
这只是一个快速的黑客,我没有测试过多.
祝好运!