LowLevelKeyboardProc is called twice with every keystroke - c#

I try to do a VSTO key hook as discribed here: Excel VSTO Key hook
I used the answer of Alex Butenko to create this class which (should) call OnKeyPress every time a key is pressed. The problem is, when I press one key, OnKeyPress is called twice:
static class ShortcutManager
{
delegate int LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
static readonly LowLevelKeyboardProc _proc = HookCallback;
static IntPtr _hookID = IntPtr.Zero;
const int WH_KEYBOARD = 2;
const int HC_ACTION = 0;
const int WM_KEYDOWN = 0x0100;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern short GetKeyState(int nVirtKey);
static bool _keyHookingStarted;
public static void Start(IShortcutDistributor dist)
{
m_dist = dist;
if (!_keyHookingStarted)
{
#pragma warning disable 0618
_hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
_keyHookingStarted = true;
}
}
public static void Stop()
{
m_dist = null;
if (_keyHookingStarted)
{
UnhookWindowsHookEx(_hookID);
_hookID = IntPtr.Zero;
_keyHookingStarted = false;
}
}
static IShortcutDistributor m_dist = null;
static void OnKeyPress(uint keys)
{
var crtl = IsKeyDown(Keys.LControlKey) || IsKeyDown(Keys.RControlKey);
Debug.WriteLine("Keys: "+ keys.ToString()+ " Crtl: "+ crtl.ToString());
m_dist?.RaiseKeyPressedEvent(new KeyPressedEventArgs((Keys)keys, crtl));
}
static bool IsKeyDown(Keys keys)
{
return (GetKeyState((int)keys) & 0x8000) == 0x8000;
}
static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
}
if (nCode == HC_ACTION)
{
Debug.WriteLine("nCode: " + nCode.ToString() + " wParam:" + wParam.ToString());
OnKeyPress((uint)wParam);
}
return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
So when I press crtl+q the debug output is:
nCode: 0 wParam:81
Keys: 81 Crtl: True
nCode: 0 wParam:81
Keys: 81 Crtl: True
One press of space results in this debug output:
nCode: 0 wParam:32
Keys: 32 Crtl: False
nCode: 0 wParam:32
Keys: 32 Crtl: False
The microsoft documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985(v=vs.85).aspx says that: "wParam is either WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN or WM_SYSKEYUP" but in my case its the same in both calls.
So, what I'm doing wrong? Am I missing something?

Ok, it seems like Ive found what my problem was:
I mixed up LowLevelKeyboardProc and KeyboardProc.
When I use "SetWindowsHookEx(WH_KEYBOARD,..." the function is KeyboardProc and not LowLevelKeyboardProc. So this is the correct microsoft documentation:
https://msdn.microsoft.com/en-us/library/ms644984(v=vs.85).aspx
https://learn.microsoft.com/de-de/windows/desktop/inputdev/about-keyboard-input#_win32_Keystroke_Message_Flags
So lParam are the "Keystroke Message Flags". This means that the flag KF_REPEAT = 30 tells me how often the keypress is repeated. When I check if repeat = 0 I just get the first call. So this is my (hopefully correct) code:
static class ShortcutManager
{
delegate int KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
static readonly KeyboardProc _proc = HookCallback;
static IntPtr _hookID = IntPtr.Zero;
const int WH_KEYBOARD = 2;
const int WH_KEYBOARD_LL = 13;
const int HC_ACTION = 0;
const int WM_KEYDOWN = 0x0100;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern short GetKeyState(int nVirtKey);
static bool _keyHookingStarted;
public static void Start(IShortcutDistributor dist)
{
m_dist = dist;
if (!_keyHookingStarted)
{
#pragma warning disable 0618
_hookID = SetWindowsHookEx(WH_KEYBOARD, _proc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
_keyHookingStarted = true;
}
}
public static void Stop()
{
m_dist = null;
if (_keyHookingStarted)
{
UnhookWindowsHookEx(_hookID);
_hookID = IntPtr.Zero;
_keyHookingStarted = false;
}
}
static IShortcutDistributor m_dist = null;
const int KF_REPEAT = 0x4000;
static void OnKeyPress(uint keys)
{
var crtl = IsKeyDown(Keys.LControlKey) || IsKeyDown(Keys.RControlKey);
Debug.WriteLine("Keys: "+ keys.ToString()+ " Crtl: "+ crtl.ToString());
m_dist?.RaiseKeyPressedEvent(new KeyPressedEventArgs((Keys)keys, crtl));
}
static bool IsKeyDown(Keys keys)
{
return (GetKeyState((int)keys) & 0x8000) == 0x8000;
}
static int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
}
if (nCode == HC_ACTION)
{
var repeat = (HiWord(lParam) & KF_REPEAT);
Debug.WriteLine("nCode: " + nCode.ToString() + " wParam:" + wParam.ToString() + " repeat: "+ repeat.ToString());
if (repeat == 0)
{
OnKeyPress((uint)wParam);
}
}
return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private static ulong HiWord(IntPtr ptr)
{
if (((ulong)ptr & 0x80000000) == 0x80000000)
return ((ulong)ptr >> 16);
else
return ((ulong)ptr >> 16) & 0xffff;
}
}

