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
Unlike WH_MOUSE_LL, the callback for WH_CBT must be in the process that is hooked; hence the callback must be in a DLL.
Related
I have .NET6 Background Service application from which I want to listen to keyboard and when Print Screen button is pressed, to run some code from that background service.
I have found multiple answers to listening for key press but haven't managed to make any work. All of them use User32.dll and methods that I tried using are RegisterHotkey and SetWindowsHookEx.
Closes I think I came is with SetWindowsHookEx since when I run it and try opening new tab in chrome with Control + T I see it lags for few milliseconds, but for some reason callback handler in my code is not hit.
Here is the that code I managed to get to kind a work:
namespace LSShot;
using System.Drawing;
using System.Runtime.InteropServices;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
#region Import Methods
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
public static extern void ReleaseDC(IntPtr hwnd, IntPtr dc);
[DllImport("User32.dll")]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("User32.dll")]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("User32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
#region Structures
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
#endregion
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public IntPtr hhook = IntPtr.Zero;
private static keyboardHookProc callbackDelegate;
const int WH_KEYBOARD_LL = 13;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
Hook();
}
~Worker()
{
Unhook();
}
private void Hook()
{
if(callbackDelegate != null)
throw new InvalidOperationException("Can't hook more than once!");
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
if (hhook == IntPtr.Zero)
throw new Exception("IntPtr.Zero");
}
private void Unhook()
{
}
private int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
_logger.LogInformation(code.ToString());
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
}
}
}
I have a powershell script where i have written the below code to get the hookid. But the setwindowshookex returns null if the user doenot have access to C:/Windows/Temp path. if i give the access then the setwindowshookex returns integer value.
I have tried passing GetCurrentThreadId() as the last parameter of SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
Add-Type #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
public static class NativeMethods{
public static bool KeyEvent { get; set; }
public static bool KeyEventPrevious { get; set; }
public static System.Collections.Generic.List<bool> Buffer = new System.Collections.Generic.List<bool>();
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
public static HookProc hookProc = HookCallback;
private static IntPtr hookId = IntPtr.Zero;
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
KeyEvent = true;
}
else{
KeyEvent = false;
}
if(KeyEvent != KeyEventPrevious){
Buffer.Add(KeyEvent);
KeyEventPrevious = KeyEvent;
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
public static IntPtr GetHookId(HookProc hookProc){
IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, moduleHandle, GetCurrentThreadId());
}
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll")]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
$nativeMethodCode
}
"#
Hookid is required to monitor the keyboard events.
I want to create a program, which could make screenshot of scrolling window. As opposed to making screenshots, scrolling another app is difficult for me. Basing on a few scripts from web, I’ve written that:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
namespace scroller
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll")]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
private void Form1_Load(object sender, EventArgs e)
{
List<IntPtr> result = new List<IntPtr>();
Thread.Sleep(5000);
IntPtr ParenthWnd = GetForegroundWindow();
if (!ParenthWnd.Equals(IntPtr.Zero))
{
StringBuilder myStringBuilder = new StringBuilder(256);
GetWindowText(ParenthWnd, myStringBuilder, 256);
this.Text = myStringBuilder.ToString();
IntPtr prevChild = IntPtr.Zero;
IntPtr currChild = IntPtr.Zero;
while (true)
{
currChild = FindWindowEx(ParenthWnd, prevChild, null, null);
if (currChild == IntPtr.Zero) break;
result.Add(currChild);
prevChild = currChild;
}
}
for(int i=0 ;i<=result.Count-1;i++){
IntPtr myHandle = result[i];
SetActiveWindow(ParenthWnd);
SwitchToThisWindow(myHandle, true);
SetScrollPos(myHandle, 0x1, 0, true);
PostMessage(myHandle, 0x115, 4 + 0x10000 * 0,0);
}
}
}
}
This waits 5 seconds, and moves scrollbars of all windows to top. It works e.g. with notepad, but not with web browsers, ms word and many others. What am I doing wrong?
I'm writing an application in c# and I need to know when the for the ground window has changed
I used SetWindowsHookEx but I don't get the call back when I switch between windows
my code:
private const int WH_CALLWNDPROC = 4;
private delegate IntPtr windowName(int nCode, IntPtr wParam, IntPtr lParam);
private static windowName _name = HookCallback;
private static IntPtr _hook = IntPtr.Zero;
public static void start()
{
_hook = SetHook(_name);
Application.Run();
UnhookWindowsHookEx(_hook);
}
private static IntPtr SetHook(windowName proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_CALLWNDPROC, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
browser = GetActiveWindow();
Console.WriteLine(browser);
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
ok i have an answer
public static void start()
{
WinEventDelegate dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
string window = GetActiveWindowTitle();
Console.WriteLine(window);
while (true)
{
if (window != GetActiveWindowTitle())
{
window = GetActiveWindowTitle();
Console.WriteLine(window);
}
}
}
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private static string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Console.WriteLine(GetActiveWindowTitle());
}
#region imports
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
#endregion
its a bit messy but it works
How to hide Start button while openning camera using CameraCaptureDialog in windows mobile
Do you just want to hide the Start button, or the whole taskbar?
You can hide and show the complete taskbar by using this code:
[DllImport("coredll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(
[In] string lpClassName,
[In] string lpWindowName);
[DllImport("coredll", EntryPoint = "ShowWindow")]
public static extern bool ShowWindow(
[In] IntPtr hWnd,
[In] int nCmdShow);
[DllImport("coredll", EntryPoint = "EnableWindow")]
public static extern bool EnableWindow(
[In] IntPtr hWnd,
[In] bool bEnable);
public const int SW_HIDE = 0x0000;
public const int SW_SHOW = 0x0001;
public static void HideTaskBar()
{
IntPtr hWnd = FindWindow("HHTaskBar", null);
EnableWindow(hWnd, false);
ShowWindow(hWnd, Win32.SW_HIDE);
}
public static void ShowTaskBar()
{
IntPtr hWnd = FindWindow("HHTaskBar", null);
EnableWindow(hWnd, true);
ShowWindow(hWnd, Win32.SW_SHOW);
}