Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/opengl/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi CreateProcess、PowerShell和WaitForSingleObject_Delphi - Fatal编程技术网

Delphi CreateProcess、PowerShell和WaitForSingleObject

Delphi CreateProcess、PowerShell和WaitForSingleObject,delphi,Delphi,我正在使用管道在程序中获取cmd.exe输出。有时,我注意到,如果cmd.exe请求用户输入(我创建了隐藏的cmd窗口),程序将挂起,因为没有人会将输入放入窗口,cmd将保持不变。因此,我实现了WaitForSingleObject,以避免在cmd请求用户输入或由于其他原因而挂起的情况下挂起。当我尝试执行powershell命令时会出现问题,因为它看起来对WaitForSingleObject没有响应,而且我总是达到超时。功能是: function GetDosOutput(const Exe,

我正在使用管道在程序中获取cmd.exe输出。有时,我注意到,如果cmd.exe请求用户输入(我创建了隐藏的cmd窗口),程序将挂起,因为没有人会将输入放入窗口,cmd将保持不变。因此,我实现了WaitForSingleObject,以避免在cmd请求用户输入或由于其他原因而挂起的情况下挂起。当我尝试执行powershell命令时会出现问题,因为它看起来对WaitForSingleObject没有响应,而且我总是达到超时。功能是:

function GetDosOutput(const Exe, Param: string): string;
const
  InheritHandleSecurityAttributes: TSecurityAttributes =
    (nLength: SizeOf(TSecurityAttributes); bInheritHandle: True);
var
  hReadStdout, hWriteStdout: THandle;
  si: TStartupInfo;
  pi: TProcessInformation;
  WaitTimeout, BytesRead: DWord;
  lReadFile: boolean;
  Buffer: array[0..255] of AnsiChar;
begin
  Result:= '';
  if CreatePipe(hReadStdout, hWriteStdout, @InheritHandleSecurityAttributes, 0) then
  begin
    try
      si:= Default(TStartupInfo);
      si.cb:= SizeOf(TStartupInfo);
      si.dwFlags:= STARTF_USESTDHANDLES;
      si.hStdOutput:= hWriteStdout;
      si.hStdError:= hWriteStdout;
      if CreateProcess(Nil, PChar(Exe + ' ' + Param), Nil, Nil, True, CREATE_NO_WINDOW,
                        Nil, PChar(ExtractFilePath(ParamStr(0))), si, pi) then
      begin
        CloseHandle(hWriteStdout);
        while True do
        begin
          try
            WaitTimeout:= WaitForSingleObject(pi.hProcess, 20000);
            if WaitTimeout = WAIT_TIMEOUT then
            begin
              Result:= 'No result available';
              break;
            end
            else
            begin
              repeat
                lReadFile:= ReadFile(hReadStdout, Buffer, SizeOf(Buffer) - 1, BytesRead, nil);
                if BytesRead > 0 then
                begin
                  Buffer[BytesRead]:= #0;
                  OemToAnsi(Buffer, Buffer);
                  Result:= Result + String(Buffer);
                end;
              until not (lReadFile) or (BytesRead = 0);
            end;
            if WaitTimeout = WAIT_OBJECT_0 then
              break;
          finally
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
          end;
        end;
      end;
    finally
      CloseHandle(hReadStdout);
    end;
  end;
end;
如果我调用此函数传递:

cmd.exe/C目录C:\

一切顺利。但如果我使用以下方法调用:

powershell目录c:\cmd.exe/c powershell目录c:\


WaitForSingleObject达到超时,但什么也没有发生。这方面有什么帮助吗?

管道的缓冲区可能已满。子进程被阻止,等待进程从管道中读取数据并为更多输出腾出空间。但是,您的程序也被阻止,等待子进程完成。因此,僵局

您需要继续从管道中读取,但问题是,如果调用
ReadFile
,并且进程由于完全管道缓冲区以外的其他原因而挂起,那么您的程序也会挂起
ReadFile
不提供超时参数

ReadFile
没有超时参数,因为异步读取是使用重叠I/O来完成的。您将传递给
ReadFile
一条包含Windows事件句柄的
tooverlapped
记录
ReadFile
将立即返回,并在读取完成时发出事件信号。使用
WaitForMultipleObjects
不仅可以等待进程句柄,还可以等待此新事件句柄

不过有个障碍
CreatePipe
创建匿名管道,匿名管道不支持重叠I/O。因此,您必须改用
CreateNamedPipe
。在运行时为管道生成唯一的名称,这样它就不会干扰任何其他程序(包括程序的其他实例)

下面是代码如何运行的示意图:

var
  Overlap: TOverlapped;
  WaitHandles: array[0..1] of THandle;
begin
  hReadStdout := CreateNamedPipe('\\.\pipe\unique-pipe-name-here',
    Pipe_Access_Inbound, File_Flag_First_Pipe_Instance or File_Flag_Overlapped,
    Pipe_Type_Byte or Pipe_Readmode_Byte, 1, x, y, 0, nil);
  Win32Check(hReadStdout <> Invalid_Handle_Value);
  try
    hWriteStdout := CreateFile('\\.\pipe\unique-pipe-name-here', Generic_Write,
      @InheritHandleSecurityAttributes, ...);
    Win32Check(hWriteStdout <> Invalid_Handle_Value);
    try
      si.hStdOutput := hWriteStdout;
      si.hStdError := hWriteStdout;
      Win32Check(CreateProcess(...));
    finally
      CloseHandle(hWriteStdout);
    end;
    try
      Overlap := Default(TOverlapped);
      Overlap.hEvent := CreateEvent(nil, True, False, nil);
      Win32Check(Overlap.hEvent <> 0);
      try
        WaitHandles[0] := Overlap.hEvent;
        WaitHandles[1] := pi.hProcess;
        repeat
          ReadResult := ReadFile(hReadStdout, ..., @Overlap);
          if ReadResult then begin
            // We read some data without waiting. Process it and go around again.
            SetString(NewResult, Buffer, BytesRead div SizeOf(Char));
            Result := Result + NewResult;
            continue;
          end;
          Win32Check(GetLastError = Error_IO_Pending);
          // We're reading asynchronously.
          WaitResult := WaitForMultipleObjects(Length(WaitHandles),
            @WaitHandles[0], False, 20000);
          case WaitResult of
            Wait_Object_0: begin
              // Something happened with the pipe.
              ReadResult := GetOverlappedResult(hReadStdout, @Overlap, @BytesRead, True);
              // May need to check for EOF or broken pipe here.
              Win32Check(ReadResult);
              SetString(NewResult, Buffer, BytesRead div SizeOf(Char));
              Result := Result + NewBuffer;
              ResetEvent(Overlap.hEvent);
            end;
            Wait_Object_0 + 1: begin
              // The process terminated. Cancel the I/O request and move on,
              // returning any data already in Result. (There's no further data
              // in the pipe, because if there were, WaitForMultipleObjects would
              // have returned Wait_Object_0 instead. The first signaled handle
              // determines the return value.
              CancelIO(hReadStdout);
              break;
            end;
            Wait_Timeout: begin
              // Timeout elapsed without receiving any more data.
              Result := 'no result available';
              break;
            end;
            Wait_Failed: Win32Check(False);
            else Assert(False);
          end;
        until False;
      finally
        CloseHandle(Overlap.hEvent);
      end;
    finally
      CloseHandle(pi.hProcess);
      CloseHandle(pi.hThread);
    end;
  finally
    CloseHandle(hReadStdout);
  end;
end;
var
重叠:重叠;
WaitHandles:THandle的数组[0..1];
开始
hReadStdout:=CreateNamedPipe('\\.\pipe\unique pipe name here',
管道\u访问\u入站、文件\u标志\u第一个\u管道\u实例或文件\u标志\u重叠,
管道类型字节或管道读取模式字节,1,x,y,0,nil);
Win32Check(hReadStdout无效的\u句柄\u值);
尝试
hWriteStdout:=CreateFile('\.\pipe\unique pipe name here',Generic\u Write,
@继承人担保物(贡品等);
Win32Check(hWriteStdout无效的句柄值);
尝试
si.hst输出:=hWriteStdout;
si.hStdError:=hWriteStdout;
Win32Check(CreateProcess(…);
最后
闭合手柄(hWriteStdout);
结束;
尝试
重叠:=默认值(ToOverlapped);
Overlap.hEvent:=CreateEvent(nil、True、False、nil);
Win32Check(Overlap.hEvent 0);
尝试
WaitHandles[0]:=Overlap.hEvent;
WaitHandles[1]:=pi.hProcess;
重复
ReadResult:=ReadFile(hReadStdout,…@Overlap);
如果读取结果,则开始
//我们不用等待就读取了一些数据。处理它,然后再四处走动。
SetString(NewResult、Buffer、BytesRead div SizeOf(Char));
结果:=结果+新结果;
继续;
结束;
Win32检查(GetLastError=Error\u IO\u Pending);
//我们正在异步读取。
WaitResult:=WaitForMultipleObjects(长度(WaitHandles),
@WaitHandles[0],False,20000);
案件结果
等待\u对象\u 0:开始
//管子出了点问题。
ReadResult:=GetOverlappedResult(hReadStdout,@Overlap,@BytesRead,True);
//可能需要检查此处是否有EOF或管道破裂。
Win32Check(ReadResult);
SetString(NewResult、Buffer、BytesRead div SizeOf(Char));
结果:=结果+新缓冲;
重置事件(重叠.hEvent);
结束;
等待\u对象\u 0+1:开始
//进程终止。取消I/O请求并继续,
//返回结果中已存在的任何数据。(没有进一步的数据
//在管道中,因为如果存在,WaitForMultipleObjects将
//已返回等待对象0。第一个信号句柄
//确定返回值。
CancelIO(线程标准输出);
打破
结束;
等待超时:开始
//超时已过,未接收任何更多数据。
结果:='无可用结果';
打破
结束;
等待_失败:Win32Check(False);
else断言(False);
结束;
直到错误;
最后
闭合手柄(重叠。hEvent);
结束;
最后
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
结束;
最后
闭合手柄(螺纹标准件);
结束;
结束;

请注意,在上面的代码中,程序的任何新输出实际上都会重置分配给进程完成的20秒超时。这可能是可以接受的行为,但如果不是,则必须跟踪已过的时间,并在调用
WaitForMultipleObjects
(可能在调用
ReadFile
之前,如果操作系统选择处理非重叠的
ReadFile
,如果调用时已有可用数据,则可能会这样做).

我将尝试实现您所说的。如何使用WaitForMultipleObjects等待管道?嗯,通过使用假设的Windows未来版本,管道句柄可以使用
WaitForMultipleObjects来等待,当然!我确信管道句柄可以