6

I've got a command line application that starts up and does some work. During that time, it listens to keystrokes (s => show status). It's not the typical command prompt where you press 's' and <ENTER> - it's the type which reacts as soon as the key is pressed the status is shown.

Now I'm trying to "control" that command line application from a fancy GUI application by sending keystrokes. I've tried the more conventional approach of writing to the Process' StandardInput but that doesn't seem to have an effect at all. Also, because the actual process doesn't have a window (it's started with CreateNoWindow=true) I can't try the Win32 API for sending keystrokes to a window.

Is there any other way of doing it?

4
  • 4
    Is the command line application one you wrote (or at least have the source code to)? If so, I'd refactor out the bit that you want to reuse into a separate library which both the GUI and the command-line tool use. Commented Dec 24, 2011 at 20:58
  • Did you made sure to Flush the stream when using the Process'StandardInput ? Commented Dec 24, 2011 at 21:00
  • @JonSkeet: Heh, if I had the luxury of source code I wouldn't be asking the question :P I'd just change the application to work better. Yeah, I did FLUSH the stream by calling the Flush() command. Didn't help. Commented Dec 24, 2011 at 21:24
  • 1
    Can you create the console application with a hidden window (wShowWindow=SW_HIDE) and then send keystrokes to it? Commented Dec 24, 2011 at 21:56

3 Answers 3

3

Fancy console applications are problematic.

They have a tendency to directly read the keyboard input, instead of going through stdin. They also have a tendency to directly control their console, instead of going through stdout.

AFAIK, there is no way to programmatically control these apps. If you really, really need to, I would explore something like AutoHotKey controlling the app on a private desktop (AHK uses a virtual keyboard/mouse driver). I'm not sure how you would read the results off the console, though; it may be possible to create an intermediate console app that's started by your program (in the private desktop) and starts the target app. The intermediate app would then share its console with the target app and use low-level I/O to detect changes.

Or you could use Detours to bend the target app to your will.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the answer. Actually, I'm able to read from it easily. Only sending input seems to be a problem. I'll check those two things out and see if any of it works. EDIT: Quite strange that sending a keystroke (as if it was typed on a keyboard) isn't as standard as writing to STDIN :)
There's an arms race, actually. On the one side are the apps that really want to read from a real keyboard, and on the other side are the apps that really want to fake out those apps. WM_KEYUP/WM_KEYDOWN, keybd_event, SendInput (SendKeys), etc. I once was contracted to write a virtual keyboard driver because there was a program that simply refused to be virtualized. In the arms race, virtualization always wins. :) Until TPM is used. :(
2

Well, I seem to have found an answer to my own question. It's a real "kludged together" solution, but it works - and for all the intents and purposes of the application I'm building, it doesn't matter.

So, what I did was use two WinAPI functions called

static extern bool ShowWindow(IntPtr WindowHandle, int nCmdShow); static extern bool SetForegroundWindow(IntPtr WindowHandle); 

The first one can be used to Show/Hide a window by changing nCmdShow to 1 and 0 respectively. The other one puts the window (determined by WindowHandle) to the front. Combining these two together, I was able to programmaticly bring the console window up front, do a simple SendKeys.Send(); operation and then hide it again.

// Use a WIN API command to bring the command line to front SetForegroundWindow(workerProcess.MainWindowHandle); // Send a keystore to re-display the STATUS of the worker SendKeys.Send("s"); // Hide the window again. ShowWindow(workerProcess.MainWindowHandle, 0); 

Now, it's a real kludge job, but it gets the job done. One potential pitfall would be if a user is using the computer for something else, and would nail that 1 in a 10000000 moment when the window is active with a 'q' - it would quit the worker program. But the application is intended to be used on dedicated machines that most likely won't even have monitors, keyboards or mice attached to them so it wouldn't be an issue.

Thanks to all who answered, since you did - in one way or another, steer me towards the right solution.

Comments

2

I found an even better way to accomplish the functionality without the theoretical risk of causing problems with simultaneous user input and window-switching.

The trick is to use the WinAPI functions called PostMessage to send up KeyDown (or KeyUp) message to the process which does the same thing. No need to bring the process window to the front and hide it immediately afterwards!

I'm sending the key-down command with key 'S' as the argument:

 // 0x0100 - VM_KEYDOWN // 0x0101 - VM_KEYUP // 0x53 - S-key PostMessage(workerProcess.MainWindowHandle, 0x0100, 0x53, 0); PostMessage(workerProcess.MainWindowHandle, 0x0101, 0x53, 0); 

5 Comments

PostMessage? Where it come from?
WinAPI functions
Does it work for hidden cmd application ? i try to send message to visible application and i can without any problem but when target application is hidden it doesn't work in my case
@Cozdemir It worked in 2011, but I haven't touched that code since, so I am really not sure if the same WinAPI works the same way 11 years later.
Thank you for response. Actually still works but when i redirectstartdardoutput it doesn't work.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.