Send fast textinput to another process (Window)

2019-05-15 01:00发布

问题:

I am writing a C# WPF program which sends text messages to another program's window. I have a macro program as part of my keyboard drivers (Logitech g15) which already does this, though it does not send keystrokes directly to the process, but to the currently focused window. It works well but i need to be able to send inputs from my program as well. There are other people using the process so the input text messages from my program needs to be fast enough so that my text does not interfere with their input. The problem is that when I try to do this with a c# program I get too much delay. The macro program (Logitech G-Series Profiler) sends a command instantly. I have tried the following three commands for sending messages to process. (Listed by order of slowest to fastest)

SetForegroundWindow(_hWnd);
SendKeys.SendWait("{Enter}This is a keystroke input.{Enter}");

It is probably in the name, but this performs the command so slowly that I can actually follow with my eyes the text as it is input letter by letter. I have tried using the “SendKeys.Send” method but I get an error saying: “SendKeys cannot run inside this application because the application is not handling Windows messages.”

PostMessage((IntPtr)_hWnd, (uint)WMessages.WM_KEYUP, (int)key, (int)key);

PostMessage is a bit faster but still not fast enough for the purpose of my program. Besides the method returns before the message has been read by the process, which means two sequential PostMessage calls may not send sequential messages.

SendMessage(_hWnd, 0x100, (int) VKeys.VK_2, (int) VKeys.VK_2);

This is faster than the PostMessage but not nearly as fast as the macro program from Logitech. Also, the receiving program handles the input strangely, apparently not treating it the same way it does "genuine" input from the keyboard.

SetForegroundWindow(_hWnd);

const string text = "This is a keystroke input.";
IInputElement  target = Keyboard.FocusedElement;

IInputElement target = InputManager.Current.PrimaryKeyboardDevice.FocusedElement;

var routedEvent = TextCompositionManager.TextInputEvent;

target.RaiseEvent(new TextCompositionEventArgs(InputManager.Current.PrimaryKeyboardDevice, new TextComposition(InputManager.Current, target, text)) { RoutedEvent = routedEvent });

This is the last thing I have tried. It seems instant with the way the text is sent to a process. However, I have only been able to send this to my own program since Keyboard.FocusedElement returns null when I have another program set as foreground window. If someone can tell me how to get an IInputElement of another window I would sure like to know. Alternatively, if someone has a suggestion for a better method of sending input, I would dearly like to hear it.

Specs: Windows 7, 64bit Visual Studio 2010, Framework 4

回答1:

First of all, are you intentionally using WM_KEYDOWN (0x0100) instead of WM_KEYUP (0x0101) in your SendMessage example? This would just press the keys, and never release them, so the application would not process them properly.

Another way worth trying would be to send WM_SETTEXT, assuming the control interprets it correctly (like edit controls or combo boxes).

A last option would be to use SendInput which synthesizes keyboard and mouse input on a very low level, but similarly to you keyboard's macro program, this requires you to activate the correct window and set the focus, which can be quite painful.



回答2:

Depending on your other's program window type, you could use UI Automation. See this example here:

Add Content to a Text Box Using UI Automation