In my console application, I would like to be able to detect for key presses somewhat like in mono, where you can get a list of the currently pressed keys and check if a key is on the list, or detect if a key is being pressed. My problem with using
if( Console.KeyAvailable ) k = Console.ReadKey( true ).Key;
is the fact that there will be a delay after I press the first key. You can see an example of this if you open notepad and hold down "A". A will be pressed, then a delay, and then A will be spammed.
How can I get keyboard input without the delay in between presses? I'm not afraid to use low level functions such as hooking into kernel32.dll
So I wrote some code, based off what I've read so far.
Step 1: Copy the following code into your console application. It must be STAThread or it will throw an error. Put in the commands you want to use in the switch statement. All other keys will be blocked by ReadKey(true).
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System;
using System.Windows.Forms;
using System.Windows.Input;
namespace ConsoleApplication10
{
class Program
{
[STAThread]
static void Main(string[] args)
{
KeyListener.RegisterHotKey(Keys.A);
KeyListener.HotKeyPressed += new EventHandler<HotKeyEventArgs>(KeyListener_HotKeyPressed);
while (true)
{
Console.ReadKey(true);
}
}
static void KeyListener_HotKeyPressed(object sender, HotKeyEventArgs e)
{
switch (e.Key)
{
case Keys.A:
{
Console.WriteLine("Do stuff");
return;
}
default:
return;
}
}
}
}
Step 2: Add a reference to System.Windows.Forms. You need this to have a hidden Form that is necessary for the message loop for the keyboard hook.
Step 3: Add the following static class. It does all the heavy lifting of the keyboard hooks for you, so you don't have to do it.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApplication10
{
public static class KeyListener
{
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 int RegisterHotKey(Keys key)
{
_windowReadyEvent.WaitOne();
int id = System.Threading.Interlocked.Increment(ref _id);
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)KeyModifiers.None, (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 (KeyListener.HotKeyPressed != null)
{
KeyListener.HotKeyPressed(null, e);
}
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static KeyListener()
{
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);
KeyListener.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
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}
Step 4:
Now, there's still a delay. It's way more elegant, but you still have the operating system fighting you. So what to do?
You have two options.
a) You implement a timing option and you simply repeat the action for as long as a key is down on the timer tick event. You can either copy the code or merge it with the hotkey approach that I've given you.
See here for details: Removing the delay after KeyDown event?
private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;
// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
public MainForm()
{
InitializeComponent();
_movementTimer.Tick += movementTimer_Tick;
}
private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}
private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}
// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}
_DoMovement();
_movementTimer.Start();
}
public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}
if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}
b) In your Main method, you get the delay setting, set the delay programmatically to the lowest setting, and on Application Exit, you set it back to its original setting.
See here for where to find it in the registry: https://superuser.com/questions/388160/keyboard-repeat-rate-repeat-delay-values-in-win7
And how to read/write to the registry: https://msdn.microsoft.com/en-us/library/microsoft.win32.registry_methods(v=vs.110).aspx
Note: With this approach, there's still a small delay. It's small, but it's there. Good luck.
Related
I am trying to create global mouse hotkeys in my program using SetWindowsHookEx(). So far I have tried to create them like in one post I have seen here but, I dont really know how to finsh this.
The problem is currently that I dont exactly know what _globalMouseHookCallback is.
This is what I have written so far:
class GlobalHotkey
{
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
internal delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private IntPtr _hGlobalMouseHook;
MainWindow _m;
private const int WH_KEYBOARD_LL = 13;
private const int WH_MOUSE_LL = 14;
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_RBUTTONUP = 0x0205;
private static IntPtr hook = IntPtr.Zero;
public GlobalHotkey(MainWindow m)
{
_m = m;
}
public void SetUpHook()
{
_m.rtbLog.AppendText("Setting up global Hotkey \n");
_globalMouseHookCallback = LowLevelMouseProc;
_hGlobalMouseHook = SetWindowsHookEx(WH_MOUSE_LL, _globalMouseHookCallback, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
if (_hGlobalMouseHook == IntPtr.Zero)
{
_m.rtbLog.AppendText("Unable to set up global mouse hook\n");
}
}
public void ClearHook()
{
_m.rtbLog.AppendText("Deleting global mouse hook\n");
if (_hGlobalMouseHook != IntPtr.Zero)
{
if (!UnhookWindowsHookEx(_hGlobalMouseHook))
{
_m.rtbLog.AppendText("Unable to delete global mouse hook\n");
}
_hGlobalMouseHook = IntPtr.Zero;
}
}
public int LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
var wmMouse = wParam;
if (wmMouse == (IntPtr)WM_LBUTTONDOWN)
{
_m.rtbLog.AppendText("Right Mouse down");
}
if (wmMouse == (IntPtr)WM_LBUTTONUP)
{
_m.rtbLog.AppendText("Left Mouse up");
}
if (wmMouse == (IntPtr)WM_RBUTTONDOWN)
{
_m.rtbLog.AppendText("Right Mouse down");
}
if (wmMouse == (IntPtr)WM_RBUTTONUP)
{
_m.rtbLog.AppendText("Right Mouse up");
}
}
return CallNextHookEx(_hGlobalMouseHook, nCode, wParam, lParam);
}
}
This global hotkey stuff is pretty hard is there like a tutorial that explains it easily for newbs like me :P ?
EDIT
So I tried adapting Koby Ducks example to my code.
This is my Hotkey class:
class GlobalHotkey
{
MainWindow _m;
private static readonly object sSyncObj = new object();
private static readonly HashSet<Key> sDownKeys = new HashSet<Key>();
private static readonly Dictionary<Key, Action> sPressActions = new Dictionary<Key, Action>();
private static readonly Dictionary<Key, Action> sReleaseActions = new Dictionary<Key, Action>();
public GlobalHotkey(MainWindow m)
{
_m = m;
}
public static void ProcessKeyDown(KeyEventArgs args)
{
var key = args.Key;
var action = default(Action);
lock (sSyncObj)
{
if (!sDownKeys.Contains(key))
{
sDownKeys.Add(key);
if (sPressActions.TryGetValue(key, out action))
{
args.Handled = true;
}
}
}
action.Invoke();
}
public static void ProcessKeyUp(KeyEventArgs args)
{
var key = args.Key;
var action = default(Action);
lock (sSyncObj)
{
if (sDownKeys.Remove(key))
{
if (sReleaseActions.TryGetValue(key, out action))
{
args.Handled = true;
}
}
}
action.Invoke();
}
public static void AttachPressAction(Key key, Action action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
lock (sSyncObj)
{
sPressActions.Add(key, action);
}
}
public static bool DetachPressAction(Key key)
{
lock (sSyncObj)
{
return sPressActions.Remove(key);
}
}
public static void AttachReleaseAction(Key key, Action action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
lock (sSyncObj)
{
sReleaseActions.Add(key, action);
}
}
public static bool DetachReleaseAction(Key key)
{
lock (sSyncObj)
{
return sReleaseActions.Remove(key);
}
}
}
And I created my action
public void MyTestAction()
{
rtbLog.AppendText("The B key was pressed");
}
myAction = new Action(MyTestAction);
But as soon as I added my Eventhandlers to the PreviewKeyUp- and Down Event it gave me an error saying that the Parameters of ProcessKeyUp- and Down, are not the same as PreviewKeyUp- and Down.
PreviewKeyDown += GlobalHotkey.ProcessKeyDown;
PreviewKeyUp += GlobalHotkey.ProcessKeyUp;
Edit: For input regardless of the currently focused window(aka "global"), see this answer on the Win32 keyboard API.
For input while your app is focused(aka "local"), you could use preview events.
public static class HotKeySystem
{
public static void ProcessKeyDown(object sender, KeyEventArgs args)
{
var key = args.Key;
var action = default(Action);
lock (sSyncObj) {
if (!sDownKeys.Contains(key)) {
sDownKeys.Add(key);
if (sPressActions.TryGetValue(key, out action)) {
args.Handled = true;
}
}
}
// Invoke outside of the lock.
action?.Invoke();
}
public static void ProcessKeyUp(object sender, KeyEventArgs args)
{
var key = args.Key;
var action = default(Action);
lock (sSyncObj) {
if (sDownKeys.Remove(key)) {
if (sReleaseActions.TryGetValue(key, out action)) {
args.Handled = true;
}
}
}
// Invoke outside of the lock.
action?.Invoke();
}
public static void AttachPressAction(KeyCode key, Action action)
{
if (action == null) {
throw new ArgumentNullException(nameof(action));
}
lock (sSyncObj) {
sPressActions.Add(key, action);
}
}
public static bool DetachPressAction(KeyCode key)
{
lock (sSyncObj) {
return sPressActions.Remove(key);
}
}
public static void AttachReleaseAction(KeyCode key, Action action)
{
if (action == null) {
throw new ArgumentNullException(nameof(action));
}
lock (sSyncObj) {
sReleaseActions.Add(key, action);
}
}
public static bool DetachReleaseAction(KeyCode key)
{
lock (sSyncObj) {
return sReleaseActions.Remove(key);
}
}
private static readonly object sSyncObj = new object();
// The keys that are currently down.
private static readonly HashSet<KeyCode> sDownKeys = new HashSet<KeyCode>();
// Actions triggered when a key was up, but is now down.
private static readonly Dictionary<KeyCode, Action> sPressActions = new Dictionary<KeyCode, Action>();
// Actions triggered when a key was down, but is now up.
private static readonly Dictionary<KeyCode, Action> sReleaseActions = new Dictionary<KeyCode, Action>();
}
// When possible, subclass your windows from this to automatically add hotkey support.
public class HotKeyWindow : Window
{
protected override void OnPreviewKeyDown(KeyEventArgs args)
{
HotKeySystem.ProcessKeyDown(this, args);
base.OnPreviewKeyDown(args);
}
protected override void OnPreviewKeyUp(KeyEventArgs args)
{
HotKeySystem.ProcessKeyUp(this, args);
base.OnPreviewKeyUp(args);
}
}
// When not possible, attach event handlers like this:
window.PreviewKeyDown += HotKeySystem.ProcessKeyDown;
window.PreviewKeyUp += HotKeySystem.ProcessKeyUp;
// Use it like this:
HotKeySystem.AttachPressAction(KeyCode.F1, () => {
// F1 hotkey functionality.
});
Regardless of if you're using this method or the Win32 API, consider the implications. If you have 'A' bound, then you won't be able to input 'a' or 'A' into text input controls. One way to work around this is:
public static void ProcessKeyDown(object sender, KeyEventArgs args)
{
// Detect keyboard input controls you may have issues with.
// If one has keyboard focus, skip hotkey processing.
if (Keyboard.FocusedElement is TextBox) {
return;
}
// ...
}
I want to know how to use it....I want to say that if I Click Up and Right Arrow (Form1_KeyDown Event) then timer1.Start();
and When I release the Up and Right Arrow (Form1_KeyUp Event) then timer1.Stop();
I have already Imported the "User32.dll"
using System.Runtime.InteropServices;
[DllImport("User32.dll")]
public static extern short GetAsyncKeyState(Keys ArrowKeys);
so How to use it...I see a lot of website but can't get it
Implement IMessageFilter for you Form and track when the Up/Right Arrows are toggled:
public partial class form1 : Form, IMessageFilter
{
public form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
private bool UpDepressed = false;
private bool RightDepressed = false;
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_KEYDOWN:
if ((Keys)m.WParam == Keys.Up)
{
UpDepressed = true;
}
else if ((Keys)m.WParam == Keys.Right)
{
RightDepressed = true;
}
break;
case WM_KEYUP:
if ((Keys)m.WParam == Keys.Up)
{
UpDepressed = false;
}
else if ((Keys)m.WParam == Keys.Right)
{
RightDepressed = false;
}
break;
}
timer1.Enabled = (UpDepressed && RightDepressed);
label1.Text = timer1.Enabled.ToString();
return false;
}
private void timer1_Tick(object sender, EventArgs e)
{
label2.Text = DateTime.Now.ToString("ffff");
}
}
Here is how you can use it
int keystroke;
byte[] result = BitConverter.GetBytes(GetAsyncKeyState(keystroke));
if (result[0] == 1)
Console.Writeline("the key was pressed after the previous call to GetAsyncKeyState.")
if (result[1] == 0x80)
Console.Writeline("The key is down");
change
[DllImport("User32.dll")]
public static extern short GetAsyncKeyState(Keys ArrowKeys);
to
[DllImport("User32.dll")]
public static extern bool GetAsyncKeyState(Keys ArrowKeys);
async key state should be a bool not a short
I am on the last piece of code before I can finally launch my app into alpha, however I'm stuck trying to get a key combination to register globally. I found a class that seems like it will do what I want (found from here):
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
namespace Hotkey
{
public class Hotkey : IMessageFilter
{
#region Interop
[DllImport("user32.dll", SetLastError = true)]
private static extern int RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, Keys vk);
[DllImport("user32.dll", SetLastError=true)]
private static extern int UnregisterHotKey(IntPtr hWnd, int id);
private const uint WM_HOTKEY = 0x312;
private const uint MOD_ALT = 0x1;
private const uint MOD_CONTROL = 0x2;
private const uint MOD_SHIFT = 0x4;
private const uint MOD_WIN = 0x8;
private const uint ERROR_HOTKEY_ALREADY_REGISTERED = 1409;
#endregion
private static int currentID;
private const int maximumID = 0xBFFF;
private Keys keyCode;
private bool shift;
private bool control;
private bool alt;
private bool windows;
[XmlIgnore]
private int id;
[XmlIgnore]
private bool registered;
[XmlIgnore]
private Control windowControl;
public event HandledEventHandler Pressed;
public Hotkey() : this(Keys.None, false, false, false, false)
{
// No work done here!
}
public Hotkey(Keys keyCode, bool shift, bool control, bool alt, bool windows)
{
// Assign properties
this.KeyCode = keyCode;
this.Shift = shift;
this.Control = control;
this.Alt = alt;
this.Windows = windows;
// Register us as a message filter
Application.AddMessageFilter(this);
}
~Hotkey()
{
// Unregister the hotkey if necessary
if (this.Registered)
{ this.Unregister(); }
}
public Hotkey Clone()
{
// Clone the whole object
return new Hotkey(this.keyCode, this.shift, this.control, this.alt, this.windows);
}
public bool GetCanRegister(Control windowControl)
{
// Handle any exceptions: they mean "no, you can't register" :)
try
{
// Attempt to register
if (!this.Register(windowControl))
{ return false; }
// Unregister and say we managed it
this.Unregister();
return true;
}
catch (Win32Exception)
{ return false; }
catch (NotSupportedException)
{ return false; }
}
public bool Register(Control windowControl)
{
// Check that we have not registered
if (this.registered)
{ throw new NotSupportedException("You cannot register a hotkey that is already registered"); }
// We can't register an empty hotkey
if (this.Empty)
{ throw new NotSupportedException("You cannot register an empty hotkey"); }
// Get an ID for the hotkey and increase current ID
this.id = Hotkey.currentID;
Hotkey.currentID = Hotkey.currentID + 1 % Hotkey.maximumID;
// Translate modifier keys into unmanaged version
uint modifiers = (this.Alt ? Hotkey.MOD_ALT : 0) | (this.Control ? Hotkey.MOD_CONTROL : 0) |
(this.Shift ? Hotkey.MOD_SHIFT : 0) | (this.Windows ? Hotkey.MOD_WIN : 0);
// Register the hotkey
if (Hotkey.RegisterHotKey(windowControl.Handle, this.id, modifiers, keyCode) == 0)
{
// Is the error that the hotkey is registered?
if (Marshal.GetLastWin32Error() == ERROR_HOTKEY_ALREADY_REGISTERED)
{ return false; }
else
{ throw new Win32Exception(); }
}
// Save the control reference and register state
this.registered = true;
this.windowControl = windowControl;
// We successfully registered
return true;
}
public void Unregister()
{
// Check that we have registered
if (!this.registered)
{ throw new NotSupportedException("You cannot unregister a hotkey that is not registered"); }
// It's possible that the control itself has died: in that case, no need to unregister!
if (!this.windowControl.IsDisposed)
{
// Clean up after ourselves
if (Hotkey.UnregisterHotKey(this.windowControl.Handle, this.id) == 0)
{ throw new Win32Exception(); }
}
// Clear the control reference and register state
this.registered = false;
this.windowControl = null;
}
private void Reregister()
{
// Only do something if the key is already registered
if (!this.registered)
{ return; }
// Save control reference
Control windowControl = this.windowControl;
// Unregister and then reregister again
this.Unregister();
this.Register(windowControl);
}
public bool PreFilterMessage(ref Message message)
{
// Only process WM_HOTKEY messages
if (message.Msg != Hotkey.WM_HOTKEY)
{ return false; }
// Check that the ID is our key and we are registerd
if (this.registered && (message.WParam.ToInt32() == this.id))
{
// Fire the event and pass on the event if our handlers didn't handle it
return this.OnPressed();
}
else
{ return false; }
}
private bool OnPressed()
{
// Fire the event if we can
HandledEventArgs handledEventArgs = new HandledEventArgs(false);
if (this.Pressed != null)
{ this.Pressed(this, handledEventArgs); }
// Return whether we handled the event or not
return handledEventArgs.Handled;
}
public override string ToString()
{
// We can be empty
if (this.Empty)
{ return "(none)"; }
// Build key name
string keyName = Enum.GetName(typeof(Keys), this.keyCode);;
switch (this.keyCode)
{
case Keys.D0:
case Keys.D1:
case Keys.D2:
case Keys.D3:
case Keys.D4:
case Keys.D5:
case Keys.D6:
case Keys.D7:
case Keys.D8:
case Keys.D9:
// Strip the first character
keyName = keyName.Substring(1);
break;
default:
// Leave everything alone
break;
}
// Build modifiers
string modifiers = "";
if (this.shift)
{ modifiers += "Shift+"; }
if (this.control)
{ modifiers += "Control+"; }
if (this.alt)
{ modifiers += "Alt+"; }
if (this.windows)
{ modifiers += "Windows+"; }
// Return result
return modifiers + keyName;
}
public bool Empty
{
get { return this.keyCode == Keys.None; }
}
public bool Registered
{
get { return this.registered; }
}
public Keys KeyCode
{
get { return this.keyCode; }
set
{
// Save and reregister
this.keyCode = value;
this.Reregister();
}
}
public bool Shift
{
get { return this.shift; }
set
{
// Save and reregister
this.shift = value;
this.Reregister();
}
}
public bool Control
{
get { return this.control; }
set
{
// Save and reregister
this.control = value;
this.Reregister();
}
}
public bool Alt
{
get { return this.alt; }
set
{
// Save and reregister
this.alt = value;
this.Reregister();
}
}
public bool Windows
{
get { return this.windows; }
set
{
// Save and reregister
this.windows = value;
this.Reregister();
}
}
}
}
my understanding of how it should work is like so:
Hotkey hk = new Hotkey();
hk.KeyCode = Keys.1;
hk.Windows = true;
hk.Pressed += delegate { Console.WriteLine("Windows+1 pressed!"); };
hk.Register(myForm);
and my implementation into my form:
Hotkey hk = new Hotkey();
private void Form1_Load(object sender, EventArgs e)
{
hk.KeyCode = Keys.Alt;
hk.Windows = true;
hk.Pressed += hk_Pressed;
hk.Register(Form1);
}
private void hk_Pressed(object sender, EventArgs e)
{
MessageBox.Show("pressed");
}
but I get an error : Form1 is a type but is used as a variable now frankly I am completely lost as to where I am going wrong. I assumed that the name of the form is what needs passed in Register. I am new to C#, so it could be something that I'm doing fundamentally wrong, but I'm at my wits end here any help would be great.
hk.Register(this);
Just do that instead! It's looking for an instance of Form1.
Change hk.Register(myForm); to hk.Register(this);
It's the same thing as your mainForm
Also, I saw that you posted undernearth Michael's answer saying that it still doesn't fire.
You need to remove hk.Windows = true;
I want to make in my application, than when this application is in background, when I will click F10, the function with loop will be start.
This is my code:
namespace test
{
public partial class Form1 : Form
{
[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);
public Form1() //(lub Form1_Load(object sender, System.EventArgs e))
{
RegisterHotKey(this.Handle,9000, 2, (int) Keys.F10);
InitializeComponent();
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
UnregisterHotKey(this.Handle,9000);
UnregisterHotKey(this.Handle,9001);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case 0x312:
switch (m.WParam.ToInt32())
{
case 9000:
//code
break;
case 9001:
//code
break;
}
break;
}
}
}
}
But don't work :(
Can you help me please?
when I will click F10
Wrong key, you have to type Ctrl+F10. You passed 2 as the 3rd argument to RegisterHotKey(), that's MOD_CONTROL. Using const or enum declarations instead of raw literals help you fall in the pit of success.
How can I create an application that performs an action with keyboard shortcut (App must be unvisible). For example Shows MessageBox when user press Ctrl + Alt + W.
One solution would be to use interop and use the Win32 RegisterHotKey API. Here is a quick and dirty example I just put together so it is not well tested and I am not sure that there are no unexepcted side effects, but it should work.
First here is a simple HotKeyManager this takes care of the basic interop, provides a hidden window to handle the native Windows messages (WM_HOTKEY) which is translated into a .NET event HotKeyPressed
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class HotKeyManager
{
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 (HotKeyManager.HotKeyPressed != null)
{
HotKeyManager.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);
HotKeyManager.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
}
The following shows a simple windows forms application which will keep the main form hidden and respond to the hot key events. I did not handle the closing of the application and the unregistering of the hot key, you can handle that.
using System;
using System.Windows.Forms;
namespace HotKeyManager
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
HotKeyManager.RegisterHotKey(Keys.A, KeyModifiers.Alt);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
}
void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
MessageBox.Show("Hello");
}
protected override void SetVisibleCore(bool value)
{
// Quick and dirty to keep the main window invisible
base.SetVisibleCore(false);
}
}
}
Add this to KeyPress event of your form:
if(e.KeyCode == (char)Keys.W && e.Modifiers == Keys.Control && e.Modifiers = Keys.Alt)
{
MessageBox.Show("I think this is a homework and that you should study instead of asking for an already cooked up answer on programming websites","Cheater");
}