Как в Delphi 2009 перенаправить консоль (stin, sterr)? - PullRequest
3 голосов
/ 08 мая 2009

Я пробую несколько примеров в интернете, и ни один из них не работает - сценарии не выполняются (возможно, потому что они предназначены для Unicode до Delphi 2009?).

Мне нужно запустить некоторые скрипты 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;

Ответы [ 2 ]

7 голосов
/ 08 мая 2009

Create_Unicode_Environment - это флаг создания процесса , предназначенный для использования в параметре dwCreationFlags CreateFile. Это не флаг для использования в записи TStartupInfo. Функции API могут потерпеть неудачу, если вы дадите им значения флагов, которые они не понимают, и они могут делать странные вещи, если вы дадите им значения флагов, которые означают нечто иное, чем вы ожидали.

Вы объявляете буфер 256 Char с; Напомним, что Char в Delphi 2009 - это 2-байтовый тип Unicode. Затем вы вызываете ReadFile и говорите ему, что буфер имеет длину 255 байт вместо действительного значения 512. Когда в документации говорится, что значением является число байтов, примите это как сигнал для использования функция SizeOf.

Поскольку ReadFile читает байты, было бы неплохо объявить ваш буферный массив как массив элементов байтового размера, таких как AnsiChar. Таким образом, когда вы устанавливаете Buffer[BytesRead], вы не будете включать вдвое больше данных, которые вы фактически читаете.

Unicode-версия CreateProcess может изменять аргумент командной строки. Вы должны убедиться, что строка, передаваемая этому параметру, имеет счетчик ссылок 1. Вызовите UniqueString(DosApp), прежде чем вызывать CreateProcess.

При сбое функции API вы, конечно, захотите узнать, почему. Не просто составляют причину. Используйте предоставленные функции, такие как Win32Check и RaiseLastOSError. По крайней мере, позвоните GetLastError, как MSDN говорит вам. Не бросайте универсальный тип исключения, если более конкретный тип доступен.

4 голосов
/ 08 мая 2009

Я не уверен, что WaitForSingleObject - это правильный путь ... Я думаю, что лучше выполнить цикл с GetExitCodeProcess (pi.hProcess, iExitCode) до iExitCode <> STILL_ACTIVE, а затем проверять данные при каждом проходе цикла.

Написанный код также не работает в Delphi 2007, так что это не проблема Unicode в Delphi 2009.

Изменение вашего внутреннего цикла на следующие работы:

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;

Я также переместил CloseHandle (StdOutPipeWrite) непосредственно перед закрытием StdOutPipeRead.

...