如何从GUI应用程序发送命令到控制台应用程序 [英] How to send command to console application from GUI application

查看:200
本文介绍了如何从GUI应用程序发送命令到控制台应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个从GUI应用程序启动的控制台应用程序。控制台应用程序接受文件名的参数进行解析和处理。目前,我可以捕获其输出并将其显示在GUI应用程序中,但我希望能够向其发送命令,以便控制甚至停止其执行。



如何将命令或字符串或任何内容发送到控制台应用程序,最好使用我打开的管道来读取其输出?

  const 
CReadBuffer = 2400;
var
saSecurity:TSecurityAttributes;
hRead:THandle;
hWrite:THandle;
suiStartup:TStartupInfo;
piProcess:TProcessInformation;
pBuffer:AnsiChar的数组[0..CReadBuffer];
dRead:DWord;
dRunning:DWord;
dWritten:DWord;
命令:String;
BytesLeft:Integer;
BytesAvail:Integer;
begin
saSecurity.nLength:= SizeOf(TSecurityAttributes);
saSecurity.bInheritHandle:= True;
saSecurity.lpSecurityDescriptor:= nil;

如果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或STARTF_USESHOWWINDOW;
suiStartup.wShowWindow:= SW_HIDE;
命令:='messageparser.exe c:\messagefile.msg';
UniqueString(Command);
如果CreateProcess(nil,PChar(Command),@saSecurity,
@saSecurity,True,NORMAL_PRIORITY_CLASS,nil,nil,suiStartup,piProcess)然后
begin
repeat
dRunning:= WaitForSingleObject(piProcess.hProcess,100);
Application.ProcessMessages;
重复
dRead:= 0;

如果没有PeekNamedPipe(hread,@pbuffer,CReadBuffer,@dRead,@BytesAvail,@BytesLeft)然后
RaiseLastOSError;
如果dRead<> 0然后
begin
ReadFile(hRead,pBuffer [0],CReadBuffer,dRead,nil);
pBuffer [dRead]:=#0;
OemToCharA(pBuffer,pBuffer);
//如果一个条件存在,请执行一些数据
//执行以下操作:
// WriteFile(hWrite,some_command,size_of_buffer,DWritten,nil);
结束
until(dRead< CReadBuffer);
until(dRunning<> WAIT_TIMEOUT);
CloseHandle(piProcess.hProcess);
CloseHandle(piProcess.hThread);
结束
CloseHandle(hRead);
CloseHandle(hWrite);
结束

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

 而不是终止do 
begin
ReadLn(Command);
//进程命令
睡眠(10);
结束

这对我来说很新,所以如果有关于如何正确的提示,我欢迎他们:) 。然而,无论何时发送命令,它都会像在ReadPipe中从pBuffer中读取的一样,而不是命令是什么。



希望这有帮助。



-



根据Nat。的提示找到一个解决方案。



gui和控制台之间的双向通信

解决方案

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



从您的代码中,您将相同的管道的两端都放在 TStartupInfo 记录。所以你正在有效地使这个过程自我介绍。 : - )



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

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



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



编辑: (再次)



对于此页面(具体在代码示例中),您需要来确保您发送到进程的句柄是可继承的完成了)。



应该还要确保父进程使用的管道的句柄不可继承。但是你不要这样做...我已经离开了,没有这样做。



你可以这样做在句柄上调用 DuplicateHandle(),指定它们不可继承并关闭旧句柄,或调用 SetHandleInformation()为标志指定0(如 here < a>)。



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



HTH。



N @


I have a console application that I launch from a GUI applicaiton. The console application takes parameters for filenames to parse and process. Currently I am able to capture its output and display it in the GUI application but I would like to be able to send commands to it so as to control or even halt its execution.

How can I send a command or string or anything to the console application, preferably using the pipes that I opened in order to read its output?

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;

Then on the console side, there is a thread waiting for the input. Here is the execute method:

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

This is new to me so if there are tips on how do it right, I welcome them :). However whenever I send a Command, it comes over as whatever I read in the pBuffer from the ReadPipe and not what the command is.

Hope this helps.

--

Found a solution based on the tip by Nat.

Bi-directional communication between gui and console

解决方案

You need two pipes, one for the process to send output to you (stdout), and one for you to send input to the process (stdin).

From your code, it looks like you are putting both ends of the same pipe into the TStartupInfo record. So you are effectively making the process talk to itself. :-)

So, you need to call CreatePipe() twice, to create two pipes, one for stdin, one for stdout (and stderr).

Then, put the reading handle of stdin in suiStartup.hStdInput and the writing handle of stdout in suiStartup.hStdOutput

To send data to the process, write to the write handle of the stdin pipe. To read the output of the process, read the read handle of the stdout pipe.

Edit: (again)

As for all the duplicating handles and inheritable and non-inheritable stuff described on this page (specifically in the code example), you need to make sure the handles you send to the process are inheritable (as you have done).

You should also make sure the handles of the pipes that the parent process use are not inheritable. But you don't have to do this... I've gotten away with not doing it before.

You can do this by either calling DuplicateHandle() on the handles, specifying they are not inheritable and closing the old handles, or calling SetHandleInformation() with 0 specified for the flags (as discussed here).

It's been a while since I have done this myself, but I'm pretty sure this is so that the reference count for the handles is associated with the calling process, rather than the child process. This prevents a handle being closed whilst you're still using it (the calling process might close 'stdin' for example). Make sure you close the handles though, otherwise you will end up leaking handles.

HTH.

N@

这篇关于如何从GUI应用程序发送命令到控制台应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