如何从GUI应用程序发送命令到控制台应用程序 [英] How to send command to console application from GUI application
问题描述
如何将命令或字符串或任何内容发送到控制台应用程序,最好使用我打开的管道来读取其输出?
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。的提示找到一个解决方案。
您需要两个管道,一个用于向您发送输出的过程( 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屋!