I'm trying to check if a key is pressed, but I get these Error Message:
Error 1 The name 'Keyboard' does not exist in the current context
Error 2 The name 'Key' does not exist in the current context
Can you tell me how to fix it?
public void Main()
{
while(true)
{
if (Keyboard.IsKeyPressed(Key.A))
{
//...
}
return;
}
}
It looks like you are trying to create a global hotkey in the system and your application should respond when it is pressed.
You will need two Win32 API functions RegisterHotKey and UnregisterHotKey.
Looking at your using System.Windows.Input, it seems like you are trying to do this with WPF, which is possible.
Let's start with your fairly basic P/Invokes:
using System.Runtime.InteropServices;
internal static class NativeMethods
{
[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr windowHandle, int hotkeyId, uint modifierKeys, uint virtualKey);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr windowHandle, int hotkeyId);
}
Now, when you register your Window, what happens is that a WM_HOTKEY message is sent to your application's message pump. However, WPF abstracts this message pump away from you, so you'll need to add a HwndSourceHook to tap into it.
How do we do all this? Let's start by initializing our HwndSourceHook delegate. Add this snippet to your MainWindow:
using System.Windows.Interop;
static readonly int MyHotKeyId = 0x3000;
static readonly int WM_HOTKEY = 0x312;
void InitializeHook()
{
var windowHelper = new WindowInteropHelper(this);
var windowSource = HwndSource.FromHwnd(windowHelper.Handle);
windowSource.AddHook(MessagePumpHook);
}
IntPtr MessagePumpHook(IntPtr handle, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_HOTKEY)
{
if ((int)wParam == MyHotKeyId)
{
// The hotkey has been pressed, do something!
handled = true;
}
}
return IntPtr.Zero;
}
Alright, so now we have everything in place to respond to the WM_HOTKEY message. However, we need to register our hotkey still! Let's add another couple initialization methods:
void InitializeHotKey()
{
var windowHelper = new WindowInteropHelper(this);
// You can specify modifiers such as SHIFT, ALT, CONTROL, and WIN.
// Remember to use the bit-wise OR operator (|) to join multiple modifiers together.
uint modifiers = (uint)ModifierKeys.None;
// We need to convert the WPF Key enumeration into a virtual key for the Win32 API!
uint virtualKey = (uint)KeyInterop.VirtualKeyFromKey(Key.A);
NativeMethods.RegisterHotKey(windowHelper.Handle, MyHotKeyId, modifiers, virtualKey);
}
void UninitializeHotKey()
{
var windowHelper = new WindowInteropHelper(this);
NativeMethods.UnregisterHotKey(windowHelper.Handle, MyHotKeyId);
}
Alright! Where do we put these? Do not put them in the constructor! Why? Because the Handle will be 0 and invalid! Put them here (in MainWindow):
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
InitializeHook();
InitializeHotKey();
}
You can register multiple hotkeys, un-register and re-register new ones.. it's up to you. Just remember that each hotkey must have a unique ID registered to it. It only makes sense, as your message pump hook has to know which hotkey caused the WM_HOTKEY message!
It's also good practice to unregister all hotkeys when your application closes.
You can use a keyboard hook. Check this out, from this answer to a similar question:
Global keyboard hooks are not the right solution if you only want a
few global hotkeys. A high level keyboard hook means that your dll
will be injected into other applications, and shouldn't be done at all
in managed code. A low level keyboard hook is a bit better, since it
processes the keyboard events in your own application. Still it
requires that every keyboard event is handled by your thread.
The windows API function RegisterHotKey is much better suited for
that.
But using a smple F-Key as global hotkey is problematic since it might
collide with a local hotkey of the application that has focus. So you
should make global hotkeys configurable, so the user can avoid
collisions with his commonly used applications.
If you are trying to do this in a windows forms application, maybe you can add these codes into the Form's KeyDown event(or which key event you need to use):
switch (e.KeyData)
{
//Detects that you pressed the Up Arrow Key, which key do you need, just
//write it here.
case Keys.Up:
//enter code here`
break;
}
You need to add PresentationCore.dll and WindowsBase to references and add to the header of the method [STAThread]
The type of application is not clear. If you have a console application, not a Windows form one, you can try this:
while (true)
if (Console.KeyAvailable)
if (Console.ReadKey(true).Key == ConsoleKey.A)
{
// Do something
}
and read this if you want to have a global hotkey in a windows forms app: http://www.liensberger.it/web/blog/?p=207
Related
I need to launch a new search form from all the open windows of my application if the user presses Control + F.
Currently I am using windows user32.dll methods to register a hotkey that I want to show across my application like this:
// Registers a hot key with Windows.
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
// Unregisters the hot key with Windows.
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
The issue with this method is that the shortcut is launched even when a different application like a web browser is selected.
I have tried using MainForm_KeyDown with KeyPreview set to true but it only works for the main form. I also tried overriding ProcessCmdKey but again, it only works for the main form.
I was thinking that maybe the UI Automation library offers support to watch for input. Otherwise would it be possible to add a filter to the windows hotkey registration so that it only works for the process id of my application?
As I understand it, you have these requirements:
Control-F makes a new form
Regardless of which Form (or control) has the focus currently, the hotkey should still work.
The hotkey is local to the application.
Implementing IMessageFilter should be effective in achieving these objectives:
public partial class MainForm : Form, IMessageFilter
{
public MainForm()
{
InitializeComponent();
Application.AddMessageFilter(this);
Disposed += (sender, e) =>Application.RemoveMessageFilter(this);
}
const int WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_KEYDOWN:
switch((Keys)m.WParam | ModifierKeys)
{
case Keys.Control | Keys.F:
onNewForm();
break;
}
break;
}
return false;
}
int _count = 0;
private void onNewForm()
{
char c = (char)(_count + 'A');
int dim = ++_count * SystemInformation.CaptionHeight;
new TextBoxForm
{
Name = $"textBoxForm{c}",
Text = $"TextBoxForm {c}",
Size = this.Size,
StartPosition= FormStartPosition.Manual,
Location = new Point(Left + dim, Top + dim),
}.Show(this);
}
}
Is there a way to detect if the windows/os language changed even when my app is not in focus?
So far I was able to achieve what I wanted only if the app was focused using:
string language = "";
System.Windows.Input.InputLanguageManager.Current.InputLanguageChanged +=
new System.Windows.Input.InputLanguageEventHandler((sender, e) =>
{
language = e.NewLanguage.DisplayName;
MessageBox.Show(language);
});
But as you can understand, this is not exactly what I want..
I was thinking about other solution such as hooking the keys that change the language (for example alt+shift) but I wont be able to know what language is currently in use and a user can change the default hotkey...
Would appreciate your help.
The problem you are facing is related with how WM_INPUTLANGCHANGE message works. This message is sent to programs by operating system in order to inform them about language changes. However, according to documentation this message is sent only to "to the topmost affected window". It means that you can even call a native method GetKeyboardLayout (it is used by InputLanguageManager by the way) but if an application is not active GetKeyboardLayout will always return the last known, outdated, language.
Taking this into account it might be a good idea to use the solution pointed by #VDohnal i.e. find the current topmost window and read keyboard layout for it. Here is a quick proof of concept how to do it inside WPF application. I used an additional thread that periodically finds the topmost window and ready keyboard layout for it. The code is far from being perfect but it works and it might help you to implement your own solution.
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
private CultureInfo _currentLanaguge;
public MainWindow()
{
InitializeComponent();
Task.Factory.StartNew(() =>
{
while (true)
{
HandleCurrentLanguage();
Thread.Sleep(500);
}
});
}
private static CultureInfo GetCurrentCulture()
{
var l = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero));
return new CultureInfo((short)l.ToInt64());
}
private void HandleCurrentLanguage()
{
var currentCulture = GetCurrentCulture();
if (_currentLanaguge == null || _currentLanaguge.LCID != currentCulture.LCID)
{
_currentLanaguge = currentCulture;
MessageBox.Show(_currentLanaguge.Name);
}
}
}
Is there a simple way to do this?
Or at the very least check if the console is currently in focus?
Imagine something like a game (thats not the case here but the analogy holds) - it would be useful if it could pause automatically. I need something similar.
If the window you were interested in were not a console window, this would have been very simple to do by just tapping into the appropriate focus event. But console windows don't have focus events, so the easy way out is not available here.
What you can do is set up an event handler to receive WinEvents generated by the UI Automation services. An event is generated whenever the window focus changes; you can get the HWND of the newly focused window and compare it to that of your console window. If they match, you just got focus; if they don't, you don't have focus (either just lost it or never had it to begin with).
The most convenient way to tap into UI Automation is through the System.Windows.Automation namespace. You can set up the event handler with AddAutomationFocusChangedEventHandler, which will give you an instance of AutomationFocusChangedEventArgs from which you can determine which window has received focus.
Here's some sample code:
AutomationFocusChangedEventHandler focusHandler = OnFocusChange;
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
MessageBox.Show("Listening to focus changes");
Automation.RemoveAutomationFocusChangedEventHandler(focusHandler);
where OnFocusChange is:
void OnFocusChange(object source, AutomationFocusChangedEventArgs e)
{
var focusedHandle = new IntPtr(AutomationElement.FocusedElement.Current.NativeWindowHandle);
var myConsoleHandle = Process.GetCurrentProcess().MainWindowHandle;
if (focusedHandle == myConsoleHandle)
{
// ...
}
}
Note that I am assuming the console is your process's main window for simplicity; if that's not the case, you need to get a HWND to the console window some other way.
Also note that in order to receive automation events, your process must be running a message loop (in this case also known as a "dispatcher loop"), which in turn requires a thread being dedicated to running it. In the example above this happens automatically when MessageBox.Show is called, but in the general case you will have to take proper care of it.
I can't add a comment so I'm just going to have to post an answer. You can test the theory posted by DJ KRAZE like this:
/// <summary>Returns true if the current application has focus, false otherwise</summary>
public static bool ApplicationIsActivated()
{
var activatedHandle = GetForegroundWindow();
if (activatedHandle == IntPtr.Zero) {
return false; // No window is currently activated
}else{
Console.WriteLine("Application is focused!");
}
var procId = Process.GetCurrentProcess().Id;
int activeProcId;
GetWindowThreadProcessId(activatedHandle, out activeProcId);
return activeProcId == procId;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
So if it doesn't return false, then it will print "Application is focused!" in the application. You can always add something where "return false" is to do something when it's not focused as well.
I'd like to hear any options for closing Win UI application from windows service.
My service runs under System account. UI application runs for every logged-in user, so there can be many app instances. I need to close them all. I know UI process name and can bind to each process instance and kill it. BUT UI application has tray icon which stays visible (ghost icon, disappears when hovered by mouse) after the process is killed. I'd like to close the UI application correctly, via managed or unmanaged code. Any ideas would be greatly appreciated.
ADDITION 1:
The UI application has no main window, but only tray icon (NotifyIcon component).
ADDITION 2:
I can modify source code of the UI application. But it is written in a way that prevents it from recieving CUSTOM window messages, only standard ones.
ADDITION 3:
The UI application does not show up any Form, it just creates ApplicationContext and executes NotifyIcon within the context.
Program.cs
public static class Program
{
[STAThread]
private static void Main()
{
ApplicationContext context = new TrayApplicationContext();
Application.Run(context);
}
}
TIA
Ivan
Call Process.CloseMainWindow instead of Kill.
If you have access to the source code of the WinUI app then in your main form (the one you start in Application.Run(mainFormGoesHere)) subscribe to the close event and make the notifyIcon.Visible = false; just before you exit. It is a known issue with the NotifyIcon and the system tray.
If it is a 3rd party app, then hope that they too have something implemented like this to properly clean up after being asked to close through CloseMainWindow()
Another approach would be to attempt to refresh the system tray from your service as described here
public const int WM_PAINT = 0xF;
[DllImport("USER32.DLL")]
public static extern int SendMessage(IntPtr hwnd, int msg, int character, IntPtr lpsText);
//Send WM_PAINT Message to paint System Tray which will refresh it.
SendMessage(traynotifywnd,WM_PAINT,0,IntPtr.Zero);
Try sending a WM_CLOSE message to the application:
const uint WM_CLOSE = 0x10;
[DllImport("user32.dll",EntryPoint="SendMessage", SetLastError=true)]
public static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int
lParam);
SendMessage(hWnd, WM_CLOSE, 0, 0);
hWnd is the window handle of the process' main window you are trying to shut down. You possibly also need to send the same message to the window handle of the notify icon
UPDATE
As you can't receive custom messages, you might try this:
enumerate all windows
iterate over each window and retrieve the processID
if the processID matches the one you want to shutdown, send WM_CLOSE to it
retrieving process ID for hWnd:
[DllImport("user32")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
Maybe silly, and I'm not sure this can work within multiple sessions.
Define a custom message and send it to broadcast using PostMessage; within your app capture incoming messages and if you receive your custom one, close gracefully.
In service:
public const int HWND_BROADCAST = 0xffff;
[DllImport("user32")]
public static extern bool PostMessage(int hwnd, int msg, int wparam, int lparam);
int WM_MYMSG = WM_USER + 1;
When you need to send message:
PostMessage(HWND_BROADCAST,WM_MYMSG,0,0);
In your app:
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}
EDITED:
If you want to override WndProc you need to have a form, but that doesn't mean you have to show a form: in your app create a form and run it, while in form code you write:
private void Form1_Shown(object sender, EventArgs e)
{
// Show here tray icon
....
....
// Hide form
this.Hide();
}
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}
I realize that this would be COMPLETELY bad practice in normal situations, but this is just for a test app that needs to be taking input from a bar code scanner (emulating a keyboard). The problem is that I need to start up some scripts while scanning, so I need the window to regain focus directly after I click the script to run it. I've tried using Activate(), BringToFront(), Focus() as well as some Win32 calls like SetForegroundWindow(), Setcapture() and SetActiveWindow()... however the best I can get any of them to do is to make the taskbar item start blinking to tell me that it wants to have focus, but something is stopping it. BTW, I'm running this on XP SP2 and using .NET 2.0.
Is this possible?
Edit: To clarify, I am running the scripts by double-clicking on them in explorer. So I need it to steal focus back from explorer and to the test app.
I struggled with a similar problem for quite a while. After much experimentation and guessing, this is how I solved it:
// Get the window to the front.
this.TopMost = true;
this.TopMost = false;
// 'Steal' the focus.
this.Activate();
Visibility
Make the window a "Top-Most" window. This is the way the Task-Manager can remain on top of other windows. This is a property of a Form and you make the form top-most (floating above other windows) by setting the value to true.
You shouldn't need to override any of the "Active window" behaviour with the top-most setting.
Focus
I asked a similar question previously here on StackOverflow and the answer would solve your problem. You can make the application use a low-level input hook and get notification of the key-codes coming from the scanner. This way, your application always gets these keys even though the application does not have focus.
You may need to enhance the solution to squash the key-codes so that they are not transmitted to the "in-focus" application (e.g. notepad).
Since Windows 2000, there is no official mechanism for an application to grab focus without direct intervention of the user. Peeking at the input streams through the RawInputDevices hook is the only sensible way to go.
A number of articles may help (C# implementations)
RawInput article on CodeProject
MSDN documentation of RawInput
I had a similar problem and found the following to do the trick. Adapted to C# from here
// force window to have focus
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
}
form.Activate();
EDIT: Here are the necessary PInvoke definitions for C#:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
The way I approached this problem was to spawn another thread whose only purpose was to ensure the Form is TopMost and has focus at all times. This code will make all other applications unusable while it is running, which is what I needed for my specific applications. You can add in a Sleep in keepFocus or have some other event trigger it.
using System.Threading; // be sure to include the System.Threading namespace
//Delegates for safe multi-threading.
delegate void DelegateGetFocus();
private DelegateGetFocus m_getFocus;
//Constructor.
myForm()
{
m_getFocus = new DelegateGetFocus(this.getFocus); // initialise getFocus
InitializeComponent();
spawnThread(keepFocus); // call spawnThread method
}
//Spawns a new Thread.
private void spawnThread(ThreadStart ts)
{
try
{
Thread newThread = new Thread(ts);
newThread.IsBackground = true;
newThread.Start();
}
catch(Exception e)
{
MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//Continuously call getFocus.
private void keepFocus()
{
while(true)
{
getFocus();
}
}
//Keeps Form on top and gives focus.
private void getFocus()
{
//If we need to invoke this call from another thread.
if (this.InvokeRequired)
{
try
{
this.Invoke(m_getFocus, new object[] { });
}
catch (System.ObjectDisposedException e)
{
// Window was destroyed. No problem but terminate application.
Application.Exit();
}
}
//Otherwise, we're safe.
else
{
this.TopMost = true;
this.Activate();
}
}
}
You might try focusing on a specific input, or try the setting .TopMost property to true (and then unsetting it again).
But I suspect your problem is that these methods all just place messages in the windows event queue, and your program has to wait for all existing events to finish processing before it will handle that one and focus the app.