Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Doskonsole nutzen und Rückgabewerte einlesen (https://www.delphipraxis.net/164364-doskonsole-nutzen-und-rueckgabewerte-einlesen.html)

fuchsle 9. Nov 2011 16:28

Doskonsole nutzen und Rückgabewerte einlesen
 
Hallo zusammen,

die Suchfunktion ist mir bekannt :( und ich habe auch schon einige Beiträge gefunden.
Bevorzugt wird von mir die Lösung mit der Funtion GetConsoleOutput, welche hier bekannt sein dürfte.

Delphi-Quellcode:
function TForm1.GetConsoleOutput(const Command: string; Output,
  Errors: TStringList): Boolean;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  SecurityAttr: TSecurityAttributes;
  PipeOutputRead: THandle;
  PipeOutputWrite: THandle;
  PipeErrorsRead: THandle;
  PipeErrorsWrite: THandle;
  Succeed: Boolean;
  Buffer: array [0..255] of Char;
  NumberOfBytesRead: DWORD;
  Stream: TMemoryStream;
begin
  //Initialisierung ProcessInfo
  FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);

  //Initialisierung SecurityAttr
  FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
  SecurityAttr.nLength := SizeOf(SecurityAttr);
  SecurityAttr.bInheritHandle := true;
  SecurityAttr.lpSecurityDescriptor := nil;

  //Pipes erzeugen
  CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
  CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);

  //Initialisierung StartupInfo
  FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  StartupInfo.cb:=SizeOf(StartupInfo);
  StartupInfo.hStdInput := 0;
  StartupInfo.hStdOutput := PipeOutputWrite;
  StartupInfo.hStdError := PipeErrorsWrite;
  StartupInfo.wShowWindow := sw_Hide;
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;

  if CreateProcess(nil, PChar(command), nil, nil, true,
  CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil,
  StartupInfo, ProcessInfo) then begin
    result:=true;
    //Write-Pipes schließen
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsWrite);

    //Ausgabe Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while true do begin
        succeed := ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil);
        if not succeed then break;
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Output.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeOutputRead);

    //Fehler Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while true do begin
        succeed := ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil);
        if not succeed then break;
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Errors.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeErrorsRead);

    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    CloseHandle(ProcessInfo.hProcess);
  end
  else begin
    result:=false;
    CloseHandle(PipeOutputRead);
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsRead);
    CloseHandle(PipeErrorsWrite);
  end;
end;
Habe diese Funktion eingebunden und rufe diese wie folgt auf

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var output, errors: TStringList;
begin
  output:=TStringList.Create;
  try
    errors:=TStringList.Create;
    if GetConsoleOutput('cmd /c dir c:\', output, errors) then
      Memo1.Lines.AddStrings(output);
  finally
    output.free;
    errors.free;
  end;
end;
Meine Fehlermeldung:
Zugriffsverletzung bei Adresse ... in Modul 'kernel32.dll'.
Schreiben von Adresse ...


Fehler tritt an folgender Stelle auf
Delphi-Quellcode:
  if CreateProcess(nil, PChar(command), nil, nil, true,
  CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil,
  StartupInfo, ProcessInfo) then begin
Arbeite mit Windows7 und Delphi 2010 Prof
Habe nur den Quellcode übernommen, ich bin mir nicht sicher ober ich noch etwas einbinden oder die dll wie manch Andere ins Verzeichnis der Exe kopieren.

DeddyH 9. Nov 2011 16:40

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Das kann ich unter XE nachvollziehen, unter Delphi 2007 keine Probleme. Es ist zwar nicht doll, aber wenn Du explizit auf Ansi einstellst, dann klappt das auch unter XE. Auf die Schnelle:
Delphi-Quellcode:
function GetConsoleOutput(const Command: Ansistring; Output,
  Errors: TStringList): Boolean;
var
  StartupInfo: TStartupInfoA;
  ProcessInfo: TProcessInformation;
  SecurityAttr: TSecurityAttributes;
  PipeOutputRead: THandle;
  PipeOutputWrite: THandle;
  PipeErrorsRead: THandle;
  PipeErrorsWrite: THandle;
  Succeed: Boolean;
  Buffer: array [0..255] of AnsiChar;
  NumberOfBytesRead: DWORD;
  Stream: TMemoryStream;
begin
  //Initialisierung ProcessInfo
  FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);

  //Initialisierung SecurityAttr
  FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
  SecurityAttr.nLength := SizeOf(SecurityAttr);
  SecurityAttr.bInheritHandle := true;
  SecurityAttr.lpSecurityDescriptor := nil;

  //Pipes erzeugen
  CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
  CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);

  //Initialisierung StartupInfo
  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  StartupInfo.cb:=SizeOf(StartupInfo);
  StartupInfo.hStdInput := 0;
  StartupInfo.hStdOutput := PipeOutputWrite;
  StartupInfo.hStdError := PipeErrorsWrite;
  StartupInfo.wShowWindow := sw_Hide;
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;

  if CreateProcessA(nil, PAnsiChar(command), nil, nil, true,
  CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil,
  StartupInfo, ProcessInfo) then begin
    result:=true;
    //Write-Pipes schließen
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsWrite);

    //Ausgabe Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while true do begin
        succeed := ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil);
        if not succeed then break;
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Output.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeOutputRead);

    //Fehler Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while true do begin
        succeed := ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil);
        if not succeed then break;
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Errors.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeErrorsRead);

    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    CloseHandle(ProcessInfo.hProcess);
  end
  else begin
    result:=false;
    CloseHandle(PipeOutputRead);
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsRead);
    CloseHandle(PipeErrorsWrite);
  end;
