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

Delphi 如何从GUI应用程序向控制台应用程序发送命令,delphi,console,Delphi,Console,我有一个从GUI应用程序启动的控制台应用程序。控制台应用程序获取文件名的参数以进行解析和处理。目前,我能够捕获它的输出并在GUI应用程序中显示它,但我希望能够向它发送命令,以便控制甚至停止它的执行 如何向控制台应用程序发送命令、字符串或任何东西,最好使用我打开的管道读取其输出 const CReadBuffer = 2400; var saSecurity: TSecurityAttributes; hRead: THandle; hWrite: THandle; suiSt

我有一个从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;
这对我来说是新的,所以如果有关于如何正确操作的提示,我欢迎他们:)。但是,每当我发送一个命令时,它都会显示为我从ReadPipe在pBuffer中读取的内容,而不是该命令是什么

希望这有帮助

--

根据Nat提供的提示找到了解决方案


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

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

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


我相信我说的没错,但是已经很晚了,我要睡觉了。

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

从您的代码中,看起来您正在将同一管道的两端放入
TStartupInfo
记录中。因此,您可以有效地让流程自言自语。:-)

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

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

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

编辑:(再次)

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

您还应该确保父进程使用的管道的句柄不可继承。但你不必这么做。。。我以前没做这件事,但我侥幸逃脱了惩罚

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

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


是的,我想是的,但在控制台里,那是捡到的吗??我假设它是我需要的ReadLn,所以我尝试在线程中检查它,但是输入与控制台应用程序的输出相同。它并没有从GUI应用程序中获取命令,就像它在阅读自己一样。也许我做错了什么。根据你对进程的写入方式,你可能需要刷新。如果使用WriteFile()API,则不必这样做。流程的标准输入句柄位于hStdInput字段的STARTUPINFO记录中。如果控制台应用程序没有读取其输入,您对此无能为力。当前,它有一个线程执行Readln命令,但信息是镜像或它在其Writeln中输出的内容。所以我想知道为什么会这样。嗯,一个显而易见的评论:输入和输出是两边的两个不同句柄。你读给一个人听,写给另一个人听(也有stderr,但那是另一个故事)。写入控制台进程的输出管道不会作为输入到达控制台进程。请参阅我对fupsduck的响应,因为我认为您没有理解我的问题。我知道句柄是不同的,但是进入控制台的数据并不是我使用StartupInfo提供的WriteFile和WritePie句柄发送的数据。所以你有这两个应用程序的源代码吗?它们都是用Delphi编写的吗?我添加了源代码。+1用于发布源代码-在尝试帮助时起到了很大作用。谢谢。从一开始就应该这样做。我有控制台应用程序的源代码。在GUI应用程序中,我能够使用以下-ReadFile(ReadPipe,pBuffer[0],BufferSize,BytesRead,nil)读取控制台应用程序的输出。这是因为ReadPipe由传递给CreateProcess的StartupInfo提供。现在,我假设以另一种方式移动数据,我使用由相同startupinfo提供的WriteFile和WritePie。现在,当我这样做时,我发送到控制台应用程序的信息与ReadPipe中的信息完全相同,而不是我想要发送的信息。希望这能澄清我想说的内容。+1为了清晰和简洁,但您如何看待这里描述的可继承句柄。谢谢您的回答。根据你关于两次创建管道的提示,我找到了一篇完美满足我需求的文章。我已经包括了一个链接到它。
  while not Terminated do
  begin
    ReadLn(Command);
    // process command
    Sleep(10);
  end;