Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CreateProcess Redirect Output

923 views
Skip to first unread message

Richard

unread,
Jul 1, 2008, 11:15:16 AM7/1/08
to
I've created an anonymous pipe then I start a process with CreateProcess.
Currently, I can read the pipe when the process is complete and display the
output in a memo. But what I need is to update the memo or whatever live,
or at least every 100 milliseconds or so while the process is running.

After I create the process, I want to loop, reading the contents of the pipe
via a thread, display it to a memo (live) until the process is complete.

How do I test if the process is still running without stopping my
application with WaitForSingleObject?

Or, if someone knows of any code that will redirect the output live instead
of waiting until the process is complete that would be awesome.

Richard


Krisztian Pinter

unread,
Jul 1, 2008, 11:29:16 AM7/1/08
to
On Tue, 01 Jul 2008 17:15:16 +0200, Richard <rwsk...@ccwip.net> wrote:


> After I create the process, I want to loop, reading the contents of the
> pipe
> via a thread, display it to a memo (live) until the process is complete.

two things:

1. you can test if the pipe has data with the PeekNamedPipe call. it seems
strange, but in contradiction with its name, this API does work with
anonymous
pipes as well. you need that because reading the pipe waits indefinitely,
rendering your program dead.

2. WaitForSingleObject has a timeout parameter. set it to 10ms or 50ms, as
you
see fit. check the pipe, check any other stuff that needs to be checked,
and
wait again. in that loop, you will need to call Application.ProcessMessages
so WM_PAINT messages arrive, and user can interact. BEWARE! A.PM needs some
rethinking of program logic. as an alternative, you can simply update the
form with TForm.Update. if you omit these, you will not see the memo
updating
its content.

Remy Lebeau (TeamB)

unread,
Jul 1, 2008, 1:07:19 PM7/1/08
to

"Krisztian Pinter" <pinter.k...@chello.hu> wrote in message
news:op.udmda2jcobk3tt@karwst_pk.karatnet.hu...

> you need that because reading the pipe waits indefinitely,
> rendering your program dead.

You can use overlapped I/O on the pipe to avoid that.


Gambit


Remy Lebeau (TeamB)

unread,
Jul 1, 2008, 1:06:21 PM7/1/08
to

"Richard" <rwsk...@ccwip.net> wrote in message
news:486a4a04$1...@newsgroups.borland.com...

> How do I test if the process is still running without stopping
> my application with WaitForSingleObject?

What's wrong with using WaitForSingleObject()? Simply give it a small
timeout so you can check the message queue frequently for new messages.

Better would be to use MsgWaitForMultipleObjects() instead. That way, you
can wait on both the spawned process and the message queue at the same time,
and react accordingly to whichever one generates a signal.


Gambit


Mike Dixon

unread,
Jul 1, 2008, 1:24:58 PM7/1/08
to

Rather than writing your own message handling loop, you can also use a
thread to listen to the pipe. You'll need to make a separate function to
update the memo and call it with Synchronize(), as the VCL is not
thread-safe. The function will likely be on the form/frame that the
memo's on, and set to an event on the thread when the thread's created.

-Mike

Martin Stoeckli

unread,
Jul 1, 2008, 3:02:55 PM7/1/08
to
Mike Dixon schrieb:

> Rather than writing your own message handling loop, you can also use
> a thread to listen to the pipe. You'll need to make a separate
> function to update the memo and call it with Synchronize(), as the
> VCL is not thread-safe. The function will likely be on the form/frame
> that the memo's on, and set to an event on the thread when the
> thread's created.
>
> -Mike

Hello Richard

I can provide an example using threads to listen to the stdoutput, it
adds the output to a "Content" string, but you could also update your
memo component. As long as "ReadFile" don't return 0 bytes read, you
know that the pipe is still open and the process is running.

http://www.martinstoeckli.ch/delphi/delphi.html#AppRedirectOutput

Good luck:
Martin

--
Martin Stoeckli
http://www.martinstoeckli.ch/delphi

Richard

unread,
Jul 1, 2008, 6:12:28 PM7/1/08
to
Almost all the examples show the output when the process is done.
I did a little hacking on some code from Delphi.about and modified the line
feeds and crs so it shows in the memo properly. I'm sure there are some big
gotchas.

procedure RunDosInMemo(DosApp:String;AMemo:TMemo) ;
const
ReadBuffer = 2400;
var
Security : TSecurityAttributes;
ReadPipe,WritePipe : THandle;
start : TStartUpInfo;
ProcessInfo : TProcessInformation;
Buffer : Pchar;
BytesRead : DWord;
Apprunning : DWord;
x : Integer;
MyStr : String;
begin
x := 0;
With Security do
begin
nlength := SizeOf(TSecurityAttributes) ;
binherithandle := true;
lpsecuritydescriptor := nil;
end;