end;
Zwar nicht schön, klappt aber.

Uwe Raabe 9. Nov 2011 16:45

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
MSDN sagt dazu:

Zitat:

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

DeddyH 9. Nov 2011 16:47

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Dann scheint das MSDN wohl Recht zu haben :lol:

jaenicke 9. Nov 2011 16:57

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Sehr einfach zu fixen:
Delphi-Quellcode:
if CreateProcess(nil, PChar(command + ''), nil, nil, true,
...
Dadurch wird der String neu erzeugt und ist beschreibbar.

Uwe Raabe 9. Nov 2011 17:57

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Ein simples Weglassen des
Delphi-Quellcode:
const
vor Command tuts aber auch.

DeddyH 9. Nov 2011 18:49

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Hätte ich auch vermutet, stimmt aber leider nicht.

Uwe Raabe 9. Nov 2011 20:41

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Zitat:

Zitat von DeddyH (Beitrag 1135367)
Hätte ich auch vermutet, stimmt aber leider nicht.

Stimmt! Copy-on-write.:oops:

Aber ein
Delphi-Quellcode:
UniqueString(Command)
müsste doch gehen?

DeddyH 10. Nov 2011 07:16

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Japp, so geht' s. Ich habe die Funktion mal schnell umgeschrieben(TStringlist -> TStrings, UniqueString, etc.), Fehler vorbehalten.
Delphi-Quellcode:
function GetConsoleOutput(Command: string; Output, Errors: TStrings): Boolean;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  SecurityAttr: TSecurityAttributes;
  PipeOutputRead: THandle;
  PipeOutputWrite: THandle;
  PipeErrorsRead: THandle;
  PipeErrorsWrite: THandle;
  Succeed: Boolean;
  Buffer: array [0 .. 255] of Char;
  NumberOfBytesRead: DWORD;
  Stream: TMemoryStream;
begin
  // Initialisierung ProcessInfo
  FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);

  // Initialisierung SecurityAttr
  FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
  SecurityAttr.nLength := SizeOf(SecurityAttr);
  SecurityAttr.bInheritHandle := true;
  SecurityAttr.lpSecurityDescriptor := nil;

  // Pipes erzeugen
  CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
  CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);

  // Initialisierung StartupInfo
  FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  StartupInfo.cb := SizeOf(StartupInfo);
  StartupInfo.hStdInput := 0;
  StartupInfo.hStdOutput := PipeOutputWrite;
  StartupInfo.hStdError := PipeErrorsWrite;
  StartupInfo.wShowWindow := sw_Hide;
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;

  UniqueString(Command);
  if CreateProcess(nil, PChar(Command), nil, nil, true,
    CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
    nil, nil, StartupInfo, ProcessInfo) then
    begin
      result := true;
      // Write-Pipes schließen
      CloseHandle(PipeOutputWrite);
      CloseHandle(PipeErrorsWrite);

      if Assigned(Output) then
        begin
          // Ausgabe Read-Pipe auslesen
          Stream := TMemoryStream.Create;
          try
            while true do
              begin
                Succeed := ReadFile(PipeOutputRead, Buffer, 255,
                  NumberOfBytesRead, nil);
                if not Succeed then
                  break;
                Stream.Write(Buffer, NumberOfBytesRead);
              end;
            Stream.Position := 0;
            Output.LoadFromStream(Stream);
          finally
            Stream.Free;
          end;
        end;
      CloseHandle(PipeOutputRead);

      if Assigned(Errors) then
        begin
          // Fehler Read-Pipe auslesen
          Stream := TMemoryStream.Create;
          try
            while true do
              begin
                Succeed := ReadFile(PipeErrorsRead, Buffer, 255,
                  NumberOfBytesRead, nil);
                if not Succeed then
                  break;
                Stream.Write(Buffer, NumberOfBytesRead);
              end;
            Stream.Position := 0;
            Errors.LoadFromStream(Stream);
          finally
            Stream.Free;
          end;
        end;
      CloseHandle(PipeErrorsRead);

      WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
      CloseHandle(ProcessInfo.hProcess);
    end
  else
    begin
      result := false;
      CloseHandle(PipeOutputRead);
      CloseHandle(PipeOutputWrite);
      CloseHandle(PipeErrorsRead);
      CloseHandle(PipeErrorsWrite);
    end;
