AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Cmd.exe über Pipes steuern - Probleme
Thema durchsuchen
Ansicht
Themen-Optionen

Cmd.exe über Pipes steuern - Probleme

Ein Thema von ArneH · begonnen am 9. Apr 2006 · letzter Beitrag vom 9. Apr 2006
Antwort Antwort
ArneH

Registriert seit: 8. Apr 2006
7 Beiträge
 
#1

Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 00:18
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
  Mit Zitat antworten Zitat
Benutzerbild von faux
faux

Registriert seit: 18. Apr 2004
Ort: Linz
2.044 Beiträge
 
Turbo Delphi für Win32
 
#2

Re: Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 00:48
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
Faux Manuel
Wer weiß, dass er nichts weiß, weiß mehr, als der der nicht weiß, dass er nichts weiß.
GoTrillian
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#3

Re: Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 09:42
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:
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]
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
Benutzerbild von Harry M.
Harry M.

Registriert seit: 29. Okt 2004
Ort: Halle
462 Beiträge
 
#4

Re: Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 11:14
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
Harry
Gruß Harry
www.H-Soft.info
  Mit Zitat antworten Zitat
ArneH

Registriert seit: 8. Apr 2006
7 Beiträge
 
#5

Re: Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 15:39
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.

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?
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#6

Re: Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 20:46
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.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
ArneH

Registriert seit: 8. Apr 2006
7 Beiträge
 
#7

Re: Cmd.exe über Pipes steuern - Probleme

  Alt 9. Apr 2006, 20:52
OK, Danke!
  Mit Zitat antworten Zitat
Antwort Antwort


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 14:26 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