if Createpipe (ReadPipe, WritePipe,
@Security, 0) then
begin
Buffer := AllocMem(ReadBuffer + 1) ;
FillChar(Start,Sizeof(Start),#0) ;
start.cb := SizeOf(start) ;
start.hStdOutput := WritePipe;
start.hStdInput := ReadPipe;
start.dwFlags := STARTF_USESTDHANDLES +
STARTF_USESHOWWINDOW;
start.wShowWindow := SW_HIDE;

if CreateProcess(nil,
PChar(DosApp),
@Security,
@Security,
true,
NORMAL_PRIORITY_CLASS,
nil,
nil,
start,
ProcessInfo)
then
begin
repeat
Inc(x);
Apprunning := WaitForSingleObject
(ProcessInfo.hProcess,100) ;
Application.ProcessMessages;

BytesRead := 0;
ReadFile(ReadPipe,Buffer[0],ReadBuffer,BytesRead,nil) ;
Buffer[BytesRead]:= #0;
OemToAnsi(Buffer,Buffer) ;

MyStr := '';
for x := 0 to BytesRead do
Begin
If Buffer[x] = #13 then
MyStr := MyStr + #13#10 else
If Buffer[x] = #10 then
MyStr := MyStr + '' else
MyStr := MyStr + buffer[x];
End;

AMemo.Lines.Add(MyStr);
AMemo.Refresh;
Application.ProcessMessages;

until (Apprunning <> WAIT_TIMEOUT) ;
FreeMem(Buffer) ;
CloseHandle(ProcessInfo.hProcess) ;
CloseHandle(ProcessInfo.hThread) ;
CloseHandle(ReadPipe) ;
CloseHandle(WritePipe) ;
end;
end;
end;


procedure TForm2.Button1Click(Sender: TObject);
begin
RunDosInMemo('chkdsk.exe c:',memo1) ;
end;


Rob Kennedy

unread,
Jul 1, 2008, 7:02:27 PM7/1/08
to
Richard wrote:
> I've created an anonymous pipe then I start a process with CreateProcess.
> Currently, I can read the pipe when the process is complete and display the
> output in a memo.

That's risky. If you don't read anything from the pipe until the process
terminates, then the pipe might fill up. Once the pipe fills up, the
writer will block, waiting for someone to read from the pipe and make
room for more data. If you're blocked waiting for the process to end,
and the process is blocked waiting for you to read, then you have deadlock.

If this has worked for you so far, then it's probably because the
process doesn't generate enough output to fill the pipe's buffer.

> But what I need is to update the memo or whatever live,
> or at least every 100 milliseconds or so while the process is running.
>
> After I create the process, I want to loop, reading the contents of the pipe
> via a thread, display it to a memo (live) until the process is complete.
>
> How do I test if the process is still running without stopping my
> application with WaitForSingleObject?

If the process has terminated, then the write end of the pipe will get
closed. Subsequent calls to ReadFile will fail with Error_Broken_Pipe.

--
Rob

Krisztian Pinter

unread,
Jul 2, 2008, 3:56:28 AM7/2/08
to

please show me a reference to it, because as i know, it is not possible

Richard

unread,
Jul 2, 2008, 8:21:17 AM7/2/08
to
Martin,
What if your readfile loop was modified to send windows messages?:
Would you see something like that working? It should make it threadsafe and
syncronized. The
Main Application Thread could process the messages and add to whatever
visual control it wanted to?

type
PStoReadPipeThreadParam = ^TStoReadPipeThreadParam;
TStoReadPipeThreadParam = record
Pipe: THandle;
Content: String;
end;

function Sto_ReadPipeThreadProc(Parameter: PStoReadPipeThreadParam):
Integer;
const
BLOCK_SIZE = 4096;
var
iBytesRead: DWORD;
szBuffer: array[0..BLOCK_SIZE-1] of Char;
PBuffer : ^szBuffer;

begin
Result := 0;
repeat
if ReadFile(Parameter^.Pipe, szBuffer, BLOCK_SIZE, iBytesRead, nil) then
Begin
new(PBuffer);
PBuffer^ := szBuffer;
Postmessage(MainForm.Handle,MyThreadMsg,MyConsoleMsg,Integer(PBuffer));
End;
until (iBytesRead = 0);
end;

"Martin Stoeckli" <martinst...@gmx.ch> wrote in message
news:486a...@newsgroups.borland.com...


> I can provide an example using threads to listen to the stdoutput, it
> adds the output to a "Content" string, but you could also update your

> http://www.martinstoeckli.ch/delphi/delphi.html#AppRedirectOutput


Martin Stoeckli

unread,
Jul 2, 2008, 9:35:04 AM7/2/08
to
Richard

The code in the example uses threads only to avoid a blocked pipe after
the "ReadFile" function, this is possible because there are two pipes
reading stdoutput and stderror separately. The function itself doesn't
return before the child process is closed.

If you want to collect all output into the same memo control, then you
can do without the second pipe (hPipeErrorRead, hPipeErrorWrite) and
without the second thread (hThreadErrorRead). Use the same pipe for both:

myStartupInfo.hStdInput := 0;
myStartupInfo.hStdOutput := hPipeOutputWrite;
myStartupInfo.hStdError := hPipeOutputWrite;

This way you won't have any threading problems, the main function
"Sto_RedirectedExecute" waits with "WaitForSingleObject" for the end of
the child process and the loop inside your thread procedure is
serialized anyway.

Instead of a "Content" string you can also place your component into the
parameter, so you can work directly with the memo control after each
call to ReadFile .

TStoReadPipeThreadParam = record
Pipe: THandle;

Memo: TMemo;
end;

Hope this helps:
Martin

Richard schrieb:

Krisztian Pinter

unread,
Jul 2, 2008, 10:21:28 AM7/2/08
to
On Wed, 02 Jul 2008 15:35:04 +0200, Martin Stoeckli
<martinst...@gmx.ch> wrote:

> myStartupInfo.hStdInput := 0;
> myStartupInfo.hStdOutput := hPipeOutputWrite;
> myStartupInfo.hStdError := hPipeOutputWrite;

little remark here. there are misbehaving programs that does not want to
use
the error output, so start with closing the error handle. no kidding, there
are such programs. since you give the same handle to both place, these
programs
cut themselves off of the pipe.

to work around that "wisdom", you can use DuplicateHandle to make a copy
of the
write-handle. this way, if the program prematurely closes the error handle,
the stdout handle will still be open, and pointing to the same pipe
entrance.

Martin Stoeckli

unread,
Jul 2, 2008, 12:07:56 PM7/2/08
to
Krisztian Pinter schrieb:

Good to know, i didn't think of that.
Thanks:
Martin

0 new messages