mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 10:33:03 +02:00
243 lines
6.1 KiB
ObjectPascal
243 lines
6.1 KiB
ObjectPascal
unit LHelpControl;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
{$IFDEF UNIX}
|
|
{$if FPC_FULLVERSION <= 20700}
|
|
{$DEFINE STALE_PIPE_WORKAROUND}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF STALE_PIPE_WORKAROUND}
|
|
BaseUnix,
|
|
{$ENDIF}
|
|
Classes, SysUtils, FileUtil, SimpleIPC, UTF8Process;
|
|
|
|
type
|
|
TRequestType = (rtFile, rtUrl, rtContext);
|
|
|
|
TLHelpResponse = (srNoAnswer, srUnknown, srSuccess, srInvalidFile, srInvalidURL, srInvalidContext);
|
|
|
|
TFileRequest = record
|
|
RequestType: TRequestType;
|
|
FileName: array[0..512] of char;
|
|
end;
|
|
TUrlRequest = record
|
|
FileRequest: TFileRequest;
|
|
Url: array[0..512] of char;
|
|
end;
|
|
TContextRequest = record
|
|
FileRequest: TFileRequest;
|
|
HelpContext: THelpContext;
|
|
end;
|
|
|
|
TProcedureOfObject = procedure of object;
|
|
|
|
{ TLHelpConnection }
|
|
|
|
TLHelpConnection = class(TObject)
|
|
private
|
|
FProcessWhileWaiting: TProcedureOfObject;
|
|
fServerOut: TSimpleIPCClient; // sends messages to lhelp
|
|
fServerIn: TSimpleIPCServer; // recieves messages from lhelp
|
|
function WaitForMsgResponse: TLHelpResponse;
|
|
function SendMessage(Stream: TStream): TLHelpResponse;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function ServerRunning: Boolean;
|
|
function StartHelpServer(NameForServer: String; ServerEXE: String = ''): Boolean;
|
|
|
|
function OpenURL(HelpFileName: String; Url: String): TLHelpResponse;
|
|
function OpenContext(HelpFileName: String; Context: THelpContext): TLHelpResponse;
|
|
function OpenFile(HelpFileName: String): TLHelpResponse;
|
|
|
|
property ProcessWhileWaiting: TProcedureOfObject read FProcessWhileWaiting write FProcessWhileWaiting;
|
|
end;
|
|
|
|
{$IFDEF STALE_PIPE_WORKAROUND}
|
|
function IPCPipeIsStale(AIPC: TSimpleIPC): Boolean;
|
|
{$ENDIF}
|
|
|
|
implementation
|
|
|
|
{ TLHelpConnection }
|
|
|
|
function TLHelpConnection.WaitForMsgResponse: TLHelpResponse;
|
|
var
|
|
Stream: TStream;
|
|
WaitTime: Integer = 5000;
|
|
begin
|
|
Result := srNoAnswer;
|
|
while WaitTime >= 0 do
|
|
begin
|
|
Dec(WaitTime, 50);
|
|
if fServerIn.PeekMessage(50, True) then
|
|
begin
|
|
Stream := fServerIn.MsgData;
|
|
Stream.Position:=0;
|
|
Result := TLHelpResponse(Stream.ReadDWord);
|
|
Exit;
|
|
end;
|
|
if Assigned(FProcessWhileWaiting) then FProcessWhileWaiting();
|
|
end;
|
|
end;
|
|
|
|
function TLHelpConnection.SendMessage(Stream: TStream): TLHelpResponse;
|
|
begin
|
|
//try
|
|
fServerOut.SendMessage(mtUnknown, Stream);
|
|
Result := WaitForMsgResponse;
|
|
//except
|
|
// on EIPCError do Result := srNoAnswer;
|
|
//end;
|
|
end;
|
|
|
|
constructor TLHelpConnection.Create;
|
|
begin
|
|
fServerOut := TSimpleIPCClient.Create(nil);
|
|
fServerIn := TSimpleIPCServer.Create(nil);
|
|
end;
|
|
|
|
destructor TLHelpConnection.Destroy;
|
|
begin
|
|
if fServerOut.Active then
|
|
fServerOut.Active:=False;
|
|
if fServerIn.Active then
|
|
fServerIn.Active:=False;
|
|
fServerOut.Free;
|
|
fServerIn.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{$IFDEF STALE_PIPE_WORKAROUND}
|
|
function IPCPipeIsStale(AIPC: TSimpleIPC): Boolean;
|
|
var
|
|
PipeName: String;
|
|
fd: cint;
|
|
begin
|
|
Result := False;
|
|
PipeName:='/tmp/'+AIPC.ServerID;
|
|
if (AIPC is TSimpleIPCServer) and (not TSimpleIPCServer(AIPC).Global) and (TSimpleIPCServer(AIPC).InstanceID <> '') then
|
|
PipeName := PipeName +'-'+TSimpleIPCServer(AIPC).InstanceID;
|
|
|
|
// it's possible to have a stale file that is not open for reading which will
|
|
// cause fpOpen to hang/block later when .Active is set to true while it
|
|
// wait's for the pipe to be opened on the other end
|
|
|
|
// O_WRONLY | O_NONBLOCK causes fpOpen to return -1 if the file is not open for reading
|
|
// so in fact the 'server' is not running
|
|
fd := FpOpen(PipeName, O_WRONLY or O_NONBLOCK);
|
|
if fd = -1 then
|
|
begin
|
|
Result := True;
|
|
// delete the named pipe since it's orphaned
|
|
FpUnlink(PipeName);
|
|
end
|
|
else
|
|
FpClose(fd);
|
|
|
|
end;
|
|
|
|
{$ENDIF}
|
|
function TLHelpConnection.ServerRunning: Boolean;
|
|
{$IFDEF STALE_PIPE_WORKAROUND}
|
|
{$ENDIF}
|
|
begin
|
|
Result := (fServerOut<>nil) and (fServerOut.ServerRunning);
|
|
{$IFDEF STALE_PIPE_WORKAROUND}
|
|
if not Result then
|
|
Exit; // ==>
|
|
Result := not IPCPipeIsStale(fServerOut);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TLHelpConnection.StartHelpServer(NameForServer: String;
|
|
ServerEXE: String): Boolean;
|
|
var
|
|
X: Integer;
|
|
begin
|
|
Result := False;
|
|
|
|
fServerIn.Active := False;
|
|
fServerIn.ServerID := NameForServer+'client';
|
|
fServerIn.Global := True;
|
|
fServerIn.Active := True;
|
|
|
|
fServerOut.Active := False;
|
|
fServerOut.ServerID := NameForServer;
|
|
if not ServerRunning then begin
|
|
with TProcessUTF8.Create(nil) do begin
|
|
CommandLine := ServerExe + ' --ipcname ' + NameForServer;
|
|
Execute;
|
|
Free;
|
|
end;
|
|
// give the server some time to get started
|
|
for X := 0 to 40 do begin
|
|
// use fServerOut.ServerRunning here instead of Self.ServerRunning to avoid a race condition
|
|
if not fServerOut.ServerRunning then Sleep(200);
|
|
end;
|
|
end;
|
|
if fServerOut.ServerRunning then begin
|
|
fServerOut.Active := True;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TLHelpConnection.OpenURL(HelpFileName: String; Url: String): TLHelpResponse;
|
|
var
|
|
UrlRequest: TUrlRequest;
|
|
Stream: TMemoryStream;
|
|
begin
|
|
Stream := TMemoryStream.Create;
|
|
try
|
|
UrlRequest.FileRequest.FileName := HelpFileName+#0;
|
|
UrlRequest.FileRequest.RequestType := rtURL;
|
|
UrlRequest.Url := Url+#0;
|
|
Stream.Write(UrlRequest,SizeOf(UrlRequest));
|
|
Result := SendMessage(Stream);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLHelpConnection.OpenContext(HelpFileName: String;
|
|
Context: THelpContext) : TLHelpResponse;
|
|
var
|
|
ContextRequest: TContextRequest;
|
|
Stream: TMemoryStream;
|
|
begin
|
|
Stream := TMemoryStream.Create;
|
|
try
|
|
ContextRequest.FileRequest.FileName := HelpFileName+#0;
|
|
ContextRequest.FileRequest.RequestType := rtContext;
|
|
ContextRequest.HelpContext := Context;
|
|
Stream.Write(ContextRequest, SizeOf(ContextRequest));
|
|
Result := SendMessage(Stream);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLHelpConnection.OpenFile(HelpFileName: String): TLHelpResponse;
|
|
var
|
|
FileRequest : TFileRequest;
|
|
Stream: TMemoryStream;
|
|
begin
|
|
Stream := TMemoryStream.Create;
|
|
try
|
|
FileRequest.RequestType := rtFile;
|
|
FileRequest.FileName := HelpFileName+#0;
|
|
Stream.Write(FileRequest, SizeOf(FileRequest));
|
|
Result := SendMessage(Stream);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|