2012-02-02 58 views
28

我有一個命令行應用程序編碼在德爾福,我需要從一個正常的桌面應用程序(也在delphi編碼)調用。簡而言之,我想調用命令行應用程序,並將它輸出的文本顯示在列表框中。從殼/ DOS應用程序獲取輸出到德爾福應用程序

自從我玩過shell之後就已經過時了,但我清楚地記得,爲了從命令行應用中獲取文本 - 我必須使用管道符號「>」。像這樣:

C:/mycmdapp.exe> C:/result.txt

這將需要打印到(使用writeLn)的殼的任何文本,並將其轉儲到一個被稱爲 「的Result.txt」 文本文件。

但是..(和這裏來的泡菜),我想要一個活的結果,而不是積壓文件。一個典型的例子是Delphi編譯器本身 - 它設法向IDE報告發生了什麼。如果我的記憶正確地爲我服務,我似乎記得我必須創建一個「管道」通道(?),然後將管道名稱分配給shell調用。

我試圖谷歌這個,但我真的不知道如何制定它。希望來自社區的人可以指引我走向正確的方向。

更新:此問題可能與How do I run a command-line program in Delphi?相同。有些答案符合我的要求,但標題和問題本身並不完全相同。

+5

[控制檯應用程序亞軍類(HT:

function Execute(const CommandLine: string; OutputLineCallback: TTextHandler; RawOutput: Boolean = False; AbortPtr: PBoolean = nil): Cardinal; 

你可以用一個回調過程提供它tp://www.delphidabbler.com/software/consoleapp/main?mid = 3.2) - 看起來很有前途 – kobik 2012-02-02 21:18:14

+0

使用Delphi XE2的Im(儘管我有Delphi 4以上版本的Delphi版本)。早先的Delphi 2010+與XE和XE2是二進制兼容的嗎?在這種情況下,班級應該工作。非常感謝你的反饋! – 2012-02-02 21:28:39

+0

@Jon:不,那是不正確的。 Delphi編譯器版本之間沒有二進制兼容性(D6和D7之間有單例外)。不能使用D2010編譯的.dcu文件與任何其他版本的Delphi,而無需重新編譯源代碼。 – 2012-02-02 22:39:06

回答

45

與往常一樣,Zarco Gajic有一個解決方案:Capture the output from a DOS (command/console) Window。這是他的文章的副本供將來參考:

該示例運行'chkdsk.exe c:\'並將輸出顯示爲Memo1。 將TMemo(Memo1)和TButton(Button1)放在窗體上。把OnCLick事件過程中此代碼爲Button1

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; 

更新: 上面的例子讀取輸出在一個步驟中。下面是從DelphiDabbler展示瞭如何輸出可以在過程仍在運行中讀取另一個例子:

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; 
+0

這是完美的!非常感謝!我沒想過檢查delphi.about.com,非常好! – 2012-02-02 21:18:06

+3

@JonLennartAasenden,我添加了第二個例子,顯示了一個更好的方法。 – 2012-02-02 21:24:40

+0

你的男人Uwe!謝謝! – 2012-02-02 23:04:11

13

你可能已經在你的硬盤上的代碼:在JclSysUtils單位JCL的Execute功能(JEDI代碼庫)做你需要什麼:
TTextHandler = procedure(const Text: string) of object;

相關問題