【问题标题】:How to send command to console application from GUI application如何从 GUI 应用程序向控制台应用程序发送命令
【发布时间】:2010-01-06 18:54:24
【问题描述】:

我有一个从 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

【问题讨论】:

  • 那么你有这两个应用程序的源代码吗?它们都是用 Delphi 编写的吗?
  • 我添加了源代码。
  • +1 用于发布源代码 - 在提供帮助方面发挥了重要作用。
  • 谢谢。从一开始就应该这样做。

标签: delphi console


【解决方案1】:

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

从您的代码看来,您将 same 管道的两端都放入了TStartupInfo 记录。因此,您正在有效地使流程自言自语。 :-)

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

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

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

编辑:(再次)

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

应该还确保父进程使用的管道句柄不可继承。但你没有必须这样做......我以前不这样做就逃脱了。

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

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

HTH。

N@

【讨论】:

  • +1 为清晰和简洁起见,但您如何看待此处描述的可继承句柄 support.microsoft.com/kb/190351
  • 感谢您的回答。根据您关于两次创建管道的提示,我找到了一篇完美满足我需求的文章。我已经包含了它的链接。
【解决方案2】:

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

【讨论】:

  • 是的,我怀疑是这样,但在控制台中,是否被拾取?我假设它是我需要的 ReadLn,所以我尝试在线程中检查它,但是输入与控制台应用程序输出的相同。它没有从 GUI 应用程序获取命令,几乎就像它正在读取自身一样。也许我做错了什么。
  • 根据您写入进程的方式,您可能需要刷新。如果您使用 WriteFile() API,则不必这样做。进程的标准输入句柄位于 STARTUPINFO 记录的 hStdInput 字段中。如果控制台应用未读取其输入,则您无能为力。
  • 目前它有一个线程执行 Readln 命令,但信息是镜像或它在其 Writeln 中输出的内容。所以我想知道为什么会这样。
  • 嗯,一个明显的船长评论:输入和输出是两边不同的两个句柄。你读一个,写另一个(还有stderr,但那是另一回事)。写入控制台进程的输出管道不会作为输入到达控制台进程。
  • 查看我对 fupsduck 的回复,因为我认为您没有理解我的问题。我知道句柄是不同的,但是进入控制台的数据不是我使用 WriteFile 和 StartupInfo 提供的 WritePipe 句柄发送的数据。
【解决方案3】:

检查一下,您需要创建两个管道(通过调用 WINAPI 两次),正如 Nat 重申的那样,但是可继承的句柄呢 - 不确定为什么需要这样做?

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

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

我相信我说的没错,但时间不早了,我要睡觉了。

【讨论】:

  • 我确实有控制台应用程序的源代码。在 GUI 应用程序中,我可以使用以下命令读取控制台应用程序的输出 - ReadFile(ReadPipe, pBuffer[0], BufferSize, BytesRead, nil)。这是因为 ReadPipe 由传递给 CreateProcess 的 StartupInfo 提供。现在,我假设移动数据的另一种方式是使用由相同启动信息提供的 WriteFile 和 WritePipe。现在,当我这样做时,我发送到控制台应用程序的信息正是 ReadPipe 中的信息,而不是我想要发送的信息。希望这能澄清我想说的。
猜你喜欢
  • 1970-01-01
  • 2012-08-14
  • 2017-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多