Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Cmd.exe über Pipes steuern - Probleme (https://www.delphipraxis.net/67068-cmd-exe-ueber-pipes-steuern-probleme.html)

ArneH 9. Apr 2006 01:18


Cmd.exe über Pipes steuern - Probleme
 
Hallo!

Programmbeschreibung:
Ich habe ein Programm geschrieben, dass nicht nur (wie bei vielen Beispielen aus dem Forum hier) die Ausgabe einer Konsolen-Anwendung via Pipe auslesen kann, sondern auch die interaktive Kommunikation mit einem solchen Prozess erlaubt. Beispielhaft habe ich hier die "cmd.exe" verwendet.

http://www.arnehaak.de/console-control/screenshot.png
(Ich hoffe es ist OK, dass ich diesen Screenshot (23 KB) hier einbinde.)

Die Bedienung ist eigentlich selbsterklärend. Klickt man auf "Start", so wird eine neue Instanz der "cmd.exe" (muss im Moment noch in "c:\windows\system32\cmd.exe" liegen) gestartet, und man kann sie bedienen, fast so die normale Konsole von Windows. Unten werden dann noch Debug-Infos ausgegeben, das was dort angezeigt wird ist jedoch schon gefilter (Zeichensätze umgewandelt, Füllzeichen entfernt, Zeilenumbrüche korrigiert). Das ganze soll später nochmal stark ausgebaut werden.


Problem:
Meistens klappt alles schon so, wie ich mir das vorstelle, jedoch hakt die Ausgabe des öften, beispielsweise wenn man mit "dir" den Verzeichnisinhalt anzeigen lässt, oder wenn man - wie auf dem Screenshot - einen Ping macht. Das kann 20x gutgehen, und dann hakt es irgendwann: Zuerst steht nichts dort, bestätigt man nochmal mit Return, dann steht dort "Mehr?", bestätigt man nochmal, dann erscheint dort die Ausgabe, die eigentlich erwartet wurde.
Was läuft da falsch?


Code:
Den (vermutlich) wichtigen Code habe ich mal herauskopiert:

Erstmal ein paar Variablen, die im anschließenden Code-Sample genutzt werden:

Delphi-Quellcode:
buf: Array[0..1023] of Char;           //i/o buffer
read_stdout: HWND;                     //pipe handle
bread: Cardinal;                       //bytes read
avail: Cardinal;                       //bytes available

Und hier der eigentliche Code, der noch nicht 100%ig läuft:

Delphi-Quellcode:
//Haupt-Programmschleife (im Thread)
begin
  bzero(buf);

  if avail > 1023 then
    while bread >= 1023 do
      begin
        ReadFile(read_stdout, buf, 1023, bread, nil); //read the stdout pipe
        MainForm.CLBack(BufferToString(buf));

        bzero(buf);
      end
  else
    begin
      ReadFile(read_stdout, buf, 1023, bread, nil);
      MainForm.CLBack(BufferToString(buf));
    end;
end;

Download:
Das Programm könnt ihr hier zum Testen runterladen:
http://www.arnehaak.de/console-contr...ntrol_test.exe


Bin für jegliche Tipps dankbar!

Arne

faux 9. Apr 2006 01:48

Re: Cmd.exe über Pipes steuern - Probleme
 
Hallo!

Also zwei Dinge zum Programm selbst (kann dir bei dem Problem leider nicht helfen):
1. Immer wenn man nen Befehl per Enter bestätigt, gibts so ein "Ding". Einfach mal Key auf #0 setzen, wenn er als #13 erkannt wird. Dann sollte das Ding nichtmehr kommen. ;)
2. Also bei mir verbraucht das Programm 100% einer CPU, ist das normal?

Aber sonst echt gut gelungen!

Grüße
Faux

SirThornberry 9. Apr 2006 10:42

Re: Cmd.exe über Pipes steuern - Probleme
 
ich kenn mich mit den Pipes nicht so aus. Allerdings klingt das für mich danach als ob du die Ausgabe erst lesen kannst wenn sie vollständig abgearbeitet ist. Also würde ich vermuten das die Anwendung (cmd.exe) so lange nicht reagiert. Wenn man also Dir ausführt braucht es einen Moment bis die platte durchsucht ist und dann wird alles in den ausgabepuffer geschrieben und in dieser Zeit reagiert das ganze wohl nicht mehr auf ausgaben. Gegen diese Theorie spricht jedoch das man bei der CMD.exe live sieht wie der Inhalt aufgelistet wird wenn man es direkt ausführt und dir eingibt.

Vom Thema los gelöst möchte ich dich bitten den Screenshot als Anhang dem Beitrag hinzu zufügen. Einfach aus dem Grund das der Screenshot auch noch in 3 Jahren vorhanden ist wenn dein Server nicht mehr existiert oder du das bild von deinem Webspace gelöscht hast.

[Edit]
Hab mir grad selbst mal nen kleines Testprogramm mit den Pipes gebaut und konnte deine Beschriebenen Probleme nicht nachvollziehen. Im übrigen hast du geschrieben das bei dir die cmd.exe derzeit immer unter "c:\windows\system32" liegen muss. Gib als command bei CreateProcess doch einfach "cmd" an dann brauchst du dich auch nicht drum kümmern wo die exe liegt.

Was mir an deinem Quelltext negativ aufgefallen ist und gleichzeitig der Grund für das hängen sein kann ist folgende zeile:
Delphi-Quellcode:
MainForm.CLBack(BufferToString(buf));
du scheinst also unsyncronisiert vom Thread auf den Haupttrehad der Anwendung zu zugreifen und gibst dort vermutlich sogar etwas auf der Oberfläche aus. Du solltest das ganze mit syncronize syncronisieren oder per Message an den Hauptthread schicken.

Ich würde dir auch empfehlen das ganze objectorientierter zu machen. Also nicht vom Thread aus auf eine Globale Variable (dein Form) zu zugreifen sondern die CLB-Funktion lieber als Parameter übergeben. Hier mal ein Beispiel wie der Thread aussehen könnte (natürlich mit syncronisierter Benachrichtigung)
Delphi-Quellcode:
type
  TPipeClbProc = procedure(Sender: TObject; const ABuffer: String; ABufSize: Cardinal) of Object;
  TPipeReadThread = class(TThread)
  private
    fBuffer: String;
    fBytesRead: Cardinal;
    fClbProc: TPipeClbProc;
    fPipeOutput: Cardinal;
    procedure FSyncProc;
  protected
    procedure Execute; override;
    constructor Create(AClbProc: TPipeClbProc; APipeOutput: Cardinal);
  end;
[...]
constructor TPipeReadThread.Create(AClbProc: TPipeClbProc; APipeOutput: Cardinal);
begin
  inherited Create(True);
  fClbProc   := AClbProc;
  fPipeOutput := APipeOutput;
  SetLength(fBuffer, 5000);
  FreeOnTerminate := True;
  Resume;
end;

procedure TPipeReadThread.Execute;
var LBufSize: Cardinal;
    LRes   : Boolean;
begin
  LBufSize  := Length(fBuffer);
  repeat
    LRes := ReadFile(fPipeOutput, fBuffer[1], LBufSize, fBytesRead, nil);
    Synchronize(fSyncProc);
  until not(LRes) or Terminated;
end;

procedure TPipeReadThread.FSyncProc;
begin
  fClbProc(Self, fBuffer, fBytesRead);
end;
[/Edit]

Harry M. 9. Apr 2006 12:14

Re: Cmd.exe über Pipes steuern - Probleme
 
Ich habe hier schon mal ähnliches versucht http://www.delphipraxis.net/internal...hlight=console
Vielleichts hilf's.

Würdest Du mir bitte deinen kompletten Source mal via PN zustellen? Bin da nämlich immernoch dran :-)

