Blocking input from keyboard HID - c#

I am in the process of integrating support for USB barcode scanners into a WPF application, but I have hit a snag.
Currently I am successfully identifying and capturing input with the help of this article.
The issue that I am facing is that although I am successfully identifying and routing input from the scanner device, I am unable to block the input events for further processing (e.g. if a textbox control on my application has input focus, the input from the scanner will be written to it).
I have tried two methods of capturing input:
var wndSourceHandle = HwndSource.FromHwnd(new WindowInteropHelper(
_wndEventSource = value).Handle);
wndSourceHandle.AddHook(this.WndProc);
WndProc definition:
private IntPtr WndProc(IntPtr hwnd, int iMessage, IntPtr wParam, IntPtr lParam, ref bool bisHandled)
{
if (iMessage == Win32.WM_INPUT)
{
var result = ProcessRawInput(lParam);
bisHandled = result != null && result.Value;
return IntPtr.Zero;
}
return IntPtr.Zero;
}
As well as:
ComponentDispatcher.ThreadFilterMessage += (ref MSG msg, ref bool handled) =>
{
if (msg.message == Win32.WM_INPUT)
{
var result = ProcessRawInput(msg.lParam);
handled = result != null && result.Value;
return;
}
handled = false;
};
The ProcessRawInput method returns true if the source of the input is the barcode scanner, false otherwise.

After a bit more research I found a solution applicable to WinForms here. I was able to modify it for WPF as follows:
ComponentDispatcher.ThreadFilterMessage += (ref MSG msg, ref bool handled) =>
{
if (msg.message == Win32.WM_INPUT)
{
var result = ProcessRawInput(msg.lParam);
this.m_bIgnoreNextKeyDownMessage = result != null && result.Value;
return;
}
if (msg.message == Win32.WM_KEYDOWN && this.m_bIgnoreNextKeyDownMessage)
{
handled = true;
}
this.m_bIgnoreNextKeyDownMessage = false;
};
This solution basically marks the first WM_KEYDOWN message after a barcode WM_INPUT message as "handled". I am not sure if this is the only/best/safest solution, but it looks like it does the trick.
Update:
With the above solution I still found that every now and again one random character from the scanned barcode would slip through to a focussed textbox - I am not sure why this is the case - could be a timing issue with the keyboard events as they are passed through the message handler. Another solution for checking whether the WM_KEYDOWN message should be ignored:
if (msg.message == Win32.WM_KEYDOWN && !String.IsNullOrEmpty(this.m_strKeyInput))
{
handled = true;
}
The buffer m_strKeyInput contains the current scanned barcode value - this buffer is empty when no barcode is available, built up one character at a time as the barcode scanner pushes down the barcode - and then emptied once a custom BarcodeScanned event is generated. The only drawback of this solution that I can think of is that all keyboards will stop functioning for the few milliseconds the barcode is being pushed from the scanner - which is acceptable for my scenario.

It sounds like since you are routing the input from the scanner (treating it as a keyboard) and into a textbox you could simply use one of the Preview* events on that textbox to perform your additional processing. For example you could override PreviewTextInput
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
// MyBarcodeScanner would be a reference to the IInput device that represents the scanner:
if (e.Device == MyBarcodeScanner)
{
// Process the text, rejecting it in this case.
// Marking the event as handled will prevent the 'TextChanged' event
// from firing, so the characters will not appear in the text box.
e.Handled = true;
}
else
{
// This is some other keyboard, by not handling the event,
// the users will be allowed to enter text normally.
}
}
You may have to do a little trial and error to figure out how to identify the barcode scanner that is identified by 'e.Device' but that should be relatively easy. Sorry I can't be more complete, but I don't have a barcode scanner of my own to experiment with.

Related

PostMessage in C# is not sending the key