end;
Damit kann der Aufruf vereinfacht werden:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  GetConsoleOutput('cmd /c dir c:\', Memo1.Lines, nil);
end;

fuchsle 10. Nov 2011 09:19

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Zitat:

Zitat von DeddyH (Beitrag 1135441)
Japp, so geht' s. Ich habe die Funktion mal schnell umgeschrieben(TStringlist -> TStrings, UniqueString, etc.), Fehler vorbehalten.

...

Damit kann der Aufruf vereinfacht werden:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  GetConsoleOutput('cmd /c dir c:\', Memo1.Lines, nil);
end;


Habe den Aufruf nun so modifiziert, da es Fehlermeldungen gab durch Verwendung von TStringList und TStrings.
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  SLOut, SLErr: TStringList;
  I: Integer;
begin
  SLOut := TStringList.Create;
  SLErr := TStringList.Create;
  if GetConsoleOutput('cmd /c dir c:\', SLOut, SLErr) then
  begin
    for I := 0 to SLOut.Count - 1 do
    begin
      Memo1.Lines.Add(SLOut.Strings[I]);
    end;
  end;
end;
Bin mir jedoch nicht sicher, ob es da noch eine elegantere Lösung gibt, aber es funktioniert.
Vielen Dank für die Unterstützung.

daywalker9 10. Nov 2011 09:22

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Hast Du TStrings.Create aufgerufen vorher?

TStrings ist nur die Basisklasse, dann kannst Du auch sowas wie DeddyH bereits gepostet hat zB Memo1.Lines übergeben.

Erzeugen musst Du aber TStringList.

Uwe Raabe 10. Nov 2011 09:30

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Dies
Delphi-Quellcode:
    for I := 0 to SLOut.Count - 1 do
    begin
      Memo1.Lines.Add(SLOut.Strings[I]);
    end;
kann man auch einfach so schreiben

Delphi-Quellcode:
Memo1.Lines := SLOut;

DeddyH 10. Nov 2011 09:37

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Ich habe meinen Code aus #9 genauso ausprobiert, wie er da steht (mit direkter Übergabe der Memo.Lines). Das hat nach Änderung auf TStrings-Parameter ohne Probleme funktioniert, damit kann man sich doch temporäre Listen sparen. Wieso das bei fuchsle nicht klappen soll, ist mir nicht ganz klar :gruebel:

fuchsle 14. Nov 2011 08:02

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1135466)
Dies
Delphi-Quellcode:
    for I := 0 to SLOut.Count - 1 do
    begin
      Memo1.Lines.Add(SLOut.Strings[I]);
    end;
kann man auch einfach so schreiben

Delphi-Quellcode:
Memo1.Lines := SLOut;

Danke. Sieht gleich besser aus.

fuchsle 14. Nov 2011 08:31

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Zitat:

Zitat von DeddyH (Beitrag 1135467)
Ich habe meinen Code aus #9 genauso ausprobiert, wie er da steht (mit direkter Übergabe der Memo.Lines). Das hat nach Änderung auf TStrings-Parameter ohne Probleme funktioniert, damit kann man sich doch temporäre Listen sparen. Wieso das bei fuchsle nicht klappen soll, ist mir nicht ganz klar :gruebel:

Habe es nochmals probiert.
Geht einwandfrei.
Vielen Dank.

himitsu 14. Nov 2011 08:44

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1135466)
Dies
Delphi-Quellcode:
    for I := 0 to SLOut.Count - 1 do
    begin
      Memo1.Lines.Add(SLOut.Strings[I]);
    end;
kann man auch einfach so schreiben

Delphi-Quellcode:
Memo1.Lines := SLOut;

Das ist aber keine korrekte Übersetzung.

Delphi-Quellcode:
Memo1.Lines.AddStrings(SLOut);
wäre richtig.
Das andere stimmt nur überein, wenn das Memo vorher leer ist, bzw. wenn es vorher geleert wird,
denn AddString hängt die Strings an den vorhandenen Inhalt an, wärend die Zuweisung über .Text den Inhalt ersetzt.

fuchsle 14. Nov 2011 10:00

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Ich gehe davon aus, dass hiermit
Delphi-Quellcode:
Memo1.Lines := SLOut;
der Inhalt von Memo1 mit den Daten von SLOut überschrieben wird und dadurch immer "geleert" wird.

In meinem speziellen Fall ist es gewünscht, dass immer nur der aktuelle Inhalt geladen wird.
Daher ist es mir nicht negativ aufgefallen.

MaBuSE 28. Jul 2021 12:31

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Hallo,
sorry wenn ich diesen alten Beitrag wieder rauskrame, aber mein Problem ist hier wohl am besten aufgehoben, da ich den hier veröffentlichen Quelltext verwende. ;)

