[gelöst]TProcess großen output lesen - in real time

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

[gelöst]TProcess großen output lesen - in real time

Beitrag von hubblec4 »

Hallo Lazarusgemeinde

Ich würde gerne für meine Projekte den Zugriff auf externe Konsolenprogramme und das auslesen deren outputs besser gestallten wollen.

Das erstellen eines Threads(damit der TProcess schön unabhängig arbeiten kann)klappt.
Auch habe ich im netz gesucht und man wird sehr oft immer auf bekannte Seite:
http://wiki.lazarus.freepascal.org/Executing_External_Programs/de weitergeleitet.

Ich habe mir dann das Beispiel für das lesen eines grossen outputs genommen, leicht angepasst, und siehe da am ende des TProcesses wird der output auch ordentlich angezeigt.

Als erstes mal der Quellcode für den Thread welcher dann den TProcess startet.

Code: Alles auswählen

procedure Tmkvmerge.Execute;
var
  mkvmerge_exe: string;
  aprocess: TProcess;
  READ_BYTES: integer = 2048;
  b: LongInt;
  BytesRead: LongInt = 0;
 
 // das sind globale variablen
 gv_mem_stream - TMemoryStream
 gv_mkvmerge_error - String
 gv_mkvmerge_paras - String
 
begin
  if (not Terminated) and (true {any condition required}) then
  begin
   // error code leeren
   gv_mkvmerge_error:='';
 
   // prüfen ob die mkvmerge exe vorhanden ist
   mkvmerge_exe:=settings.Frm_Settings.DiE_Path_MKVToolNix.Text+ 'mkvmerge.exe';
 
   if FileExists(Utf8ToAnsi(mkvmerge_exe)) then
   begin
    try
     // den memory stream init
     gv_mem_stream:=TMemoryStream.Create;
 
     // den process initialisieren
     aprocess:=TProcess.Create(nil);
     // den process mit der datei verbinden
     aprocess.Executable:=Utf8ToAnsi(mkvmerge_exe);
     // den output für den process aktivieren
     aprocess.Options := aprocess.Options + [poUsePipes];
     // den parameter string angeben
     aprocess.Parameters.Add(Utf8ToAnsi(gv_mkvmerge_paras));
     // den process verstecken
     aprocess.ShowWindow:=swoHIDE;
     // process starten
     aprocess.Execute;
 
     // solange der process andauert
     while aprocess.Running do
     begin
       // stellt sicher, dass wir Platz haben
       gv_mem_stream.SetSize(BytesRead + READ_BYTES);
 
       // versuche, es zu lesen
       b:=aprocess.Output.Read((gv_mem_stream.Memory + BytesRead)^,READ_BYTES);
       if b > 0 then
       begin
        Inc(BytesRead, b);
      
        //@ hier sollte die Auswertung für real time rein. also immer wenn der output gelesen wurde sofort verarbeiten
        // Main Thread - Auswertung des mkvmerge outputs
        // Synchronize(@mkvmerge_output_verarbeiten);
       end
       else
       begin
         // keine Daten, warte 1 ms
         Sleep(1);
       end;
     end;
     // lese den letzten Teil
     repeat
       // stellt sicher, dass wir Platz haben
       gv_mem_stream.SetSize(BytesRead + READ_BYTES);
       // versuche es zu lesen
       b:=aprocess.Output.Read((gv_mem_stream.Memory + BytesRead)^,READ_BYTES);
       if b > 0 then
       begin
        Inc(BytesRead, b);
       end;
     until b <= 0;
     // die größes des memory-stream einstellen
     gv_mem_stream.SetSize(BytesRead);
 
 
     // Main Thread - Auswertung des mkvmerge outputs
     Synchronize(@mkvmerge_output_verarbeiten);
 
    Except
     on e: Exception do
     begin
      gv_mkvmerge_error:='mkvmerge process error';
     end;
    end;
 
   end
   else
   // die mkvmerge.exe wurde nicht gefunden
   gv_mkvmerge_error:='keine mkvmerge.exe';
 
 
   // Main Thread - Auswertung des mkvmerge errors
   Synchronize(@mkvmerge_error_verarbeiten);
 
   // memory stream frei geben
   if Assigned(gv_mem_stream) then
   gv_mem_stream.Free;
 
   Terminate;
  end;
