delphi 如何将PowerShell命令行的输出输出到我的项目?

vc9ivgsu  于 2022-11-04  发布在  Shell
关注(0)|答案(3)|浏览(202)

我正在尝试制作一个应用程序来测试我打算稍后添加到主程序中的某些功能,但没有破坏主程序的风险。
我是Pascal的新手,我仍然不明白有多少东西是有效的。
这个程序的目标是,通过在同一个应用程序中执行一个PowerShell命令,获取几个JSON,从一个特定的目录中逐一查看它们并检查某些东西。
这些东西可以是特定文件扩展名在某些目录中的存在,或大量的信息在他们的存在。
在 Delphi 中为这个应用程序创建3个对象。一个启动程序的按钮,一个计时器和一个JvCreateProcess。
计时器的间隔属性是10(ms)。onTimer属性是Timer1Timer方法。
Timer1Timer代码:

procedure TUProbando.Timer1Timer(Sender: TObject);
var
  Comando: TStrings;
  ValorJSON: TStrings;
begin
  if ContadorDirectorios = Length(rutas) then
  begin
    Timer1.Enabled := false;
    exit;
  end;

  Timer1.Enabled := false;
  Lista.Clear;
  // spacer to improve visibilitys

  memo_Salida_Command_gci.Lines.Add('*******' + rutas[ContadorDirectorios] +
    '*******');
  Comando := TStringList.Create;

  try
    // Open Powershell
    Comando.Add('C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe "');

    // I add the command line to create a JSON with the contents of the specified directory
    Comando.Add('"gci ' + rutas[ContadorDirectorios] +
      ' -Recurse | ConvertTo-Json"');

    // I add the command line to the process
    JvCreateProcess1.CommandLine := Comando.Text;

    // I run the process
    JvCreateProcess1.run;
    ValorJSON := JvCreateProcess1.ConsoleOutput;

  finally
    begin
      Comando.Free;
    end;
  end;
end;

rutas[]数组是一个字符串数组,其中包含要检查的目录。

rutas: array [0 .. 2] of String = ('C:\Users\operario43\Desktop',
    'C:\Users\operario43\Documents', 'C:\Users\operario43\Pictures');

现在,JvCreateProcess1代码:

procedure TUProbando.JvCreateProcess1Terminate(Sender: TObject;
  ExitCode: Cardinal);
begin
  // Calculate the size of the current directory

// I WANT GET JSON OF PREVIOUS POWER SHELL COMMAND HERE
  SizeDirectory := GetSizeFromJson('JSON HERE', Extensiones);

  if checkSizeDirectory(SizeDirectory, SizeControlDirectory) then
  begin
    ShowMessage('This Directory' + rutas[ContadorDirectorios] +
      ' has important files not protected by a backup');
  end;

  // +1 counter of directories traversed
    inc(ContadorDirectorios);
end;

我使用的其他方法:

function GetSizeFromJson(JSON: String; vExtensiones: array of string): integer;
var

  Contenedor: TJSONArray;
  Jfichero: TJSONObject;
  JNombre: TJSONValue;
  // value of the 'length' element of each element of the JSON
  JSizeFile: TJSONNumber; //

  I: integer;

  SizeDirectory: Int64;
begin
  SizeDirectory := 0;
  result := -1;
  try
    Contenedor := TJSONObject.ParseJSONValue(JSON) as TJSONArray;

    if Contenedor = nil then
    begin
      ShowMessage('Error al parsear el json' + JSON);
      exit;
    end;

    for I := 0 to Contenedor.Count - 1 do
    begin

      Jfichero := Contenedor.Items[I] as TJSONObject;
      if Jfichero <> nil then
      begin
        // I extract the name of the element
        JNombre := Jfichero.GetValue('Name');

        // If the extensions of the files in the directory are in the 'Extensions' array
        if MatchStr(ExtractFileExt(JNombre.Value), vExtensiones) then
        begin
          // get the value of the lenght element in bytes
          JSizeFile := Jfichero.GetValue('Length') as TJSONNumber;

          if JSizeFile <> nil then
          begin

            // I add the value of 'leght' of the element to the variable SizeDirectory
            inc(SizeDirectory, JSizeFile.AsInt);
          end; // if JSizeFile <> nil
        end; // if MatchStr(ExtractFileExt(JNombre.Value), vExtensiones)
      end; // if Jfichero <> nil
    end; // for I := 0 to Contenedor.Count - 1

    // I return the value of the size of the directory
    result := SizeDirectory;
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
end;

// method to find out if the size of the current directory exceeds the recommended size for directories in the variable called SizeControlDirectory
function checkSizeDirectory(vSizeDirectory, vSizeControlDirectory
  : Int64): Boolean;
var
  Contenedor: TJSONArray;