Mein Problem (Aufgabenstellung):
Ich möchte die Aufrufe von dcc32 protokollieren, um zu sehen, in welcher Reihenfolge und mit welchen Parametern dcc32 aufgerufen wird.

Umsetzung:
Ich habe einen kleinen Wrapper um dcc32 geschrieben.
Der loggt die Aufrufe und wenn eine dcc32_org.exe (Kopie der originalen dcc32.exe) vorhanden ist, wird diese auch aufgerufen und die Ausgabe geloggt.
(Dazu habe ich die GetConsoleOutput von hier verwendet.)

-> Das funktioniert auch meistens, aber manchmal auch nicht :evil:

Mein Problem (der Fehler):
Im Log steht für jeden Eintrag:
Code:
40705308-0001: rem cd /d C:\Temp\DevExpress\VCL\Library\RS27
40705308-0002: "C:\Program Files (x86)\Embarcadero\Studio\21.0\Bin\dcc32.exe" "C:\Temp\DevExpress\VCL\Library\RS27\dxCoreRS27.dpk" -B -U"C:\Temp\DevExpress\VCL\Library\RS27" -I"C:\Temp\DevExpress\VCL\Library\RS27" -R"C:\Program Files (x86)\Embarcadero\Studio\21.0\lib\win32\release;C:\Temp\DevExpress\VCL\Library\RS27" -NU"C:\Temp\DevExpress\VCL\Library\RS27" -LE"C:\Temp\DevExpress\VCL\Library\RS27" -LN"C:\Temp\DevExpress\VCL\Library\RS27"   -NS"WinApi;Vcl;System;System.Win;Vcl.Imaging;Data;Data.Win;Bde;Xml;Xml.Win;Vcl.Shell;VclTee;Datasnap;IBX" -DSHAREPACKAGES -NB"C:\Temp\DevExpress\VCL\Library\RS27" -NH"C:\Temp\DevExpress\VCL\Library\RS27" -JL -W-UNSUPPORTED_CONSTRUCT
40705308-0003:  rem Embarcadero Delphi for Win32 compiler version 34.0
40705308-0004:  rem Copyright (c) 1983,2021 Embarcadero Technologies, Inc.
40705308-0005:  rem Fatal: F1026 File not found: 'Files.dpr'
Alle Aufrufe geben den Fehler
Code:
Fatal: F1026 File not found: 'Files.dpr'
aus, aber die Packages werden erzeugt.

