Global hotkey in console application - c#

Does anyone know how to use the RegisterHotKey/UnregisterHotKey API calls in a console application? I assume that setting up/removing the hotkey is the same, but how do I get the call back when the key was pressed?
Every example I see is for Winforms, and uses protected override void WndProc(ref Message m){...}, which isn't available to me.
update: what I have is below, but the event is never hit. I thought it could be because when you load ConsoleShell it does block further execution, but even if I put SetupHotkey into a different thread nothing happens. Any thoughts?
class Program
{
static void Main(string[] args)
{
new Hud().Init(args);
}
}
class Hud
{
int keyHookId;
public void Init(string[] args)
{
SetupHotkey();
InitPowershell(args);
Cleanup();
}
private void Cleanup()
{
HotKeyManager.UnregisterHotKey(keyHookId);
}
private void SetupHotkey()
{
keyHookId = HotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Control);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
}
void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
//never executed
System.IO.File.WriteAllText("c:\\keyPressed.txt", "Hotkey pressed");
}
private static void InitPowershell(string[] args)
{
var config = RunspaceConfiguration.Create();
ConsoleShell.Start(config, "", "", args);
}
}

What you can do is Create a hidden window in your Console application which is used to handle the hotkey notification and raise an event.
The code HERE demonstrates the principal. HERE is an article on handling messages in a Console application, using this you should be able to enhance HotKeyManager to run in a Console Application.
The following update to the HotKeyManager creates a background thread which runs the message loop and handles the windows messages.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleHotKey
{
public static class HotKeyManager
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
int id = System.Threading.Interlocked.Increment(ref _id);
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)modifiers, (uint)key);
return id;
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hwnd, id, modifiers, key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
if (HotKeyManager.HotKeyPressed != null)
{
HotKeyManager.HotKeyPressed(null, e);
}
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static HotKeyManager()
{
Thread messageLoop = new Thread(delegate()
{
Application.Run(new MessageWindow());
});
messageLoop.Name = "MessageLoopThread";
messageLoop.IsBackground = true;
messageLoop.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = this.Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
HotKeyManager.OnHotKeyPressed(e);
}
base.WndProc(ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError=true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
public class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
this.Key = key;
this.Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
uint param = (uint)hotKeyParam.ToInt64();
Key = (Keys)((param & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)(param & 0x0000ffff);
}
}
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}
Here is an example of using HotKeyManager from a Console application
using System;
using System.Windows.Forms;
namespace ConsoleHotKey
{
class Program
{
static void Main(string[] args)
{
HotKeyManager.RegisterHotKey(Keys.A, KeyModifiers.Alt);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
Console.ReadLine();
}
static void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
Console.WriteLine("Hit me!");
}
}
}

I just wanted to offer an alternative solution.
I was answering a question for someone who was using this script and I figured this might help someone else who has trouble setting up a global key hook.
Edit: Don't forget to add a reference to System.Windows.Forms
You can do this by selecting ProjectšŸ¢‚Add Reference and checking System.Windows.Forms
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleKeyhook
{
class Hooky
{
///////////////////////////////////////////////////////////
//A bunch of DLL Imports to set a low level keyboard hook
///////////////////////////////////////////////////////////
[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);
////////////////////////////////////////////////////////////////
//Some constants to make handling our hook code easier to read
////////////////////////////////////////////////////////////////
private const int WH_KEYBOARD_LL = 13; //Type of Hook - Low Level Keyboard
private const int WM_KEYDOWN = 0x0100; //Value passed on KeyDown
private const int WM_KEYUP = 0x0101; //Value passed on KeyUp
private static LowLevelKeyboardProc _proc = HookCallback; //The function called when a key is pressed
private static IntPtr _hookID = IntPtr.Zero;
private static bool CONTROL_DOWN = false; //Bool to use as a flag for control key
public static void Main()
{
_hookID = SetHook(_proc); //Set our hook
Application.Run(); //Start a standard application method loop
}
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) //A Key was pressed down
{
int vkCode = Marshal.ReadInt32(lParam); //Get the keycode
string theKey = ((Keys)vkCode).ToString(); //Name of the key
Console.Write(theKey); //Display the name of the key
if (theKey.Contains("ControlKey")) //If they pressed control
{
CONTROL_DOWN = true; //Flag control as down
}
else if (CONTROL_DOWN && theKey == "B") //If they held CTRL and pressed B
{
Console.WriteLine("\n***HOTKEY PRESSED***"); //Our hotkey was pressed
}
else if (theKey == "Escape") //If they press escape
{
UnhookWindowsHookEx(_hookID); //Release our hook
Environment.Exit(0); //Exit our program
}
}
else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
{
int vkCode = Marshal.ReadInt32(lParam); //Get Keycode
string theKey = ((Keys)vkCode).ToString(); //Get Key name
if (theKey.Contains("ControlKey")) //If they let go of control
{
CONTROL_DOWN = false; //Unflag control
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam); //Call the next hook
}
}
}