begin
  result := false;

  vSizeDirectory := GetSizeFromJson(Contenedor.ToString, Extensiones);

  if vSizeDirectory > vSizeControlDirectory then
  begin
    ShowMessage('Directory ' + rutas[ContadorDirectorios] +
      ' have files don't protected by Backup');
  end;

end;

我的问题是,如何为JvCreateProcess1Terminate方法获取JSON?
我做了很多修改,试图让它工作,并制作了一些小代码,使它更易于管理。

ljsrvy3e

ljsrvy3e1#

只是一张便条也许能帮助别人.
您可以将任何Windows终端命令或应用程序的输出保存到一个文件中。只需在ExeName或commandm后面添加“〉FileName”,输出就会保存到“FileName”中。我也可以在 Delphi 中使用此功能。假设我想将一个名为MyApp.exe的程序的输出保存到一个文件中,那么我可以使用以下命令:

Myprog := 'd:\MyApp.exe > FullOutputFileName';
r := ShellExecute(0, 'open', PChar(Myprog), nil, nil, SW_HIDE);
if r <> o then
  //Check for error
else
  //load the file FullOutputFileName and get results

当使用一些终端内部命令(如“dir,ver,vol,...”)时,我创建了一个批处理文件(FileName.bat),其中包含我想要执行的一个或多个命令,然后我使用shellexecute来执行该文件。
备注:
1.您也可以使用“〉〉FileName”将输出附加到文件的末尾。
1.此方法速度较慢,因此不建议在循环中使用。

  1. mORMot有一个很好的、易于使用的JSON实现。
hgqdbh6s

hgqdbh6s2#

我能理解你有两部分要做。
1.执行一个windows终端命令,并将输出放入字符串中。
1.将获得的字符串转换为JSON。
对于我之前提到的第一部分,使用文件来捕获输出(老方法),这里有一个函数使用pips来做同样的事情。

function ExecAndCapture(const ACmdLine: string; var AOutput: string): Boolean;
const
  cBufferSize = 2048;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of AnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  Result := False;
  AOutput := '';
  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;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE);
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    WorkDir := GetCurrentDir;
    Handle := CreateProcess(nil, PChar(ACmdLine),
                            nil, nil, True, 0, nil,
                            PChar(WorkDir), SI, PI);
    CloseHandle(StdOutPipeWrite);
    if Handle then
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            AOutput := AOutput + string(Buffer);
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;

    Result := True;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

对于第二部分,我更喜欢使用mOrmot框架,如以下示例所示:

procedure DoTest();
var
  s : string;
  UtfStr : RawUTF8;
  i : Integer;
  O : Variant;
begin
    if ExecAndCapture('powershell.exe gci E:\AIIconPack -Recurse | ConvertTo-Json', S) then
    begin
      UtfStr := s;
      O := TDocVariant.NewJSON(UtfStr);
      for i := 0 to O._Count-1 do
      begin
        Writeln(i+1, ':', O._(i).Name, ' <> ', O._(i).Parent.Name);
      end;
    end;
end;

如果您使用了mORMot,那么您的(GetSizeFromJson)将类似于下面的内容(我没有测试它):

function GetSizeFromJson(JSON: String; vExtensiones: array of string): integer;
var

  Contenedor: Variant;
  Jfichero: Variant;
  JNombre: string;
  // value of the 'length' element of each element of the JSON
  JSizeFile: Integer; //

  I: integer;

  SizeDirectory: Int64;
begin
  SizeDirectory := 0;
  result := -1;
  try
    Contenedor := TDocVariant.NewJSON(JSON);

    if Contenedor = UnAssigned then
    begin
      ShowMessage('Error al parsear el json' + JSON);
      exit;
    end;

    for I := 0 to Contenedor._Count - 1 do
    begin

      Jfichero := O._(i);
      if Jfichero <> UnAssigned then
      begin
        // I extract the name of the element
        JNombre := Jfichero.Name;

        // If the extensions of the files in the directory are in the 'Extensions' array
        if MatchStr(ExtractFileExt(JNombre), vExtensiones) then
        begin
          // get the value of the lenght element in bytes
          JSizeFile := Jfichero.Length;

          if JSizeFile <> nil then
          begin

            // I add the value of 'leght' of the element to the variable SizeDirectory
            inc(SizeDirectory, JSizeFile);
          end; // if JSizeFile <> nil
        end; // if MatchStr(ExtractFileExt(JNombre.Value), vExtensiones)
      end; // if Jfichero <> nil
    end; // for I := 0 to Contenedor.Count - 1

    // I return the value of the size of the directory
    result := SizeDirectory;
  except
    on E: Exception do
      ShowMessage(E.Message);
  end;
end;

我希望这能帮助到一些人。

xlpyo6sf

xlpyo6sf3#

下面是一小段代码片段,您可以使用它来执行带有JVCreateProcess组件的DOS应用程序:

procedure TForm2.JvImgBtn6Click(Sender: TObject);
var
  path : String;
begin
  try
    path := ExtractFilePath(Application.ExeName);

    with JvCreateProcess1 do
    begin
      ApplicationName  := 'C:\windows\system32\cmd.exe';
      CurrentDirectory :=   path;
      CommandLine      := '/c ' +
      path + 'build.bat ' + path;
      ShowMessage(ApplicationName + #13#10 + CommandLine);
      Run;
    end;
  finally
  end;
end;

在此代码中,我使用cmd.exe参数/C -这将在“build.bat”的执行完成后关闭DOS终端进程窗口。
如果您想保持DOS终端窗口打开,请使用/k参数。
'build.bat'前后的path变量代表:

C:\路径\到\build.bat C:\路径\作为\参数

因此,build.bat可以包含演示的内容:

@echo off
set DEVPATH=%1
dir %DEVPATH%

其中%1是build.bat的第一个参数,可在中使用,为命令设置占位符,如:
目录%设备路径%
希望这对你有帮助
延斯

相关问题