I'm trying to simulate a keystroke ("z") on another window, a game in particular.
I've implemented in my program a simple timer that sends the key with PostMessage every 1000ms, but the "action" related to the pressing of that key doesn't start.
I've analysed the Messages sent to the window of the game with Spy++, but the strange thing is that i can see the exact sequence of messages (KEYDOWN, CHAR and KEYUP), whether i press it manually or send it through my application. Obviously if i press "z" manually the game's function gets called correctly.
Here i report the messages that i get from Spy++, the first 3 are from me hitting z manually, the last 3 are from my software.
Messages from Spy++
And here i include the code that i'm using
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, uint lParam);
private void SendKeys(IntPtr proc_hwnd, IntPtr key)
{
PostMessage(proc_hwnd, 0x100, key, 0x002C0001);
Thread.Sleep(100);
PostMessage(proc_hwnd, 0x101, key, 0xC02C0001);
}
Process[] proc;
private void Button1_Click(object sender, EventArgs e)
{
proc = Process.GetProcessesByName("Proc_name");
if (proc.Length == 0)
{
MessageBox.Show("No process found");
return;
}
tmr_raccogli.Interval = (int)(num_raccogli.Value * 1000);
tmr_raccogli.Start();
}
private void tmr_raccogli_Tick(object sender, EventArgs e)
{
SendKeys(proc[0].MainWindowHandle, (IntPtr)Keys.Z);
}
I don't get why it's not working since from that point of view the two actions are identical.
i don't have an exact answer but i might able to guide you in the right direction.
The focus and foreground state of your window maybe important as well as the input thread.
SetFocus
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setfocus
SetForegroundWindow
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow
AttachThreadInput
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
Your windows receives the messages but i decides to ignore them, this depends on how the implementation of windows msgs is programmed in the target. I can remember creating something similar and AttachThreadInput fixed most of my code interoperability problems.

C# method getting invoked twice on Enter Key

I am in the process of creating a POS system using C# and I have an issue with Textbox to read bar-code.
I want to read bar-code from the scanner either when the textbox has focus or not. Again I want the textbox to have capability to search for product manually that is user input the bar-code and then press the enter key the bar code captured should be used to query MySQL.
I have used this code to read bar-code from scanner when my textbox has focus or not but the problem is that when I want to search for product manually using enter key the method to query db is getting invoked twice and I don't understand why. Need help to prevent the function from getting invoked twice as well as provide functionality to search bar-code on enter and when textbox has no focus that is get input from scanner
private string _barcode = "";
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)//read when no focus
{
char c = (char)keyData;
if (char.IsNumber(c))
_barcode += c;
if (c == (char)Keys.Return)
{
read_scanner(_barcode);
_barcode = "";
}
return base.ProcessCmdKey(ref msg, keyData);
}
This my code to search manually
private void txtbarcode_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// onQuery = true;
read_scanner(txtbarcode.Text);
}
}
And here is my method which is getting invoked twice when I press enter key
Product product;
public void read_scanner(string barcode)//this method is getting invoked twice when i search product manually
{
//
product = HibernateDao.getRecord("from Product u where u.barcode='"+barcoded+"'");
if (product != null)
{
pos_datagrid.Rows.Add(barcode, product.product_name, product.product_desc, null, "1", null, product_Cost);
}
else
{
AppUtil.showErrorBox("Invalid bar code!!");
}
}
The ProcessCmdKey is a virtual method on all Forms that listens to all keys and return true if it was a command key and false for other keys, so you might not need to implement KeyDown at all.
documentation here
I've changed the code a little to make use of a StringBuilder and add the values to that StringBuilder.
StringBuilder _barcodeBuilder = new StringBuilder();
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData != Keys.Return)
{
switch (keyData)
{
case Keys.NumPad0:
_barcodeBuilder.Append(0);
break;
case Keys.NumPad1:
_barcodeBuilder.Append(1);
break;
case Keys.NumPad2:
_barcodeBuilder.Append(2);
break;
case Keys.NumPad3:
_barcodeBuilder.Append(3);
break;
//and so on for the rest of the numpad keys
default:
_barcodeBuilder.Append((char)keyData);
break;
}
}
else
{
MessageBox.Show(this, _barcodeBuilder.ToString());
//read_scanner(_barcodeBuilder.ToString());
textBox1.Clear();
_barcodeBuilder = new StringBuilder();
}
return base.ProcessCmdKey(ref msg, keyData);
}
Numpad keys are a bit strange, so you can handle them with a switch statement.
The source of your problem is unclear. Although if you want to track the source of what triggers your method, use System.Environment.StackTrace and output that as a debug message. You might have to use the "Debug" configuration for that.

How to get the text of a MessageBox when it has an icon?

