如何从GUI应用程序向控制台应用程序发送命令

时间:2010-01-06 18:54:25

标签: delphi console

我有一个控制台应用程序,我从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;

然后在控制台端,有一个线程在等待输入。这是执行方法:

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

这对我来说是新的,所以如果有关于如何做的提示,我欢迎他们:)。但是每当我发送一个命令时,它都会从我在ReadPipe中读取的pBuffer中看到,而不是命令是什么。

希望这有帮助。

-

根据Nat。

的提示找到解决方案

Bi-directional communication between gui and console

3 个答案:

答案 0 :(得分:9)

您需要两个管道,一个用于向您发送输出的过程(stdout),另一个用于将输入发送到流程(stdin)。

从您的代码中,您似乎将相同的管道的两端放入TStartupInfo记录中。所以你有效地让这个过程与自己对话。 : - )

因此,您需要拨打CreatePipe()两次,以创建两个管道,一个用于stdin,一个用于stdout(和stderr)。

然后,在stdin中放置suiStartup.hStdInput的阅读句柄,在stdout中放置suiStartup.hStdOutput的书写句柄

要将数据发送到进程,请写入stdin管道的写入句柄。要读取进程的输出,请阅读stdout管道的读取句柄。

编辑:(再次)

对于this page(特别是代码示例)中描述的所有重复句柄以及可继承和不可继承的内容,您需要以确保您发送给进程的句柄是可继承的(正如你所做的那样)。

还要确保父进程使用的管道句柄不可继承。但你没有来做这件事......我之前没有这样做过。

您可以通过在句柄上调用DuplicateHandle(),指定它们不可继承并关闭旧句柄,或者为标志指定0来调用SetHandleInformation()来执行此操作(如所讨论的{{3} })。

我已经有一段时间了,但是我很确定这是因为句柄的引用计数与调用进程相关联,而不是子进程。这可以防止在您仍在使用它时关闭句柄(例如,调用进程可能会关闭'stdin')。确保你关闭手柄,否则你最终会泄漏手柄。

HTH。

N - [

答案 1 :(得分:1)

除输出管道外,还有一个输入管道。只需使用WriteFile()写入该管道。

答案 2 :(得分:1)

检查一下,看看你是否需要创建两个管道(通过调用WINAPI两次),如Nat所重申的那样但是可继承的句柄怎么样 - 不确定为什么需要它?

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

我认为可能令人困惑的是,当您创建管道时,您正在为该管道创建读取句柄和写入句柄。对于控制台的stdin管道,您将只使用写入句柄。然后为控制台的stdout创建另一个管道(它也有一个读写句柄)但你只能使用读句柄。

我相信我有正确的但是已经很晚了,我要去睡觉了。