AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Doskonsole nutzen und Rückgabewerte einlesen

Doskonsole nutzen und Rückgabewerte einlesen

Ein Thema von fuchsle · begonnen am 9. Nov 2011 · letzter Beitrag vom 28. Jul 2021
Antwort Antwort
Seite 1 von 2  1 2   
fuchsle

Registriert seit: 22. Jul 2011
Ort: Heilbronn
35 Beiträge
 
Delphi XE3 Professional
 
#1

Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 17:28
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.
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 17:40
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.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.934 Beiträge
 
Delphi 12 Athens
 
#3

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 17:45
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.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 17:47
Dann scheint das MSDN wohl Recht zu haben
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.288 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 17:57
Sehr einfach zu fixen:
Delphi-Quellcode:
if CreateProcess(nil, PChar(command + ''), nil, nil, true,
...
Dadurch wird der String neu erzeugt und ist beschreibbar.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.934 Beiträge
 
Delphi 12 Athens
 
#6

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 18:57
Ein simples Weglassen des const vor Command tuts aber auch.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 19:49
Hätte ich auch vermutet, stimmt aber leider nicht.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.934 Beiträge
 
Delphi 12 Athens
 
#8

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 9. Nov 2011, 21:41
Hätte ich auch vermutet, stimmt aber leider nicht.
Stimmt! Copy-on-write.

Aber ein UniqueString(Command) müsste doch gehen?
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.534 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 10. Nov 2011, 08:16
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;
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
fuchsle

Registriert seit: 22. Jul 2011
Ort: Heilbronn
35 Beiträge
 
Delphi XE3 Professional
 
#10

AW: Doskonsole nutzen und Rückgabewerte einlesen

  Alt 10. Nov 2011, 10:19
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.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:43 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