Der Aufruf ohne Wrapper funktioniert und auch der Aufruf von dcc32_org.exe funktioniert ohne Fehler.
Code:
 C:\Temp\DevExpress\VCL\Library\RS27>"C:\Program Files (x86)\Embarcadero\Studio\21.0\Bin\dcc32_org.exe" "C:\Temp\DevExpress\VCL\Library\RS27\dxCoreRS27.dpk" -B -U"C:\Temp\DevExpress\VCL\Library\RS27" -I"C:\Temp\DevExpress\VCL\Library\RS27" -R"C:\Program Files (x86)\Embarcadero\Studio\21.0\lib\win32\release;C:\Temp\DevExpress\VCL\Library\RS27" -NU"C:\Temp\DevExpress\VCL\Library\RS27" -LE"C:\Temp\DevExpress\VCL\Library\RS27" -LN"C:\Temp\DevExpress\VCL\Library\RS27"   -NS"WinApi;Vcl;System;System.Win;Vcl.Imaging;Data;Data.Win;Bde;Xml;Xml.Win;Vcl.Shell;VclTee;Datasnap;IBX" -DSHAREPACKAGES -NB"C:\Temp\DevExpress\VCL\Library\RS27" -NH"C:\Temp\DevExpress\VCL\Library\RS27" -JL -W-UNSUPPORTED_CONSTRUCT
Embarcadero Delphi for Win32 compiler version 34.0
Copyright (c) 1983,2021 Embarcadero Technologies, Inc.
dxCoreRS27.dpk(67)
68 lines, 0.48 seconds, 1273428 bytes code, 209496 bytes data.

Frage:
Hat jemand eine Ahnung woran das liegen könnte?

Danke im Voraus.


Der Quelltext (auf das wesentliche reduziert):
Delphi-Quellcode:
program DCC32_Dummy;

{$APPTYPE CONSOLE}

// Ersatz für DCC32.exe in Delphi Installation um BuildSkripts zu analysieren
// z.B. DevExpress
// Diese Version ruft (falls vorhanden) *_org.exe mit den übergebenen Parametern auf.

uses
  SysUtils, Windows, Classes;

const
  Filename = 'C:\TEMP\DCC32.LOG';
var
  Programmstart: Integer;
  Zeile: integer = 0;

function GetConsoleOutput(const Command : string;
                          Output, Errors : TStringList) : Boolean;
var
  Buffer : array[0..255] of Char;
  CreationFlags : DWORD;
  NumberOfBytesRead : DWORD;
  PipeErrorsRead : THandle;
  PipeErrorsWrite : THandle;
  PipeOutputRead : THandle;
  PipeOutputWrite : THandle;
  ProcessInfo : TProcessInformation;
  SecurityAttr : TSecurityAttributes;
  StartupInfo : TStartupInfo;
  Stream : TMemoryStream;
  sCmd : string;
begin
  //Initialisierung ProcessInfo
  FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);

  //Initialisierung SecurityAttr
  FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
  SecurityAttr.nLength := SizeOf(TSecurityAttributes);
  SecurityAttr.bInheritHandle := True;
  SecurityAttr.lpSecurityDescriptor := nil;

  //Pipes erzeugen
  CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
  CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);

  //Initialisierung StartupInfo
  FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  StartupInfo.cb := SizeOf(TStartupInfo);
  StartupInfo.hStdInput := 0;
  StartupInfo.hStdOutput := PipeOutputWrite;
  StartupInfo.hStdError := PipeErrorsWrite;
  StartupInfo.wShowWindow := SW_HIDE;
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;

  CreationFlags := CREATE_DEFAULT_ERROR_MODE or
                   CREATE_NEW_CONSOLE or
                   NORMAL_PRIORITY_CLASS;
  sCmd:=Command;
  UniqueString(sCmd);
  if CreateProcess(nil,
                   (PChar(sCmd)),
                   nil,
                   nil,
                   True,
                   CreationFlags,
                   nil,
                   PChar(GetCurrentDir),
                   StartupInfo,
                   ProcessInfo) then
  begin
    Result := True;
    //Write-Pipes schließen
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsWrite);

    //Ausgabe Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil) do
      begin
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Output.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeOutputRead);

    //Fehler Read-Pipe auslesen
    Stream := TMemoryStream.Create;
    try
      while ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil) do
      begin
        Stream.Write(Buffer, NumberOfBytesRead);
      end;
      Stream.Position := 0;
      Errors.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    CloseHandle(PipeErrorsRead);

    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    CloseHandle(ProcessInfo.hProcess);
  end
  else
  begin
    Result := False;
    CloseHandle(PipeOutputRead);
    CloseHandle(PipeOutputWrite);
    CloseHandle(PipeErrorsRead);
    CloseHandle(PipeErrorsWrite);
  end;