Related

How to UnHook properly?

I made a very simple Hook codes (I'm a beginner).
I opened Notepad and tested.
If I press ANY key it make a beep and printed itself.
Except "x" key, it is a terminator key.
Question :
I do not want to see "x" key printed. I just quit the program. What do I have to do ?
namespace HookingStudy
{
class HookingClass
{
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = hookCallBack;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
Beep(1111, 222);
_hookID = SetHook(_proc);
Application.Run();
}
private static IntPtr hookCallBack(int nCode, IntPtr wParam, IntPtr lParam)
{
if( nCode >= 0 && wParam == (IntPtr) WM_KEYDOWN )
{
int vkCode = Marshal.ReadInt32(lParam);
if( vkCode.ToString() == "88" ) // 88 ("x" key)
{
Beep(7777, 222);
UnhookWindowsHookEx(_hookID);
Process.GetCurrentProcess().Kill();
}
Beep(2222, 55);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using( Process curProcess = Process.GetCurrentProcess() )
using( ProcessModule curModule = curProcess.MainModule )
{
return SetWindowsHookEx(13, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("KERNEL32.DLL")]
extern public static void Beep(int freq, int dur);
[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);
}
}
I do not want to see the terminator x printed at Notepad
Then do not call next hook in chain:
return CallNextHookEx(_hookID, nCode, wParam, lParam);
The idea of hooking it to install own handler prior existing handlers (afair from winapi). By intercepting (like you are doing it already) you are not only listening, but still invoking previous handlers with that call.
Try something like (untested):
if( vkCode == 88)
{
...
return 0;
}

Globally Detecting Key Presses and Mouse Clicks

I am attempting to create a program to globally detect key presses and mouse clicks. I have googled around and found solutions for both and got them working in the same program. But I would like to combine the two separate classes into one class that does both. I have fiddled around with it for a bit and can't figure out how to do it. Does anyone know how to solve this? Thanks in advance!
Mouse clicks class:
public static class MouseHook
{
public static event EventHandler imputAction = delegate { };
public static void Start()
{
_hookID = SetHook(_proc);
}
public static void stop()
{
UnhookWindowsHookEx(_hookID);
}
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
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_LBUTTONDOWN == (MouseMessages)wParam)
{
MyGlobals.clickType = 1;
imputAction(null, new EventArgs());
}
if (nCode >= 0 && MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
{
MyGlobals.clickType = 2;
imputAction(null, new EventArgs());
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_RBUTTONDOWN = 0x0204,
}
[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);
}
Keyboard class:
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static event EventHandler imputAction = delegate { };
public static void Start()
{
_hookID = SetHook(_proc);
}
public static void stop()
{
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 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);
MyGlobals.characters = Convert.ToString((Keys)vkCode);
MyGlobals.clickType = 3;
imputAction(null, new EventArgs());
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[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);
}
I am starting these like this:
MouseHook.Start();
MouseHook.MouseAction += new EventHandler(Event);
InterceptKeys.Start();
InterceptKeys.MouseAction += new EventHandler(Event)
The function receiving both events:
private void Event(object sender, EventArgs e)
{
//do stuff
}
I am also trying to do scrolling, I got it to detect scrolling by adding these lines. But I can't figure out how to read the scroll direction or value and assign it to a variable.
WM_MOUSEWHEEL = 0x020A,
if (nCode >= 0 && MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
{
MyGlobals.imputType = 4;
// Here is where I need to set a variable to the direction or
// value of scrolling but I cant figure out how
imputAction(null, new EventArgs());
}

Keyword 'this' is not valid in a static property, static method, or static field initializer

I'm using the following code:
public partial class ThisAddIn
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr hookId = IntPtr.Zero;
private delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
private static HookProcedure procedure = HookCallback;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
hookId = SetHook(procedure);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
UnhookWindowsHookEx(hookId);
}
private static IntPtr SetHook(HookProcedure procedure)
{
using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
return SetWindowsHookEx(WH_KEYBOARD_LL, procedure, GetModuleHandle(module.ModuleName), 0);
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int pointerCode = Marshal.ReadInt32(lParam);
string pressedKey = ((Keys)pointerCode).ToString();
//Do some sort of processing on key press
var thread = new Thread(() =>
{ MessageBox.Show(pressedKey);
if (pressedKey.Equals("Space") || pressedKey.Equals("Tab"))
{
***Word.Range rng = this.Application.ActiveDocument.Words.Last;
rng.Select();
rng.Copy();
String input = Clipboard.GetText(TextDataFormat.Text);
}
});
thread.Start();
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
}
I'm getting the following error for the line marked with *** :
Error 1 Keyword 'this' is not valid in a static property, static
method, or static field initializer.
Please advice
The this keyword in the HookCallback static method is a problem:
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int pointerCode = Marshal.ReadInt32(lParam);
string pressedKey = ((Keys)pointerCode).ToString();
//Do some sort of processing on key press
var thread = new Thread(() =>
{ MessageBox.Show(pressedKey);
if (pressedKey.Equals("Space") || pressedKey.Equals("Tab"))
{
***Word.Range rng = this.Application.ActiveDocument.Words.Last;
rng.Select();
rng.Copy();
String input = Clipboard.GetText(TextDataFormat.Text);
}
});
thread.Start();
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
If Application.ActiveDocument is not available in a static context (ie it does not work without this in front of it), then you will need to pass in the values you need.

Next Song Button For External Media Player?

Feel free to edit title.
Ok so i'm trying to create a short key button that skips to the next song of any media player? like what some keyboards have as in FN + F11 and it goes to next song is there anyway to integrate that into c# for my own keyboard? i have this code so far but all its doing is posting a msg onto the textbox1.
// Structure contain information about low-level keyboard input event
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public Keys key;
public int scanCode;
public int flags;
public int time;
public IntPtr extra;
}
//System level functions to be used for hook and unhook keyboard input
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hook);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern short GetAsyncKeyState(Keys key);
//Declaring Global objects
private IntPtr ptrHook;
private LowLevelKeyboardProc objKeyboardProcess;
private IntPtr captureKey(int nCode, IntPtr wp, IntPtr lp)
{
if (nCode >= 0)
{
KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lp, typeof(KBDLLHOOKSTRUCT));
// Keys
if (objKeyInfo.key == Keys.F10 && ModifierKeys == Keys.Alt)
{
textBox1.Focus();
a += 1;
c += 1;
textBox1.Text += Convert.ToString(c) + ". " + "Previous Song" + Environment.NewLine;
PrevSongCount.Text = Convert.ToString(a);
AllUpCount.Text = Convert.ToString(c);
return (IntPtr)1;
}
if (objKeyInfo.key == Keys.F11 && ModifierKeys == Keys.Alt)
{
textBox1.Focus();
b += 1;
c += 1;
textBox1.Text += Convert.ToString(c) + ". " + "Next Song" + Environment.NewLine;
NextSongCount.Text = Convert.ToString(b);
AllUpCount.Text = Convert.ToString(c);
return (IntPtr)1;
}
}
return CallNextHookEx(ptrHook, nCode, wp, lp);
}
bool HasAltModifier(int flags)
{
return (flags & 0x20) == 0x20;
}
This was not as easy as I thought. But finally I managed to do it and the solution was shockingly simple.
After compiling this you are using the venerable virtual media keyboard codes.
http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx
I used this cmd line prog for my microsoft natural keyboard which does not have media keys but has 5 programmable keys and to get it to work you hack the registry
HKEY_CURRENT_USER\Software\Microsoft\IntelliType Pro\EventMapping\82 or
HKEY_CURRENT_USER\Software\Microsoft\IntelliType Pro\ModelSpecific\1016\EventMapping\82
where the 81 is just one of the dynamic keys (78-82)
and put the value previous or next into the Arguments key.
using System;
using System.Runtime.InteropServices;
namespace NxtTrack
{
class Program
{
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern void keybd_event(byte vkCode, byte scanCode, int flags, IntPtr extraInfo);
enum TrackMove
{
Previous,Next
}
static void Main(string[] args)
{
TrackMove trackMove;
try
{
if(args[0].ToLower().Contains("previous"))
trackMove = TrackMove.Previous;
else if(args[0].ToLower().Contains("next"))
trackMove = TrackMove.Next;
else
{
throw new Exception("wrong param");
}
}
catch
{
Console.WriteLine("Params needed: Next or Previous");
return;
}
TrackKeys(trackMove);
}
private static void TrackKeys(TrackMove trackMove)
{
//http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx
byte msg = trackMove == TrackMove.Previous ? (byte)0xB1 : (byte)0xB0;
keybd_event(msg, 0x45, 0, IntPtr.Zero);
}
}
}

SendKey command C#

I will be grateful to anybody who could help!
I'm using the InterceptKeys class below to change some of the keys. For example when I type the open bracket key "[", I want to change this to "ë" (whenever I type in my computer, not a particular program).
I use a SendKey.Send("ë"); whenever the "[" is typed, but the problem is that after "ë" alsot the "[" is typed.
Is it possible to replace the "[" with "ë" or any key, or cancel the "[" and just send "ë"?
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
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 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);
//Console.WriteLine((Keys)vkCode);
if (vkCode == (int)Keys.OemOpenBrackets))
{
SendKeys.Send("ë");
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[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);
}
Return 1 if you handled the key instead of calling CallNextHookEx.
IE, HookCallback() should look like this
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
//Console.WriteLine((Keys)vkCode);
if (vkCode == (int)Keys.OemOpenBrackets))
{
SendKeys.Send("ë");
return 1;
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

Categories