I am trying to create a low-level keyboard handler to launch an external application using reflection when a specific keyboard combination is detected.
I can launch the app once directly with no errors, however from inside a key press handler the app crashes after correctly showing the MessageBox
dialog.
I am reading the external dll into a byte array, for this test you could use any .Net application a simple Hello World win forms app will work.
string filePath = @".\x.exe";
FileStream fs = new FileStream(filePath, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();
Then loading the external Assembly
and executing it via reflection:
public Assembly a;
public MethodInfo method;
public object o;
a = Assembly.Load(bin);
method = a.EntryPoint;
o = a.CreateInstance(method.Name);
method.Invoke(o, null);
This works once, but If I repeat the above code to execute again it fails.
The following code demonstrates the failure on the second call, if you comment that out you can see the failure on the keyboard detection of Left+Down+A
Even when you comment out both calls in the program root, the app closes unexpectedly the first time when the keys are detected.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Windows.Input;
public class Program
{
public static bool lctrlKeyPressed;
public static bool f1KeyPressed;
public static bool AKeyPressed;
public static bool LeftKeyPressed;
public static bool DownKeyPressed;
public static Assembly a;
// search for the Entry Point
public static MethodInfo method;
// create an istance of the Startup form Main method
public static object o;
[STAThread]
public static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
string filePath = @".\x.exe";
FileStream fs = new FileStream(filePath, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();
//public Assembly a;
//public MethodInfo method;
//public object o;
a = Assembly.Load(bin);
method = a.EntryPoint;
o = a.CreateInstance(method.Name);
method.Invoke(o, null);
a = Assembly.Load(bin);
method = a.EntryPoint;
o = a.CreateInstance(method.Name);
method.Invoke(o, null);
LowLevelKeyboardHook kbh = new LowLevelKeyboardHook();
kbh.OnKeyPressed += kbh_OnKeyPressed;
kbh.OnKeyUnpressed += kbh_OnKeyUnPressed;
kbh.HookKeyboard();
Application.Run();
kbh.UnHookKeyboard();
}
public static void kbh_OnKeyPressed(object sender, Keys e)
{
if (e == Keys.A)
{
AKeyPressed = true;
}
else if (e == Keys.Left)
{
LeftKeyPressed = true;
}
else if (e == Keys.Down)
{
DownKeyPressed = true;
}
CheckKeyCombo();
}
public static void kbh_OnKeyUnPressed(object sender, Keys e)
{
if (e == Keys.A)
{
AKeyPressed = false;
}
else if (e == Keys.Left)
{
LeftKeyPressed = false;
}
else if (e == Keys.Down)
{
DownKeyPressed = false;
}
}
public static void CheckKeyCombo()
{
if (AKeyPressed && LeftKeyPressed && DownKeyPressed)
{
MessageBox.Show("Left Key + Down Key + A Key + Pressed");
//a = Assembly.Load(bin);
method = a.EntryPoint;
o = a.CreateInstance(method.Name);
method.Invoke(o, null);
}
}
}
public class LowLevelKeyboardHook
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private const int WM_KEYUP = 0x101;
private const int WM_SYSKEYUP = 0x105;
[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);
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public event EventHandler<Keys> OnKeyPressed;
public event EventHandler<Keys> OnKeyUnpressed;
private LowLevelKeyboardProc _proc;
private IntPtr _hookID = IntPtr.Zero;
public LowLevelKeyboardHook()
{
_proc = HookCallback;
}
public void HookKeyboard()
{
_hookID = SetHook(_proc);
}
public void UnHookKeyboard()
{
UnhookWindowsHookEx(_hookID);
}
private 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 IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
OnKeyPressed.Invoke(this, ((Keys)vkCode));
}
else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP)
{
int vkCode = Marshal.ReadInt32(lParam);
OnKeyUnpressed.Invoke(this, ((Keys)vkCode));
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}