The code below works for Delphi XE, but the 2400 buffersize is pretty ugly.
Anyone have some suggestions on cleaning this routine up ?? And making the 2400 limit disappear (without defining a 64000 buffer).
Thanks
procedure TForm1.Button1Click(Sender: TObject);
begin
CaptureConsoleOutput('c:\windows\system32\ipconfig','',Memo1);
end;
procedure TForm1.CaptureConsoleOutput(const ACommand, AParameters: String; AMemo: TMemo);
const
CReadBuffer = 2400;
var
saSecurity: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
suiStartup: TStartupInfo;
piProcess: TProcessInformation;
pBuffer: array[0..CReadBuffer] of AnsiChar;
dRead: DWord;
dRunning: DWord;
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;
if CreateProcess(nil, PChar(ACommand + ' ' + AParameters), #saSecurity,
#saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess)
then
begin
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages();
repeat
dRead := 0;
ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
pBuffer[dRead] := #0;
OemToAnsi(pBuffer, pBuffer);
AMemo.Lines.Add(String(pBuffer));
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
CloseHandle(hRead);
CloseHandle(hWrite);
end;
end;
I've got some code that does this. I've hacked out various irrelevant bits, so this may not compile as is. But you should get the idea:
procedure ReadStdout(hstdout: THandle; out stdout: string);
var
Buffer: AnsiString;
FileSize: DWORD;
NumberOfBytesRead: DWORD;
begin
FileSize := SetFilePointer(hstdout, 0, nil, FILE_END);
if FileSize>0 then begin
SetLength(Buffer, FileSize);
SetFilePointer(hstdout, 0, nil, FILE_BEGIN);
ReadFile(hstdout, Buffer[1], FileSize, NumberOfBytesRead, nil);
//should really check that NumberOfBytesRead=FileSize
stdout := Buffer;
end else begin
stdout := '';
end;
end;
function CreateFileHandle(const FileName: string): THandle;
var
SecurityAttributes: TSecurityAttributes;
begin
ZeroMemory(#SecurityAttributes, SizeOf(SecurityAttributes));
SecurityAttributes.nLength := SizeOf(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor := nil;
SecurityAttributes.bInheritHandle := True;
Result := CreateFile(
PChar(FileName),
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
#SecurityAttributes,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL or FILE_FLAG_WRITE_THROUGH,
0
);
end;
procedure Execute(const ExecutableFileName, DataFileName, TempFolder: string);
var
hstdin, hstdout: THandle;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
ExitCode: DWORD;
stdout: string;
begin
hstdin := CreateFileHandle(TempFolder+'stdin');
hstdout := CreateFileHandle(TempFolder+'stdout');
Try
ZeroMemory(#StartupInfo, SizeOf(StartupInfo));
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
StartupInfo.wShowWindow := SW_HIDE;
StartupInfo.hStdInput := hstdin;
StartupInfo.hStdError := hstdout;
if CreateProcess(
PChar(ExecutableFileName),
'',
nil,
nil,
True,
CREATE_NO_WINDOW or NORMAL_PRIORITY_CLASS,
nil,
PChar(TempFolder),
StartupInfo,
ProcessInfo
) then begin
Try
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
GetExitCodeProcess(ProcessInfo.hProcess, ExitCode);
ReadStdout(hstdout, stdout);
Finally
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
End;
end else begin
//error;
end;
Finally
CloseHandle(hstdout);
CloseHandle(hstdin);
End;
end;
You'll want to clean up the temp files at some point.
Related
I'm using the following procedure to put the result of DOS commands in a TMemo:
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;
begin
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
Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 200);
Application.ProcessMessages;
until (Apprunning <> WAIT_TIMEOUT);
repeat
BytesRead := 0;
ReadFile(ReadPipe, Buffer[0], ReadBuffer, BytesRead, nil);
Buffer[BytesRead] := #0;
OemToAnsi(Buffer, Buffer);
AMemo.Text := AMemo.text + String(Buffer);
until (BytesRead < ReadBuffer);
end;
FreeMem(Buffer);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ReadPipe);
CloseHandle(WritePipe);
end;
end;
Then I use it with a copy of netsh.exe to get a list of wireless signals and MAC addresses like this:
RunDosInMemo('C:\Edge LR\netsh.exe wlan show networks mode=Bssid', Memo3);
But it shows only the first 9 wireless signals from the list. When I run it on the console directly, it shows the full list with all wireless signals and specs.
Does anyone have an idea how to solve this?
If you read the following MSDN documentation, you will see that you are missing some very important steps:
Creating a Child Process with Redirected Input and Output
Most notably, you are using the parent process's read end of the pipe for the child process's STDIN, which is wrong. And you are letting the child process inherit that read end of the pipe, which is also wrong.
You also need to close the write end of the pipe after the child process has inherited it, before you then start reading from the pipe. Otherwise, the child process will not fully exit and signal the handle returned by CreateProcess(). By closing the write end, you ensure the process can fully terminate, and that ReadFile() will fail with an ERROR_BROKEN_PIPE error when the child process closes its end of the pipe and there is no more data to be read.
Try something more like this:
procedure RunDosInMemo(DosApp: String; AMemo: TMemo);
const
ReadBuffer = 2400;
var
Security : TSecurityAttributes;
ReadPipe, WritePipe : THandle;
start : TStartUpInfo;
ProcessInfo : TProcessInformation;
Buffer : array of AnsiChar;
Str: AnsiString;
BytesRead : DWord;
AppRunning : DWord;
begin
with Security do begin
nLength := SizeOf(TSecurityAttributes);
bInherithandle := true;
lpSecurityDescriptor := nil;
end;
if not CreatePipe(ReadPipe, WritePipe, #Security, 0) then RaiseLastOSError;
try
if not SetHandleInformation(ReadPipe, HANDLE_FLAG_INHERIT, 0) then RaiseLastOSError;
SetLength(Buffer, ReadBuffer);
FillChar(Start, Sizeof(Start), 0);
start.cb := SizeOf(start);
start.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
start.hStdOutput := WritePipe;
start.hStdError := WritePipe;
start.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
start.wShowWindow := SW_HIDE;
if not CreateProcess(nil,
PChar(DosApp),
#Security,
#Security,
true,
NORMAL_PRIORITY_CLASS,
nil,
nil,
start,
ProcessInfo) then RaiseLastOSError;
try
CloseHandle(WritePipe);
WritePipe := 0;
repeat
AppRunning := MsgWaitForMultipleObjects(1, ProcessInfo.hProcess, False, 200, QS_ALLINPUT);
if AppRunning = (WAIT_OBJECT_0 + 1) then Application.ProcessMessages;
until (AppRunning <> WAIT_TIMEOUT);
repeat
BytesRead := 0;
if not ReadFile(ReadPipe, Buffer[0], ReadBuffer, BytesRead, nil) then
begin
if GetLastError() <> ERROR_BROKEN_PIPE then RaiseLastOSError;
Break;
end;
if BytesRead = 0 then Break;
SetString(Str, #Buffer[0], BytesRead);
AMemo.SelStart := AMemo.GetTextLen;
AMemo.SelLength := 0;
AMemo.SelText := String(Str);
until False;
finally
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;
finally
CloseHandle(ReadPipe);
if WritePipe <> 0 then CloseHandle(WritePipe);
end;
end;
You could do:
uses
JCLSysUtils;
procedure TForm1.HandleOutput( const Text: string );
begin
AMemo.Lines.Add( Text );
end;
procedure TForm1.Button1Click( Sender: TObject );
begin
AMemo.Clear;
JCLSysUtils.Execute( 'C:\Edge LR\netsh.exe wlan show networks mode=Bssid',
HandleOutput );
end;
I get from other statckoverflow post the function listed below.
I have a encoding problem with accented chars to show the result in a TMemo.
for sample if I use:
Memo1.Text := GetDosOutput('Help DIR');
My Memo1.Text shows:
"Exibe uma lista de arquivos e subdiret¢rios em um diret¢rio.
DIR [unidade:][caminho][arquivo] [/A[[:]atributos]] [/B] [/C] [/D] [/L] [/N]
[/O[[:]ordem_de_classifica‡Æo]] [/P] [/Q] [/R] [/S] [/T[[:]campo_de_tempo]]
[/W] [/X] [/4]
.......
"
Please, How do I convert the result string of the function to shows correctly in TMemo?
I tried
Memo1.Text := UnicodeString(GetDosOutput('Help DIR'));
but, no look.
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;
From J.. suggestion, I created the StrOemToAnsi function showing below using
OemToCharBuffA.
To test I did:
procedure Tfm_nh_maindicom.Button1Click(Sender: TObject);
var s:string;
function StrOemToAnsi(const aStr : AnsiString) : AnsiString;
var
Len : Integer;
begin
if aStr = '' then Exit;
Len := Length(aStr);
SetLength(Result, Len);
OemToCharBuffA(PAnsiChar(aStr), PAnsiChar(Result), Len);
end;
begin
S:=GetDosOutput('Help DIR');
Memo1.Text:=StrOemToAnsi(s);
end;
And the result in Memo1 was:
"Exibe uma lista de arquivos e subdiretórios em um diretório.
DIR [unidade:][caminho][arquivo] [/A[[:]atributos]] [/B] [/C] [/D] [/L] [/N]
[/O[[:]ordem_de_classificação]] [/P] [/Q] [/R] [/S] [/T[[:]campo_de_tempo]]
[/W] [/X] [/4]
....
"
I'm using the code bellow to run a few "netsh wlan" commands in order to check wifi status, connect to a wifi profile, etc.
The problem that I'm having is that every now and then the app will hang on any of the commands, it's just a random thing, plus, sometimes the output returned get overwritten with "nothing", when I debugged it seemed like a timing issue.
I tried the conventional approach to run a command with Pascal but it didn't work with netsh, the approach is "cmd.exe /C netsh wlan....".
I appreciate any advise on getting this freezing procedure working better or another approach.
I'm running DelphiXE5.
Thanks
Sample commands: netsh wlan show profiles, netsh wlan show interfaces, etc.
procedure GetDosOutput(const ACommand, AParameters: String; CallBack: TArg<PAnsiChar>);
const
CReadBuffer = 2400;
var
saSecurity: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
suiStartup: TStartupInfo;
piProcess: TProcessInformation;
pBuffer: array [0 .. CReadBuffer] of AnsiChar;
dBuffer: array [0 .. CReadBuffer] of AnsiChar;
dRead: DWord;
dRunning: DWord;
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;
if CreateProcess(nil, pChar(ACommand + ' ' + AParameters), #saSecurity, #saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess) then
begin
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages();
repeat
dRead := 0;
ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
pBuffer[dRead] := #0;
//OemToAnsi(pBuffer, pBuffer);
//Unicode support by Lars Fosdal
OemToCharA(pBuffer, dBuffer);
CallBack(dBuffer);
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
CloseHandle(hRead);
CloseHandle(hWrite);
end;
end;
After following all the advises I got this portion of code changed and so far the app hasn't hanged anymore.
Thanks a lot!
procedure GetDosOutput(const ACommand, AParameters: String; CallBack: TArg<PAnsiChar>);
const
CReadBuffer = 2400;
var
saSecurity: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
suiStartup: TStartupInfo;
piProcess: TProcessInformation;
pBuffer: array [0 .. CReadBuffer] of AnsiChar;
dBuffer: array [0 .. CReadBuffer] of AnsiChar;
dRead: DWord;
dRunning: DWord;
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;
if CreateProcess(nil, pChar(ACommand + ' ' + AParameters), #saSecurity, #saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess) then
begin
Application.ProcessMessages();
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
repeat
dRead := 0;
try
ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
except on E: Exception do
Exit;
end;
pBuffer[dRead] := #0;
//OemToAnsi(pBuffer, pBuffer);
//Unicode support by Lars Fosdal
OemToCharA(pBuffer, dBuffer);
CallBack(dBuffer);
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
CloseHandle(hRead);
CloseHandle(hWrite);
end;
end;
I created this wrapper to simplify the process:
function GetDosOutputSimple(const ACommand, AParameters: String) : String;
var
Tmp, S : String;
begin
GetDosOutput(ACommand, AParameters, procedure (const Line: PAnsiChar)
begin
Tmp := Line;
S := S + Tmp;
end);
GetDosOutputSimple := S;
end;
If for any reason by the time you call ReadFile, the process have not completed a write operation, or your buffer is not filled, ReadFile will block. Normally it should fail, but it can't since you're holding a handle to the write end. See documentation:
... It is important for the parent process to close its handle to the
write end of the pipe before calling ReadFile. If this is not done,
the ReadFile operation cannot return zero because the parent process
has an open handle to the write end of the pipe.
So close 'hWrite' before reading from the pipe.
Note that, in this case - if the process have not been able to write anything to the pipe yet, instead of blocking, ReadFile will properly fail - and GetLastError will report ERROR_BROKEN_PIPE. Under this condition, you'd probably gracefully fail too. So better check return of ReadFile.
Alternatively, wait until the process terminates. Then you won't risk ReadFile blocking waiting for writing since the handles on child's side will have been closed.
...
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages();
until (dRunning <> WAIT_TIMEOUT);
repeat
dRead := 0;
...
If there's a chance that you'll have some sizeable output, read from the pipe when the application is running:
saSecurity.nLength := SizeOf(TSecurityAttributes);
saSecurity.bInheritHandle := True;
saSecurity.lpSecurityDescriptor := nil;
if CreatePipe(hRead, hWrite, #saSecurity, 0) then begin
try
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;
if CreateProcess(nil, pChar(ACommand + ' ' + AParameters), #saSecurity,
#saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil,
suiStartup, piProcess) then begin
CloseHandle(hWrite);
try
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages();
repeat
dRead := 0;
if ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil) then begin
pBuffer[dRead] := #0;
OemToCharA(pBuffer, dBuffer);
CallBack(dBuffer);
end;
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
finally
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
end;
finally
CloseHandle(hRead);
if GetHandleInformation(hWrite, flags) then
CloseHandle(hWrite);
end;
end;
I use to get to dos output using delphi.
What causes the code from http://delphi.about.com/cs/adptips2001/a/bltip0201_2.htm won't work with delphi2009 on vista? but it works on D7 in XP. i dont know what part to be modified to make it work.
DelphiDabbler has a solution, although I have not personally tested it:
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;
There is also a solution on Torry:
http://www.swissdelphicenter.ch/torry/showcode.php?id=683
I need to execute a Windows "find" command from a Delphi software. I've tried to use the ShellExecute command, but it doesn't seem to work. In C, I'd use the system procedure, but here... I don't know. I'd like to do something like this:
System('find "320" in.txt > out.txt');
Edit : Thanks for the answer :)
I was trying to run 'Find' as an executable, not as argument for cmd.exe.
An example using ShellExecute():
procedure TForm1.Button1Click(Sender: TObject);
begin
ShellExecute(0, nil, 'cmd.exe', '/C find "320" in.txt > out.txt', nil, SW_HIDE);
Sleep(1000);
Memo1.Lines.LoadFromFile('out.txt');
end;
Note that using CreateProcess() instead of ShellExecute() allows for much better control of the process.
Ideally you would also call this in a secondary thread, and call WaitForSingleObject() on the process handle to wait for the process to complete. The Sleep() in the example is just a hack to wait some time for the program started by ShellExecute() to finish - ShellExecute() will not do that. If it did you couldn't for example simply open a notepad instance for editing a file, ShellExecute() would block your parent app until the editor was closed.
Variant 1 (using the "advanced" CreateProcess):
This will run a 'DOS' program and retrieve its output:
function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string; { Run a DOS program and retrieve its output dynamically while it is running. }
var
SecAtrrs: TSecurityAttributes;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
pCommandLine: array[0..255] of AnsiChar;
BytesRead: Cardinal;
WorkDir: string;
Handle: Boolean;
begin
Result := '';
with SecAtrrs do begin
nLength := SizeOf(SecAtrrs);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
CreatePipe(StdOutPipeRead, StdOutPipeWrite, #SecAtrrs, 0);
try
with StartupInfo do
begin
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
cb := SizeOf(StartupInfo);
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), StartupInfo, ProcessInfo);
CloseHandle(StdOutPipeWrite);
if Handle then
try
repeat
WasOK := windows.ReadFile(StdOutPipeRead, pCommandLine, 255, BytesRead, nil);
if BytesRead > 0 then
begin
pCommandLine[BytesRead] := #0;
Result := Result + pCommandLine;
end;
until not WasOK or (BytesRead = 0);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
finally
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
finally
CloseHandle(StdOutPipeRead);
end;
end;
Variant 2:
Capture console output in [Realtime] and how it in a TMemo:
procedure CaptureConsoleOutput(const ACommand, AParameters: String; AMemo: TMemo);
const
CReadBuffer = 2400;
var
saSecurity: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
suiStartup: TStartupInfo;
piProcess: TProcessInformation;
pBuffer: array[0..CReadBuffer] of AnsiChar; <----- update
dRead: DWord;
dRunning: DWord;
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;
if CreateProcess(nil, PChar(ACommand + ' ' + AParameters), #saSecurity,
#saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess)
then
begin
repeat
dRunning := WaitForSingleObject(piProcess.hProcess, 100);
Application.ProcessMessages();
repeat
dRead := 0;
ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
pBuffer[dRead] := #0;
OemToAnsi(pBuffer, pBuffer);
AMemo.Lines.Add(String(pBuffer));
until (dRead < CReadBuffer);
until (dRunning <> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
end;
CloseHandle(hRead);
CloseHandle(hWrite);
end;
end;
Source: delphi.wikia.com