ArneH 9. Apr 2006 16:39

Re: Cmd.exe über Pipes steuern - Probleme
 
So, die Ausgabe der Konsole wird jetzt in einer Synchronize-Prozedur übergeben.

Das war allerdings noch gar nicht mal der Fehler, sondern:
Bei der Eingabe sollte eigentlich wenn das Zeichen #13 kommt auch das Zeichen #10 manuell hinzugefügt werden, aus irgendeinem Grund passierte das allerdings ab und zu nicht. :gruebel:

Ich habe jetzt jedenfalls das ganze Eingabe-Konzept etwas überarbeitet, der Fehler tritt nun nicht mehr auf.

Wer will kann sich ja die neue, korrigierte Version mal runterladen:
http://www.arnehaak.de/console-contr...ntrol_test.exe


Noch eine andere Frage: Wenn ich den Thread mit Daten aus dem Hauptthread füttere, kann ich das dann genauso mit einer Synchronize-Funktion machen, wie ich es hier umgekehrt schon gemacht habe?

SirThornberry 9. Apr 2006 21:46

Re: Cmd.exe über Pipes steuern - Probleme
 
ich weiß zwar nicht in welchem Fall das sinn macht aber es gilt folgendes. Man sollte nie aus einem Thread heraus unsyncronisiert auf visuelle Objecte zugreifen (zum Beispielen ausgaben in einem Memo etc.). Wenn du Daten in deinen Thread hinein reichst passiert ja in dem Sinne kein unsyncronisierter Zugriff auf grafische Elemente. In dem Fall könnte man höchstens darüber nachdenken CritialSection's zu verwenden um bei MehrProzessorsystemen sicherzustellen das nicht von 2 Threads gleichzeitig auf eine Variable zugegriffen wird.

ArneH 9. Apr 2006 21:52

Re: Cmd.exe über Pipes steuern - Probleme
 
OK, Danke!


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:17 Uhr.

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