【问题标题】:How in Delphi 2009 redirect console (stin, sterr)?如何在 Delphi 2009 重定向控制台(stdin、stderr)?
【发布时间】:2009-05-08 17:57:09
【问题描述】:

我在互联网上尝试了几个示例,但都没有工作 - 脚本未执行 - (可能是因为用于 Delphi 2009 之前的 unicode?)。

我需要运行一些 python 脚本并向它们传递参数,例如:

python "..\Plugins\RunPlugin.py" -a login -u Test -p test

并将输出捕获到字符串并将错误捕获到其他。

这就是我现在拥有的:

procedure RunDosInMemo(DosApp:String; var OutData: String);
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of Char;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  OutData := '';
  with SA do begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  try
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES or CREATE_UNICODE_ENVIRONMENT;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    WorkDir := 'C:\';
    Handle := CreateProcess(nil, PChar(DosApp),
                            nil, nil, True, 0, nil,
                            PChar(WorkDir), SI, PI);
    CloseHandle(StdOutPipeWrite);
    if Handle then
    begin
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            OutData := OutData + String(Buffer);
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
    end else begin
      raise Exception.Create('Failed to load python plugin');
    end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

【问题讨论】:

    标签: delphi delphi-2009


    【解决方案1】:

    Create_Unicode_Environment 是一个进程创建标志,用于CreateFiledwCreationFlags 参数。它不是用于TStartupInfo 记录的标志。如果你给 API 函数提供他们不理解的标志值,它们很可能会失败,如果你给它们提供的标志值与你预期的不同,它们很可能会做奇怪的事情。

    你声明了一个 256 Chars 的缓冲区;回想一下 Delphi 2009 中的 Char 是一个 2 字节的 Unicode 类型。然后您调用ReadFile 并告诉它缓冲区的长度为 255 字节 而不是实际值 512。当文档说值是字节数时,请将此作为提示使用SizeOf 函数。

    由于ReadFile 读取字节,因此最好将缓冲区数组声明为字节大小的元素数组,例如AnsiChar。这样,当您设置Buffer[BytesRead] 时,您将不会包含实际读取的数据的两倍。

    CreateProcess 的 Unicode 版本可能会修改其命令行参数。您必须确保传递给该参数的字符串的引用计数为 1。在调用 CreateProcess 之前调用 UniqueString(DosApp)

    当某个 API 函数失败时,您当然会想知道原因。不要只是编造一个理由。使用提供的函数,例如Win32CheckRaiseLastOSError。至少,请致电GetLastError,就像 MSDN 告诉您的那样。当有更具体的异常类型可用时,不要抛出通用异常类型。

    【讨论】:

      【解决方案2】:

      我不确定 WaitForSingleObject 是要走的路...我认为最好使用 GetExitCodeProcess(pi.hProcess,iExitCode) 循环,直到 iExitCode STILL_ACTIVE 然后在每次通过循环时检查数据。

      所写的代码也不能在 Delphi 2007 下运行,所以它不是 Delphi 2009 的 unicode 问题。

      将您的内部循环更改为以下作品:

      if Handle then
      begin
        try
          repeat
            WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
            for ix := 0 to BytesRead-1 do
              begin
                OutData := OutData + AnsiChar(Buffer[ix]);
              end;
            GetExitCodeProcess(pi.hProcess,iExit);
          until (iExit <> STILL_ACTIVE);
        finally
          CloseHandle(PI.hThread);
          CloseHandle(PI.hProcess);
        end;
      

      我对局部变量进行了以下更正/添加:

      Buffer: array[0..255] of byte;
      iExit : Cardinal;
      IX : integer;
      

      我还在 StdOutPipeRead 关闭之前移动了 CloseHandle(StdOutPipeWrite)。

      【讨论】:

      • 如果脚本之前没有运行,这些更改不会使脚本运行。 “for”循环运行了太多次;解决这个问题,将零分配给 Buffer[BytesRead] 不再有任何效果。通过从一开始就将 Buffer 声明为 AnsiChar 数组来避免类型转换。您的 WaitForSingleObject 调用的目的是什么?你忽略返回值。
      • waitforsingleobject 调用是“烧掉一些时间”的占位符。睡眠也可以,但是带有超时的 waitforsingleobject 只是说要等待,除非应用程序不再运行。我只是不想在循环中占用 CPU。
      • 好的,我从别处复制了这段代码,我没有 API 开发技能,所以我几乎不明白发生了什么。感谢您的意见!
      • 延迟是不必要的。 ReadFile 不会返回,直到有数据可供读取或管道损坏。
      猜你喜欢
      • 1970-01-01
      • 2012-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-28
      • 2011-05-14
      • 1970-01-01
      相关资源
      最近更新 更多