TProcess zur Laufzeit lesen

Rund um die LCL und andere Komponenten
Antworten
carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

TProcess zur Laufzeit lesen

Beitrag von carli »

Hi,

Ich hab folgendes Problem: Ich will einen Prozess erstellen und seine StdOut in eine selbstgemachte Konsole umleiten (einfaches Memo)

Jedoch gibt es da das Problem, dass erst zur Beendigung des Prozesses der Output gefüllt wird, sich sozusagen auf dem Schlauch stehe.

Hier der Code:

Code: Alles auswählen

c:=engine.Output.NumBytesAvailable;
   if c<>0 then begin
    setlength(buffer, c);
    c:=engine.Output.Read(buffer[1], c);
    if c<>0 then begin // neue Ausgabedaten
     setlength(buffer, c);
     showmessage('daten: '+buffer);
     outlog+=buffer;
     tty.Lines.text:=outlog;
     tty.SelStart:=length(outlog);
    end;
   end;
c ist integer, buffer ist string, engine ist TProcess.
Jedoch bekomme ich die Daten erst zum Ende der Laufzeit. Gibt es für TProcess irgendeinen Flush-Befehl?

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: TProcess zur Laufzeit lesen

Beitrag von Teekeks »

Die Variablen musst du nur noch an deine anpassen

Code: Alles auswählen

const
   READ_BYTES = 2048;
var proc:TProcess;
   n: LongInt;
   str:string;
begin
  proc:=TProcess.Create(nil);
  proc.Options:=proc.Options-[poWaitOnExit];
   Proc.CommandLine :=extractfilepath(paramstr(0))+'tmp.bat';
   Proc.Execute;
   while Proc.Running do
   begin
     // versuche es zu lesen
     n := Proc.Output.Read(str, READ_BYTES);
     if n > 0
     then begin
//Was machen mit dem gelesenen, z.B.
       aMemo.Text:=aMemo.Text+str;
     end
     else begin
       // keine Daten, warte 100 ms
       Sleep(100);
     end;
   end;
   // lese den letzten Teil
   repeat
     n := Proc.Output.Read(str, READ_BYTES);
     if n > 0
     then begin
//Was machen mit dem gelesenen Rest, z.B.
       aMemo.Text:=aMemo.Text+str;
     end;
   until n <= 0;
  proc.Free;
end;

carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Re: TProcess zur Laufzeit lesen

Beitrag von carli »

Das ist doch exakt das, was ich habe.
Nur mit 2 unterschieden:
- ich habe kein poWaitOnExit drin, damit Execute nicht blockiert
- ich habe poUsePipes drin, damit ich überhaupt eine Pipe lesen kann

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: TProcess zur Laufzeit lesen

Beitrag von Teekeks »

Wenn du mal genau guckst dann fällt dir auf:
1) Ich habe auch kein poWaitOnExit drin, sondern ich Subtrahiere es sogar extra noch raus damit es auf keinen Fall drin ist.
2) Das mit dem poUsePipes ist richtig, muss noch rein.
3) Woher soll ich wissen wie das oben bei dir aussieht?
Du hast es ja nicht mit dazu geschrieben ;)

Und: Das geht bei dir nicht?
Bei mir geht das... (aber auch so ohne das poUsePipes).

shokwave
Beiträge: 475
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win11/Ubuntu Budgie (L 3.0 FPC 3.2.2)
CPU-Target: i386, x64
Wohnort: Gera

Re: TProcess zur Laufzeit lesen

Beitrag von shokwave »

Dein Programm ist die ganze Zeit damit beschäftigt, die Nachrichten zu empfangen und ans Memo weiterzuleiten, so dass es keine Zeit hat das Memo neu zu zeichnen. Ein Application.ProcessMessages in der Schleife sollte das fixen.
mfg Ingo

carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Re: TProcess zur Laufzeit lesen

Beitrag von carli »

Mein Programm ist gar nicht beschäftigt.

Ein Timer läuft die ganze Zeit und testet, ob Bytes in der Warteschlange sind.

Sollten welche drin sein, updatet er sofort das Memo.

