Suppressing global windows shortcuts while recording keypresses - c#

Is it possible to suppress the windows global shortcuts while recording keypresses ?
I have a Windows Form application written in c#, and using this library to record keypresses to use later in macros. Now when I record the key combinations that are used by Windows (i.e. L Control + Win + Right Arrow to change virtual desktop on Win 10), I'd like my app to record it but avoid windows actually using it while I record which is quite annoying.
I have a checkbox to enable key capturing, on click event
m_KeyboardHookManager.KeyDown += HookManager_KeyDown;
the HookManager_KeyDown is simply like this
private void HookManager_KeyDown(object sender, KeyEventArgs e)
{
Log(string.Format("KeyDown \t\t {0}\n", e.KeyCode));
string [] sArr = new string [2];
if (keyBindingArea1.Text != "")
{
sArr[0] = keyBindingArea1.Text;
sArr[1] = string.Format("{0}", e.KeyCode);
keyBindingArea1.Text = string.Join("+", sArr);
}
else
{
keyBindingArea1.Text = string.Format("{0}", e.KeyCode);
}
}
which display the key combination in a comboText control. (This code is taken directly from the demo attached to the package.
Now the recording work well if for instance I press L Control + Win, then I release the keys and press the third one (i.e. Right Arrow), this will not trigger Windows shortcuts but it is quite annoying to have it work like that.
Appreciate any help. Thanks

Try to use e.Handled property of the event. If you set it to true it will terminate key procesing chain. As a rule oher applications in the processing chain will not get it. I am not sure it is going to work for such a low level windows staff as virtual desktop switch.
private void OnKeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
bool isThirdKey = //Some state evaluation to detect the third key was pressed
if (isThirdKey) e.Handled = true;
}

Related

SendKeys.Send() does not send or react as I expect

I have a problem using Sendkeys.Send in my C# application and I really cannot understand why. When using it then it does not send what I expect to the active application. I am using it together with the global hotkey manager, https://github.com/thomaslevesque/NHotkey
I have created this simple PoC that, for my part at least, will be able to reproduce my problem. Just launch Wordpad and press the hotkey, ALT + O:
using System.Windows.Forms;
using System.Diagnostics;
using NHotkey.WindowsForms;
namespace WindowsFormsApp5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Convert string to keys
string hotkey = "Alt + O";
KeysConverter cvt;
Keys key;
cvt = new KeysConverter();
key = (Keys)cvt.ConvertFrom(hotkey);
// Setup the hotkey
HotkeyManager.Current.AddOrReplace("MyID", key, HotkeyAction);
// Copy some text to the clipboard that I want to paste to the active application
Clipboard.SetText("My String");
}
private void HotkeyAction(object sender, NHotkey.HotkeyEventArgs e)
{
Debug.WriteLine("Pressed the hotkey");
SendKeys.Send("^v");
// SendKeys.Send("Test string");
e.Handled = true;
}
}
}
When I do this in Wordpad, then instead of pasting the clipboard (^v equals CTRL + V) then it tries to "Paste Special":
Even if I do the most simple thing and then just put some text in SendKeys.Send, then it seems to be messing with the menus in Wordpad? SendKeys.SendWait is not any different.
I have been trying to figure this out for quite some time now but I simply do not understand why it does that. Basically, I need to paste the clipboard on a hotkey though it doesn't need to be with this exact method so if anyone knows another way of doing it then I would appreciate some hints.
MY IMPLEMENTED SOLUTION
Based on the accepted answer then I did change my implementation slightly as I could not get it working with just a timer. I may have missed something(?) but this is working.
In basic then I change focus to my application as soon as the hotkey is detected, to avoid conflict with modifier keys (ALT etc) in the active application. I then create an invisible form and when I detect a KeyUp event, then I check for modifier keys and if none is pressed down then I enable a timer and immediately switch focus back to the originating application. After 50ms the clipboard will be pasted to the active application.
Something like this:
// Somewhere else in code but nice to know
// IntPtr activeApp = GetForegroundWindow(); // get HWnd for active application
// SetForegroundWindow(this.Handle); // switch to my application
private System.Timers.Timer timerPasteOnHotkey = new System.Timers.Timer();
// Main
public PasteOnHotkey()
{
InitializeComponent();
// Define the timer
timerPasteOnHotkey.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timerPasteOnHotkey.Interval = 50;
timerPasteOnHotkey.Enabled = false;
// Make the form invisble
this.Size = new System.Drawing.Size(0, 0);
this.Opacity = 0.0;
}
private void PasteOnHotkey_KeyUp(object sender, KeyEventArgs e)
{
// Check if modifier keys are pressed
bool isShift = e.Shift;
bool isAlt = e.Alt;
bool isControl = e.Control;
// Proceed if no modifier keys are pressed down
if (!isShift && !isAlt && !isControl)
{
Hide();
// Start the timer and change focus to the active application
timerPasteOnHotkey.Enabled = true;
SetForegroundWindow(activeApp);
}
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
timerPasteOnHotkey.Enabled = false;
SendKeys.SendWait("^v"); // send "CTRL + v" (paste from clipboard)
}
When you use SendKeys.Send in a response to a key press then the keys you send may be combined with the physical keys you’re holding at that moment. In this case you’re holding Alt, so Wordpad assumes you pressed Alt-Ctrl-V instead of just Ctrl-V. Also Alt opens menu, so sending other keys may relate to hotkeys there.
Adding a delay will remove this issue, and usually when sending key presses it would be done as not relating to other key presses so it won’t be a problem.

