2010-01-06 135 views
9

我有一個從GUI應用程序啓動的控制檯應用程序。控制檯應用程序使用文件名參數來解析和處理。目前我能夠捕獲它的輸出並將其顯示在GUI應用程序中,但我希望能夠向其發送命令以便控制甚至停止其執行。如何從GUI應用程序向控制檯應用程序發送命令

如何向控制檯應用程序發送命令或字符串或任何內容,最好使用爲打開輸出而打開的管道?

const 
    CReadBuffer = 2400; 
var 
    saSecurity: TSecurityAttributes; 
    hRead: THandle; 
    hWrite: THandle; 
    suiStartup: TStartupInfo; 
    piProcess: TProcessInformation; 
    pBuffer: array[0..CReadBuffer] of AnsiChar; 
    dRead: DWord; 
    dRunning: DWord; 
    dWritten: DWord; 
    Command: String; 
    BytesLeft: Integer; 
    BytesAvail: Integer; 
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; 
    Command := 'messageparser.exe c:\messagefile.msg'; 
    UniqueString(Command); 
    if CreateProcess(nil, PChar(Command), @saSecurity, 
    @saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess) then 
    begin 
     repeat 
     dRunning := WaitForSingleObject(piProcess.hProcess, 100); 
     Application.ProcessMessages; 
     repeat 
      dRead := 0; 

      if not PeekNamedPipe(hread, @pbuffer, CReadBuffer, @dRead, @BytesAvail, @BytesLeft) then 
      RaiseLastOSError; 
      if dRead <> 0 then 
      begin 
      ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil); 
      pBuffer[dRead] := #0; 
      OemToCharA(pBuffer, pBuffer); 
      // do something with the data 
      // if a condition is present then do the following: 
      // WriteFile(hWrite, some_command, size_of_buffer, DWritten, nil); 
      end; 
     until (dRead < CReadBuffer); 
     until (dRunning <> WAIT_TIMEOUT); 
     CloseHandle(piProcess.hProcess); 
     CloseHandle(piProcess.hThread); 
    end; 
    CloseHandle(hRead); 
    CloseHandle(hWrite); 
    end; 

然後在控制檯側,存在等待輸入一個線程。下面是execute方法:

while not Terminated do 
    begin 
    ReadLn(Command); 
    // process command 
    Sleep(10); 
    end; 

所以如果有關於如何做是正確的,我歡迎他們:)提示這是新的我。然而,無論何時我發送一個命令,它都會以我從ReadPipe讀入pBuffer而不是命令的方式來進行。

希望這會有所幫助。

-

實測值基於由納特尖端的溶液。

Bi-directional communication between gui and console

+0

所以你有這兩個應用程序的源代碼?他們都是用德爾福寫的嗎? – fupsduck 2010-01-06 20:23:40

+0

我添加了源代碼。 – yozey 2010-01-06 22:27:42

+0

用於發佈源代碼 - 在嘗試提供幫助時發揮所有作用。 – fupsduck 2010-01-07 01:30:05

回答

9

您需要兩個管道,一個用於向您發送輸出的過程(stdout),另一個用於將輸入發送給過程(stdin)。

從你的代碼,它看起來像你正在把相同管的兩端進入TStartupInfo記錄。所以你正在有效地讓這個過程自己談論。 :-)

所以,你需要調用CreatePipe()兩次,以創建兩個管道,一個用於stdin,一個用於stdout(和stderr)。

然後,放stdinsuiStartup.hStdInput閱讀手柄和stdout寫作手柄suiStartup.hStdOutput

發送數據的過程,寫stdin管道寫句柄。要讀取過程的輸出,請閱讀stdout管道的讀取手柄。

編輯:(再次)

對於所有的複製手柄和this page描述(特別是在代碼示例)遺傳和非遺傳的東西,你需要以確保您發送的手柄這個過程是可以繼承的(就像你所做的那樣)。

應該也確保父進程使用的是不可繼承管道的句柄。但你不要做到這一點......我已經逃脫了之前不這樣做。

您可以通過調用句柄DuplicateHandle(),指定他們是不可繼承和關閉舊的把手,或調用SetHandleInformation()與標誌(如討論here)指定0做到這一點。

這已經有一段時間,因爲我已經這樣做了我自己,但我敢肯定,這是如此,對於手柄的引用計數與調用進程,而不是子進程相關聯。這可以防止在您仍在使用它時關閉句柄(例如,調用過程可能會關閉「stdin」)。確保你關閉手柄,否則你會最終泄漏手柄。

HTH。

N @

+0

+1爲清晰和簡潔,但你在這裏http://support.microsoft.com/kb/190351描述的可繼承的句柄你做了什麼。 – fupsduck 2010-01-07 01:05:12

+0

在回答中回答。:) – Nat 2010-01-07 03:01:41

+0

感謝您的回答。根據您關於創建管道兩次的提示,我找到了一篇完美滿足我需求的文章。我已經包含了一個鏈接。 – yozey 2010-01-07 07:16:22

1

隨着輸出管道有一個輸入管道。只需使用WriteFile()寫入該管道即可。

+0

是的,我懷疑,但在控制檯,是拾起?我假設它是一個ReadLn我需要,所以我試圖在線程中檢查,但輸入是從控制檯應用程序輸出的相同。它沒有從GUI應用程序獲取命令,就像它正在讀取自身一樣。也許我做錯了什麼。 – yozey 2010-01-06 19:11:20

+0

根據您寫入流程的方式,您可能需要刷新。如果你使用WriteFile()API,你不需要。該進程的stdin句柄位於STARTUPINFO記錄中的hStdInput字段中。 如果控制檯應用程序無法讀取它的輸入,還有什麼可以做的。 – 2010-01-06 19:23:52

+0

目前它有一個執行Readln命令的線程,但該信息是一個鏡像或其在Writeln中輸出的內容。所以我想知道爲什麼。 – yozey 2010-01-06 20:05:01

1

檢查一下,看看你需要創建兩個管道(通過調用WINAPI兩次),但由Nat重申,但可繼承的句柄怎麼樣 - 不知道爲什麼這是必需的?

http://support.microsoft.com/kb/190351

我認爲可能會令人困惑的是,當您創建管道時,您正在爲該管道創建讀取句柄和寫入句柄。在控制檯的stdin管道的情況下,你只能使用寫入句柄。然後你爲控制檯的stdout創建另一個管道(它也有一個讀寫句柄),但你只能使用讀句柄。

我相信我有那個正確的,但已經晚了,我要去睡覺了。

+0

我有控制檯應用程序的源代碼。在GUI應用程序中,我可以使用以下內容讀取控制檯應用程序的輸出:ReadFile(ReadPipe,pBuffer [0],BufferSize,BytesRead,nil)。這是因爲ReadPipe由傳遞給CreateProcess的StartupInfo提供。現在,我假設移動數據的方式是將WriteFile與由同一個startupinfo提供的WritePipe一起使用。現在當我這樣做時,我發送的信息就像控制檯應用程序一樣,與ReadPipe中的內容完全相同,而不是我想要發送的內容。希望清除我想說的話。 – yozey 2010-01-06 21:37:16

相關問題