[fpc-devel] StdOut capture for FPC RTL
Anton Kavalenka
anton.k at tut.by
Wed Nov 24 20:24:15 CET 2010
Dear All.
Currently to implement logger we use the following approach (the code
was implemented for Delphi/Kylix/FPC).
Logger is intended for displaying in the GUI outputs of several threads
and child processes and also for storage into internal bases.
Logic is the following:
Get stdout handle (duplicate it under linux),
create pipe,
replace the stdout (keeping the old stdout) for current process with
write handle of pipe,
There are following problems with FPC
Under Windows: I have to call *rewrite(output) *for every thread which
wants to use new (captured) stdout
So the questions:
How to force all the threads of process and all DLLs write into same
captured stdout?
How to revert stdout back (stop capturing)?
procedure TLogger.Start;
{$IFDEF LINUX}
{$ifdef fpc}
type TPipeDescriptors=TFilDes;
{$endif}
var
pds:TPipeDescriptors;
{$ENDIF}
begin
if fCaptureStdout then
{$IFDEF MSWINDOWS}
hConsole := GetStdHandle(STD_OUTPUT_HANDLE);
{$ELSE}
{$ifdef fpc}
hConsole := fpdup(StdOutputHandle);;//stdout;
{$else}
hConsole := dup(STDOUT_FILENO);;//stdout;
{$endif}
{$ENDIF}
{$IFDEF MSWINDOWS}
CreatePipe(hReadPipe, hWritePipe, nil, 0);
{$ENDIF}
{$IFDEF LINUX}
{$ifdef fpc}
fppipe(pds);
hReadPipe:=pds[0];
hWritePipe:=pds[1];
{$else}
pipe(pds);
hReadPipe:=pds.ReadDes;
hWritePipe:=pds.WriteDes;
{$endif}
{$ENDIF}
if fCaptureStdout then
begin
{$IFDEF MSWINDOWS}
if not SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe)
then MessageBox(0,PChar(Format('Function: SetStdHandle(%x,%x),
Error: %d',[STD_OUTPUT_HANDLE, hWritePipe, GetLastError])),'Failed',0);
{$IFDEF fpc}
* StdOutputHandle:=hWritePipe; // modify global runtime handle*
{$ENDIF}
if GetStdHandle(STD_OUTPUT_HANDLE)<>hWritePipe then
begin
safeputs('Looks like SetStdHandle failed in Windows 7, will try
AllocConsole workaround'#13#10,hWritePipe);
if not fWinConsole then
begin
AllocConsole;
FreeConsole;
SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe);
end;
if GetStdHandle(STD_OUTPUT_HANDLE)<>hWritePipe then
begin
safeputs('AllocConsole workaround failed, will use
GlobalWritePipe workaround'#13#10,hWritePipe);
GlobalWritePipe:=hWritePipe;
TTextRec(output).Handle:=hWritePipe;
end;
end;
*rewrite(output);*
{$ENDIF}
{$IFDEF LINUX}
{$ifdef fpc}
fpdup2(hWritePipe, StdOutputHandle);
{$else}
dup2(hWritePipe, STDOUT_FILENO);
{$endif}
{$ENDIF}
end;
end;
finalizing capture made via
procedure TLogger.Stop;
var dummy:integer;
{$IFDEF LINUX}
p:pointer;
{$ENDIF}
begin
if fExitFlag then Exit;
fExitFlag := True;
// make empty write to wake up the thread sleeping on read operation
dummy:=0;
FileWrite(hWritePipe,dummy,1);
if hThread<>0 then
begin
{$IFDEF MSWINDOWS}
if WaitForSingleObject(hThread,2000)=WAIT_TIMEOUT
then begin
TerminateThread(hThread,0);
MessageBeep(UINT(-1));
writeln('Thread was terminated abnormally');
end;
Closehandle(hThread);
{$ENDIF}
{$IFDEF LINUX}
{$ifndef FPC} // Kylix
pthread_join(hThread,nil); //Unfortunately, will wait INFINITEly
pthread_detach(hThread);
{$else}
WaitForThreadTerminate (hThread, 2000); // implies pthread_join
KillThread(hThread); // implies pthread_detach and pthread_kill
{$endif}
{$ENDIF}
hThread:=0;
end;
if fCaptureStdout then
begin
{$IFDEF MSWINDOWS}
SetStdHandle(STD_OUTPUT_HANDLE, hConsole);
if GlobalWritePipe = hWritePipe then
GlobalWritePipe:=INVALID_HANDLE_VALUE;
{$ENDIF}
{$IFDEF LINUX}
{$ifdef fpc}
fpdup2(hConsole, StdOutputHandle);
{$else}
dup2(hConsole, STDOUT_FILENO);
{$endif}
{$ENDIF}
rewrite(output);
end;
if hReadPipe <> 0 then
begin
FileClose(hReadPipe);
hReadPipe := 0;
end;
if hWritePipe <> 0 then
begin
FileClose(hWritePipe);
hWritePipe := 0;
end;
if IsLogFileOpened then
begin
FileClose(hLogFile);
hLogFile := 0;
end;
end;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-devel/attachments/20101124/858f11b8/attachment.html>
More information about the fpc-devel
mailing list