end;                             


//@
Wenn ich die Synchronize(@mkvmerge_output_verarbeiten); ausführe an besagter stelle
habe ich am ende eine leere Stringlist/ Memo.

Wird die Synchronize(@mkvmerge_output_verarbeiten); nur am ende ausgeführt habe ich alles vollständig da.

Wie kann ich es erreichen das der output immer direkt gelesen wird und nach und nach in die Stringlist/Memo geschrieben wird.


hubble
Zuletzt geändert von hubblec4 am So 12. Jul 2015, 10:53, insgesamt 1-mal geändert.

baumina
Beiträge: 152
Registriert: Mo 3. Feb 2014, 14:07
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: TProcess großen output lesen - in real time

Beitrag von baumina »

Was passiert denn in mkvmerge_output_verarbeiten?
.

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: TProcess großen output lesen - in real time

Beitrag von hubblec4 »

baumina hat geschrieben:Was passiert denn in mkvmerge_output_verarbeiten?



Code: Alles auswählen

procedure Tmkvmerge.mkvmerge_output_verarbeiten;
var
  slist: TStringList;
begin
  // die slist init
  slist:=TStringList.Create;
  // den mkvmerge output memory stream in die slist laden
  slist.LoadFromStream(gv_mem_stream);
 
  Unit1.Form1.Mem_BD2mkv.Text:=slist.Text;
 
  // abhängig vom mkvmerge task
  case gv_mkvmerge_task
  of
   '--identify-for-mmg:m2ts tracks':
   begin
    mtx_identify_for_mmg_verarbeiten(slist);
   end;
  end;
 
 
 
 
  // slist freigeben
  slist.Free;
 
end;



eigentlich nur das beladen einer stringlist, die ausgabe ins memo ist erstmal nur für die Optik drin ob alles geklappt hat.

die Stringlist wird dann verarbeitet aber das sollte erstmal nicht wichtig sein.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TProcess großen output lesen - in real time

Beitrag von Michl »

Während dein Process läuft findet keine Ausgabe im Memo statt:

Code: Alles auswählen

while aprocess.Running do
     begin
       // stellt sicher, dass wir Platz haben
       gv_mem_stream.SetSize(BytesRead + READ_BYTES);
 
       // versuche, es zu lesen
       b:=aprocess.Output.Read((gv_mem_stream.Memory + BytesRead)^,READ_BYTES);
       if b > 0 then
       begin
        Inc(BytesRead, b);
 
        //@ hier sollte die Auswertung für real time rein. also immer wenn der output gelesen wurde sofort verarbeiten
        // Main Thread - Auswertung des mkvmerge outputs
        // Synchronize(@mkvmerge_output_verarbeiten);
       end
       else
       begin
         // keine Daten, warte 1 ms
         Sleep(1);
       end;
     end;

Da ich letzte Woche erst ein einfaches Testprog dazu gemacht hatte, dieses anbei (ohne Exceptionsbehandlungen) (für Windows).
- zuerst Projekt "OutPut" kompilieren
- dann Projekt "Project1" starten
Dateianhänge
test.zip
(6.49 KiB) 86-mal heruntergeladen

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: TProcess großen output lesen - in real time

Beitrag von hubblec4 »

Hi Michl

zuerst meine Frage wieso findet bei mir keine Ausgabe im Memo statt?
Ich habe die

Code: Alles auswählen

Synchronize(@mkvmerge_output_verarbeiten);

lediglich kommentiert, weil es nicht funktionierte, in der Prozedur wird ja schon der Globale MemoryStream in eine Stringlist und dann ins Memo geschrieben.
ABER eben nur am ende wenn der Process fertig ist klappt das.

ich hatte da was vergessen anzugeben:

Code: Alles auswählen

