I have a commandline application coded in delphi that I need to call from a normal desktop application (also coded in delphi). In short, I want to call the commandline app and display the text it outputs "live" in a listbox.
It's been ages since I have played around with the shell, but I distinctly remember that in order to grab the text from a commandline app - I have to use the pipe symbol ">". Like this:
C:/mycmdapp.exe >c:/result.txt
This will take any text printed to the shell (using writeLn) and dump it to a textfile called "result.txt".
But.. (and here comes the pickle), I want a live result rather than a backlog file. A typical example is the Delphi compiler itself - which manages to report back to the IDE what is going on. If my memory serves me correctly, I seem to recall that I must create a "pipe" channel (?), and then assign the pipe-name to the shell call.
I have tried to google this but I honestly was unsure of how to formulate it. Hopefully someone from the community can point me in the right direction.
Updated: This question might be identical to How do I run a command-line program in Delphi?. Some of the answers fit what I'm looking for, although the title and question itself is not identical.
As ever so often Zarco Gajic has a solution: Capture the output from a DOS (command/console) Window. This is a copy from his article for future reference:
The example runs 'chkdsk.exe c:\' and displays the output to Memo1. Put a TMemo (Memo1) and a TButton (Button1) on your form. Put this code in the
OnCLick
event procedure forButton1
:
procedure RunDosInMemo(DosApp: string; AMemo:TMemo); const READ_BUFFER_SIZE = 2400; var Security: TSecurityAttributes; readableEndOfPipe, writeableEndOfPipe: THandle; start: TStartUpInfo; ProcessInfo: TProcessInformation; Buffer: PAnsiChar; BytesRead: DWORD; AppRunning: DWORD; begin Security.nLength := SizeOf(TSecurityAttributes); Security.bInheritHandle := True; Security.lpSecurityDescriptor := nil; if CreatePipe({var}readableEndOfPipe, {var}writeableEndOfPipe, @Security, 0) then begin Buffer := AllocMem(READ_BUFFER_SIZE+1); FillChar(Start, Sizeof(Start), #0); start.cb := SizeOf(start); // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. // - Redirect the output and error to the writeable end of our pipe. // - We must still supply a valid StdInput handle (because we used STARTF_USESTDHANDLES to swear that all three handles will be valid) start.dwFlags := start.dwFlags or STARTF_USESTDHANDLES; start.hStdInput := GetStdHandle(STD_INPUT_HANDLE); //we're not redirecting stdInput; but we still have to give it a valid handle start.hStdOutput := writeableEndOfPipe; //we give the writeable end of the pipe to the child process; we read from the readable end start.hStdError := writeableEndOfPipe; //We can also choose to say that the wShowWindow member contains a value. //In our case we want to force the console window to be hidden. start.dwFlags := start.dwFlags + STARTF_USESHOWWINDOW; start.wShowWindow := SW_HIDE; // Don't forget to set up members of the PROCESS_INFORMATION structure. ProcessInfo := Default(TProcessInformation); //WARNING: The unicode version of CreateProcess (CreateProcessW) can modify the command-line "DosApp" string. //Therefore "DosApp" cannot be a pointer to read-only memory, or an ACCESS_VIOLATION will occur. //We can ensure it's not read-only with the RTL function: UniqueString UniqueString({var}DosApp); if CreateProcess(nil, PChar(DosApp), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, start, {var}ProcessInfo) then begin //Wait for the application to terminate, as it writes it's output to the pipe. //WARNING: If the console app outputs more than 2400 bytes (ReadBuffer), //it will block on writing to the pipe and *never* close. repeat Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 100); Application.ProcessMessages; until (Apprunning <> WAIT_TIMEOUT); //Read the contents of the pipe out of the readable end //WARNING: if the console app never writes anything to the StdOutput, then ReadFile will block and never return repeat BytesRead := 0; ReadFile(readableEndOfPipe, Buffer[0], READ_BUFFER_SIZE, {var}BytesRead, nil); Buffer[BytesRead]:= #0; OemToAnsi(Buffer,Buffer); AMemo.Text := AMemo.text + String(Buffer); until (BytesRead < READ_BUFFER_SIZE); end; FreeMem(Buffer); CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); CloseHandle(readableEndOfPipe); CloseHandle(writeableEndOfPipe); end; end; procedure TForm1.Button1Click(Sender: TObject); begin {button 1 code} RunDosInMemo('chkdsk.exe c:\',Memo1); end;
Update: The above example reads the output in one step. Here is another example from DelphiDabbler showing how the output can be read while the process is still running:
function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string; var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; StdOutPipeRead, StdOutPipeWrite: THandle; WasOK: Boolean; Buffer: array[0..255] of AnsiChar; BytesRead: Cardinal; WorkDir: string; Handle: Boolean; begin Result := ''; with SA do begin nLength := SizeOf(SA); bInheritHandle := True; lpSecurityDescriptor := nil; end; CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0); try with SI do begin FillChar(SI, SizeOf(SI), 0); cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; wShowWindow := SW_HIDE; hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin hStdOutput := StdOutPipeWrite; hStdError := StdOutPipeWrite; end; WorkDir := Work; Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI); CloseHandle(StdOutPipeWrite); if Handle then try repeat WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); if BytesRead > 0 then begin Buffer[BytesRead] := #0; Result := Result + Buffer; end; until not WasOK or (BytesRead = 0); WaitForSingleObject(PI.hProcess, INFINITE); finally CloseHandle(PI.hThread); CloseHandle(PI.hProcess); end; finally CloseHandle(StdOutPipeRead); end; end;
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