Allerdings kommen die Bytes WIRKLICH erst nach Beendungung des Programms an.

Unter Windows hab ich mir sagen lassen, ging es, aber nicht wirklich Zeitnah. Vielleicht ein Fehler in der Prozesssteuerung? (im Pipe-Stream?) Ein Flush wäre ganz nett

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: TProcess zur Laufzeit lesen

Beitrag von Teekeks »

Was DEFINITIV gehen MUSS ist das hier: (ist unter Linux+Windows getestet)

Code: Alles auswählen

function GetProcessOutput(ap:TProcess): string;
var AStringList: TStringList;
begin
   AStringList:=TStringList.Create;
   try
      AStringList.LoadFromStream(ap.Output);
      Result:=AStringList.Strings[0];
   except
     //eine Exception deiner Wahl auslösen... 
     result:='';
   end;
   AStringList.Free;
end;

carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Re: TProcess zur Laufzeit lesen

Beitrag von carli »

Schönes "DEFINITIV"..
Das gibt die selben Resultate wie alle anderen Versuche, den Stream zu lesen:
Das LoadFromStream wartet bis das Programm terminiert und dann kommen die Zeilen an.

Versteht ihr nicht?
Ich will die Ausgabe nicht irgendwann lesen, sondern zu dem Augenblick, wo sie ankommt.
Ich arbeite mit Lazarus Trunk unter Linux64

Ich hab so langsam die Vermutung, dass die Library mir die Pipe bloß vorfakt und in wirklichkeit wird die Ausgabe in eine Datei umgeleitet und ich bekomm n FileStream

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: TProcess zur Laufzeit lesen

Beitrag von Teekeks »

Wir verstehen und bei mir funktioniert das von mir genannte sehr wohl.
Mein Process wurde so erzeugt:

Code: Alles auswählen

ap:=TProcess.Create(self);
  ap.Options:=ap.Options+[poUsePipes];
Dann den Process ganz normal über ap.Execute gestartet.
z.B. 2h normal laufen lassen und dann mal die genannte Funtion aufgerufen.
=> Ich erhalte alles was seit dem letzten Aufruf ausgegeben wurde.
Process läuft normal weiter...
usw.
Geht Perfekt.

vl. kannst du ja mal mehr Code zeigen, vl. liegt der Fehler ja wo anders?

carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Re: TProcess zur Laufzeit lesen

Beitrag von carli »

Hier der Komplett-Code:

Code: Alles auswählen

procedure TMainForm.StartEngineClick(Sender: TObject);
var p: TProcess;
    t: TStringList;
begin
 p:=TProcess.create(self);
 p.CommandLine:='gwX';
 p.options:=p.options+[poUsePipes]-[poWaitOnExit];
 p.Execute;
 t:=TStringList.create;
 while p.Running do begin
  t.LoadFromStream(p.Output);
  showmessage(t.text);
 end;
 t.free;
 p.free;
end;
Wie gesagt kommt der Log erst an, wenn ich das Programm beende

carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Re: TProcess zur Laufzeit lesen [gelöst]

Beitrag von carli »

Aaaah, perfekt!
Wenn ich im Host-Programm regelmäßig Flush(StdOut); aufrufe, geht alles wie geschmiert :)

Teekeks
Beiträge: 359
Registriert: Mi 27. Mai 2009, 20:54
OS, Lazarus, FPC: OpenSuse11.4 x86 (Lazarus: 0.9.30 FPC 2.4.2)
CPU-Target: x86
Wohnort: Cottbus

Re: TProcess zur Laufzeit lesen

Beitrag von Teekeks »

Das ist ja schon mal gut.
Scheint aber ein Problem von 64Bit zu sein, bei 32 geht es...

Nunja, jetzt geht es ja wenigstens :)

carli
Beiträge: 657
Registriert: Sa 9. Jan 2010, 17:32
OS, Lazarus, FPC: Linux 2.6.x, SVN-Lazarus, FPC 2.4.0-2
CPU-Target: 64Bit

Re: TProcess zur Laufzeit lesen

Beitrag von carli »

Oder scheint eine Optimierung des neuen Kernels zu sein.

Antworten