b:=aprocess.Output.Read((gv_mem_stream.Memory + BytesRead)^,READ_BYTES);
       if b > 0 then
       begin
        Inc(BytesRead, b);
 
   --- Hier das gehörte noch dazu so das der memorystream funktionieren sollte...
       // die größes des memory-stream einstellen
       gv_mem_stream.SetSize(BytesRead);
 
        //@ hier sollte die Auswertung für real time rein. also immer wenn der output gelesen wurde sofort verarbeiten
        // Main Thread - Auswertung des mkvmerge outputs
         Synchronize(@mkvmerge_output_verarbeiten);
       end


Auf jedenfall recht vielen Dank für das Test.zip. Schick und einfach.
Ich werde das sicher so nachbauen und dann schauen wie es klappt.

Im Prinzip liest du den output NICHT in einen MemoryStream sondern direkt in einen String (muss der String innerhalb des "TPipeThread = class(TThread)" sein, oder würde auch eine globale variable=String gehen?).

hubble

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: TProcess großen output lesen - in real time

Beitrag von Michl »

hubblec4 hat geschrieben:zuerst meine Frage wieso findet bei mir keine Ausgabe im Memo statt?
Ich habe die

Code: Alles auswählen

Synchronize(@mkvmerge_output_verarbeiten);


lediglich kommentiert, weil es nicht funktionierte, in der Prozedur wird ja schon der Globale MemoryStream in eine Stringlist und dann ins Memo geschrieben.
ABER eben nur am ende wenn der Process fertig ist klappt das.
Aha, das hatte ich falsch verstanden/gelesen. K.A. warum das nicht funktioniert. MMn kommst du bei so etwas am besten, wenn du ein Minimalbeispiel hier postest, da kann man das besser nachvollziehen (zumindest mir geht es so).
Geht denn der "Live"-Mitschnitt mit dem von mir geposteten Beispiel und deiner Executable, die du auswerten willst? Es kann auch an dem Programm liegen, welches in das Pipe bzw. in die Console schreibt.

hubblec4 hat geschrieben:(muss der String innerhalb des "TPipeThread = class(TThread)" sein, oder würde auch eine globale variable=String gehen?
Rein theoretisch kann man das machen. Man muss allerdings darauf achten, dass keine zweiter Threads (z.B. WorkerThread und MainThread) darauf zugreift, wenn einer gerade diesen String beschreibt. Dies kann man z.B. mit CriticalSections verhindern.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: TProcess großen output lesen - in real time

Beitrag von hubblec4 »

Michl hat geschrieben:MMn kommst du bei so etwas am besten,....

Was ist MMn?

Michl hat geschrieben:Geht denn der "Live"-Mitschnitt mit dem von mir geposteten Beispiel und deiner Executable, die du auswerten willst? Es kann auch an dem Programm liegen, welches in das Pipe bzw. in die Console schreibt.

Ich werde erst die nacht, oder morgen nacht dazu kommen das aus zuprobieren. Ich melde aber sofort ob es geht oder nicht.
Es könnte an meinem Programm liegen?? Woran könnte es denn liegen, auf was müsste ich achten?


Michl hat geschrieben:
hubblec4 hat geschrieben:(muss der String innerhalb des "TPipeThread = class(TThread)" sein, oder würde auch eine globale variable=String gehen?
Rein theoretisch kann man das machen. Man muss allerdings darauf achten, dass keine zweiter Threads (z.B. WorkerThread und MainThread) darauf zugreift, wenn einer gerade diesen String beschreibt. Dies kann man z.B. mit CriticalSections verhindern.


Muss ich jetzt mal sagen das ich das nicht bedacht hatte, eine variable im Thread selber, wird ja jedes mal für sich neu erzeugt und stört den gleichen Thread, wenn er mehrmals aufgerufen würde, ja nicht.
Werde das sicher auch übernehmen.

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: TProcess großen output lesen - in real time

Beitrag von hubblec4 »

Hi Michl

Also: wie immer funktioniert der Quellcode von dir bestens :-)

Einzige KLeinigkeit, es wird immer mal eine völlig leere Zeile eingefügt. In meinem Beispiel scheint er zweimal "nach-lesen" zu müssen somit habe ich in dem "Text" dann zwei leer Zeilen. Muss ich noch mal schauen wieso er die da rein macht, aber auch so funktioniert es erstmal.

hubble

Antworten