Let user start typing when winform pops up without them having to click on the textbox

I am working on a barcode reader project in Visual Studio using C#. I have created a WinForm Application and have added a RichTextBox to it. I want the user to be able to start scanning when they open the program without having to click on the textbox.
Thanks in advance!
(I'm assuming you have an application with a multitude of stuff in it. However there is one field that needs to be filled in with a scanned barcode.)
I faced a simular issue a while ago. I needed to capture a barcode in WPF. Setting the focus property in load seemed a good idea but because there were a multitude of other controls on the page that the user could click etc. focus jumped from one control to the other, making the barcode go in the wrong fields or vanish in a grid that has focus for example.
We were not able to use any other way of reading the barcode from the scanner because it was used for other applications too. It had to be configured as input.
We came up with a solution of capturing the keypresses instead.
By using the keydown events we could track the scanner input and stated that if more than 5 keys came in within a limited time + with our prefix and suffix it had to be a barcode.
EDIT: here is a simplified version of the class.
public delegate void BarcodeRead(string barcode);
public class ManualReader
{
private string barcode = "no barcode detected";
private string possible = "";
private DateTime timestarted = DateTime.MinValue;
private Timer InputTimeout;
public BarcodeRead OnBarcodeRead;
public void OnKeyDown(object sender, KeyEventArgs.KeyEventArgs e)
{
//create timer if it does not exist
if (InputTimeout == null)
{
InputTimeout = new Timer(100);
InputTimeout.Enabled = true;
InputTimeout.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
//reset timer
InputTimeout.Stop();
InputTimeout.Start();
//possible barcode
possible += CharToKey.GetCharFromKey(e);
if (timestarted == DateTime.MinValue)
{
timestarted = DateTime.Now;
}
}
//Timer elapses
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
//Is it a barcode?
if ((timestarted.AddMilliseconds(600) > DateTime.Now) && (possible.Length > 5)
&& (timestarted != DateTime.MinValue) && possible.Contains("\r"))
{
barcode = possible;
barcode = barcode.Remove(0, 1);
barcode = barcode.Replace("\r", "");
//launch delegate
if (OnBarcodeRead != null)
{
OnBarcodeRead.Invoke(barcode);
}
}
//delete timers
timestarted = DateTime.MinValue;
InputTimeout.Dispose();
InputTimeout = null;
possible = null;
}
}
}
I'm aware that for really short timeouts datetime functions aren't precise but still this little 'hack' worked perfectly for our application.
You can add directly in the element. This works for textbox but not sure with RichTexBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"

WH_KEYBOARD_LL could not capture keyboard events when RDC is in full screen mode

