AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi ReadFileEx Callback wird nicht aufgerufen

ReadFileEx Callback wird nicht aufgerufen

Ein Thema von Namenloser · begonnen am 31. Jan 2010 · letzter Beitrag vom 6. Feb 2010
Antwort Antwort
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#1

ReadFileEx Callback wird nicht aufgerufen

  Alt 31. Jan 2010, 21:50
Hallo,

ich habe wieder ein Problem

Ich möchte gerne die Konsolenausgabe eines Programms in ein Memo umleiten - gut, das gab's hier schon oft. Allerdings brauche ich die Ausgabe schon, während das Programm noch läuft, und nicht erst am Ende. Dafür habe ich leider keine fertige Lösung gefunden, weshalb ich angefangen habe, selbst eine zu basteln, auf Basis des Codes aus der CodeLib.

Zuerst habe ich in der Schleife entsprechende Ereignisse ausgelöst, was auch ganz gut funktionierte. Aber da ich eine allgemeingültige Lösung wollte, die auch StdErr mitauswertet, musste ich eine andere Lösung finden, da das Auslesen von StdErr immer das ganze Programm blockierte, wenn keine Fehler ausgegeben wurden. Die Idee war jetzt, die API-Funktion ReadFile durch die asynchrone Funktion ReadFileEx zu ersetzen, um zu verhindern, dass eine Pipe die andere blockiert. Also habe ich alles, was für das asynchrone auslesen einer Pipe nötig ist, in die Klasse TPipeReader gekapselt. Soweit so gut.

Problem: Es funktioniert nicht. Um genau zu sein funktioniert es einmal, bei der ersten Ausgabe die das Programm tätigt... danach wird das Callback nicht mehr aufgerufen, obwohl der Puffer des Readers sich füllt.

Hier mal der zurechtgestutzte Code:

Delphi-Quellcode:
procedure TConsoleRedirector.Run(Command: string);
var
  CreationFlags: DWORD;
  ProcessInfo: TProcessInformation;
  SecurityAttr: TSecurityAttributes;
  StartupInfo: TStartupInfo;
begin
    { ... }
    // Pipe-Reader seine Lesepipe zuweisen und lauschen
    FOutputReader.Pipe := PipeOutputRead;
    FOutputReader.Listen;

// Mit diesen Zeilen friert das Programm leider komplett ein:
// FErrorReader.Pipe := PipeErrorsRead;
// FErrorReader.Listen;

    // Schleife ausführen solange gestarteter Prozess läuft
    // Der letzte Parameter gibt an, dass an dieser Stelle das Callback
    // ausgeführt werden kann
    while not (WaitForSingleObjectEx(ProcessInfo.hProcess, 0, True) in
      [WAIT_OBJECT_0, WAIT_ABANDONED]) do
    begin
      Application.ProcessMessages;
    end;
    { ... }
end;

procedure PipeReadCallback(ErrorCode: dword; NumberOfBytesTransfered: dword;
  Overlapped: POverlapped); stdcall;
var
  PipeReadBuffer: TPipeReader;
begin
  WriteLn(Format('PipeReadCallback %d, %d', [ErrorCode, NumberOfBytesTransfered]));

  // In hEvent dürfen laut MSDN Zusatzdaten gespeichert werden, da es von ReadFileEx nicht berücksichtigt wird
  PipeReadBuffer := TPipeReader(Overlapped^.hEvent);
  PipeReadBuffer.FBytesRead := NumberOfBytesTransfered;
  PipeReadBuffer.DoRead;
end;

{ TPipeBuffer }

procedure TPipeReader.DoRead;
begin
  if Assigned(FOnRead) then
    FOnRead(Self, Data);
  // Auf Folgedaten warten
  Listen;
end;

procedure TPipeReader.Listen;
begin
  if ReadFileEx(Pipe, Buffer, BufferSize, @FOverlapped, @PipeReadCallback) then
    WriteLn('ReadFileEx ok')
  else
    WriteLn('ReadFileEx NOT OK');
end;

end.
Wie ich bereits sagte funktioniert das ganze genau einmal. Wenn man jetzt z.B. das Kommando "cmd /K dir C:" ausführt, erhält man die erste ausgegebene Zeile, aber der Rest fehlt. Danach friert das Programm ein, weil es darauf wartet, dass das aufgerufene Programm beendet wird, aber das aufgerufene Programm wiederum darauf wartet, dass die Pipe geleert wird, was nicht passiert, da das Callback ja aus unerfindlichen Gründen nicht aufgerufen wird. Bei meinem Brainfuck-JIT-Compiler, wofür ich das ganze eigentlich geschrieben habe, kann ich so nur das erste Zeichen empfangen.

Was mache ich falsch?

Vielen Dank.
Angehängte Dateien
Dateityp: zip consoleredirector_145.zip (292,2 KB, 12x aufgerufen)
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#2

Re: ReadFileEx Callback wird nicht aufgerufen

  Alt 6. Feb 2010, 01:54
So, habe es jetzt anders gelöst, und zwar mit Threads.

Unit und Beispiel-Projekt befinden sich im Anhang. Man kann im Grunde jedes Konsolenprogramm (inklusive der cmd selbst) darin einsperren und dann interaktiv damit arbeiten. Die einzige Ausnahme stellen Programme dar, die direkt auf den Konsolen-Buffer zugreifen um z.B. eine eigene GUI zu zeichnen.

Eventuell werde ich die Unit auch noch mal für die CodeLib vorschlagen.

[edit]Sorry, das wichtigste im Anhang vergessen - die eigentliche Unit[/edit]
Angehängte Dateien
Dateityp: zip consoleredirector_133.zip (223,2 KB, 9x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.097 Beiträge
 
Delphi 12 Athens
 
#3

Re: ReadFileEx Callback wird nicht aufgerufen

  Alt 6. Feb 2010, 08:02
Bei Verwendung des Callbacks wird das Overlappt nicht verwendet.
Wenn du unbedingt dieses nutzen willst (z.B. mit WaitForSingleObjectEx), dann mußt du das Event innerhalb der Prozedur selber auslösen (deswegen wird es dir auch als Parameter in die Prozedur übergeben).

PS: Wenn du dann sowieso wartest, warum dann diese Prozedur?
AsyncIO kann man auch ohne diese Nutzen.

Einfach mit ReadFileEx eine asynchrone Transraktion starten, und in der Schleife auf das Ende dieser warten.
danach dann den Puffer auslesen.

PSS: eine Schleife, nur mit "wilden" Nachrichtenbehandlungen, ist nicht unbedingt Resourcenschonend. Die CPU läuft da schnell mal auf 100% ... ein kleines Sleep kann da Abhilfe schaffen.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#4

Re: ReadFileEx Callback wird nicht aufgerufen

  Alt 6. Feb 2010, 12:45
Hallo himi,

Danke für deine Antwort, aber sie kommt leider etwas zu spät. Ich habe ja mittlerweile eine funktionierende Lösung. Mit ReadFileEx könnte man vielleicht noch ein paar Zeilen einsparen, aber ich habe jetzt lange genug an dem Code gearbeitet und bin froh dass er endlich funktioniert
Immerhin habe ich jetzt aber glaube ich verstanden, wie der Abschnitt zu hEvent in der Hilfe gemeint war. Vielleicht probiere ich es ja später noch mal mit ReadFileEx, wenn der Perfektionismus wieder anfängt zu wirken
  Mit Zitat antworten Zitat
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 08:18 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