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

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.

Related

Suppressing global windows shortcuts while recording keypresses

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;
}

Pressing a key will spawn a lot of screens

I have the code below:
else if (keyboardState.IsKeyDown(Keys.E)) // if the 'E' key is pressed
{
form = new InventoryScreen(); //define winform instance
form.Show(); //display winform
}
It works, maybe too well. Because when the user presses and holds the 'E' key, the program generates a lot of winform screens. Is there a way to instruct the program to only run the following only once? Also, how would I write it so that if I press the 'E' key again, the window will disapear?
I would create a separate event handler firing on keyup. Within the handler you can check to see if the key is indeed 'E'. The keyup event will only fire once when the key is pressed and not fire while the key is held down.
See:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keydown(v=vs.110).aspx
XNA is not event-driven, it is instead a game loop.
There is no built-in event that fires on keys being press or not. One option is to only handle the 'E' event if no form is created.
A second option is to keep the old keyboard state, like this:
KeyboardState oldState;
Update(..):
KeyboardState now = Keyboard.GetState();
if (now.IsKeyUp(Keys.E) && oldState.IsKeyDown(Keys.E))
// i.e. a "release event"
{
form.Show();
}
oldState = now;
For the variant with disappearing, this might work or something similar, but you should probably instead handle it in the created form:
KeyboardState now = Keyboard.GetState();
if (now.IsKeyUp(Keys.E) && oldState.IsKeyDown(Keys.E))
// i.e. a "release event"
{
if (form.Visible)
{
form.Hide();
}
else
{
form.Show();
}
}
oldState = now;

Capture keyboard inputs without a textbox

I have a application that reads data from health cards and parse them for basic info like D.O.B., Health Card #, and names. Right now, I have a textbox that takes input from the card swiper and it works great, but I feel there could be a better approach for this.
I want to have a keyboard listener in the background of the application that captures input from the card swiper and parse the data without the need of a textbox. I figure I'll need a loop function in the Form1_Load that actively listens for keyboard inputs, prepare a buffer for the input, and then when a carriage return is detected, go ahead and parse the buffered data. When the parsing is done, clear the buffer.
My problem is I'm relatively new to C# and I don't know what I should use for listening to keyboard inputs without a textbox. Could someone point me in the right direction?
Here's my code in case if anyone's interested: http://pastebin.com/q6AkghvN
Just a note, I followed the credit card swipe guide from
http://www.markhagan.me/Samples/CreditCardSwipeMagneticStripProcessing and modified it slightly for my usecase.
--- EDITED ---
Thanks Paul and everyone else for their help!
Here is my solution if anyone is interested:
private void frmMain_KeyPress(object sender, KeyPressEventArgs e)
{
lblStatus.Text = "Reading Card...";
lblStatus.ForeColor = Color.Blue;
if (e.KeyChar != (char)Keys.Enter)
{
buffer += e.KeyChar;
}
else
{
lblStatus.Text = "Parsing Card...";
if (buffer.Contains('^') && buffer.Contains(';') && buffer.Contains('='))
{
try
{
string[] cardData = buffer.Split(';');
string[] caretData = cardData[0].Split('^');
string[] nameData = caretData[1].Split('/');
string[] equalData = cardData[1].Split('=');
tBoxHealthCardNumber.Text = equalData[0];
tBoxDateOfBirth.Text = FormatBirthday(equalData[1]);
tBoxFirstName.Text = TrimName(nameData[1]);
tBoxLastName.Text = TrimName(nameData[0]);
tBoxDateTimeScanned.Text = DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm");
e.Handled = true;
}
catch (Exception)
{
throw;
}
}
else
{
lblStatus.Text = "Error Reading Card";
}
buffer = "";
lblStatus.Text = "Ready";
lblStatus.ForeColor = Color.Green;
}
}
If you add a key handler to the form you will not see the key presses when focus is on a control, e.g. a textbox. For the form to see the key presses even when there is a focused control, you must also enable the KeyPreview property.
You can then add a handler for KeyDown, KeyPress and/or KeyUp on the form as you desire to receive these events.
As you can read in the documentation to KeyPreview, if you set the Handled property to true, you can prevent the event from being subsequently sent to the focused control, i.e. you can hide certain key events from being seen by the focused control.

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
}
}

Custom tray icon notification balloon

I have a C# program that sits in the system tray and pops up a notification balloon now and then. I'd like to provide 2-3 buttons on the notification balloon to allow the user to take various actions when the notification appears - rather than, for example, having to click the notification balloon to display a form containing buttons for each possible action.
I'm looking for suggestions on the best way to go about implementing this.
Edit: clarification, I want to provide buttons on the notification balloon so the user can take direct action on the notification rather than having to take action through some other part of the application (a form or menu for example).
There's no built-in method for this. I would suggest writing your own "balloon" and activating that instead of calling .ShowBalloon()
This is how I do it. It may not be the correct way of doing it. I do this way because .ShowBalloonTip(i) doesn't work as expected for me. It doesn't stay for i seconds and go off. So I do in another thread and forcefully dispose off.
private static NotifyIcon _notifyIcon;
//you can call this public function
internal static void ShowBalloonTip(Icon icon)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync(icon);
}
private static void worker_DoWork(object sender, DoWorkEventArgs e)
{
Show(e);
Thread.Sleep(2000); //meaning it displays for 2 seconds
DisposeOff();
}
private static void Show(DoWorkEventArgs e)
{
_notifyIcon = new NotifyIcon();
_notifyIcon.Icon = (Icon)e.Argument;
_notifyIcon.BalloonTipTitle = "Environment file is opened";
_notifyIcon.BalloonTipText = "Press alt+tab to switch between environment files";
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
_notifyIcon.Visible = true;
_notifyIcon.ShowBalloonTip(2000); //sadly doesnt work for me :(
}
private static void DisposeOff()
{
if (_notifyIcon == null)
return;
_notifyIcon.Dispose();
_notifyIcon = null;
}

Categories