I came up with a solution based on Chris' answer that uses WPF instead of WinForms:
public sealed class GlobalHotkeyRegister : IGlobalHotkeyRegister, IDisposable
{
private const int WmHotkey = 0x0312;
private Application _app;
private readonly Dictionary<Hotkey, Action> _hotkeyActions;
public GlobalHotkeyRegister()
{
_hotkeyActions = new Dictionary<Hotkey, Action>();
var startupTcs = new TaskCompletionSource<object>();
Task.Run(() =>
{
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
_app = new Application();
_app.Startup += (s, e) => startupTcs.SetResult(null);
_app.Run();
});
startupTcs.Task.Wait();
}
public void Add(Hotkey hotkey, Action action)
{
_hotkeyActions.Add(hotkey, action);
var keyModifier = (int) hotkey.KeyModifier;
var key = KeyInterop.VirtualKeyFromKey(hotkey.Key);
_app.Dispatcher.Invoke(() =>
{
if (!RegisterHotKey(IntPtr.Zero, hotkey.GetHashCode(), keyModifier, key))
throw new Win32Exception(Marshal.GetLastWin32Error());
});
}
public void Remove(Hotkey hotkey)
{
_hotkeyActions.Remove(hotkey);
_app.Dispatcher.Invoke(() =>
{
if (!UnregisterHotKey(IntPtr.Zero, hotkey.GetHashCode()))
throw new Win32Exception(Marshal.GetLastWin32Error());
});
}
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
{
if (msg.message != WmHotkey)
return;
var key = KeyInterop.KeyFromVirtualKey(((int) msg.lParam >> 16) & 0xFFFF);
var keyModifier = (KeyModifier) ((int) msg.lParam & 0xFFFF);
var hotKey = new Hotkey(keyModifier, key);
_hotkeyActions[hotKey]();
}
public void Dispose()
{
_app.Dispatcher.InvokeShutdown();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
}
public class Hotkey
{
public Hotkey(KeyModifier keyModifier, Key key)
{
KeyModifier = keyModifier;
Key = key;
}
public KeyModifier KeyModifier { get; }
public Key Key { get; }
#region ToString(), Equals() and GetHashcode() overrides
}
[Flags]
public enum KeyModifier
{
None = 0x0000,
Alt = 0x0001,
Ctrl = 0x0002,
Shift = 0x0004,
Win = 0x0008,
NoRepeat = 0x4000
}
To use this, you need to add references to PresentationFramework.dll and WindowsBase.dll.
public static void Main()
{
using (var hotkeyManager = new GlobalHotkeyManager())
{
var hotkey = new Hotkey(KeyModifier.Ctrl | KeyModifier.Alt, Key.S);
hotkeyManager.Add(hotkey, () => System.Console.WriteLine(hotkey));
System.Console.ReadKey();
}
}

Changed the HotKeyManager class
public static class HotKeyManager
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, HotKeyEventArgs.KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, Interlocked.Increment(ref _id), (uint)modifiers, (uint)key);
return Interlocked.Increment(ref _id);
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
private delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
private delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hWnd: hwnd, id: id, fsModifiers: modifiers, vk: key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
HotKeyPressed?.Invoke(null, e);
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static HotKeyManager()
{
new Thread(delegate ()
{
Application.Run(new MessageWindow());
})
{
Name = "MessageLoopThread",
IsBackground = true
}.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
var e = new HotKeyEventArgs(hotKeyParam: m.LParam);
OnHotKeyPressed(e);
}
base.WndProc(m: ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
Class HotKeyEventArgs:
public partial class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
Key = key;
Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
Key = (Keys)(((uint)hotKeyParam.ToInt64() & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)((uint)hotKeyParam.ToInt64() & 0x0000ffff);
}
}
And class: HotKeyEventArgs
public partial class HotKeyEventArgs
{
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}

