Delphi XE8:运行外部控制台应用程序、等待其结果和捕获其结果时出现问题
在Windows下的DelphiXe8中,我试图调用外部控制台应用程序并捕获其输出。我使用以下代码,如和中所述:Delphi XE8:运行外部控制台应用程序、等待其结果和捕获其结果时出现问题,delphi,pipe,console-application,createprocess,waitforsingleobject,Delphi,Pipe,Console Application,Createprocess,Waitforsingleobject,在Windows下的DelphiXe8中,我试图调用外部控制台应用程序并捕获其输出。我使用以下代码,如和中所述: procedure TForm1.按钮1点击(发送方:TObject); 过程RunDosInMemo(DosApp:String;AMemo:TMemo); 常数 ReadBuffer=2400; 变量 安全性:t安全属性; ReadPipe,WritePipe:THandle; 开始:TStartUpInfo; ProcessInfo:TProcessInformation; 缓
procedure TForm1.按钮1点击(发送方:TObject);
过程RunDosInMemo(DosApp:String;AMemo:TMemo);
常数
ReadBuffer=2400;
变量
安全性:t安全属性;
ReadPipe,WritePipe:THandle;
开始:TStartUpInfo;
ProcessInfo:TProcessInformation;
缓冲区:Pchar;
拜特斯拉德:德沃德;
通知:德沃德;
S:字符串;
开始
从安全开始
nlength:=SizeOf(TSecurityAttributes);
binherithandle:=真;
lpsecuritydescriptor:=零;
结束;
如果Createpipe(ReadPipe、WritePipe、,
@安全,0)那么
开始
缓冲区:=AllocMem(ReadBuffer+1);
FillChar(开始,Sizeof(开始),#0);
start.cb:=SizeOf(start);
start.hStdOutput:=writepe;
start.hStdInput:=读取管道;
start.dwFlags:=STARTF_USESTDHANDLES+STARTF_USESHOWWINDOW;
start.wShowWindow:=SW_HIDE;
S:=唯一字符串(DosApp);
如果CreateProcess(无,
PChar(S),
@保安,,
@保安,,
是的,
正常优先级等级,
无
无
开始
ProcessInfo)然后
开始
重复
Apprunning:=WaitForSingleObject
(ProcessInfo.hProcess,100);
Application.ProcessMessages;
直到(通知等待超时);
重复
字节读取:=0;
ReadFile(ReadPipe,Buffer[0],ReadBuffer,BytesRead,nil);
缓冲区[字节读取]:=#0;
OemToAnsi(缓冲区,缓冲区);
AMemo.Text:=AMemo.Text+字符串(缓冲区);
直到(字节读取<读取缓冲区);
结束;
FreeMem(缓冲区);
CloseHandle(ProcessInfo.hproces);
CloseHandle(ProcessInfo.hThread);
关闭手柄(读取管);
关闭手柄(WritePie);
结束;
结束;
开始{按钮1代码}
rundosinemo('cmd.exe/c dir',备忘录1)// 您正在运行的程序要么期望输入(如来自键盘),要么产生超出管道缓冲区的输出。在任何一种情况下,该程序都会挂起等待进一步的I/O操作,但您的父程序会在处理任何输出之前等待该子程序终止,并且从不提供任何输入
您需要在程序仍在运行时处理输出管道。否则,缓冲区可能会被填满,并且子级将阻塞,直到有更多的空间可用。同样,如果您不打算向另一个进程提供任何输入,则可能不应该为它提供有效的输入句柄。这样,如果它试图读取输入,它将失败,而不是阻塞
此外,您给该程序的输入句柄被附加到输出句柄。如果程序尝试读取,它将读取自己的输出,就像I/O ouroboros一样。要处理输入和输出,需要两个管道
(请注意,此死锁正是问题所在。同一答案中的第二个代码块解决了该问题以及ouroboros问题。)您正在运行的程序期望输入(如来自键盘)或产生超出管道缓冲区的输出。在任何一种情况下,该程序都会挂起等待进一步的I/O操作,但您的父程序会在处理任何输出之前等待该子程序终止,并且从不提供任何输入
您需要在程序仍在运行时处理输出管道。否则,缓冲区可能会被填满,并且子级将阻塞,直到有更多的空间可用。同样,如果您不打算向另一个进程提供任何输入,则可能不应该为它提供有效的输入句柄。这样,如果它试图读取输入,它将失败,而不是阻塞
此外,您给该程序的输入句柄被附加到输出句柄。如果程序尝试读取,它将读取自己的输出,就像I/O ouroboros一样。要处理输入和输出,需要两个管道
(请注意,这个死锁正是问题所在。同一答案中的第二个代码块解决了这个问题,以及ouroboros问题。)总结一下:@David Heffernan的代码正在运行。问题是控制台应用程序发出UTF-16 总结一下:@David Heffernan的代码在工作中。问题是控制台应用程序发出UTF-16 坦率地说,令人惊讶的是,这个程序能找到多少不同的变体。我想我的代码是有效的:大卫,谢谢你的帮助。我试过你的代码,它适用于几个控制台应用程序,除了我需要使用的那个。控制台应用程序已执行,文件大小似乎正确,但AnsiBuffer只包含无意义的信息。不幸的是,由于许可和保密原因,我无法命名或上传控制台应用程序。控制台应用程序在Wondows Commad提示符下工作正常。我可以尝试做些什么来进一步分析或解决问题?谢谢,不知道。如果你不能告诉我们关于这个应用程序的情况,这会有点棘手。大卫,我用以下命令尝试了你的代码:='cmd.exe'和参数:='/c d:\ConsoleApp.exe/parameter>c:\test.txt'。我使用重定向符号“>”将输出写入文件。这很有效。(命令处理器cmd.exe似乎是必要的,以便“>”工作。)管道中的重定向一定有问题。如果你有一个私人电子邮件地址,我可以通过电子邮件将控制台应用程序发送给你。也许你可以看看?你真是太好了。如果你没有时间,当然没问题。非常感谢你!不,我不能那样做。很抱歉如果你需要帮助,在这里问一个问题。坦率地说,令人惊讶的是
procedure TForm1.Button1Click(Sender: TObject) ;
procedure RunDosInMemo(DosApp:String;AMemo:TMemo) ;
const
ReadBuffer = 2400;
var
Security : TSecurityAttributes;
ReadPipe,WritePipe : THandle;
start : TStartUpInfo;
ProcessInfo : TProcessInformation;
Buffer : Pchar;
BytesRead : DWord;
Apprunning : DWord;
S: String;
begin
With Security do begin
nlength := SizeOf(TSecurityAttributes) ;
binherithandle := true;
lpsecuritydescriptor := nil;
end;
if Createpipe (ReadPipe, WritePipe,
@Security, 0) then
begin
Buffer := AllocMem(ReadBuffer + 1) ;
FillChar(Start,Sizeof(Start),#0) ;
start.cb := SizeOf(start) ;
start.hStdOutput := WritePipe;
start.hStdInput := ReadPipe;
start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
start.wShowWindow := SW_HIDE;
S:=UniqueString(DosApp);
if CreateProcess(nil,
PChar(S),
@Security,
@Security,
true,
NORMAL_PRIORITY_CLASS,
nil,
nil,
start,
ProcessInfo) then
begin
repeat
Apprunning := WaitForSingleObject
(ProcessInfo.hProcess,100) ;
Application.ProcessMessages;
until (Apprunning <> WAIT_TIMEOUT) ;
Repeat
BytesRead := 0;
ReadFile(ReadPipe,Buffer[0], ReadBuffer,BytesRead,nil) ;
Buffer[BytesRead]:= #0;
OemToAnsi(Buffer,Buffer) ;
AMemo.Text := AMemo.text + String(Buffer) ;
until (BytesRead < ReadBuffer) ;
end;
FreeMem(Buffer) ;
CloseHandle(ProcessInfo.hProcess) ;
CloseHandle(ProcessInfo.hThread) ;
CloseHandle(ReadPipe) ;
CloseHandle(WritePipe) ;
end;
end;
begin {button 1 code}
RunDosInMemo('cmd.exe /c dir',Memo1) ; //<-- this works
RunDosInMemo('"c:\consoleapp.exe" "/parameter"',Memo1) //<-- this hangs in the repeat until (Apprunning <> WAIT_TIMEOUT) ;
end;