Capture Print Screen key pressed from background service c# - c#

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

Related

SetWindowsHookEx returns null if the user doenot have access to C:/Windows/Temp path

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.

CBTProc callback not working, setEventHookEx

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.

The instructions at 0xXXXXXXXX referenced memory at 0xXXXXXXXX, The memory could not be written

I get this error when trying to implement a windows hook using SetWindowsHookEx and CallWndProc. I'm wondering if i implemented the hook correctly. Here is the code: This code will work as a keyboard hook if its replaced with LowLevelKeyboardProc
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, CallWndProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
private delegate IntPtr CallWndProc(int nCode, IntPtr wParam, IntPtr lParam);
const int WH_CALLWNDPROC = 4;
const int WM_PASTE = 0x302;
private CallWndProc _proc = hookProc;
private static IntPtr hhook = IntPtr.Zero;
public void SetHook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_CALLWNDPROC, _proc, hInstance, 0);
}
public static void UnHook()
{
UnhookWindowsHookEx(hhook);
}
public static IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0 && wParam == (IntPtr)WM_PASTE)
{
MessageBox.Show("Paste");
return (IntPtr)1;
}
else
return CallNextHookEx(hhook, code, (int)wParam, lParam);
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
UnHook();
}
private void Form1_Load(object sender, EventArgs e)
{
SetHook();
}
The module handle passed to SetWindowsHookEx should be the handle for your dll, not "user32".

Global Key/MouseHook with UI

I want to make a C# Application with a Keyboard and Mouse on screen.
Every Key or Button that is clicked should be seen in this Application by for example coloring one of the keys ( i know how to do that ). This should also work if the Application is not focused. Currently i am using a global Key- and Mousehook which works fine.
The problem is, the Keyhook does only intercept one Key at a time which means i can only show on Key at a time. I want to be able to show multiple keys at a time on screen.
KeyListeners are unfortunately no option because they dont work outside the Application.
Does anyone has an idea how to make this possible?
Here's the KeyHook i am using:
public class KeyHook
{
private delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
static int hHook = 0;
public static List<Keys> KeyCodes = new List<Keys>();
const int WH_KEYBOARD_LL = 13;
HookProc KeyboardHookProcedure;
[StructLayout(LayoutKind.Sequential)]
private class keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern bool UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(int idHook, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public KeyHook()
{
Hook();
}
~KeyHook()
{
UnHook();
}
public int Hook()
{
KeyboardHookProcedure = new HookProc(KeyHook.KeyboardHookProc);
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, (IntPtr)LoadLibrary("User32"), 0);
return hHook;
}
public bool UnHook()
{
bool ret = UnhookWindowsHookEx(hHook);
if (ret)
hHook = 0;
return ret;
}
private static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
else
{
if (((int)wParam == 256) || ((int)wParam == 260))
{
keyboardHookStruct MyKeyboardHookStruct = (keyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(keyboardHookStruct));
//Adding Key to a log i use for other stuff
KeyCodes.Add((Keys)MyKeyboardHookStruct.vkCode);
//Code for coloring Key in the UI for pressed Key
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
}
}

C# Low-Level Keyboard Hook Not Working

This is the code for my keyhooking class, but it doesn't work. I was wondering if someone can tell me why? I'm instansiating it in another Console application. The debug message gives the proper output, but the keyboard hook simply doesn't catch keys. I was hoping if someone could tell me why.
namespace GlobalHooks
{
public class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr _hookID = IntPtr.Zero;
private static String keysHooked = String.Empty;
private static LowLevelHookProc keyboardHook;
public delegate IntPtr LowLevelHookProc(int nCode, Int32 wParam, IntPtr lParam);
public delegate void KeyboardHandleFunction(int vkCode);
public static event KeyboardHandleFunction keyHookReturn;
public InterceptKeys(KeyboardHandleFunction func)
{
keyHookReturn = func;
keyboardHook = new LowLevelHookProc(HookCallback);
}
public static void debug()
{
Console.Write("\n[Success!] _hookID: "+_hookID);
Console.Write("\n[Success!] keyboardProc: "+keyboardHook.ToString());
}
private IntPtr SetupHook(LowLevelHookProc keyProcess)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, keyProcess,
GetModuleHandle(curModule.ModuleName), 0);
}
}
public void Hook()
{
_hookID = SetupHook(keyboardHook);
debug();
}
public void Unhook()
{
UnhookWindowsHookEx(_hookID);
}
public static void OnCallbackReturn(int nCode)
{
if (keyHookReturn != null)
{
keyHookReturn(nCode);
}
else
{
throw new Exception();
}
}
public static IntPtr HookCallback(int nCode, Int32 wParam, IntPtr lParam)
{
Console.WriteLine("Calledback"Wink;
if (nCode >= 0 && wParam == WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
OnCallbackReturn(nCode);
}
return CallNextHookEx((int)_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelHookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(int hhk, int nCode, int wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
}
Are you calling Application.Run in your Main function?
The standard Console thread doesn't have a message loop, which is required for hooks to work properly, Application.Run takes care of that.

Categories

Resources