2010-01-06 137 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

相关问题