gpt4 book ai didi

delphi - 执行DOS程序并动态获取输出

转载 作者:行者123 更新时间:2023-12-03 18:21:14 24 4
gpt4 key购买 nike

我需要执行一个“DOS”程序(控制台应用程序)并动态检索其输出(能够随时结束 DOS 程序也很好,因为 DOS 程序可能会运行数小时)。

我有这个这个功能,但它有时(很少)卡住。
我需要一个新功能或修复以下功能。

procedure ExecuteAndGetOutDyn(CONST ACommand, AParameters: String; AMemo: TMemo);
CONST
CReadBuffer = 128*KB; //original was 2400bytes
VAR
SecurityAttrib: TSecurityAttributes;
hRead: THandle;
hWrite: THandle;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
pBuffer: array[0..CReadBuffer] of AnsiChar;
dRead: DWord;
dRunning: DWord;
WasOK: Boolean;
begin
SecurityAttrib.nLength := SizeOf(TSecurityAttributes);
SecurityAttrib.bInheritHandle := True;
SecurityAttrib.lpSecurityDescriptor := nil;

if CreatePipe(hRead, hWrite, @SecurityAttrib, 0) then
begin
FillChar(StartupInfo, SizeOf(TStartupInfo), #0);
StartupInfo.cb := SizeOf(TStartupInfo);
StartupInfo.hStdInput := hRead;
StartupInfo.hStdOutput := hWrite;
StartupInfo.hStdError := hWrite;
StartupInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow:= SW_HIDE;

if CreateProcess(NIL, PChar(ACommand + ' ' + AParameters), @SecurityAttrib, @SecurityAttrib, True, NORMAL_PRIORITY_CLASS, NIL, NIL, StartupInfo, ProcessInfo) then
begin
REPEAT
dRunning:= WaitForSingleObject(ProcessInfo.hProcess, 100);
Application.ProcessMessages;
REPEAT
dRead := 0;
WasOK := Windows.ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, NIL);
if NOT WasOK then mesajerror('Cannot read console output.');
pBuffer[dRead] := #0;

OemToAnsi(pBuffer, (pBuffer));
AMemo.Lines.Add(String(pBuffer));
UNTIL (dRead < CReadBuffer) OR NOT WasOK;
UNTIL (dRunning <> WAIT_TIMEOUT) { OR Abort};
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;

CloseHandle(hRead);
CloseHandle(hWrite);
end;
end;

最大的问题是没有特定的条件可以卡住程序。我只是调用 ExecuteAndGetOutDyn,有时它会在“DOS”程序完成后卡住。我一发现就会发布卡住出现的条件。

最佳答案

一个明显的问题是你的管道。您有一个管道,您安排子进程 stdout 写入一端,子进程 stdin 从另一端读取。那不好。为什么你希望进程从它自己的输出中读取它的输入?同时父进程从管道中读取。你有两个进程试图读取这个管道。我无法想象结局会很好。

你需要两个管道。一个用于 child 的标准输入。 parent 写入它, child 从中读取。另一个管道用于 child 的标准输出。 child 写, parent 读。

或者,如果您不希望子进程有任何标准输入,则创建一个管道,将写入端连接到子进程标准输出并让父进程从读取端读取。

另一个问题是,如果进程已经终止,并且您已经阅读了它的所有内容,那么调用 ReadFile将无限期阻塞。在尝试从中读取之前,您需要确保管道包含某些内容。我会使用 GetFileSizeEx为了那个原因。

就我个人而言,我倾向于在一个线程中完成所有这些操作,以避免调用 ProcessMessages。 .

您还应该始终检查 API 返回值是否有错误。对 WaitForSingleObject 的调用没有这样做。和 ReadFile .

我提出以下几点建议:

program DynamicStdOutCapture;

{$APPTYPE CONSOLE}

uses
System.SysUtils,
System.Math,
Winapi.Windows;

function GetFileSizeEx(hFile: THandle; var FileSize: Int64): BOOL; stdcall;
external kernel32;

procedure Execute(const Command: string; const Parameters: string;
const Timeout: DWORD; const Output: TProc<string>);

const
InheritHandleSecurityAttributes: TSecurityAttributes =
(nLength: SizeOf(TSecurityAttributes); bInheritHandle: True);

var
hReadStdout, hWriteStdout: THandle;
si: TStartupInfo;
pi: TProcessInformation;
WaitRes, BytesRead: DWORD;
FileSize: Int64;
AnsiBuffer: array [0 .. 1024 - 1] of AnsiChar;

begin
Win32Check(CreatePipe(hReadStdout, hWriteStdout,
@InheritHandleSecurityAttributes, 0));
try
si := Default (TStartupInfo);
si.cb := SizeOf(TStartupInfo);
si.dwFlags := STARTF_USESTDHANDLES;
si.hStdOutput := hWriteStdout;
si.hStdError := hWriteStdout;
Win32Check(CreateProcess(nil, PChar(Command + ' ' + Parameters), nil, nil,
True, CREATE_NO_WINDOW, nil, nil, si, pi));
try
while True do
begin
WaitRes := WaitForSingleObject(pi.hProcess, Timeout);
Win32Check(WaitRes <> WAIT_FAILED);
while True do
begin
Win32Check(GetFileSizeEx(hReadStdout, FileSize));
if FileSize = 0 then
begin
break;
end;
Win32Check(ReadFile(hReadStdout, AnsiBuffer, SizeOf(AnsiBuffer) - 1,
BytesRead, nil));
if BytesRead = 0 then
begin
break;
end;
AnsiBuffer[BytesRead] := #0;
OemToAnsi(AnsiBuffer, AnsiBuffer);
if Assigned(Output) then
begin
Output(string(AnsiBuffer));
end;
end;
if WaitRes = WAIT_OBJECT_0 then
begin
break;
end;
end;
finally
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
end;
finally
CloseHandle(hReadStdout);
CloseHandle(hWriteStdout);
end;
end;

procedure DoOutput(Text: string);
begin
Write(Text);
end;

procedure Main;
begin
Execute('ping', 'stackoverflow.com -t', 100, DoOutput);
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

关于delphi - 执行DOS程序并动态获取输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25723807/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com