I am doing a c# window project that capture screen shot for each keyboard event on RDC.
In this project i used WH_KEYBOARD_LL hook to catch keyboard event and
Microsoft's RDC.
The problem is if remote session of remote machine already existed meaning that
someone used that remote machine and disconnected without logging off,WH_KEYBOARD_LL
hook could not catch keyboard events in full screen mode of RDC.
So images could not be captured.This problem happened on Microsoft server 2008 r2 64 bit.
I have search for this as much as i can. I could not still find out the solution.
Please, anybody help me for this problem.
Edit : I used Gma.Useractivity project from code project.
In this project, following is the route given by Gma.Useractivty project to add custom event handler
public static event KeyEventHandler KeyUp
{
add
{
EnsureSubscribedToGlobalKeyboardEvents();
s_KeyUp += value;
}
remove
{
s_KeyUp -= value;
TryUnsubscribeFromGlobalKeyboardEvents();
}
}
private static void EnsureSubscribedToGlobalKeyboardEvents()
{
// install Keyboard hook only if it is not installed and must be installed
if (s_KeyboardHookHandle == 0)
{
//See comment of this field. To avoid GC to clean it up.
s_KeyboardDelegate = KeyboardHookProc;
//install hook
s_KeyboardHookHandle = SetWindowsHookEx(
WH_KEYBOARD_LL,
s_KeyboardDelegate,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
//If SetWindowsHookEx fails.
if (s_KeyboardHookHandle == 0)
{
//Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
int errorCode = Marshal.GetLastWin32Error();
//do cleanup
//Initializes and throws a new instance of the Win32Exception class with the specified error.
throw new Exception("### Gma.UserActivityMonitor >> HookeManager.Callbacks.cs [" + "Creating Keyboard Hook is fail] ###");
}
}
}
I registered my keyboard event handler in the form load as following...
private void RDR9902_Load(object sender, EventArgs e)
{
rdpProductionNode.FullScreenTitle = "????????(" + CommonData.userID + ")";
WinAPI.SetWinFullScreen(this.Handle);
HookManager.KeyDown += new KeyEventHandler(HookManager_KeyDown);
HookManager.KeyUp += new KeyEventHandler(HookManager_KeyUp);
HookManager.MouseUp += new MouseEventHandler(HookManager_MouseUp);
}

How do I handle modifier keys on the keydown event in a Windows Store / Modern UI App?

I have a simple Windows Store / Modern UI app which has a RichEditBox. I'm trying to handle CTRL+B / CTRL+I etc to set bold / italic text on and off but I'm encountering some weird behaviour. Here's my code:
private void RichEditBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
var state = Window.Current.CoreWindow.GetKeyState(Windows.System.VirtualKey.Control);
if (state == CoreVirtualKeyStates.Down)
{
console.Text += "^";
}
else
{
console.Text += ".";
}
}
console is just a TextBlock above the RichEditBox control
If I press CTRL ten times, I would expect the output to be
^^^^^^^^^^
However, what I get is this
.^.^.^.^.^
CTRL only registers every second time. What's happening?
Beware of an enum type that have the [Flags] attribute, values of the enum type can have multiple flags turned on. Certainly the case here, you'll also get the Locked flag turned on for modifier keys. Quirky since the Ctrl key isn't actually a locked key, it is still synthesized by Windows though.
For enum types that have [Flags] you need to isolate the flag you are interested in, like this:
if ((state & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down) {
console.Text += "^";
}
Which fixes your problem.

Maximize C# Application from System Tray using Keyboard Shortcut

Can I know if there is anyway that I can maximise my windows form application from the system tray using say a Keyboard Shortcut rather than clicking on it?
I am currently minimizing using this piece of code
//Minimize to Tray with over-ride for short cut
private void MinimiseToTray(bool shortCutPressed)
{
notifyIcon.BalloonTipTitle = "Minimize to Tray App";
notifyIcon.BalloonTipText = "You have successfully minimized your app.";
if (FormWindowState.Minimized == this.WindowState || shortCutPressed)
{
notifyIcon.Visible = true;
notifyIcon.ShowBalloonTip(500);
this.Hide();
}
else if (FormWindowState.Normal == this.WindowState)
{
notifyIcon.Visible = false;
}
}
Hence, I need a keyboard shortcut that should maximize it. Much thanks!
EDIT: If you simply want to 'reserve a key combination' to perform something on your application, a Low-Level keyboard hook whereby you see every keypress going to any other application is not only an overkill, but bad practice and in my personal view likely to have people thinking that you're keylogging! Stick to a HOT-KEY!
Given that your icon will not have keyboard focus, you need to register a global keyboard hotkey.
Other similar questions:
How can I register a global hot key to say CTRL+SHIFT+(LETTER)
Best way to tackle global hotkey processing in c#?
Example from Global Hotkeys With .NET:
Hotkey hk = new Hotkey();
hk.KeyCode = Keys.1;
hk.Windows = true;
hk.Pressed += delegate { Console.WriteLine("Windows+1 pressed!"); };
if (!hk.GetCanRegister(myForm))
{
Console.WriteLine("Whoops, looks like attempts to register will fail " +
"or throw an exception, show error to user");
}
else
{
hk.Register(myForm);
}
// .. later, at some point
if (hk.Registered)
{
hk.Unregister();
}
To do this, you must use "Low-Level Hook".
You will find all information about it on this article : http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx
Look at this too : http://www.codeproject.com/Articles/6362/Global-System-Hooks-in-NET
I second Franck's suggestion about a global keyboard hook. Personally, I had very good experiences with the CodeProject article "Processing Global Mouse and Keyboard Hooks in C#".
As they write in their article, you can do things like:
private UserActivityHook _actHook;
private void MainFormLoad(object sender, System.EventArgs e)
{
_actHook = new UserActivityHook();
_actHook.KeyPress += new KeyPressEventHandler(MyKeyPress);
}
You could then call a function in your MyKeyPress handler that opens your window.
If you follow the guide here. It will show you how to register a global shortcut key.
public partial class Form1 : Form
{
KeyboardHook hook = new KeyboardHook();
public Form1()
{
InitializeComponent();
// register the event that is fired after the key press.
hook.KeyPressed += new EventHandler<KeyPressedEventArgs>(hook_KeyPressed);
// register the CONTROL + ALT + F12 combination as hot key.
// You can change this.
hook.RegisterHotKey(ModifierKeys.Control | ModifierKeys.Alt, Keys.F12);
}
private void hook_KeyPressed(object sender, KeyPressedEventArgs e)
{
// Trigger your function
MinimiseToTray(true);
}
private void MinimiseToTray(bool shortCutPressed)
{
// ... Your code
}
}

Categories