end;

{ TMyLog }

type
  TMyLog = class(TObject)
  private
    Log: TextFile;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(const s: string);
  end;

constructor TMyLog.Create;
begin
  inherited;
  ForceDirectories(ExtractFilePath(Filename));
  AssignFile(Log, Filename);
  if FileExists(Filename) then
  begin
    Append(Log);
  end
  else
  begin
    ReWrite(Log);
  end;
end;

destructor TMyLog.Destroy;
begin
  CloseFile(Log);
  inherited;
end;

procedure TMyLog.Add(const s: string);
begin
  Inc(Zeile);
  WriteLn(Log, Format('%.8d-%.4d: %s', [ProgrammStart, Zeile, s]));
  Flush(Log);
end;

var
  i, j: Integer;
  App, Params: string;
  sl1, sl2: TStringList;
  MyLog: TMyLog;
begin
  Programmstart :=  DateTimeToTimeStamp(now).Time;
  try
    MyLog := TMyLog.Create;
    try
      MyLog.Add('rem cd /d '+GetCurrentDir);
      MyLog.Add(CmdLine);
    finally
      MyLog.Free;
    end;

    // Wenn *_org.exe da ist, dann diese mit Parametern starten
    App := ChangeFileExt(ParamStr(0), '_org.exe');
//    WriteLn(App);
    if FileExists(App) then
    begin
      for i := 1 to ParamCount do Params := Params + #32 + ParamStr(i);
//      WriteLn(Params);
      sl1 := TStringList.Create;
      sl2 := TStringList.Create;
      try
        GetconsoleOutPut(App + ' ' + Params, sl1, sl2);
        MyLog := TMyLog.Create;
        try
          for j := 0 to sl1.Count-1 do
          begin
            MyLog.Add(' rem ' + sl1[j]);
            WriteLn(sl1[j]);
          end;
          for j := 0 to sl2.Count-1 do
          begin
            MyLog.Add(' rem ERROR:' + sl2[j]);
            WriteLn(sl1[j]);
          end;
        finally
          MyLog.Free;
        end;
      finally
        sl1.Free;
        sl2.Free;
      end;
    end;
    except
    on E: Exception do
      Writeln('Es ist ein Fehler aufgetreten:',#13#10,E.ClassName, ': ', E.Message);
  end;
end.

generic 28. Jul 2021 13:49

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Ideen beim ersten lesen:
Ich denke die Parameter werden nicht "richtig" übergeben.
Das "Files" kommt bestimmt aus den langen Dateinamen welche in " gefasst sind bzw. welche Leerzeichen enthalten.

MaBuSE 28. Jul 2021 17:26

AW: Doskonsole nutzen und Rückgabewerte einlesen
 
Zitat:

Zitat von generic (Beitrag 1492926)
Ideen beim ersten lesen:
Ich denke die Parameter werden nicht "richtig" übergeben.
Das "Files" kommt bestimmt aus den langen Dateinamen welche in " gefasst sind bzw. welche Leerzeichen enthalten.

Danke, ja das wars.
Manchmal sieht man vor lauter Wald die Bäume nicht. :stupid:

Delphi-Quellcode:
 App := ChangeFileExt(ParamStr(0), '_org.exe');
...
for i := 1 to ParamCount do Params := Params + #32 + ParamStr(i);
...
// -> App + ' ' + Params
Ist natürlich Humbug. Da muss es schon spät gewesen sein :stupid:

Ich mache es nun über ein Suchen und Ersetzen mit
Delphi-Quellcode:
StringReplace
direkt in der
Delphi-Quellcode:
cmdline
. -> funktioniert super.

:dp:


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:23 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz