I have a console application that I launch from a GUI applicaiton. The console application takes parameters for filenames to parse and process. Currently I am able to capture its output and display it in the GUI application but I would like to be able to send commands to it so as to control or even halt its execution.
How can I send a command or string or anything to the console application, preferably using the pipes that I opened in order to read its output?
const
CReadBuffer = 2400;
var
saSecurity: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
suiStartup: TStartupInfo;
piProcess: TProcessInformation;
pBuffer: array[0..CReadBuffer] of AnsiChar;
dRead: DWord;
dRunning: DWord;
dWritten: DWord;
Command: String;
BytesLeft: Integer;
BytesAvail: Integer;
begin
saSecurity.nLength := SizeOf(TSecurityAttributes);
saSecurity.bInheritHandle := True;
saSecurity.lpSecurityDescriptor := nil;
if CreatePipe(hRead, hWrite, @saSecurity, 0) then
begin
FillChar(suiStartup, SizeOf(TStartupInfo), #0);
suiStartup.cb := SizeOf(TStartupInfo);
suiStartup.hStdInput := hRead;
suiStartup.hStdOutput := hWrite;
suiStartup.hStdError := hWrite;
suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
suiStartup.wShowWindow := SW_HIDE;
Command := 'messageparser.exe c:\messagefile.msg';
UniqueString(Command);
if CreateProcess(nil, PChar(Command), @saSecurity,
@saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess) then
begin
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages;
repeat
dRead := 0;
if not PeekNamedPipe(hread, @pbuffer, CReadBuffer, @dRead, @BytesAvail, @BytesLeft) then
RaiseLastOSError;
if dRead <> 0 then
begin
ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
pBuffer[dRead] := #0;
OemToCharA(pBuffer, pBuffer);
// do something with the data
// if a condition is present then do the following:
// WriteFile(hWrite, some_command, size_of_buffer, DWritten, nil);
end;
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
CloseHandle(hRead);
CloseHandle(hWrite);
end;
Then on the console side, there is a thread waiting for the input. Here is the execute method:
while not Terminated do
begin
ReadLn(Command);
// process command
Sleep(10);
end;
This is new to me so if there are tips on how do it right, I welcome them :). However whenever I send a Command, it comes over as whatever I read in the pBuffer from the ReadPipe and not what the command is.
Hope this helps.
--
Found a solution based on the tip by Nat.
Bi-directional communication between gui and console
You need two pipes, one for the process to send output to you (stdout
), and one for you to send input to the process (stdin
).
From your code, it looks like you are putting both ends of the same pipe into the TStartupInfo
record. So you are effectively making the process talk to itself. :-)
So, you need to call CreatePipe()
twice, to create two pipes, one for stdin
, one for stdout
(and stderr
).
Then, put the reading handle of stdin
in suiStartup.hStdInput
and the writing handle of stdout
in suiStartup.hStdOutput
To send data to the process, write to the write handle of the stdin
pipe. To read the output of the process, read the read handle of the stdout
pipe.
Edit: (again)
As for all the duplicating handles and inheritable and non-inheritable stuff described on this page (specifically in the code example), you need to make sure the handles you send to the process are inheritable (as you have done).
You should also make sure the handles of the pipes that the parent process use are not inheritable. But you don't have to do this... I've gotten away with not doing it before.
You can do this by either calling DuplicateHandle()
on the handles, specifying they are not inheritable and closing the old handles, or calling SetHandleInformation()
with 0 specified for the flags (as discussed here).
It's been a while since I have done this myself, but I'm pretty sure this is so that the reference count for the handles is associated with the calling process, rather than the child process. This prevents a handle being closed whilst you're still using it (the calling process might close 'stdin' for example). Make sure you close the handles though, otherwise you will end up leaking handles.
HTH.
N@
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With