Einzelnen Beitrag anzeigen

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