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

时间:2021-04-07 20:45:00

[Delphi XE4, Win 7]

[Delphi XE4,Win 7]

At startup my GUI application spawns a visible 'cmd.exe' console window (using 'CreateProcess'). The console is up as long as the GUI app is running.

启动时,我的GUI应用程序会生成一个可见的“cmd.exe”控制台窗口(使用“CreateProcess”)。只要GUI应用程序正在运行,控制台就会启动。

The console window expects input from the keyboard, but the input/command has to come from the GUI app (the GUI sends the command to be executed to the console, and the user can watch what's happening in the console window - so no console output redirection). I tried to use an anon pipe for sending commands to the console (stdin redirected), but it doesn't work. Code snippet :

控制台窗口需要来自键盘的输入,但输入/命令必须来自GUI应用程序(GUI将要执行的命令发送到控制台,用户可以查看控制台窗口中发生的事情 - 因此没有控制台输出重定向)。我试图使用anon管道向控制台发送命令(stdin重定向),但它不起作用。代码段:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    Fsa: SECURITY_ATTRIBUTES;
    Fsi: STARTUPINFO;
    Fpi: PROCESS_INFORMATION;
    Fpipe_stdin_read : THandle;
    Fpipe_stdin_write: THandle;
    procedure CreateProcessCmd;
  end;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  // write command to pipe
  WriteFile(Fpipe_stdin_write, ...);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateProcessCmd;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(Fpi.hProcess);
  CloseHandle(Fpi.hThread);
  CloseHandle(Fpipe_stdin_read);
  CloseHandle(Fpipe_stdin_write);
  inherited;
end;

procedure TForm1.CreateProcessCmd;
begin
  // init security structure
  Fsa.nLength := SizeOf(SECURITY_ATTRIBUTES);
  Fsa.bInheritHandle := True;
  Fsa.lpSecurityDescriptor := nil;
  // create a pipe for the child process's stdin
  if not CreatePipe(Fpipe_stdin_read, Fpipe_stdin_write, @Fsa, 0) then
  begin
    ...;
    Exit;
  end;
  // init startup info structure
  FillChar(Fsi, SizeOf(STARTUPINFO), #0);
  Fsi.cb := SizeOf(STARTUPINFO);
  Fsi.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW or STARTF_USEPOSITION;
  Fsi.hStdError := GetStdHandle(STD_ERROR_HANDLE);  // don't redirect std err
  Fsi.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE);  // don't redirect std out
  Fsi.hStdInput := Fpipe_stdin_read;
  Fsi.wShowWindow := SW_SHOW;
  Fsi.dwX := 1000;
  Fsi.dwY := 50;
  // init process info structure
  FillChar(Fpi, SizeOf(PROCESS_INFORMATION), #0);
  // create process with new console
  if not CreateProcess(nil,PChar(GetEnvironmentVariable('COMSPEC')),nil,nil,True,CREATE_NEW_CONSOLE,nil,nil,Fsi,Fpi) then
    ...;
end;

The cmd console window will be created but immediately closed after that. Without the redirecting code parts the console is up and running (expecting keyboard input, of course).

将创建cmd控制台窗口,但在此之后立即关闭。如果没有重定向代码部分,控制台就会启动并运行(当然需要键盘输入)。

What am I doing wrong? Any insight / working code is really appreciated.

我究竟做错了什么?任何见解/工作代码都非常感谢。

Note: A similiar question has already been asked (How to send command to console application from GUI application) but that one works with capturing/redirecting the console output - in my case this is not an option, the output generated after sending a command has to be displayed in the console window.

注意:已经提出了一个类似的问题(如何从GUI应用程序向控制台应用程序发送命令)但是一个用于捕获/重定向控制台输出 - 在我的情况下这不是一个选项,发送命令后生成的输出显示在控制台窗口中。

1 个解决方案

#1


I don't think it's possible to redirect only some of the standard handles. When you set Fsi.hStdError := GetStdHandle(STD_ERROR_HANDLE), you're assigning that field the stderr handle of your process. Since you're running a GUI process, that handle doesn't exist. The created process inherits that bogus handle, tries to write to it, fails, and terminates.

我认为不可能只重定向一些标准句柄。当您设置Fsi.hStdError:= GetStdHandle(STD_ERROR_HANDLE)时,您将为该字段分配进程的stderr句柄。由于您正在运行GUI进程,因此该句柄不存在。创建的进程继承该伪造句柄,尝试写入它,失败并终止。

You could try calling AllocConsole first. That will give your process standard console I/O handles that the new process can inherit.

您可以先尝试调用AllocConsole。这将为您的进程提供新进程可以继承的标准控制台I / O句柄。

Otherwise, it looks like you're going to have to capture the output yourself and display it somewhere in your program.

否则,您似乎必须自己捕获输出并将其显示在程序中的某个位置。

#1


I don't think it's possible to redirect only some of the standard handles. When you set Fsi.hStdError := GetStdHandle(STD_ERROR_HANDLE), you're assigning that field the stderr handle of your process. Since you're running a GUI process, that handle doesn't exist. The created process inherits that bogus handle, tries to write to it, fails, and terminates.

我认为不可能只重定向一些标准句柄。当您设置Fsi.hStdError:= GetStdHandle(STD_ERROR_HANDLE)时,您将为该字段分配进程的stderr句柄。由于您正在运行GUI进程,因此该句柄不存在。创建的进程继承该伪造句柄,尝试写入它,失败并终止。

You could try calling AllocConsole first. That will give your process standard console I/O handles that the new process can inherit.

您可以先尝试调用AllocConsole。这将为您的进程提供新进程可以继承的标准控制台I / O句柄。

Otherwise, it looks like you're going to have to capture the output yourself and display it somewhere in your program.

否则,您似乎必须自己捕获输出并将其显示在程序中的某个位置。