if CreateProcess(nil, CmdLine, nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then begin ExitCode := 0;
while ExitCode = 0 do begin wrResult := WaitForSingleObject(PI.hProcess, 1000);
if PeekNamedPipe(hReadPipe, @Dest[0], 32768, @Avail, nil, nil) then begin if Avail > 0 then begin try FillChar(Dest, SizeOf(Dest), 0); ReadFile(hReadPipe, Dest[0], Avail, BytesRead, nil); TmpStr := Copy(Dest, 0, BytesRead - 1); Line := Line + TmpStr; except end; end; end; if wrResult<>WAIT_TIMEOUT then ExitCode := 1; end; GetExitCodeProcess(PI.hProcess, ExitCode); CloseHandle(PI.hProcess); CloseHandle(PI.hThread); end; finally ShowMessage('a'); if Line = '' then Line :='命令没有输出回应!'; ItemList.Text := Line; CloseHandle(hReadPipe); CloseHandle(hWritePipe); end; end;
----------------------------------------------
-
{$REGION 'Functions to execute command-line and capture output'} //--- JclBase and JclSysUtils ---------- const // line delimiters for a version of Delphi/C++Builder NativeLineFeed = Char(#10); NativeCarriageReturn = Char(#13);
function CharIsReturn(const C: Char): Boolean; begin Result := (C = NativeLineFeed) or (C = NativeCarriageReturn); end;
// memory initialization procedure ResetMemory(out P; Size: Longint); begin if Size > 0 then begin Byte(P) := 0; FillChar(P, Size, 0); end; end;
type // e.g. TStrings.Append TTextHandler = procedure(const Text: string) of object;
function MuteCRTerminatedLines(const RawOutput: string): string; const Delta = 1024; var BufPos, OutPos, LfPos, EndPos: Integer; C: Char; begin SetLength(Result, Length(RawOutput)); OutPos := 1; LfPos := OutPos; EndPos := OutPos; for BufPos := 1 to Length(RawOutput) do begin if OutPos >= Length(Result)-2 then SetLength(Result, Length(Result) + Delta); C := RawOutput[BufPos]; case C of NativeCarriageReturn: OutPos := LfPos; NativeLineFeed: begin OutPos := EndPos; Result[OutPos] := NativeCarriageReturn; Inc(OutPos); Result[OutPos] := C; Inc(OutPos); EndPos := OutPos; LfPos := OutPos; end; else Result[OutPos] := C; Inc(OutPos); EndPos := OutPos; end; end; SetLength(Result, OutPos - 1); end;
function InternalExecute(CommandLine: string; var Output: string; OutputLineCallback: TTextHandler; RawOutput: Boolean; AbortPtr: PBoolean; const CurrentDir: string): Cardinal;
const BufferSize = 255; type TBuffer = array [0..BufferSize] of AnsiChar;
procedure ProcessLine(const Line: string; LineEnd: Integer); begin if RawOutput or (Line[LineEnd] <> NativeCarriageReturn) then begin while (LineEnd > 0) and CharIsReturn(Line[LineEnd]) do Dec(LineEnd); OutputLineCallback(Copy(Line, 1, LineEnd)); end; end;
procedure ProcessBuffer(var Buffer: TBuffer; var Line: string; PipeBytesRead: Cardinal); var CR, LF: Integer; begin Buffer[PipeBytesRead] := #0; Line := Line + string(Buffer); if Assigned(OutputLineCallback) then repeat CR := Pos(NativeCarriageReturn, Line); if CR = Length(Line) then CR := 0; // line feed at CR + 1 might be missing LF := Pos(NativeLineFeed, Line); if (CR > 0) and ((LF > CR + 1) or (LF = 0)) then LF := CR; // accept CR as line end if LF > 0 then begin ProcessLine(Line, LF); Delete(Line, 1, LF); end; until LF = 0; end;
var Buffer: TBuffer; Line: string; PipeBytesRead: Cardinal; {$IFDEF MSWINDOWS} var StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; SecurityAttr: TSecurityAttributes; PipeRead, PipeWrite: THandle; PCurrentDir: PChar; begin Result := $FFFFFFFF; SecurityAttr.nLength := SizeOf(SecurityAttr); SecurityAttr.lpSecurityDescriptor := nil; SecurityAttr.bInheritHandle := True; PipeWrite := 0; PipeRead := 0; Line := ''; ResetMemory(Buffer, SizeOf(Buffer)); if not CreatePipe(PipeRead, PipeWrite, @SecurityAttr, 0) then begin Result := GetLastError; Exit; end; ResetMemory(StartupInfo, SizeOf(TStartupInfo)); StartupInfo.cb := SizeOf(TStartupInfo); StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; StartupInfo.wShowWindow := SW_HIDE; StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE); StartupInfo.hStdOutput := PipeWrite; StartupInfo.hStdError := PipeWrite; UniqueString(CommandLine); // CommandLine must be in a writable memory block ProcessInfo.dwProcessId := 0; try if CurrentDir <> '' then PCurrentDir := PChar(CurrentDir) else PCurrentDir := nil; if CreateProcess(nil, PChar(CommandLine), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, PCurrentDir, StartupInfo, ProcessInfo) then begin CloseHandle(PipeWrite); PipeWrite := 0; if AbortPtr <> nil then {$IFDEF FPC} AbortPtr^ := 0; {$ELSE ~FPC} AbortPtr^ := False; {$ENDIF ~FPC} PipeBytesRead := 0; while ((AbortPtr = nil) or not LongBool(AbortPtr^)) and ReadFile(PipeRead, Buffer, BufferSize, PipeBytesRead, nil) and (PipeBytesRead > 0) do ProcessBuffer(Buffer, Line, PipeBytesRead); if (AbortPtr <> nil) and LongBool(AbortPtr^) then TerminateProcess(ProcessInfo.hProcess, Cardinal(ABORT_EXIT_CODE)); if (WaitForSingleObject(ProcessInfo.hProcess, INFINITE) = WAIT_OBJECT_0) and not GetExitCodeProcess(ProcessInfo.hProcess, Result) then Result := $FFFFFFFF; CloseHandle(ProcessInfo.hThread); ProcessInfo.hThread := 0; CloseHandle(ProcessInfo.hProcess); ProcessInfo.hProcess := 0; end else begin CloseHandle(PipeWrite); PipeWrite := 0; end; CloseHandle(PipeRead); PipeRead := 0; finally if PipeRead <> 0 then CloseHandle(PipeRead); if PipeWrite <> 0 then CloseHandle(PipeWrite); if ProcessInfo.hThread <> 0 then CloseHandle(ProcessInfo.hThread); if ProcessInfo.hProcess <> 0 then begin TerminateProcess(ProcessInfo.hProcess, Cardinal(ABORT_EXIT_CODE)); WaitForSingleObject(ProcessInfo.hProcess, INFINITE); GetExitCodeProcess(ProcessInfo.hProcess, Result); CloseHandle(ProcessInfo.hProcess); end; end; {$ENDIF MSWINDOWS} {$IFDEF UNIX} var Pipe: PIOFile; Cmd: string; begin Cmd := Format('%s 2>&1', [CommandLine]); Pipe := nil; try Pipe := Libc.popen(PChar(Cmd), 'r'); { TODO : handle Abort } repeat PipeBytesRead := fread_unlocked(@Buffer, 1, BufferSize, Pipe); if PipeBytesRead > 0 then ProcessBuffer(Buffer, Line, PipeBytesRead); until PipeBytesRead = 0; Result := pclose(Pipe); Pipe := nil; wait(nil); finally if Pipe <> nil then pclose(Pipe); wait(nil); end; {$ENDIF UNIX} if Line <> '' then if Assigned(OutputLineCallback) then // output wasn't terminated by a line feed... // (shouldn't happen, but you never know) ProcessLine(Line, Length(Line)) else if RawOutput then Output := Output + Line else Output := Output + MuteCRTerminatedLines(Line); end;
function RunDosCmd(const CommandLine: string; Output: TLogProc; RawOutput: Boolean = False; AbortPtr: PBoolean = nil; const CurrentDir: string = ''): Cardinal; var Dummy: string; begin Dummy := ''; Result := InternalExecute(CommandLine, Dummy, Output, RawOutput, AbortPtr, CurrentDir); end;