I have some code here that is targeting windows event hooks in order to write to a log file when triggered. I am running this in powershell. I have successfully used this code to log mouse/keyboard events however when I use WH_CBT 5 using the CBTProc callback I receive no events. Even when using a mouse target of WH_MOUSE_LL 14 works just fine... can someone explain why? Have I missed something... or is it not possible for some reason?
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyLogger {
  public static class Program {
    private const int HOOK_CODE = 5;
    private const int CALLBACK_CODE = 9;
    private const string logPath = @"c:\MyTest.txt";
    private const string logFileName = "log.txt";
    private static StreamWriter logFile;
    private static HookProc hookProc = HookCallback;
    private static IntPtr hookId = IntPtr.Zero;
    public static void Main() {
      logFile = File.AppendText(logPath);
      logFile.AutoFlush = true;
      hookId = SetHook(hookProc);
      Application.Run();
      UnhookWindowsHookEx(hookId);
    }
    private static IntPtr SetHook(HookProc hookProc) {
      IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
      return SetWindowsHookEx(HOOK_CODE, hookProc, moduleHandle, 0);
    }
    private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
      logFile.WriteLine("gg");
      return CallNextHookEx(hookId, nCode, wParam, lParam);
    }
    [DllImport("user32.dll")]
    private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
    [DllImport("user32.dll")]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);
    [DllImport("user32.dll")]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
  }
}
"@ -ReferencedAssemblies System.Windows.Forms
[MyLogger.Program]::Main();
Modded code
Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyLogger {
  public static class Program {
    private const int WINEVENT_OUTOFCONTEXT = 0;
    private const int EVENT_OBJECT_FOCUS = 0x8005;
    private const string logPath = @"c:\MyTest.txt";
    private const string logFileName = "log.txt";
    private static StreamWriter logFile;
    private static HookProc hookProc = HookCallback;
    private static IntPtr hookId = IntPtr.Zero;
    public static void Main() {
      logFile = File.AppendText(logPath);
      logFile.AutoFlush = true;
      hookId = SetHook(hookProc);
      Application.Run();
    }
    private static IntPtr SetHook(HookProc hookProc) {
      return SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, null, hookProc, 0, 0, WINEVENT_OUTOFCONTEXT);
    }
    private delegate IntPtr HookProc(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
    private static IntPtr HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
      logFile.WriteLine("gg");
    }
    internal enum SetWinEventHookFlags
      {
          WINEVENT_INCONTEXT = 4,
          WINEVENT_OUTOFCONTEXT = 0,
          WINEVENT_SKIPOWNPROCESS = 2,
          WINEVENT_SKIPOWNTHREAD = 1
      } 
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
    private static extern int UnhookWinEvent(IntPtr hWinEventHook);
    [DllImport("user32.dll")]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
  }
}
"@ -ReferencedAssemblies System.Windows.Forms
[MyLogger.Program]::Main();
This code logs the HWND that has the focus.
Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyLogger {
    public static class Program {
        private const int WINEVENT_OUTOFCONTEXT = 0;
        private const int EVENT_OBJECT_FOCUS = 0x8005;
        private const int WM_GETTEXT = 0x000D;
        private const string logPath = @"c:\Temp\MyTest.txt";
        private static StreamWriter logFile;
        private static HookProc hookProc = HookCallback;
        private static IntPtr hookId = IntPtr.Zero;
        public static void Main() {
            logFile = File.AppendText(logPath);
            logFile.AutoFlush = true;
            hookId = SetHook(hookProc);
            Application.Run();
        }
        private static IntPtr SetHook(HookProc hookProc) {
            return SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, IntPtr.Zero, hookProc, 0, 0, WINEVENT_OUTOFCONTEXT);
        }
        private delegate void HookProc(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
        private static void HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
            logFile.WriteLine(string.Format("{0}", hWnd));
        }
        internal enum SetWinEventHookFlags
        {
            WINEVENT_INCONTEXT = 4,
            WINEVENT_OUTOFCONTEXT = 0,
            WINEVENT_SKIPOWNPROCESS = 2,
            WINEVENT_SKIPOWNTHREAD = 1
        } 
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
        [DllImport("user32.dll")]
        private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
    }
}
"@ -ReferencedAssemblies System.Windows.Forms
[MyLogger.Program]::Main();
The hWnd passed to HookCallback can be a child window (like a list control or tree control, etc.), it is not always the outermost application window like you might be expecting from WH_CBT.  
If you need the outermost application window, you can simply do something like:
HWND hwnd = hWndPassedToHookCallback;
HWND hwndApp;
do
{
    hwndApp = hwnd;
    hwnd = GetParent(hwnd)
} while(hwnd);
// hwndApp now is the outermost application window
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With