How can I capture Ctrl + Alt + K + P keys on a C# form?
thanks
It is a chord, you cannot detect it without memorizing having seen the first keystroke of the chord. This works:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private bool prefixSeen;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (prefixSeen) {
if (keyData == (Keys.Alt | Keys.Control | Keys.P)) {
MessageBox.Show("Got it!");
}
prefixSeen = false;
return true;
}
if (keyData == (Keys.Alt | Keys.Control | Keys.K)) {
prefixSeen = true;
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
I'm not sure if you can. What you CAN do however, is the way Visual Studio does it.
It has shortcuts like Ctrl + K, C. You first press Ctrl+K, then hold down Ctrl and press C. In your case, you could check for Ctrl+Alt+K,P.
You can first check for only Ctrl+Alt+K as done in the other answers, then set a member variable/flag to indicate Ctrl+Alt+K has been pressed. In the same method where you check for K you can check for P, and if the flag you just set was set to true, do whatever you need to do. Otherwise set the flag back to false.
Rough pseudo-code:
private bool m_bCtrlAltKPressed = false;
public void KeyDown() {
if (Ctrl+Alt+K)
{
m_bCtrlAltKPressed = true;
}
else if (Ctrl+Alt+P && m_bCtrlAltKPressed) {
//do stuff
}
else {
m_bCtrlAltKPressed = false;
}
}
Hope that's clear enough.
MessageFilters can help you in this case.
public class KeystrokMessageFilter : System.Windows.Forms.IMessageFilter
{
public KeystrokMessageFilter() { }
#region Implementation of IMessageFilter
public bool PreFilterMessage(ref Message m)
{
if ((m.Msg == 256 /*0x0100*/))
{
switch (((int)m.WParam) | ((int)Control.ModifierKeys))
{
case (int)(Keys.Control | Keys.Alt | Keys.K):
MessageBox.Show("You pressed ctrl + alt + k");
break;
//This does not work. It seems you can only check single character along with CTRL and ALT.
//case (int)(Keys.Control | Keys.Alt | Keys.K | Keys.P):
// MessageBox.Show("You pressed ctrl + alt + k + p");
// break;
case (int)(Keys.Control | Keys.C): MessageBox.Show("You pressed ctrl+c");
break;
case (int)(Keys.Control | Keys.V): MessageBox.Show("You pressed ctrl+v");
break;
case (int)Keys.Up: MessageBox.Show("You pressed up");
break;
}
}
return false;
}
#endregion
}
Now in your C# WindowsForm, register the MessageFilter for capturing key-strokes and combinations.
public partial class Form1 : Form
{
KeystrokMessageFilter keyStrokeMessageFilter = new KeystrokMessageFilter();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Application.AddMessageFilter(keyStrokeMessageFilter);
}
}
Somehow it only detects Ctrl + Alt + K. Please Read the comment in MessageFilter code.
You can use GetKeyState to get the status of the other keys when one of the keys in the combination has been pressed. Here's an example on a form.
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace DetectKeyChord
{
public partial class Form1 : Form
{
private const int WM_KEYDOWN = 0x100;
private const int KEY_PRESSED = 0x80;
public Form1()
{
InitializeComponent();
}
public void ShortcutAction()
{
MessageBox.Show("Ctrl+Alt+K+P has been pressed.");
}
private bool IsKeyDown(Keys key)
{
return (NativeMethods.GetKeyState(key) & KEY_PRESSED) == KEY_PRESSED;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
//If any of the keys in the chord have been pressed, check to see if
//the entire chord is down.
if (new[] { Keys.P, Keys.K, Keys.ControlKey, Keys.Menu }.Contains((Keys)m.WParam))
{
if (IsKeyDown(Keys.ControlKey) && IsKeyDown(Keys.Menu) && IsKeyDown(Keys.K) && IsKeyDown(Keys.P))
{
this.ShortcutAction();
}
}
}
base.WndProc(ref m);
}
}
public static class NativeMethods
{
[DllImport("USER32.dll")]
public static extern short GetKeyState(Keys nVirtKey);
}
}
See this great blog post about setting up hotkeys in c#
Christian Liensberger » Blog »
Installing a global hot key with
C#
There's also a good article that explains all of this here
Simple steps to enable Hotkey and ShortcutInput user control - CodeProject
Related
Is it possible to determine the pressed letter key depending on the layout?
Here is code (class):
class GlobalKeyboardHookEventArgs : HandledEventArgs
{
public GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; }
public GlobalKeyboardHook.LowLevelKeyboardInputEvent KeyboardData { get; private set; }
public GlobalKeyboardHookEventArgs(
GlobalKeyboardHook.LowLevelKeyboardInputEvent keyboardData,
GlobalKeyboardHook.KeyboardState keyboardState)
{
KeyboardData = keyboardData;
KeyboardState = keyboardState;
}
}
//Based on https://gist.github.com/Stasonix
class GlobalKeyboardHook : IDisposable
{
public event EventHandler<GlobalKeyboardHookEventArgs> KeyboardPressed;
public GlobalKeyboardHook()
{
_windowsHookHandle = IntPtr.Zero;
_user32LibraryHandle = IntPtr.Zero;
_hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.
_user32LibraryHandle = LoadLibrary("User32");
if (_user32LibraryHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode, $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
.............
and call:
private void OnKeyPressed(object sender, GlobalKeyboardHookEventArgs e)
{
KeysConverter kc = new KeysConverter();
if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown)
{
GrabbedKeys += kc.ConvertToString(e.KeyboardData.VirtualCode);
return;
}
if (e.KeyboardData.VirtualCode != GlobalKeyboardHook.VkSnapshot)
return;
}
Its work fine, if I press on keyboard 's' my programm stored 's', but how I can make same for russian keyboard layout? If keyboard layout is ru-RU, I press 's' and my app stored 'ы'?
I'm working on a small extension for autocad using its .net api.
I'm trying to filter keys using IMessageFilter. And it "sort of" works.
the problem is, its "leaking" first 2 keys I press through... and after that it starts working.
I'm not sure where the bug is occurring (tried posting this at autodesk forum to no avail).
as for the "ps2" , its an instance of autocads "paletteset" object which houses UserControls to fit into acad graphic interface.
I'd appreciate any idea as to why the only first 2 keys get leaked, or methods as to why this is occurring.
here's my code:
[CommandMethod("TestCheck")]
public void MyTab()
{
filter = new MyMessageFilter(ps1, ps2);
filter.OnNumber+=filter_OnNumber;
...
}
private bool keyboardFilterEnabled = false;
public bool KeyboardFilterEnabled
{
get { return keyboardFilterEnabled; }
set
{
keyboardFilterEnabled = value;
if (keyboardFilterEnabled == true)
System.Windows.Forms.Application.AddMessageFilter(filter);
else
System.Windows.Forms.Application.RemoveMessageFilter(filter);
}
}
}
public delegate void NotifyParentDelegate(Keys source);
public class MyMessageFilter : IMessageFilter
{
public MyMessageFilter(PaletteSet p1, PaletteSet p2)
: base()
{
PaletteSet ps1 = p1;
PaletteSet ps2 = p2;
}
public event NotifyParentDelegate OnNumber;
public event NotifyParentDelegate OnLetter;
public event NotifyParentDelegate OnControl;
public const int WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
// Check for the Escape keypress
Keys kc = (Keys)(int)m.WParam & Keys.KeyCode;
if (m.Msg == WM_KEYDOWN && kc == Keys.Escape)
{
return false;
}
if (m.Msg == WM_KEYDOWN && char.IsDigit((char)kc) == true)
{
OnNumber(kc);
return false;
}
return true;
}
return false;
}
}
public void filter_OnNumber(Keys source)
{
ps2.Focus();
if (ps2.KeepFocus != true)
{ps2.KeepFocus=true; }
formDimensions.input_dimension.Focus();
//SendKeys.Send(source.ToString());
}
I'm working on a game for desktop platforms that uses MonoGame. Previously, it was targeted only at Windows and used pure XNA. I'm exploring options to make it support other platforms using MonoGame and my current alternate test platform is Ubuntu 15.04.
One of the features of the game is support for text boxes that behave similarly to the standard windows UI textbox controls. In order to accomplish this, I used code from this StackOverflow answer.
My question is: How can I write a portable version of the functionality from the linked answer in such a way that it will work on alternate desktop platforms (in this case, Ubuntu)? I'm looking for something that will capture keypress events from the game window on both Windows and Ubuntu.
Additional info:
The linked answer overrides the WndProc of the game's window and passes input into the current textbox by firing events (CharEntered, KeyDown, KeyUp). I've refactored the answer's code to a point where I can encapsulate all the Windows functionality in a single "events" class:
internal class Win32KeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
private readonly IntPtr _prevWndProc;
private readonly NativeMethods.WndProc _hookProcDelegate;
public Win32KeyboardEvents(GameWindow window)
{
_hookProcDelegate = HookProc;
_prevWndProc = (IntPtr)NativeMethods.SetWindowLong(window.Handle, NativeMethods.GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(_hookProcDelegate));
}
private IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = NativeMethods.CallWindowProc(_prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case NativeMethods.WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | NativeMethods.DLGC_WANTALLKEYS);
break;
case NativeMethods.WM_KEYDOWN:
if (KeyDown != null) //fire events that are subscribed to by textbox object based on which windows message is received
KeyDown(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_CHAR:
if (CharEntered != null)
CharEntered(null, new Win32CharEnteredEventArgs((char)wParam, lParam.ToInt32()));
break;
}
return returnCode;
}
}
My first attempt for something that would work on Ubuntu was creating a GameComponent-derived object that listened for keyboard input in the component's Update() and fired events appropriately, but this quickly got too complicated.
I've investigated using GTK#, but there are quite a few dependencies that I don't want to install unless GTK# is the best way to go. Here is my GTK# attempt, which is currently untested due to a missing dependency:
internal class CrossPlatformKeyboardEvents : DrawingArea, IKeyboardEvents
{
public event CharEnteredHandler CharEntered = delegate { };
public event KeyEventHandler KeyDown = delegate { };
public event KeyEventHandler KeyUp = delegate { };
public CrossPlatformKeyboardEvents()
{
Application.Init();
Application.Run();
AddEvents((int)EventMask.KeyPressMask);
AddEvents((int)EventMask.KeyReleaseMask);
}
[ConnectBefore]
protected override bool OnKeyPressEvent(EventKey evnt)
{
if (IsPastEvent(evnt))
{
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_PASTE_CODE));
return base.OnKeyPressEvent(evnt);
}
switch (evnt.Key)
{
case Key.Return:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_RETURNKEY_CODE));
break;
case Key.BackSpace:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_BACKSPACE_CODE));
break;
case Key.Tab:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_TAB_CODE));
break;
}
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyDown(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyPressEvent(evnt);
}
[ConnectBefore]
protected override bool OnKeyReleaseEvent(EventKey evnt)
{
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyUp(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyReleaseEvent(evnt);
}
private bool IsPastEvent(EventKey evnt)
{
return (evnt.State & ModifierType.ControlMask) > 0 && (evnt.Key == Key.V || evnt.Key == Key.v);
}
private Keys GetXNAKey(Key key)
{
if ((key >= Key.Key_0 && key <= Key.Key_9) ||
(key >= Key.A && key <= Key.Z))
{
return (Keys) key;
}
if (key >= Key.a && key <= Key.z)
{
return (Keys) (key - 32);
}
return Keys.None;
//switch (key)
//{
//}
}
I got around this by using the TextInput event on GameWindow in MonoGame.
This is an extension that is part of MonoGame but not part of Xna. Currently, I have 2 projects - one for standard Xna and one for Monogame. The Monogame project has links to all the files in my Xna project so they share the same code.
Because of the way my projects are set up, I also had to add a compiler flag (MONO) that excludes this code from being compiled for standard XNA.
Full class (replaces CrossPlatformKeyboardEvents posted in the question):
internal sealed class MonoGameKeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
private readonly GameWindow _window;
public MonoGameKeyboardEvents(GameWindow window)
{
_window = window;
#if !MONO
if (CharEntered != null) //hide warning for "member is not used"
CharEntered(null, null);
#else
_window.TextInput += GameWindow_TextInput;
#endif
}
#if MONO
private void GameWindow_TextInput(object sender, TextInputEventArgs e)
{
if (CharEntered != null)
{
CharEntered(null, new CharEnteredEventArgs(e.Character));
}
}
~MonoGameKeyboardEvents()
{
Dispose(false);
}
#endif
public void Dispose()
{
#if !MONO
}
#else
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
_window.TextInput -= GameWindow_TextInput;
}
#endif
}
I have a application that runs in the background like i may keep my app in the system tray.If it remains on system tray my app will do it's job. Whenever a user press F10 or F9 some works will be done. i tried this:
public partial class Form1 : Form
{
public int a = 1;
[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")]
private static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Int32 vKey);
const int MYACTION_HOTKEY_ID = 1;
public Form1()
{
InitializeComponent();
RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F9);
RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int)Keys.F10);
this.ShowInTaskbar = false;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID && (GetAsyncKeyState(Keys.F9) == -32767))
{
if ((a % 2) != 0)
{
a++;
MessageBox.Show(a.ToString()+"not equal F9");
label1.Text = "not equal F9";
}
if ((a % 2) == 0)
{
a++;
MessageBox.Show(a.ToString()+"equal F9");
label1.Text = " equal F9";
}
}
else if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID && (GetAsyncKeyState(Keys.F10) == -32767))
{
if ((a % 2) != 0)
{
a++;
MessageBox.Show(a.ToString() + "not equal F10");
label1.Text = "not equal F10";
}
if ((a % 2) == 0)
{
a++;
MessageBox.Show(a.ToString() + "equal F10");
label1.Text = " equal F10";
}
}
base.WndProc(ref m);
}
}
As i use set "this.ShowInTaskbar = false" this line it doesn't work.But if i don't set this it works fine.For my app i have to use this line.How can i solve this????
You need to subscribe to certain messages that the operating system sends by means of a native function call like RegisterHotKey(). When you call this function You tell the operating system which window to send the messages to by specifying the Handle of the window, this can be considered an address. When you set ShowInTaskbar = false the handle changes so the operating system will not know where to reach you.
See the first arugment:
RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, 0, (int) Keys.F9);
To resolve your issue you can create a class that derives from NativeWindow which "Provides a low-level encapsulation of a window handle and a window procedure." and from within that class (or at least using that class's handle depending on your implementation), register the hotkeys using a handle that will never change.
public sealed class HotkeyManager : NativeWindow, IDisposable
{
public HotkeyManager()
{
CreateHandle(new CreateParams());
}
protected override void WndProc(ref Message m)
{
if (m.Msg == Constants.WM_HOTKEY)
{
//handle hotkey message
}
base.WndProc(ref m);
}
public void Dispose()
{
DestroyHandle();
}
}
As far as I know, you need to re-register the hotkey whenever you change the "ShowInTaskbar" state.
Someone else had a similar problem; see this thread.
How can I capture Ctrl + Alt + K + P keys on a C# form?
thanks
It is a chord, you cannot detect it without memorizing having seen the first keystroke of the chord. This works:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private bool prefixSeen;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (prefixSeen) {
if (keyData == (Keys.Alt | Keys.Control | Keys.P)) {
MessageBox.Show("Got it!");
}
prefixSeen = false;
return true;
}
if (keyData == (Keys.Alt | Keys.Control | Keys.K)) {
prefixSeen = true;
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
I'm not sure if you can. What you CAN do however, is the way Visual Studio does it.
It has shortcuts like Ctrl + K, C. You first press Ctrl+K, then hold down Ctrl and press C. In your case, you could check for Ctrl+Alt+K,P.
You can first check for only Ctrl+Alt+K as done in the other answers, then set a member variable/flag to indicate Ctrl+Alt+K has been pressed. In the same method where you check for K you can check for P, and if the flag you just set was set to true, do whatever you need to do. Otherwise set the flag back to false.
Rough pseudo-code:
private bool m_bCtrlAltKPressed = false;
public void KeyDown() {
if (Ctrl+Alt+K)
{
m_bCtrlAltKPressed = true;
}
else if (Ctrl+Alt+P && m_bCtrlAltKPressed) {
//do stuff
}
else {
m_bCtrlAltKPressed = false;
}
}
Hope that's clear enough.
MessageFilters can help you in this case.
public class KeystrokMessageFilter : System.Windows.Forms.IMessageFilter
{
public KeystrokMessageFilter() { }
#region Implementation of IMessageFilter
public bool PreFilterMessage(ref Message m)
{
if ((m.Msg == 256 /*0x0100*/))
{
switch (((int)m.WParam) | ((int)Control.ModifierKeys))
{
case (int)(Keys.Control | Keys.Alt | Keys.K):
MessageBox.Show("You pressed ctrl + alt + k");
break;
//This does not work. It seems you can only check single character along with CTRL and ALT.
//case (int)(Keys.Control | Keys.Alt | Keys.K | Keys.P):
// MessageBox.Show("You pressed ctrl + alt + k + p");
// break;
case (int)(Keys.Control | Keys.C): MessageBox.Show("You pressed ctrl+c");
break;
case (int)(Keys.Control | Keys.V): MessageBox.Show("You pressed ctrl+v");
break;
case (int)Keys.Up: MessageBox.Show("You pressed up");
break;
}
}
return false;
}
#endregion
}
Now in your C# WindowsForm, register the MessageFilter for capturing key-strokes and combinations.
public partial class Form1 : Form
{
KeystrokMessageFilter keyStrokeMessageFilter = new KeystrokMessageFilter();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Application.AddMessageFilter(keyStrokeMessageFilter);
}
}
Somehow it only detects Ctrl + Alt + K. Please Read the comment in MessageFilter code.
You can use GetKeyState to get the status of the other keys when one of the keys in the combination has been pressed. Here's an example on a form.
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace DetectKeyChord
{
public partial class Form1 : Form
{
private const int WM_KEYDOWN = 0x100;
private const int KEY_PRESSED = 0x80;
public Form1()
{
InitializeComponent();
}
public void ShortcutAction()
{
MessageBox.Show("Ctrl+Alt+K+P has been pressed.");
}
private bool IsKeyDown(Keys key)
{
return (NativeMethods.GetKeyState(key) & KEY_PRESSED) == KEY_PRESSED;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
//If any of the keys in the chord have been pressed, check to see if
//the entire chord is down.
if (new[] { Keys.P, Keys.K, Keys.ControlKey, Keys.Menu }.Contains((Keys)m.WParam))
{
if (IsKeyDown(Keys.ControlKey) && IsKeyDown(Keys.Menu) && IsKeyDown(Keys.K) && IsKeyDown(Keys.P))
{
this.ShortcutAction();
}
}
}
base.WndProc(ref m);
}
}
public static class NativeMethods
{
[DllImport("USER32.dll")]
public static extern short GetKeyState(Keys nVirtKey);
}
}
See this great blog post about setting up hotkeys in c#
Christian Liensberger » Blog »
Installing a global hot key with
C#
There's also a good article that explains all of this here
Simple steps to enable Hotkey and ShortcutInput user control - CodeProject