I am working on trying to close a specific MessageBox if it shows up based on the caption and text. I have it working when the MessageBox doesn't have an icon.
IntPtr handle = FindWindowByCaption(IntPtr.Zero, "Caption");
if (handle == IntPtr.Zero)
return;
//Get the Text window handle
IntPtr txtHandle = FindWindowEx(handle, IntPtr.Zero, "Static", null);
int len = GetWindowTextLength(txtHandle);
//Get the text
StringBuilder sb = new StringBuilder(len + 1);
GetWindowText(txtHandle, sb, len + 1);
//close the messagebox
if (sb.ToString() == "Original message")
{
SendMessage(new HandleRef(null, handle), WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
The above code works just fine when the MessageBox is shown without an icon like the following.
MessageBox.Show("Original message", "Caption");
However, if it includes an icon (from MessageBoxIcon) like the following, it doesn't work; GetWindowTextLength returns 0 and nothing happens.
MessageBox.Show("Original message", "Caption", MessageBoxButtons.OK, MessageBoxIcon.Information);
My best guess is that the 3rd and/or 4th paramters of FindWindowEx need to change but I'm not sure what to pass instead. Or maybe the 2nd parameter needs to change to skip the icon? I'm not really sure.
It appears that when the MessageBox has an icon, FindWindowEx returns the text of the first child (which is the icon in this case) hence, the zero length. Now, with the help of this answer, I got the idea to iterate the children until finding one with a text. This should work:
IntPtr handle = FindWindowByCaption(IntPtr.Zero, "Caption");
if (handle == IntPtr.Zero)
return;
//Get the Text window handle
IntPtr txtHandle = IntPtr.Zero;
int len;
do
{
txtHandle = FindWindowEx(handle, txtHandle, "Static", null);
len = GetWindowTextLength(txtHandle);
} while (len == 0 && txtHandle != IntPtr.Zero);
//Get the text
StringBuilder sb = new StringBuilder(len + 1);
GetWindowText(txtHandle, sb, len + 1);
//close the messagebox
if (sb.ToString() == "Original message")
{
SendMessage(new HandleRef(null, handle), WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
Obviously, you could adjust it to fit your particular situation (e.g., keep iterating until you find the actual text you're looking for) although I think the child with the text will probably always be the second one:
This is a UI Automation method that can detect a Window Opened event anywhere in the System, identify the Window using the Text of one its child elements and close the Window upon positive identification.
The detection is initialized using Automation.AddAutomationEventHandler with WindowPattern.WindowOpenedEvent and Automation Element argument set to AutomationElement.RootElement, which, having no other ancestors, identifies the whole Desktop (any Window).
The WindowWatcher class exposes a public method (WatchWindowBySubElementText) that allows to specify the Text contained in one of the sub elements of a Window that just opened. If the specified Text is found, the method closes the Window and notifies the operation using a custom event handler that a subscriber can use to determine that the watched Window has been detected and closed.
Sample usage, using the Text string as provided in the question:
WindowWatcher watcher = new WindowWatcher();
watcher.ElementFound += (obj, evt) => { MessageBox.Show("Found and Closed!"); };
watcher.WatchWindowBySubElementText("Original message");
WindowWatcher class:
This class requires a Project Reference to these assemblies:
UIAutomationClient
UIAutomationTypes
Note that, upon identification, the class event removes the Automation
event handler before notifying the subscribers. This is just an
example: it points out that the handlers need to be removed at some
point. The class could implement IDisposable and remove the
handler(s) when disposed of.
EDIT:
Changed the condition that doesn't consider a Window created in the current Process:
if (element is null || element.Current.ProcessId != Process.GetCurrentProcess().Id)
As noted in the comments, it imposes a limitation that is probably not necessary: the Dialog could also belong to the current Process. I left there just the null check.
using System.Diagnostics;
using System.Windows.Automation;
public class WindowWatcher
{
public delegate void ElementFoundEventHandler(object sender, EventArgs e);
public event ElementFoundEventHandler ElementFound;
public WindowWatcher() { }
public void WatchWindowBySubElementText(string ElementText) =>
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement, TreeScope.Subtree, (UIElm, evt) => {
AutomationElement element = UIElm as AutomationElement;
try {
if (element is null) return;
AutomationElement childElm = element.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, ElementText));
if (childElm != null) {
(element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern).Close();
OnElementFound(new EventArgs());
}
}
catch (ElementNotAvailableException) {
// Ignore: generated when a Window is closed. Its AutomationElement
// is no longer available. Usually a modal dialog in the current process.
}
});
public void OnElementFound(EventArgs e)
{
// Automation.RemoveAllEventHandlers(); <= If single use. Add to IDisposable.Dispose()
ElementFound?.Invoke(this, e);
}
}

Low-level Keyboard Hooks/SendInput with Winkey+L possible? (workstation lockout is intercepted in Vista and higher)

I work on a project called UAWKS (Unofficial Apple Wireless Keyboard Support) that helps Windows users use Apple's bluetooth keyboard. One of the main goals of UAWKS is to swap the Cmd key (which behaves as Winkey in Windows) with Ctrl, allowing users to do Cmd+C for copy, Cmd+T for new tab, etc.
It is currently developed using AutoHotkey, which worked pretty well under Windows XP. However, on Vista and Windows 7, Cmd+L causes problems:
Regardless of low-level keyboard hooks, Win+L is always intercepted by Windows and normally locks the workstation...
You can disable workstation locking with this registry hack, but pressing Win+L still can't be rebound in AHK
Pressing Win+L leaves Winkey in the Keydown state until the next (additional) Winkey Up. Simulating a Keyup event doesn't seem to work either!
It seems that Win+L is a special chord that messes everything else up.
I've looked through the AHK source code, and they try to address this problem in SendKey() in keyboard_mouse.cpp (near line 883 in v1.0.48.05), but it doesn't work. I wrote up my own low-level keyboard hook application in C#, and I see the same problem.
Has anyone else run into this? Is there a workaround?
I figured out a way to do this in C#. There are four states involved in a possible Win+L keypress sequence (None, Win, Win+L, L). Whenever the Win+L state is reached, set a flag ("winLSet" below). Whenever all of the keys have been released, we check for this flag and simulate the press if it's been set.
The final piece of the puzzle is to simulate the WinKey's KeyUp before the Ctrl-L (no KeyDown). I've tried similar approaches in AutoHotkey and it never worked, but it seems to work perfectly here.
The code is below. Please see explanatory notes at the bottom if you plan to use this code.
public partial class MainWindow : Window
{
LowLevelKeyboardHook hook;
bool winKeyDown;
bool lKeyDown;
bool winLSet;
public MainWindow()
{
InitializeComponent();
hook = new LowLevelKeyboardHook();
hook.KeyDown += OnKeyDown;
hook.KeyUp += OnKeyUp;
}
void OnKeyDown(object sender, LowLevelKeyEventArgs e)
{
e.EventHandled = true;
switch (e.Key)
{
case Key.L:
lKeyDown = true;
UpdateWinLState();
e.EventHandled = winKeyDown;
break;
case Key.LWin:
winKeyDown = true;
UpdateWinLState();
InputSimulator.SimulateKeyDown(VirtualKeyCode.LCONTROL);
break;
case Key.LeftCtrl:
InputSimulator.SimulateKeyDown(VirtualKeyCode.LWIN);
break;
default:
e.EventHandled = false;
break;
}
}
void OnKeyUp(object sender, LowLevelKeyEventArgs e)
{
e.EventHandled = true;
switch (e.Key)
{
case Key.L:
lKeyDown = false;
UpdateWinLState();
e.EventHandled = winKeyDown;
break;
case Key.LWin:
winKeyDown = false;
UpdateWinLState();
InputSimulator.SimulateKeyUp(VirtualKeyCode.LCONTROL);
break;
case Key.LeftCtrl:
InputSimulator.SimulateKeyUp(VirtualKeyCode.LWIN);
break;
default:
e.EventHandled = false;
break;
}
}
void UpdateWinLState()
{
if (winKeyDown && lKeyDown)
{
winLSet = true;
}
else if (!winKeyDown && !lKeyDown && winLSet)
{
winLSet = false;
InputSimulator.SimulateKeyUp(VirtualKeyCode.LWIN);
InputSimulator.SimulateModifiedKeyStroke(
VirtualKeyCode.LCONTROL,
(VirtualKeyCode)'L');
}
}
}
For posterity: please note that this code uses InputSimulator and LowLevelKeyboardHook, which are not from the .NET Framework. LowLevelKeyboardHook is a class I wrote a while back that exposes global KeyDown and KeyUp events as C# events. There are similar examples here, here, and a bunch can be found here.
Also notice that I'm using System.Windows.Input.Key, not System.Windows.Forms.Keys, which could confuse some people. System.Windows.Input.Key is the new enumeration of keys in .NET 3.0 and above, while System.Windows.Forms.Keys is the old enumeration from Windows Forms.
I tried to interrupt the windows key using the Windows Input Simulator library. This is my callback:
private static unsafe IntPtr HookCallback( int nCode, IntPtr wParam, IntPtr lParam )
{
if( nCode >= 0 && ( wParam == (IntPtr)WM_KEYDOWN ) )
{
var replacementKey = (KBDLLHOOKSTRUCT*)lParam;
if( replacementKey->vkCode == (int)VirtualKeyCode.LWIN )
{
InputSimulator.SimulateKeyDown( VirtualKeyCode.SHIFT );
return (IntPtr)1;
}
}
return CallNextHookEx( m_HookID, nCode, wParam, lParam );
}
Using this hook my left windows key acts as a shift key (as implemented & expected) under Win XP.
Pressing WinKey + l returns just L.
EDIT: However, I can confirm your observation, that this code does not work under Windows 7 anymore :/ Sorry, I can't help you any further.
If you can detect the key Cmd+L could you just go ahead and lock the workstation without bothering to forward Winkey+L? you can do it with the API LockWorkstation (or rundll32.exe user32.dll,LockWorkStation)

Intercept paste event on HtmlEditor WinForms

I'm using a HtmlEditor control inside a Windows Form.
I got the control from this page:
http://windowsclient.net/articles/htmleditor.aspx
I want to extend the controls functionality by allowing the user to paste images from the clipboard. Right now you can paste plain and formatted text, but when trying to paste an image it does nothing.
Basically what I thought was to detect when the user presses Ctrl+V on the editor, check the clipboard for images and if there's an image, insert it manually to the editor.
The problem with this approach is that I cannot get the OnKeyDown or OnKeyPress events of the form to be raised.
I have the KeyPreview property set to true on the form, but still the events aren't raised.
I also tried to Subclass the form and the editor (as explained here) to intercept the WM_PASTE message, but it isn't raised either.
Any ideas on how to achieve this?
Thanks a lot
I spent all day on this problem and finally have a solution. Trying to listen for the WM_PASTE message doesn't work because Ctrl-V is being PreProcessed by the underlying mshtml Control. You can listen for OnKeyDown/Up etc to catch a Ctrl-V but this won't stop the underlying Control from proceeding with its default Paste behavior. My solution is to prevent the PreProcessing of the Ctrl-V message and then implementing my own Paste behavior. To stop the control from PreProcessing the CtrlV message I had to subclass my Control which is AxWebBrowser,
public class DisabledPasteWebBrowser : AxWebBrowser
{
const int WM_KEYDOWN = 0x100;
const int CTRL_WPARAM = 0x11;
const int VKEY_WPARAM = 0x56;
Message prevMsg;
public override bool PreProcessMessage(ref Message msg)
{
if (prevMsg.Msg == WM_KEYDOWN && prevMsg.WParam == new IntPtr(CTRL_WPARAM) && msg.Msg == WM_KEYDOWN && msg.WParam == new IntPtr(VKEY_WPARAM))
{
// Do not let this Control process Ctrl-V, we'll do it manually.
HtmlEditorControl parentControl = this.Parent as HtmlEditorControl;
if (parentControl != null)
{
parentControl.ExecuteCommandDocument("Paste");
}
return true;
}
prevMsg = msg;
return base.PreProcessMessage(ref msg);
}
}
Here is my custom method to handle Paste commands, yours might do something similar with the Image data from the Clipboard.
internal void ExecuteCommandDocument(string command, bool prompt)
{
try
{
// ensure command is a valid command and then enabled for the selection
if (document.queryCommandSupported(command))
{
if (command == HTML_COMMAND_TEXT_PASTE && Clipboard.ContainsImage())
{
// Save image to user temp dir
String imagePath = Path.GetTempPath() + "\\" + Path.GetRandomFileName() + ".jpg";
Clipboard.GetImage().Save(imagePath, System.Drawing.Imaging.ImageFormat.Jpeg);
// Insert image href in to html with temp path
Uri uri = null;
Uri.TryCreate(imagePath, UriKind.Absolute, out uri);
document.execCommand(HTML_COMMAND_INSERT_IMAGE, false, uri.ToString());
// Update pasted id
Guid elementId = Guid.NewGuid();
GetFirstControl().id = elementId.ToString();
// Fire event that image saved to any interested listeners who might want to save it elsewhere as well
if (OnImageInserted != null)
{
OnImageInserted(this, new ImageInsertEventArgs { HrefUrl = uri.ToString(), TempPath = imagePath, HtmlElementId = elementId.ToString() });
}
}
else
{
// execute the given command
document.execCommand(command, prompt, null);
}
}
}
catch (Exception ex)
{
// Unknown error so inform user
throw new HtmlEditorException("Unknown MSHTML Error.", command, ex);
}
}
Hope someone finds this helpful and doesn't waste a day on it like me today.

Categories