Related

Could detect windows touch keyboard has popped up or not?

Currently, I can open & close the windows touch keyboard,
I don't know how to detect the touch keyboard is popped up or not,
so I try to get windows style value,
when I click the button at the top-right corner, to hiding the touch keyboard, please reference this image
the windows style value is the same as I did not hide the touch keyboard,
Is there any way to detect this situation?
---Test code as below -------------------------------------------------------
namespace WindowsFormsApp7
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CloseWindowsTouchKeyboard();
}
private void btnOpen_Click(object sender, EventArgs e)
{
OpenWindowsTouchKeyboard();
}
private void btnClose_Click(object sender, EventArgs e)
{
CloseWindowsTouchKeyboard();
}
private void btnGetWindowLong_Click(object sender, EventArgs e)
{
GetWindowLong();
}
private const string IPTIP_MAIN_WINDOW_CLASS_NAME = "IPTip_Main_Window";
private const int GWL_STYLE = -16;
private const int WM_SYSCOMMAND = 274;
private const uint SC_CLOSE = 61536;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool PostMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
public static void OpenWindowsTouchKeyboard()
{
string strFilePath = #"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe";
if (!File.Exists(strFilePath))
{
return;
}
Process.Start(strFilePath);
}
public static void CloseWindowsTouchKeyboard()
{
IntPtr TouchhWnd;
TouchhWnd = FindWindow(IPTIP_MAIN_WINDOW_CLASS_NAME, null);
if (TouchhWnd == IntPtr.Zero)
{
return;
}
PostMessage(TouchhWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
}
public static void GetWindowLong()
{
IntPtr tKeyboardHandle = FindWindow(IPTIP_MAIN_WINDOW_CLASS_NAME, null);
if (tKeyboardHandle != IntPtr.Zero)
{
uint iStyle = GetWindowLong(tKeyboardHandle, GWL_STYLE);
Console.WriteLine($"iStyle: {iStyle}");
}
}
}
}
You'll need to utilize ComImport to define interfaces manually:
[ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
class ImmersiveShellBroker { }
[ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IImmersiveShellBroker
{
void M();
IInputHostManagerBroker GetInputHostManagerBroker();
}
[ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IInputHostManagerBroker { void GetIhmLocation(out Bounds rect, out DisplayMode mode); }
[StructLayout(LayoutKind.Sequential)]
struct Bounds { public int Left, Top, Right, Bottom; }
enum DisplayMode { None = 0, Floating = 2, Docked = 3 }
Then you can use them to check the touch keyboard display mode like so,
IImmersiveShellBroker broker = (IImmersiveShellBroker)(new ImmersiveShellBroker());
IInputHostManagerBroker inputHost = broker.GetInputHostManagerBroker();
Marshal.ReleaseComObject(broker);
// if mode != DisplayMode.None then the keyboard is open
DisplayMode mode;
inputHost.GetIhmLocation(out _, out mode);
you can also cache the inputHost somewhere, but it's not necessary.

configurable hotkeys for button presses c#

Im looking for a way that so Users that use my Program can change the Hotkeys theirself so its not bound to D,F12,K,F,A and so on.. i kinda want that ppl can change it via a Textbox or maybe via a Settings File. Been Stuck on it for couple Weeks now and i just cant find a way to make it happen, its my first Program im working on.
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.D:
// stuff
Salvagebtn.PerformClick();
break;
case Keys.F12:
// stuff
pausebtn.PerformClick();
break;
case Keys.K:
//stuff
Geardropbtn.PerformClick();
break;
case Keys.F:
//stuff
Gamblebtn.PerformClick();
break;
case Keys.A:
//stuff
LeftClickSpambtn.PerformClick();
break;
case Keys.H:
// stuff
openGRbtn.PerformClick();
break;
case Keys.B:
//stuff
gemupbtn.PerformClick();
break;
}
}
I had a few minutes, during commercials. So, I wrote the following:
EDIT : Code wrapped with a global keyboard handler. You will also need to add a reference to PresentationCore and WindowsBase ...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
[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);
[DllImport("user32.dll")]
static extern int MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
S_WM_KEYDOWN = 0x0104,
S_WM_KEYUP = 0x0105;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static Form1 hookForm;
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);
static bool bShortcutPressed;
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (hookForm.Handle != GetForegroundWindow())
{
if (nCode >= 0 && ((wParam == (IntPtr)WM_KEYDOWN) || (wParam == (IntPtr)S_WM_KEYDOWN)))
{
int vkCode = Marshal.ReadInt32(lParam);
bool bCtrl = (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl));
bool bAlt = (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt));
bool bShift = (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));
Keys hookKey = (Keys)vkCode;
hookKey = (bCtrl) ? ((Keys.Control | hookKey)) : hookKey;
hookKey = (bAlt) ? ((Keys.Alt | hookKey)) : hookKey;
hookKey = (bShift) ? ((Keys.Shift | hookKey)) : hookKey;
Debug.Print($"hookKey {hookKey} {bCtrl} {bAlt} {bShift}");
if (!bShortcutPressed && dicTest.ContainsValue(hookKey))
{
hookForm.OnKeyDown(new System.Windows.Forms.KeyEventArgs(hookKey));
bShortcutPressed = true; Debug.Print($"{bShortcutPressed}");
}
}
if (nCode >= 0 && (((wParam == (IntPtr)WM_KEYUP) || (wParam == (IntPtr)S_WM_KEYUP)))) { bShortcutPressed = false; Debug.Print($"{bShortcutPressed}"); }
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
static Dictionary<string, Keys> dicTest = new Dictionary<string, Keys>();
public void AddHotKey(Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false)
{
KeyDown += delegate (object sender, System.Windows.Forms.KeyEventArgs e) { if (IsHotkey(e, key, ctrl, shift, alt)) { function(); } };
}
public bool IsHotkey(System.Windows.Forms.KeyEventArgs eventData, Keys key, bool ctrl = false, bool shift = false, bool alt = false) =>
eventData.KeyCode == key && eventData.Control == ctrl && eventData.Shift == shift && eventData.Alt == alt;
public Form1() => InitializeComponent();
private void Form1_Load(object sender, EventArgs e)
{
_hookID = SetHook(_proc);
hookForm = this;
KeyPreview = true;
String[] names = Enum.GetNames(typeof(Keys));
foreach (string key in names) { comboBox1.Items.Add(key); }
comboBox1.SelectedItem = comboBox1.Items[0];
KeyDown += Form1_KeyDown;
}
private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
Debug.Print($"Form1_KeyDown : {e.KeyData}");
}
protected override void OnFormClosing(FormClosingEventArgs e) => UnhookWindowsHookEx(_hookID);
private void button1_Click(object sender, EventArgs e)
{
Keys hookKey = (Keys)Enum.Parse(typeof(Keys), comboBox1.Text);
hookKey = (checkBox1.Checked) ? ((Keys.Control | hookKey)) : hookKey;
hookKey = (checkBox2.Checked) ? ((Keys.Alt | hookKey)) : hookKey;
hookKey = (checkBox3.Checked) ? ((Keys.Shift | hookKey)) : hookKey;
if (!dicTest.ContainsValue(hookKey))
{
Debug.Print($"Going to add : {hookKey} : to our Shortcut Dictionary<>");
dicTest.Add("test", hookKey);
AddHotKey(() => { button2.PerformClick(); }, (Keys)new KeysConverter().ConvertFrom(comboBox1.Text), checkBox1.Checked, checkBox3.Checked, checkBox2.Checked);
//checkBox1.Enabled = checkBox2.Enabled = checkBox3.Enabled = comboBox1.Enabled = button1.Enabled = false;
label2.Text = "Go ahead and tryout your Shortcut now ...";
}
else { label2.Text = "Shortcut key is already stored in our Dictionary<> ..."; }
}
private void button2_Click(object sender, EventArgs e) { Debug.Print($"Button2 was clicked"); }
}
}
You're presented with:
Trying the desired shortcut out:
Just an example, to show there are many ways to do things.
Easy to write them out to a file or something. If you were going to ... I'd change the AddHotKey Action param to a string which contains a function name and then using different methods you can have it invoke that function.
Really though, I'd incorporate a Dictionary as the one talked about below.
There's a myriad of different ways you could handle this. Personally, I'd opt for using a Dictionary with the Keycode as the Key, and a delegate/action as the value. Then on the keypress, you just call the function in the dictionary that's associated with that keycode. Something along the lines of:
Dictionary<KeyCode, System.Action> keyDict = new Dictionary<KeyCode, System.Action>();
// populate it however you wish. Whether it's user entry or from a file or something.
keyDict.Add(KeyCode.D, SomeFunction);
If you want to change the delegate in the dictionary:
keyDict[KeyCode.D] = SomeOtherFunction;
Then later in the event handler:
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
keyDict[e.KeyCode].Invoke();
}
public void SomeFunction()
{
Console.WriteLine("called some function");
}
(This question might have a bit more detail if needed: Create dictionary with dynamic keys in C#)

How to detect CTRL+keypress shortcut at any time in C#

In a C# Windows Form I would like transcode information:
when user press CTRL+I, the app detect the key press combination, it takes the code in the clipboard and transcode it.
I find out this code:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (msg.Msg)
{
case 0x100:
case 0x104:
switch (keyData)
{
case Keys.Control | Keys.I:
MessageBox.Show("Ctrl + I pressed");
break;
}
break;
}
return base.ProcessCmdKey(ref msg, keyData);
}
This works fine when windows form has focus.
I would like detect the combination when the app is minimized as tray icon.
SOLUTION:
keyboardhook.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TrayIconForm
{
public static class Constants
{
public const int NOMOD = 0x0000;
public const int ALT = 0x0001;
public const int CTRL = 0x0002;
public const int SHIFT = 0x0004;
public const int WIN = 0x0008;
public const int WM_HOTKEY_MSG_ID = 0x0312;
}
public class KeyHandler
{
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private int modifier;
private int key;
private IntPtr hWnd;
private int id;
public KeyHandler(int modifier, Keys key, Form form)
{
this.modifier = modifier;
this.key = (int)key;
this.hWnd = form.Handle;
id = this.GetHashCode();
}
public override int GetHashCode()
{
return modifier ^ key ^ hWnd.ToInt32();
}
public bool Register()
{
return RegisterHotKey(hWnd, id, modifier, key);
}
public bool Unregiser()
{
return UnregisterHotKey(hWnd, id);
}
}
}
Information.cs (Form):
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TrayIconForm
{
public partial class Information : Form
{
private KeyHandler ghk;
public Information()
{
InitializeComponent();
ghk = new KeyHandler(Constants.CTRL, Keys.I, this);
ghk.Register();
}
private void HandleHotkey()
{
string s = Get_Copy();
notifyIcon1.BalloonTipText = s;
notifyIcon1.BalloonTipTitle = "You have pressed CTRL+i";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(500);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == Constants.WM_HOTKEY_MSG_ID)
HandleHotkey();
base.WndProc(ref m);
}
private string Get_Copy()
{
string r;
// Retrieves data from Clipboard
IDataObject iData = Clipboard.GetDataObject();
// Is Data Text?
if (iData.GetDataPresent(DataFormats.Text))
r = (String)iData.GetData(DataFormats.Text);
else
r = "nothing";
return r;
}
private void Information_Resize(object sender, EventArgs e)
{
if (FormWindowState.Minimized == this.WindowState)
{
notifyIcon1.BalloonTipText = "My application still working...";
notifyIcon1.BalloonTipTitle = "My Sample Application";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(500);
this.Hide();
}
else if (FormWindowState.Normal == this.WindowState)
{
notifyIcon1.Visible = false;
}
}
}
}
This works just instantiate it, subscibe to the event and you are done. It s not my script. A while ago I searched long for this but I found it and it works great. There were you subscribed to the event you can look if the pressed key (stored in the event args) is your key and when it is you can look with Keyboard.iskeydown(key.lctrl) if control is pressed to and then you can do what the combination should do.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace <yournamespace>
{
public class LowLevelKeyboardListener
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
[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<KeyPressedArgs> OnKeyPressed;
private LowLevelKeyboardProc _proc;
private IntPtr _hookID = IntPtr.Zero;
public LowLevelKeyboardListener()
{
_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);
if (OnKeyPressed != null) { OnKeyPressed(this, new KeyPressedArgs(KeyInterop.KeyFromVirtualKey(vkCode))); }
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
public class KeyPressedArgs : EventArgs
{
public Key KeyPressed { get; private set; }
public KeyPressedArgs(Key key)
{
KeyPressed = key;
}
}
}
[DllImport("user32.dll")]
public static extern int GetAsyncKeyState(Keys vKeys);
then
var ctrl = GetAsyncKeyState(Keys.ControlKey) & 0x8000;
var key = GetAsyncKeyState(Keys.F10) & 0x8000; //F10 for example
if(ctrl != 0 && key != 0)
{
//do sth
}

ContextMenu on NotifyIcon with C# and Windows CE

I have a C# application running on Windows CE 6.0 with CF 3.5, I think. The application starts minimized in the system tray with an icon. It's working so far but now I want to add context menu to this icon and I really don't get behind the secret with this context menu. The Icon is created with this code:
public event EventHandler Click;
private MyMessageWindow messageWindow;
private int uID = 5000;
private System.Drawing.Icon m_Icon;
public NotifyIcon()
{
messageWindow = new MyMessageWindow(this);
messageWindow.uID = uID;
}
public System.Drawing.Icon Icon
{
set { m_Icon = value; }
}
~NotifyIcon()
{
Remove();
}
public void Add(IntPtr hIcon)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, hIcon);
}
public void Add(string IconRes)
{
IntPtr hIcon = LoadIcon(GetModuleHandle(null), IconRes);
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, hIcon);
}
public void Add(System.Drawing.Icon icon)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, icon.Handle);
}
public void Add()
{
if (m_Icon != null)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint) uID, m_Icon.Handle);
}
}
public void Remove()
{
NotifyMessage(messageWindow.Hwnd, NIM_DELETE, (uint) uID, IntPtr.Zero);
}
public void Modify(IntPtr hIcon)
{
NotifyMessage(messageWindow.Hwnd, NIM_MODIFY, (uint) uID, hIcon);
}
private void NotifyMessage(IntPtr hwnd, int dwMessage, uint uID, IntPtr hIcon)
{
NOTIFYICONDATA notdata = new NOTIFYICONDATA();
notdata.cbSize = 152;
notdata.hIcon = hIcon;
notdata.hWnd = hwnd;
notdata.uCallbackMessage = WM_NOTIFY_TRAY;
notdata.uFlags = NIF_MESSAGE | NIF_ICON;
notdata.uID = uID;
int ret = Shell_NotifyIcon(dwMessage, ref notdata);
}
//Definition of the message.
private const int NIF_MESSAGE = 0x00000001;
private const int NIF_ICON = 0x00000002;
internal const int WM_LBUTTONDOWN = 0x0201;
internal const int NIM_ADD = 0x00000000;
internal const int NIM_MODIFY = 0x00000001;
internal const int NIM_DELETE = 0x00000002;
//Custom message
internal const int WM_NOTIFY_TRAY = 0x0400 + 2001;
internal struct NOTIFYICONDATA
{
internal int cbSize;
internal IntPtr hWnd;
internal uint uID;
internal uint uFlags;
internal uint uCallbackMessage;
internal IntPtr hIcon;
}
[DllImport("coredll.dll")]
internal static extern int Shell_NotifyIcon(
int dwMessage, ref NOTIFYICONDATA pnid);
[DllImport("coredll.dll")]
internal static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("coredll.dll")]
internal static extern int ShowWindow(
IntPtr hWnd,
int nCmdShow);
[DllImport("coredll.dll")]
internal static extern IntPtr GetFocus();
[DllImport("coredll.dll")]
internal static extern IntPtr LoadIcon(IntPtr hInst, string IconName);
[DllImport("coredll.dll")]
internal static extern IntPtr GetModuleHandle(String lpModuleName);
#endregion
#region MessageWindow
internal class MyMessageWindow : Microsoft.WindowsCE.Forms.MessageWindow
{
private int _uID = 0;
private NotifyIcon notifyIcon;
public MyMessageWindow(NotifyIcon notIcon)
{
notifyIcon = notIcon;
}
public int uID
{
set { _uID = value; }
}
protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message msg)
{
if (msg.Msg == WM_NOTIFY_TRAY)
{
if ((int) msg.LParam == WM_LBUTTONDOWN)
{
if ((int) msg.WParam == _uID)
{
if (notifyIcon.Click != null)
notifyIcon.Click(notifyIcon, null);
}
}
}
}
}
I know windows CE is not state of the art but I need to do this because of old hardware.

Global hotkey taking exclusive use of keys

I'm currently using this code for hotkey manager that i found on another post here:
public class HotKeyReader
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, KeyModifiers modifiers)
{
int id = System.Threading.Interlocked.Increment(ref _id);
RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)key);
return id;
}
public static bool UnregisterHotKey(int id)
{
return UnregisterHotKey(_wnd.Handle, id);
}
protected static void OnHotKeyPressed(HotKeyEventArgs e)
{
if (HotKeyReader.HotKeyPressed != null)
{
HotKeyReader.HotKeyPressed(null, e);
}
}
private static MessageWindow _wnd = new MessageWindow();
private class MessageWindow : Form
{
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
HotKeyReader.OnHotKeyPressed(e);
}
base.WndProc(ref m);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
public class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
this.Key = key;
this.Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
uint param = (uint)hotKeyParam.ToInt64();
Key = (Keys)((param & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)(param & 0x0000ffff);
}
}
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
But when a hotkey is registered, for example, just using "a", you can no longer use "a" anywhere else in windows. Is there anyway to stop this from happening? Or is there a better code out there that does the same.
A keyboard hook, installed with SetWindowsHook, would allow observing the keystroke without affecting its delivery to the application.

Categories