I am using SetWindowsHookEx() to create a keyboard hook. The creation seems to be successful but the procedure which is registered never gets called. Is there something I am doing wrong?
#region Windows API Functions Declarations
[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", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
=
private void CreateHook()
{
int id_hook = (int)HookType.WH_KEYBOARD_LL;
HookProc lpfn = new HookProc(this.KeyboardHookProc);
using (ProcessModule curModule = Process.GetCurrentProcess().MainModule)
hHook = SetWindowsHookEx(id_hook, lpfn, GetModuleHandle(curModule.ModuleName), 0);
if (hHook == 0)
throw new Exception("could not start monitoring mouse events");
}
=
private int KeyboardHookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0)
Console.WriteLine((Keys)wParam.ToInt32());
return CallNextHookEx(0, code, wParam, lParam);
}
=
Your P/Invoke declarations are wrong, you are using int where IntPtr is required and mixing up idHook and hHook. After editing your code, this worked:
IntPtr hHook;
private delegate IntPtr HookProc(int nCode, IntPtr wp, IntPtr lp);
HookProc lpfn;
private IntPtr KeyboardHookProc(int code, IntPtr wParam, IntPtr lParam) {
if (code >= 0)
Console.WriteLine((Keys)wParam.ToInt32());
return CallNextHookEx(hHook, code, wParam, lParam);
}
private void CreateHook() {
int id_hook = 13;
lpfn = new HookProc(this.KeyboardHookProc);
using (ProcessModule curModule = Process.GetCurrentProcess().MainModule)
hHook = SetWindowsHookEx(id_hook, lpfn, GetModuleHandle(curModule.ModuleName), 0);
if (hHook == IntPtr.Zero)
throw new Exception("could not start monitoring mouse events");
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool UnhookWindowsHookEx(IntPtr hHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
Related
I want to get acquainted with WinAPI in C#.
I need to write a global mouse hook in the console application. I found the code that does this job. But I have a console application, and I shouldn't use Application.Run () from Windows.Forms.
I need to forward messages about the coordinates of the mouse from winAPI directly to the console window without winForms. How can i do this?
class InterceptMouse
{
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
Add system.windows.forms as a reference to your project.
using System.Windows.Forms;
It should be work then.
I'm trying to hook a 3rd party app so that I can interact with the controls. But I am having issues with using SetWindowsHookEx to handle WH_KEYBOARD. It seems there is some problem with parameters that I am passing to SetWindowsHookEx.
public partial class Form1 : Form
{
private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
static IntPtr hHook;
IntPtr windowHandle;
uint processHandle;
HookProc PaintHookProcedure;
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
PaintHookProcedure = new HookProc(PaintHookProc);
windowHandle = FindWindowByCaption(0, "Untitled - Notepad");
uint threadID = GetWindowThreadProcessId(windowHandle, out processHandle);
IntPtr hMod = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(Form1).Module);
// HERE IS THE PROBLEM. It returns always zero. No matter what parameters you pass.
hHook = SetWindowsHookEx(WH_KEYBOARD, PaintHookProcedure, hMod, threadID);
}
public int PaintHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
// Do something here
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
private const int WH_KEYBOARD = 2;
}
Above is the sample code. SetWindowsHookEx is returning always zero.
Any suggestions would be very helpful.
Thanks in advance.
Did you look at this page: https://support.microsoft.com/en-us/help/318804/how-to-set-a-windows-hook-in-visual-c--net ?
Your Dllimports are subtly different
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
vs
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
It is the classic low level keyboard hook that I have used several times before quite successfully but lately it does not work right. It reads the keyboard inputs for exactly 10 times (10 times from any of the keys defined in HookCallback function) and then it stops. It is like some security feature of the OS or some other program like maybe an anivirus is getting in the way and suppressing the keyboard hook after 10 times.
Has anyone else encountered this 10 times limit? What could be the problem, in your opinion? I am using Windows 7 64 Bit and avast free antivirus. The program is written in c# 2008.
Here is the relevant code from program.cs:
static class Program
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
_hookID = SetHook(_proc);
Application.Run(new Form1());
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if ((Keys)vkCode == Keys.Delete)
{
Form1.staticmethod1();
}
else if ((Keys)vkCode == Keys.F8)
{
Form1.staticmethod2();
}
else if ((Keys)vkCode == Keys.F9)
{
Form1.staticmethod3();
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
Thanks in advance.
I'm about to write something like a screenshot programm. For now I used the PrintWindow function with the handle of the active window -> GetForgroundWindow and to start it I set a hook on the keyboard. When I capture normal windows on the desktop it's ok, besides there is no hardware accelerated effects, but in a game the hook doesn't seem to work. (I know, that the PrintWindow won't do anything here, I'm implementing a SlimDX class at the time) Is that some sort of a problem with the game-side modified message loop? And if yes, is there a way to make it work :D I would appreciate a C# way, cause I'm bad at programming UI with C++ ^^
I hope it is just some sort of bug...
Greetz Coldi
EDIT:
public class KeyboardHook
{
#region Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
static LowLevelKeyboardProc m_proc = HookCallback;
static IntPtr m_hookID = IntPtr.Zero;
static Screenshot m_screen;
public static void Hook(Screenshot screenshotManager)
{
m_screen = screenshotManager;
m_hookID = SetHook(m_proc);
}
public static void Unhook()
{
UnhookWindowsHookEx(m_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
#if DEBUG
Debug.WriteLine(((System.Windows.Forms.Keys)vkCode).ToString());
#endif
if ((System.Windows.Forms.Keys)vkCode == m_screen.CaptureKey)
{
m_screen.CaptureSimple();
}
}
return CallNextHookEx(m_hookID, nCode, wParam, lParam);
}
}
So on desktop it works pretty fine, but when I start a DirectX application, the keyhook doesn't get any pressed key (HookCallback isn't handeled)
I am writing an aplication that need to get the system input language, while the application window is not focused.
After searching Google I have found that the way to do this is to hook WM_INPUTLANGCHANGE message.
But I could not find a syntax example of the hook.
I have found the following code and tried to adapt it for my needs, but I have failed:
Edit:
I have replaced WM_KEYUP with WM_INPUTLANGCHANGE but it does not works.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace KeyHook
{
class LenHook
{
private const int WM_INPUTLANGCHANGE = 0x0051;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr 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 delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public LenHook()
{
_hookID = SetHook(_proc);
UnhookWindowsHookEx(_hookID);
System.Windows.Forms.Application.Run();
}
//Install hook
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (var curProcess = Process.GetCurrentProcess())
{
using (var curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WM_INPUTLANGCHANGE, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
}
//Do it when key press
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
MessageBox.Show(wParam.ToString());
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
}
This code from a project of mine works for me, it looks like we may have used the same example:
private static IntPtr _hookId = IntPtr.Zero;
private readonly External.LowLevelKeyboardProc _proc;
public FrmMain()
{
_proc = HookCallback;
_hookId = SetHook(_proc);
InitializeComponent();
}
private static IntPtr SetHook(External.LowLevelKeyboardProc proc)
{
using(var curProcess = Process.GetCurrentProcess())
{
using(var curModule = curProcess.MainModule)
{
return External.SetWindowsHookEx(External.WH_KEYBOARD_LL, proc, External.GetModuleHandle(curModule.ModuleName), 0);
}
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
// You can change this to WM_KEYDOWN
if (nCode >= 0 && wParam == (IntPtr)External.WM_KEYUP)
{
// Code you want to run when a button is pressed.
}
return External.CallNextHookEx(_hookId, nCode, wParam, lParam);
}
Also, this is my External class.
public static class External
{
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x0100;
public const int WM_KEYUP = 0x0101;
public const uint WM_GETTEXT = 0x0D;
public const uint WM_GETTEXTLENGTH = 0x0E;
public const uint EM_GETSEL = 0xB0;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetFocus();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, uint Msg, out int wParam, out int lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetCaretPos(out Point lPoint);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
}
I have read that you'll have problems if you try to do these hooks from a console application, although your call to Application.Run() should fix that.