Create a C # project that successfully creates a WH_SHELL hook for one application

I am trying to create a project that can detect when the taskbar icon is blinking. Usually you do this by creating a hook in the application and checking the corresponding message. In this case, the message can be detected by creating a WH_SHELL hook in the application, waiting for the HSHELL_REDRAW message and checking the lParam variable for TRUE.

According to the docs, if lParam is true, then the window referenced by the window handle in the wParam variable is blinking. Fine.

The problem is that I cannot figure out for my life how to actually create a WH_SHELL hook. The code for creating the hook seems pretty simple:

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", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, 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); } } } 

Now, from what I read, WH_SHELL requires a DLL. So mine looks like this:

 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", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); public static IntPtr hHook; public static int MyHookProc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode < 0) return CallNextHookEx(hHook, nCode, wParam, lParam); switch (nCode) { case 6: //HSHELL_REDRAW break; } return CallNextHookEx(hHook, nCode, wParam, lParam); } } } 

So what am I doing wrong? Thanks!

* Editing:

Here are the final bits of code that worked. This is basically code from the accepted answer with a few minor additions. I post it like this because I hate trying to connect pieces of code from different parts of the stream, and I believe other people too.

Please note that this is the global shell host and not application specific, so you need to check the lParam descriptor for the descriptor you are looking for to make sure that the flash message is in the correct window.

Step 1: window for receiving messages:

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

Step 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", CharSet = CharSet.Ansi, SetLastError = true, 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); } 

Step 3: Activate the hook:

 var taf = new TestAlertForm(); myWindow = FindWindowByCaption(0, "My Window Title"); uMsgNotify = RegisterWindowMessage("SHELLHOOK"); Win32.RegisterShellHookWindow(taf.Handle); 
+7
c #
source share
1 answer

OK, it turns out that this is a bit complicated in .Net - but it is possible. You can use SetWindowsHookEx to bind directly to a single thread by identifier, but this method is a bit simpler and easier to map to a window.

Caution: this depends on an internal call to USER32.dll .

Step 1: window for receiving messages:

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

Step 2: PInvokes:

 public class Win32 { [DllImport("user32.dll")] public static extern bool RegisterShellHookWindow(IntPtr handle); } 

Step 3: Activate the hook:

 var taf = new TestAlertForm(); Win32.RegisterShellHookWindow(taf.Handle); 

After all this, you should see a message box for each flash. This is just a quick hack, and I have not tested it.

Good luck

+2
source share

All Articles