Added: RunInMainThread property for Event Observer
Added: Named pipe client - uses standard observers to output log
This commit is contained in:
parent
7d3a23295c
commit
32847f4988
@ -3,7 +3,12 @@ program X2LogNamedPipeClient;
|
|||||||
uses
|
uses
|
||||||
Vcl.Forms,
|
Vcl.Forms,
|
||||||
MainFrm in 'source\MainFrm.pas' {MainForm},
|
MainFrm in 'source\MainFrm.pas' {MainForm},
|
||||||
X2Log.Intf in '..\X2Log.Intf.pas';
|
X2Log.Intf in '..\X2Log.Intf.pas',
|
||||||
|
X2Log.Client.NamedPipe in '..\X2Log.Client.NamedPipe.pas',
|
||||||
|
X2Log.Client.Base in '..\X2Log.Client.Base.pas',
|
||||||
|
X2Log.Observer.Event in '..\X2Log.Observer.Event.pas',
|
||||||
|
X2Log.Constants in '..\X2Log.Constants.pas',
|
||||||
|
X2Log.Observer.Custom in '..\X2Log.Observer.Custom.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
@ -87,6 +87,11 @@
|
|||||||
<FormType>dfm</FormType>
|
<FormType>dfm</FormType>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
<DCCReference Include="..\X2Log.Intf.pas"/>
|
<DCCReference Include="..\X2Log.Intf.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Client.NamedPipe.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Client.Base.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Observer.Event.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Constants.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Observer.Custom.pas"/>
|
||||||
<BuildConfiguration Include="Release">
|
<BuildConfiguration Include="Release">
|
||||||
<Key>Cfg_2</Key>
|
<Key>Cfg_2</Key>
|
||||||
<CfgParent>Base</CfgParent>
|
<CfgParent>Base</CfgParent>
|
||||||
|
@ -2,8 +2,12 @@ unit MainFrm;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
uses
|
uses
|
||||||
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
|
System.Classes,
|
||||||
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
|
Vcl.Controls,
|
||||||
|
Vcl.Forms,
|
||||||
|
Vcl.StdCtrls,
|
||||||
|
|
||||||
|
X2Log.Intf;
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -12,232 +16,35 @@ type
|
|||||||
|
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
private
|
private
|
||||||
FClientThread: TThread;
|
FClient: IX2LogObservable;
|
||||||
|
FObserver: IX2LogObserver;
|
||||||
procedure DoMessage(Sender: TObject; Msg: TStream);
|
protected
|
||||||
|
procedure DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
uses
|
uses
|
||||||
System.SyncObjs,
|
X2Log.Constants,
|
||||||
|
X2Log.Client.NamedPipe,
|
||||||
X2Log.Intf;
|
X2Log.Observer.Event;
|
||||||
|
|
||||||
|
|
||||||
{$R *.dfm}
|
{$R *.dfm}
|
||||||
|
|
||||||
|
|
||||||
type
|
|
||||||
TClientMessageEvent = procedure(Sender: TObject; Msg: TStream) of object;
|
|
||||||
|
|
||||||
TClientThread = class(TThread)
|
|
||||||
private
|
|
||||||
FTerminateEvent: TEvent;
|
|
||||||
FPipe: THandle;
|
|
||||||
FOverlappedRead: TOverlapped;
|
|
||||||
FReadBuffer: array[0..4095] of Byte;
|
|
||||||
FMessage: TMemoryStream;
|
|
||||||
FOnMessage: TClientMessageEvent;
|
|
||||||
protected
|
|
||||||
procedure Execute; override;
|
|
||||||
procedure TerminatedSet; override;
|
|
||||||
|
|
||||||
procedure ReadMessage;
|
|
||||||
procedure HandleMessage;
|
|
||||||
|
|
||||||
procedure DoMessage;
|
|
||||||
public
|
|
||||||
constructor Create;
|
|
||||||
destructor Destroy; override;
|
|
||||||
|
|
||||||
property OnMessage: TClientMessageEvent read FOnMessage write FOnMessage;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{ TMainForm }
|
{ TMainForm }
|
||||||
procedure TMainForm.FormCreate(Sender: TObject);
|
procedure TMainForm.FormCreate(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
FClientThread := TClientThread.Create;
|
FClient := TX2LogNamedPipeClient.Create('X2LogTest');
|
||||||
(FClientThread as TClientThread).OnMessage := DoMessage;
|
FObserver := TX2LogEventObserver.Create(DoLog);
|
||||||
|
FClient.Attach(FObserver);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.DoMessage(Sender: TObject; Msg: TStream);
|
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
||||||
|
|
||||||
function ReadString: string;
|
|
||||||
var
|
|
||||||
size: Cardinal;
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Msg.ReadBuffer(size, SizeOf(cardinal));
|
mmoLog.Lines.Add(GetLogLevelText(Level) + ': ' + Msg + ' (' + Details + ')');
|
||||||
if size > 0 then
|
|
||||||
begin
|
|
||||||
SetLength(Result, size);
|
|
||||||
Msg.ReadBuffer(Result[1], size * SizeOf(Char));
|
|
||||||
end else
|
|
||||||
Result := '';
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
level: TX2LogLevel;
|
|
||||||
logMsg: string;
|
|
||||||
detail: string;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Msg.ReadBuffer(level, SizeOf(TX2LogLevel));
|
|
||||||
logMsg := ReadString;
|
|
||||||
detail := ReadString;
|
|
||||||
|
|
||||||
mmoLog.Lines.Add(logMsg + ' (' + detail + ')');
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
const
|
|
||||||
FILE_WRITE_ATTRIBUTES = $0100;
|
|
||||||
|
|
||||||
{ TClientThread }
|
|
||||||
constructor TClientThread.Create;
|
|
||||||
begin
|
|
||||||
FTerminateEvent := TEvent.Create(nil, True, False, '');
|
|
||||||
FMessage := TMemoryStream.Create;
|
|
||||||
|
|
||||||
inherited Create(False);
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TClientThread.Destroy;
|
|
||||||
begin
|
|
||||||
FreeAndNil(FMessage);
|
|
||||||
FreeAndNil(FTerminateEvent);
|
|
||||||
|
|
||||||
inherited Destroy;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TClientThread.Execute;
|
|
||||||
var
|
|
||||||
mode: Cardinal;
|
|
||||||
readEvent: TEvent;
|
|
||||||
events: array[0..1] of THandle;
|
|
||||||
waitResult: Cardinal;
|
|
||||||
bytesTransferred: Cardinal;
|
|
||||||
|
|
||||||
begin
|
|
||||||
while not Terminated do
|
|
||||||
begin
|
|
||||||
FPipe := CreateFile('\\.\pipe\X2LogTest', GENERIC_READ or FILE_WRITE_ATTRIBUTES,
|
|
||||||
0, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
|
||||||
|
|
||||||
if FPipe = INVALID_HANDLE_VALUE then
|
|
||||||
begin
|
|
||||||
if GetLastError = ERROR_PIPE_BUSY then
|
|
||||||
begin
|
|
||||||
if not WaitNamedPipe('\\.\pipe\X2LogTest', 5000) then
|
|
||||||
exit;
|
|
||||||
end else
|
|
||||||
RaiseLastOSError;
|
|
||||||
end else
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if Terminated then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
mode := PIPE_READMODE_MESSAGE;
|
|
||||||
if not SetNamedPipeHandleState(FPipe, mode, nil, nil) then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
readEvent := TEvent.Create(nil, False, False, '');
|
|
||||||
events[0] := FTerminateEvent.Handle;
|
|
||||||
events[1] := readEvent.Handle;
|
|
||||||
|
|
||||||
FOverlappedRead.hEvent := readEvent.Handle;
|
|
||||||
ReadMessage;
|
|
||||||
|
|
||||||
while not Terminated do
|
|
||||||
begin
|
|
||||||
waitResult := WaitForMultipleObjects(Length(events), @events, False, INFINITE);
|
|
||||||
|
|
||||||
case waitResult of
|
|
||||||
WAIT_OBJECT_0:
|
|
||||||
{ Terminated }
|
|
||||||
break;
|
|
||||||
|
|
||||||
WAIT_OBJECT_0 + 1:
|
|
||||||
{ Read event completed }
|
|
||||||
if GetOverlappedResult(FPipe, FOverlappedRead, bytesTransferred, False) then
|
|
||||||
begin
|
|
||||||
FMessage.WriteBuffer(FReadBuffer[0], bytesTransferred);
|
|
||||||
HandleMessage;
|
|
||||||
ReadMessage;
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
if GetLastError = ERROR_MORE_DATA then
|
|
||||||
begin
|
|
||||||
FMessage.WriteBuffer(FReadBuffer[0], bytesTransferred);
|
|
||||||
ReadMessage;
|
|
||||||
end else
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
CloseHandle(FPipe);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TClientThread.ReadMessage;
|
|
||||||
var
|
|
||||||
bytesRead: Cardinal;
|
|
||||||
lastError: Cardinal;
|
|
||||||
|
|
||||||
begin
|
|
||||||
while True do
|
|
||||||
begin
|
|
||||||
if ReadFile(FPipe, FReadBuffer, SizeOf(FReadBuffer), bytesRead, @FOverlappedRead) then
|
|
||||||
begin
|
|
||||||
{ Immediate result }
|
|
||||||
FMessage.WriteBuffer(FReadBuffer[0], bytesRead);
|
|
||||||
HandleMessage;
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
{ More data, pending I/O or an actual error }
|
|
||||||
lastError := GetLastError;
|
|
||||||
|
|
||||||
if lastError = ERROR_IO_PENDING then
|
|
||||||
break
|
|
||||||
else if lastError = ERROR_MORE_DATA then
|
|
||||||
FMessage.WriteBuffer(FReadBuffer[0], SizeOf(FReadBuffer))
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TClientThread.HandleMessage;
|
|
||||||
begin
|
|
||||||
if FMessage.Size > 0 then
|
|
||||||
begin
|
|
||||||
FMessage.Position := 0;
|
|
||||||
Synchronize(DoMessage);
|
|
||||||
FMessage.Clear;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TClientThread.TerminatedSet;
|
|
||||||
begin
|
|
||||||
inherited TerminatedSet;
|
|
||||||
|
|
||||||
FTerminateEvent.SetEvent;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TClientThread.DoMessage;
|
|
||||||
begin
|
|
||||||
if Assigned(FOnMessage) then
|
|
||||||
FOnMessage(Self, FMessage);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -19,7 +19,9 @@ uses
|
|||||||
X2Log.Observer.NamedPipe in '..\X2Log.Observer.NamedPipe.pas',
|
X2Log.Observer.NamedPipe in '..\X2Log.Observer.NamedPipe.pas',
|
||||||
X2Log.Observer.CustomThreaded in '..\X2Log.Observer.CustomThreaded.pas',
|
X2Log.Observer.CustomThreaded in '..\X2Log.Observer.CustomThreaded.pas',
|
||||||
X2Log.Observer.MonitorForm in '..\X2Log.Observer.MonitorForm.pas' {X2LogObserverMonitorForm},
|
X2Log.Observer.MonitorForm in '..\X2Log.Observer.MonitorForm.pas' {X2LogObserverMonitorForm},
|
||||||
X2Log.Global in '..\X2Log.Global.pas';
|
X2Log.Global in '..\X2Log.Global.pas',
|
||||||
|
X2Log.Client.NamedPipe in '..\X2Log.Client.NamedPipe.pas',
|
||||||
|
X2Log.Client.Base in '..\X2Log.Client.Base.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
@ -190,6 +190,8 @@
|
|||||||
<FormType>dfm</FormType>
|
<FormType>dfm</FormType>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
<DCCReference Include="..\X2Log.Global.pas"/>
|
<DCCReference Include="..\X2Log.Global.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Client.NamedPipe.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Client.Base.pas"/>
|
||||||
<BuildConfiguration Include="Debug">
|
<BuildConfiguration Include="Debug">
|
||||||
<Key>Cfg_2</Key>
|
<Key>Cfg_2</Key>
|
||||||
<CfgParent>Base</CfgParent>
|
<CfgParent>Base</CfgParent>
|
||||||
|
BIN
Test/resources/ObserverActive.bmp
Normal file
BIN
Test/resources/ObserverActive.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
Test/resources/ObserverInactive.bmp
Normal file
BIN
Test/resources/ObserverInactive.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -26,41 +26,148 @@ object MainForm: TMainForm
|
|||||||
Margins.Top = 8
|
Margins.Top = 8
|
||||||
Margins.Right = 8
|
Margins.Right = 8
|
||||||
Margins.Bottom = 8
|
Margins.Bottom = 8
|
||||||
ActivePage = tsEvent
|
ActivePage = tsFile
|
||||||
Align = alClient
|
Align = alClient
|
||||||
|
Images = ilsObservers
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
OnChange = pcObserversChange
|
|
||||||
ExplicitTop = 113
|
|
||||||
ExplicitHeight = 361
|
|
||||||
object tsEvent: TTabSheet
|
object tsEvent: TTabSheet
|
||||||
Caption = 'Event Observer '
|
Caption = 'Event Observer '
|
||||||
ExplicitHeight = 333
|
|
||||||
object mmoEvent: TMemo
|
object mmoEvent: TMemo
|
||||||
AlignWithMargins = True
|
AlignWithMargins = True
|
||||||
Left = 8
|
Left = 8
|
||||||
Top = 8
|
Top = 40
|
||||||
Width = 571
|
Width = 571
|
||||||
Height = 261
|
Height = 229
|
||||||
Margins.Left = 8
|
Margins.Left = 8
|
||||||
Margins.Top = 8
|
Margins.Top = 40
|
||||||
Margins.Right = 8
|
Margins.Right = 8
|
||||||
Margins.Bottom = 8
|
Margins.Bottom = 8
|
||||||
Align = alClient
|
Align = alClient
|
||||||
ReadOnly = True
|
ReadOnly = True
|
||||||
ScrollBars = ssVertical
|
ScrollBars = ssVertical
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
ExplicitHeight = 317
|
ExplicitTop = 41
|
||||||
|
end
|
||||||
|
object btnEventStart: TButton
|
||||||
|
Left = 8
|
||||||
|
Top = 8
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Caption = 'Start'
|
||||||
|
TabOrder = 1
|
||||||
|
OnClick = btnEventStartClick
|
||||||
|
end
|
||||||
|
object btnEventStop: TButton
|
||||||
|
Left = 89
|
||||||
|
Top = 8
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Caption = 'Stop'
|
||||||
|
TabOrder = 2
|
||||||
|
OnClick = btnEventStopClick
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
object tsFile: TTabSheet
|
object tsFile: TTabSheet
|
||||||
Caption = 'File Observer'
|
Caption = 'File Observer'
|
||||||
ImageIndex = 1
|
ExplicitLeft = -108
|
||||||
ExplicitHeight = 333
|
ExplicitTop = -5
|
||||||
|
object lblFilename: TLabel
|
||||||
|
Left = 12
|
||||||
|
Top = 64
|
||||||
|
Width = 46
|
||||||
|
Height = 13
|
||||||
|
Caption = 'Filename:'
|
||||||
|
end
|
||||||
|
object btnFileStart: TButton
|
||||||
|
Left = 8
|
||||||
|
Top = 8
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Caption = 'Start'
|
||||||
|
TabOrder = 0
|
||||||
|
OnClick = btnFileStartClick
|
||||||
|
end
|
||||||
|
object btnFileStop: TButton
|
||||||
|
Left = 89
|
||||||
|
Top = 8
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Caption = 'Stop'
|
||||||
|
TabOrder = 1
|
||||||
|
OnClick = btnFileStopClick
|
||||||
|
end
|
||||||
|
object edtFilename: TEdit
|
||||||
|
Left = 88
|
||||||
|
Top = 61
|
||||||
|
Width = 489
|
||||||
|
Height = 21
|
||||||
|
TabOrder = 2
|
||||||
|
Text = 'X2LogTest\Test.log'
|
||||||
|
end
|
||||||
|
object rbProgramData: TRadioButton
|
||||||
|
Left = 88
|
||||||
|
Top = 88
|
||||||
|
Width = 113
|
||||||
|
Height = 17
|
||||||
|
Caption = 'Program Data'
|
||||||
|
Checked = True
|
||||||
|
TabOrder = 3
|
||||||
|
TabStop = True
|
||||||
|
end
|
||||||
|
object rbUserData: TRadioButton
|
||||||
|
Left = 88
|
||||||
|
Top = 111
|
||||||
|
Width = 113
|
||||||
|
Height = 17
|
||||||
|
Caption = 'User Application Data'
|
||||||
|
TabOrder = 4
|
||||||
|
end
|
||||||
|
object rbAbsolute: TRadioButton
|
||||||
|
Left = 88
|
||||||
|
Top = 134
|
||||||
|
Width = 113
|
||||||
|
Height = 17
|
||||||
|
Caption = 'Absolute path'
|
||||||
|
TabOrder = 5
|
||||||
|
end
|
||||||
end
|
end
|
||||||
object tsNamedPipe: TTabSheet
|
object tsNamedPipe: TTabSheet
|
||||||
Caption = 'Named Pipe Observer'
|
Caption = 'Named Pipe Observer'
|
||||||
ImageIndex = 2
|
ExplicitLeft = 0
|
||||||
ExplicitHeight = 333
|
ExplicitTop = 30
|
||||||
|
object lblPipeName: TLabel
|
||||||
|
Left = 12
|
||||||
|
Top = 64
|
||||||
|
Width = 53
|
||||||
|
Height = 13
|
||||||
|
Caption = 'Pipe name:'
|
||||||
|
end
|
||||||
|
object btnNamedPipeStart: TButton
|
||||||
|
Left = 8
|
||||||
|
Top = 8
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Caption = 'Start'
|
||||||
|
TabOrder = 0
|
||||||
|
OnClick = btnNamedPipeStartClick
|
||||||
|
end
|
||||||
|
object btnNamedPipeStop: TButton
|
||||||
|
Left = 89
|
||||||
|
Top = 8
|
||||||
|
Width = 75
|
||||||
|
Height = 25
|
||||||
|
Caption = 'Stop'
|
||||||
|
TabOrder = 1
|
||||||
|
OnClick = btnNamedPipeStopClick
|
||||||
|
end
|
||||||
|
object edtPipeName: TEdit
|
||||||
|
Left = 88
|
||||||
|
Top = 61
|
||||||
|
Width = 489
|
||||||
|
Height = 21
|
||||||
|
TabOrder = 2
|
||||||
|
Text = 'X2LogTest'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
object pnlButtons: TPanel
|
object pnlButtons: TPanel
|
||||||
@ -200,4 +307,92 @@ object MainForm: TMainForm
|
|||||||
OnClick = btnLogClick
|
OnClick = btnLogClick
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
object ilsObservers: TImageList
|
||||||
|
Height = 12
|
||||||
|
Width = 12
|
||||||
|
Left = 552
|
||||||
|
Top = 176
|
||||||
|
Bitmap = {
|
||||||
|
494C01010200140020000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||||
|
0000000000003600000028000000300000000C00000001002000000000000009
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000000000000000788D
|
||||||
|
F400274DEA00274DEA001C41E5001C37D8007380E20000000000000000000000
|
||||||
|
000000000000000000000000000074C97D001EA7330017A83000139F29001695
|
||||||
|
220071BB76000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000004C6EF7003C6B
|
||||||
|
FF003C6BFF003162FF003162FF002855F5001B3EE1003C4FD700000000000000
|
||||||
|
0000000000000000000043BE560028C1520026BF4F0026BF4F0021BA44001BB0
|
||||||
|
3800139F290039A0400000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000819BFD004876FF004674
|
||||||
|
FF004674FF004674FF003C6BFF003162FF002D5CFC001B3EE1007380E2000000
|
||||||
|
0000000000007CD58B0030C9610030C961002FC85F0028C1520028C1520026BF
|
||||||
|
4F001BB03800139F290071BB7600000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000517EFE005484FF005484
|
||||||
|
FF005484FF004B79FF004674FF004674FF003162FF002855F5001D38D8000000
|
||||||
|
00000000000046CC6A0038D16B0038D16B0038D16B0036CE690030C9610028C1
|
||||||
|
520028C152001BB0380016952200000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000749EFF006295FF006295
|
||||||
|
FF006295FF005B8CFF005484FF004674FF004674FF003162FF001C41E5000000
|
||||||
|
0000000000005CDC850041DA740041DA740041DA740038D16B0038D16B0030C9
|
||||||
|
610028C1520021BA4400139F2900000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000089B1FF0071A6FF0071A6
|
||||||
|
FF006DA2FF006599FF005B8CFF005484FF004674FF003C6BFF00274DEA000000
|
||||||
|
0000000000006EE5940049E27C0049E27C0049E27C0041DA740041DA740038D1
|
||||||
|
6B0030C9610028C152001BB03800000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000007CA7FF008EBFFF0080B8
|
||||||
|
FF007AB2FF0071A6FF006295FF005B8CFF004674FF003C6BFF00294EEA000000
|
||||||
|
00000000000067E28D0067EE940053EC860053EC860049E27C0041DA740038D1
|
||||||
|
6B0030C9610028C152001EA73300000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000009FBCFF00ADD6FF0091CB
|
||||||
|
FF0080B8FF007AB2FF006A9FFF005D90FF004B79FF004674FF00788DF4000000
|
||||||
|
00000000000095EAAF008AF6AE0061F5920056EF89004EE7810046DF790041DA
|
||||||
|
740036CE69002CC55A0074C97D00000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000000089B1FF00B4DF
|
||||||
|
FF0091CBFF007AB2FF006DA2FF006295FF00517EFE004876FF00000000000000
|
||||||
|
0000000000000000000079E99D008EFBB20068F396004FE9830047E07A003DD6
|
||||||
|
700036CE690046C45D0000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000000000000000A3C0
|
||||||
|
FF0089B1FF008EBFFF007CA7FF005484FF00819BFD0000000000000000000000
|
||||||
|
000000000000000000000000000099EEB3006EE5940070E998005CE1870048D1
|
||||||
|
6F0081DB93000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000424D3E000000000000003E000000
|
||||||
|
28000000300000000C0000000100010000000000600000000000000000000000
|
||||||
|
000000000000000000000000FFFFFF00FFFFFF0000000000E07E070000000000
|
||||||
|
C03C030000000000801801000000000080180100000000008018010000000000
|
||||||
|
801801000000000080180100000000008018010000000000C03C030000000000
|
||||||
|
E07E070000000000FFFFFF000000000000000000000000000000000000000000
|
||||||
|
000000000000}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,7 +9,7 @@ uses
|
|||||||
Vcl.Forms,
|
Vcl.Forms,
|
||||||
Vcl.StdCtrls,
|
Vcl.StdCtrls,
|
||||||
|
|
||||||
X2Log.Intf;
|
X2Log.Intf, Vcl.ImgList;
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -32,22 +32,41 @@ type
|
|||||||
btnInfo: TButton;
|
btnInfo: TButton;
|
||||||
btnWarning: TButton;
|
btnWarning: TButton;
|
||||||
btnError: TButton;
|
btnError: TButton;
|
||||||
|
btnEventStart: TButton;
|
||||||
|
btnEventStop: TButton;
|
||||||
|
ilsObservers: TImageList;
|
||||||
|
btnFileStart: TButton;
|
||||||
|
btnFileStop: TButton;
|
||||||
|
btnNamedPipeStart: TButton;
|
||||||
|
btnNamedPipeStop: TButton;
|
||||||
|
edtFilename: TEdit;
|
||||||
|
lblFilename: TLabel;
|
||||||
|
rbProgramData: TRadioButton;
|
||||||
|
rbUserData: TRadioButton;
|
||||||
|
rbAbsolute: TRadioButton;
|
||||||
|
edtPipeName: TEdit;
|
||||||
|
lblPipeName: TLabel;
|
||||||
|
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
procedure FormDestroy(Sender: TObject);
|
procedure FormDestroy(Sender: TObject);
|
||||||
procedure pcObserversChange(Sender: TObject);
|
|
||||||
procedure btnCloseClick(Sender: TObject);
|
procedure btnCloseClick(Sender: TObject);
|
||||||
procedure btnLogClick(Sender: TObject);
|
procedure btnLogClick(Sender: TObject);
|
||||||
procedure edtExceptionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
procedure edtExceptionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||||
procedure edtMessageKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
procedure edtMessageKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||||
procedure btnExceptionClick(Sender: TObject);
|
procedure btnExceptionClick(Sender: TObject);
|
||||||
procedure btnMonitorFormClick(Sender: TObject);
|
procedure btnMonitorFormClick(Sender: TObject);
|
||||||
|
procedure btnEventStartClick(Sender: TObject);
|
||||||
|
procedure btnEventStopClick(Sender: TObject);
|
||||||
|
procedure btnFileStartClick(Sender: TObject);
|
||||||
|
procedure btnFileStopClick(Sender: TObject);
|
||||||
|
procedure btnNamedPipeStartClick(Sender: TObject);
|
||||||
|
procedure btnNamedPipeStopClick(Sender: TObject);
|
||||||
private
|
private
|
||||||
FLog: IX2Log;
|
FLog: IX2Log;
|
||||||
FObserver: IX2LogObserver;
|
FEventObserver: IX2LogObserver;
|
||||||
|
FFileObserver: IX2LogObserver;
|
||||||
|
FNamedPipeObserver: IX2LogObserver;
|
||||||
protected
|
protected
|
||||||
procedure InitObserver;
|
|
||||||
|
|
||||||
procedure DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
procedure DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -90,7 +109,6 @@ begin
|
|||||||
FLog.SetExceptionStrategy(TX2LogmadExceptExceptionStrategy.Create);
|
FLog.SetExceptionStrategy(TX2LogmadExceptExceptionStrategy.Create);
|
||||||
|
|
||||||
pcObservers.ActivePageIndex := 0;
|
pcObservers.ActivePageIndex := 0;
|
||||||
InitObserver;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -100,37 +118,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.InitObserver;
|
|
||||||
var
|
|
||||||
activePage: TTabSheet;
|
|
||||||
|
|
||||||
begin
|
|
||||||
if Assigned(FObserver) then
|
|
||||||
begin
|
|
||||||
FLog.Detach(FObserver);
|
|
||||||
FObserver := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
activePage := pcObservers.ActivePage;
|
|
||||||
|
|
||||||
if activePage = tsEvent then
|
|
||||||
FObserver := TX2LogEventObserver.Create(DoLog)
|
|
||||||
else if activePage = tsFile then
|
|
||||||
FObserver := TX2LogFileObserver.CreateInProgramData('X2LogTest\Test.log');
|
|
||||||
|
|
||||||
if activePage = tsNamedPipe then
|
|
||||||
begin
|
|
||||||
FObserver := TX2LogNamedPipeObserver.Create('X2LogTest');
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
|
|
||||||
end;
|
|
||||||
|
|
||||||
if Assigned(FObserver) then
|
|
||||||
FLog.Attach(FObserver);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
||||||
begin
|
begin
|
||||||
mmoEvent.Lines.Add(GetLogLevelText(Level) + ': ' + Msg + ' (' + Details + ')');
|
mmoEvent.Lines.Add(GetLogLevelText(Level) + ': ' + Msg + ' (' + Details + ')');
|
||||||
@ -156,12 +143,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.pcObserversChange(Sender: TObject);
|
|
||||||
begin
|
|
||||||
InitObserver;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.btnCloseClick(Sender: TObject);
|
procedure TMainForm.btnCloseClick(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
Close;
|
Close;
|
||||||
@ -199,4 +180,82 @@ begin
|
|||||||
TX2LogObserverMonitorForm.ShowInstance(FLog);
|
TX2LogObserverMonitorForm.ShowInstance(FLog);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.btnEventStartClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if not Assigned(FEventObserver) then
|
||||||
|
begin
|
||||||
|
FEventObserver := TX2LogEventObserver.Create(DoLog);
|
||||||
|
FLog.Attach(FEventObserver);
|
||||||
|
|
||||||
|
tsEvent.ImageIndex := 1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.btnEventStopClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if Assigned(FEventObserver) then
|
||||||
|
begin
|
||||||
|
FLog.Detach(FEventObserver);
|
||||||
|
FEventObserver := nil;
|
||||||
|
|
||||||
|
tsEvent.ImageIndex := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.btnFileStartClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if not Assigned(FFileObserver) then
|
||||||
|
begin
|
||||||
|
if rbProgramData.Checked then
|
||||||
|
FFileObserver := TX2LogFileObserver.CreateInProgramData(edtFilename.Text)
|
||||||
|
else if rbUserData.Checked then
|
||||||
|
FFileObserver := TX2LogFileObserver.CreateInUserAppData(edtFilename.Text)
|
||||||
|
else
|
||||||
|
FFileObserver := TX2LogFileObserver.Create(edtFilename.Text);
|
||||||
|
|
||||||
|
FLog.Attach(FFileObserver);
|
||||||
|
|
||||||
|
tsFile.ImageIndex := 1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.btnFileStopClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if Assigned(FFileObserver) then
|
||||||
|
begin
|
||||||
|
FLog.Detach(FFileObserver);
|
||||||
|
FFileObserver := nil;
|
||||||
|
|
||||||
|
tsFile.ImageIndex := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.btnNamedPipeStartClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if not Assigned(FNamedPipeObserver) then
|
||||||
|
begin
|
||||||
|
FNamedPipeObserver := TX2LogNamedPipeObserver.Create(edtPipeName.Text);
|
||||||
|
FLog.Attach(FNamedPipeObserver);
|
||||||
|
|
||||||
|
tsNamedPipe.ImageIndex := 1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TMainForm.btnNamedPipeStopClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if Assigned(FNamedPipeObserver) then
|
||||||
|
begin
|
||||||
|
FLog.Detach(FNamedPipeObserver);
|
||||||
|
FNamedPipeObserver := nil;
|
||||||
|
|
||||||
|
tsNamedPipe.ImageIndex := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
76
X2Log.Client.Base.pas
Normal file
76
X2Log.Client.Base.pas
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
unit X2Log.Client.Base;
|
||||||
|
|
||||||
|
interface
|
||||||
|
uses
|
||||||
|
System.Classes,
|
||||||
|
System.Generics.Collections,
|
||||||
|
|
||||||
|
X2Log.Intf;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TX2LogBaseClient = class(TInterfacedPersistent, IX2LogObservable)
|
||||||
|
private
|
||||||
|
FObservers: TList<IX2LogObserver>;
|
||||||
|
protected
|
||||||
|
property Observers: TList<IX2LogObserver> read FObservers;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
|
||||||
|
{ IX2LogBase }
|
||||||
|
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); virtual;
|
||||||
|
|
||||||
|
{ IX2LogObservable }
|
||||||
|
procedure Attach(AObserver: IX2LogObserver);
|
||||||
|
procedure Detach(AObserver: IX2LogObserver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
uses
|
||||||
|
System.SysUtils;
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2LogBaseClient }
|
||||||
|
constructor TX2LogBaseClient.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
|
||||||
|
FObservers := TList<IX2LogObserver>.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
destructor TX2LogBaseClient.Destroy;
|
||||||
|
begin
|
||||||
|
FreeAndNil(FObservers);
|
||||||
|
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogBaseClient.Attach(AObserver: IX2LogObserver);
|
||||||
|
begin
|
||||||
|
{ Explicit cast ensures we're getting the same pointer in Attach and Detach
|
||||||
|
if, for example, the implementing interface is a descendant of IX2LogObserver }
|
||||||
|
Observers.Add(AObserver as IX2LogObserver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogBaseClient.Detach(AObserver: IX2LogObserver);
|
||||||
|
begin
|
||||||
|
Observers.Remove(AObserver as IX2LogObserver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogBaseClient.Log(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||||
|
var
|
||||||
|
observer: IX2LogObserver;
|
||||||
|
|
||||||
|
begin
|
||||||
|
for observer in Observers do
|
||||||
|
observer.Log(ALevel, AMessage, ADetails);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
310
X2Log.Client.NamedPipe.pas
Normal file
310
X2Log.Client.NamedPipe.pas
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
unit X2Log.Client.NamedPipe;
|
||||||
|
|
||||||
|
interface
|
||||||
|
uses
|
||||||
|
System.Classes,
|
||||||
|
System.Generics.Collections,
|
||||||
|
|
||||||
|
X2Log.Intf,
|
||||||
|
X2Log.Client.Base;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TX2LogNamedPipeClient = class(TX2LogBaseClient, IX2LogBase)
|
||||||
|
private
|
||||||
|
FWorkerThread: TThread;
|
||||||
|
protected
|
||||||
|
property WorkerThread: TThread read FWorkerThread;
|
||||||
|
public
|
||||||
|
constructor Create(const APipeName: string);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
uses
|
||||||
|
System.SyncObjs,
|
||||||
|
System.SysUtils,
|
||||||
|
|
||||||
|
Winapi.Windows;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TX2LogNamedPipeClientWorkerThread = class(TThread)
|
||||||
|
private
|
||||||
|
FLog: IX2LogBase;
|
||||||
|
FPipeName: string;
|
||||||
|
|
||||||
|
FTerminateEvent: TEvent;
|
||||||
|
FPipeHandle: THandle;
|
||||||
|
FOverlappedRead: TOverlapped;
|
||||||
|
FReadBuffer: array[0..4095] of Byte;
|
||||||
|
FMessageData: TMemoryStream;
|
||||||
|
protected
|
||||||
|
procedure Execute; override;
|
||||||
|
procedure TerminatedSet; override;
|
||||||
|
|
||||||
|
procedure ConnectPipe;
|
||||||
|
procedure ReadPipe;
|
||||||
|
procedure ClosePipe;
|
||||||
|
|
||||||
|
procedure ReadMessage;
|
||||||
|
procedure HandleMessage;
|
||||||
|
|
||||||
|
property Log: IX2LogBase read FLog;
|
||||||
|
property PipeName: string read FPipeName;
|
||||||
|
|
||||||
|
property TerminateEvent: TEvent read FTerminateEvent;
|
||||||
|
property PipeHandle: THandle read FPipeHandle;
|
||||||
|
property MessageData: TMemoryStream read FMessageData;
|
||||||
|
public
|
||||||
|
constructor Create(ALog: IX2LogBase; const APipeName: string);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
const
|
||||||
|
PipeNamePrefix = '\\.\pipe\';
|
||||||
|
|
||||||
|
TimeoutBusyPipe = 5000;
|
||||||
|
TimeoutNoPipe = 1000;
|
||||||
|
|
||||||
|
ClearBufferTreshold = 4096;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2LogNamedPipeClient }
|
||||||
|
constructor TX2LogNamedPipeClient.Create(const APipeName: string);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
|
||||||
|
FWorkerThread := TX2LogNamedPipeClientWorkerThread.Create(Self, APipeName);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2LogNamedPipeClientWorkerThread }
|
||||||
|
constructor TX2LogNamedPipeClientWorkerThread.Create(ALog: IX2LogBase; const APipeName: string);
|
||||||
|
begin
|
||||||
|
FTerminateEvent := TEvent.Create(nil, True, False, '');
|
||||||
|
FMessageData := TMemoryStream.Create;
|
||||||
|
|
||||||
|
FLog := ALog;
|
||||||
|
FPipeName := APipeName;
|
||||||
|
|
||||||
|
inherited Create(False);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.Execute;
|
||||||
|
begin
|
||||||
|
FPipeHandle := INVALID_HANDLE_VALUE;
|
||||||
|
try
|
||||||
|
while not Terminated do
|
||||||
|
begin
|
||||||
|
ConnectPipe;
|
||||||
|
|
||||||
|
if not Terminated then
|
||||||
|
ReadPipe;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
ClosePipe;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.TerminatedSet;
|
||||||
|
begin
|
||||||
|
inherited TerminatedSet;
|
||||||
|
|
||||||
|
TerminateEvent.SetEvent;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.ConnectPipe;
|
||||||
|
var
|
||||||
|
lastError: Cardinal;
|
||||||
|
mode: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
while not Terminated do
|
||||||
|
begin
|
||||||
|
FPipeHandle := CreateFile(PChar(PipeNamePrefix + PipeName), GENERIC_READ or FILE_WRITE_ATTRIBUTES,
|
||||||
|
0, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
||||||
|
|
||||||
|
if PipeHandle = INVALID_HANDLE_VALUE then
|
||||||
|
begin
|
||||||
|
lastError := GetLastError;
|
||||||
|
|
||||||
|
case lastError of
|
||||||
|
ERROR_PIPE_BUSY:
|
||||||
|
{ Pipe exists but is connecting to another client, wait for a new slot }
|
||||||
|
WaitNamedPipe(PChar(PipeNamePrefix + PipeName), TimeoutBusyPipe);
|
||||||
|
|
||||||
|
ERROR_FILE_NOT_FOUND:
|
||||||
|
{ Pipe does not exist, try again later }
|
||||||
|
WaitForSingleObject(TerminateEvent.Handle, TimeoutNoPipe);
|
||||||
|
else
|
||||||
|
RaiseLastOSError;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
{ Change to message mode }
|
||||||
|
mode := PIPE_READMODE_MESSAGE;
|
||||||
|
if not SetNamedPipeHandleState(PipeHandle, mode, nil, nil) then
|
||||||
|
RaiseLastOSError;
|
||||||
|
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.ReadPipe;
|
||||||
|
var
|
||||||
|
readEvent: TEvent;
|
||||||
|
events: array[0..1] of THandle;
|
||||||
|
waitResult: Cardinal;
|
||||||
|
bytesTransferred: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
readEvent := TEvent.Create(nil, False, False, '');
|
||||||
|
events[0] := TerminateEvent.Handle;
|
||||||
|
events[1] := readEvent.Handle;
|
||||||
|
|
||||||
|
FOverlappedRead.hEvent := readEvent.Handle;
|
||||||
|
ReadMessage;
|
||||||
|
|
||||||
|
while (not Terminated) and (PipeHandle <> INVALID_HANDLE_VALUE) do
|
||||||
|
begin
|
||||||
|
waitResult := WaitForMultipleObjects(Length(events), @events, False, INFINITE);
|
||||||
|
|
||||||
|
case waitResult of
|
||||||
|
WAIT_OBJECT_0:
|
||||||
|
{ Terminated }
|
||||||
|
break;
|
||||||
|
|
||||||
|
WAIT_OBJECT_0 + 1:
|
||||||
|
{ Read event completed }
|
||||||
|
if GetOverlappedResult(PipeHandle, FOverlappedRead, bytesTransferred, False) then
|
||||||
|
begin
|
||||||
|
MessageData.WriteBuffer(FReadBuffer[0], bytesTransferred);
|
||||||
|
HandleMessage;
|
||||||
|
ReadMessage;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
if GetLastError = ERROR_MORE_DATA then
|
||||||
|
begin
|
||||||
|
MessageData.WriteBuffer(FReadBuffer[0], bytesTransferred);
|
||||||
|
ReadMessage;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
ClosePipe;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.ClosePipe;
|
||||||
|
begin
|
||||||
|
if PipeHandle <> INVALID_HANDLE_VALUE then
|
||||||
|
begin
|
||||||
|
CloseHandle(PipeHandle);
|
||||||
|
FPipeHandle := INVALID_HANDLE_VALUE;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.ReadMessage;
|
||||||
|
var
|
||||||
|
bytesRead: Cardinal;
|
||||||
|
lastError: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
while PipeHandle <> INVALID_HANDLE_VALUE do
|
||||||
|
begin
|
||||||
|
if ReadFile(PipeHandle, FReadBuffer, SizeOf(FReadBuffer), bytesRead, @FOverlappedRead) then
|
||||||
|
begin
|
||||||
|
{ Immediate result }
|
||||||
|
MessageData.WriteBuffer(FReadBuffer[0], bytesRead);
|
||||||
|
HandleMessage;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
{ More data, pending I/O or an actual error }
|
||||||
|
lastError := GetLastError;
|
||||||
|
|
||||||
|
if lastError = ERROR_IO_PENDING then
|
||||||
|
break
|
||||||
|
else if lastError = ERROR_MORE_DATA then
|
||||||
|
MessageData.WriteBuffer(FReadBuffer[0], SizeOf(FReadBuffer))
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
ClosePipe;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogNamedPipeClientWorkerThread.HandleMessage;
|
||||||
|
|
||||||
|
function ReadString: WideString;
|
||||||
|
var
|
||||||
|
size: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
MessageData.ReadBuffer(size, SizeOf(cardinal));
|
||||||
|
if size > 0 then
|
||||||
|
begin
|
||||||
|
SetLength(Result, size);
|
||||||
|
MessageData.ReadBuffer(Result[1], size * SizeOf(WideChar));
|
||||||
|
end else
|
||||||
|
Result := '';
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
header: TX2LogMessageHeaderV1;
|
||||||
|
headerDiff: Integer;
|
||||||
|
msg: string;
|
||||||
|
details: string;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if MessageData.Size > 0 then
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
MessageData.Position := 0;
|
||||||
|
MessageData.ReadBuffer(header, SizeOf(header));
|
||||||
|
|
||||||
|
if header.ID <> X2LogMessageHeader then
|
||||||
|
raise EReadError.Create('Invalid header ID');
|
||||||
|
|
||||||
|
headerDiff := SizeOf(header) - header.Size;
|
||||||
|
if headerDiff > 0 then
|
||||||
|
begin
|
||||||
|
{ A larger, most likely newer version, header }
|
||||||
|
MessageData.Seek(headerDiff, soFromCurrent)
|
||||||
|
end else if headerDiff < 0 then
|
||||||
|
raise EReadError.Create('Header too small');
|
||||||
|
|
||||||
|
msg := ReadString;
|
||||||
|
details := ReadString;
|
||||||
|
|
||||||
|
Log.Log(header.Level, msg, details);
|
||||||
|
except
|
||||||
|
on E:EReadError do
|
||||||
|
ClosePipe;
|
||||||
|
|
||||||
|
on E:Exception do
|
||||||
|
RaiseLastOSError;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if MessageData.Size > ClearBufferTreshold then
|
||||||
|
MessageData.Clear
|
||||||
|
else
|
||||||
|
MessageData.Position := 0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
@ -22,7 +22,7 @@ type
|
|||||||
|
|
||||||
class procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
class procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||||
|
|
||||||
{ Facade for IX2LogMethods }
|
{ Facade for IX2LogBase }
|
||||||
class procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
class procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
||||||
|
|
||||||
class procedure Verbose(const AMessage: string; const ADetails: string = '');
|
class procedure Verbose(const AMessage: string; const ADetails: string = '');
|
||||||
|
@ -8,13 +8,13 @@ type
|
|||||||
TX2LogLevel = (Verbose, Info, Warning, Error);
|
TX2LogLevel = (Verbose, Info, Warning, Error);
|
||||||
|
|
||||||
|
|
||||||
IX2LogMethods = interface
|
IX2LogBase = interface
|
||||||
['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}']
|
['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}']
|
||||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
IX2LogObserver = interface(IX2LogMethods)
|
IX2LogObserver = interface(IX2LogBase)
|
||||||
['{CBC5C18E-84EE-43F4-8DBE-C66D06FCDE74}']
|
['{CBC5C18E-84EE-43F4-8DBE-C66D06FCDE74}']
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -25,11 +25,15 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
IX2Log = interface(IX2LogMethods)
|
IX2LogObservable = interface(IX2LogBase)
|
||||||
['{A6FF38F9-EDA8-4C76-9C95-2C0317560D78}']
|
['{50B47D5D-11E4-40E0-BBC4-8BA70697C1F9}']
|
||||||
procedure Attach(AObserver: IX2LogObserver);
|
procedure Attach(AObserver: IX2LogObserver);
|
||||||
procedure Detach(AObserver: IX2LogObserver);
|
procedure Detach(AObserver: IX2LogObserver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
IX2Log = interface(IX2LogObservable)
|
||||||
|
['{A6FF38F9-EDA8-4C76-9C95-2C0317560D78}']
|
||||||
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||||
|
|
||||||
procedure Verbose(const AMessage: string; const ADetails: string = '');
|
procedure Verbose(const AMessage: string; const ADetails: string = '');
|
||||||
@ -40,6 +44,30 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TX2LogMessageHeaderV1 = packed record
|
||||||
|
ID: Word;
|
||||||
|
Version: Byte;
|
||||||
|
Size: Word;
|
||||||
|
Level: TX2LogLevel;
|
||||||
|
|
||||||
|
{
|
||||||
|
Payload:
|
||||||
|
|
||||||
|
MessageLength: Cardinal
|
||||||
|
Message: WideString
|
||||||
|
DetailsLength: Cardinal
|
||||||
|
Details: WideString
|
||||||
|
}
|
||||||
|
end;
|
||||||
|
|
||||||
|
TX2LogMessageHeader = TX2LogMessageHeaderV1;
|
||||||
|
|
||||||
|
const
|
||||||
|
X2LogMessageHeader: Word = $B258;
|
||||||
|
X2LogMessageVersion: Byte = 1;
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -174,6 +174,8 @@ var
|
|||||||
entry: TX2LogQueueEntry;
|
entry: TX2LogQueueEntry;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
NameThreadForDebugging('TX2LogObserverWorkerThread');
|
||||||
|
|
||||||
Setup;
|
Setup;
|
||||||
try
|
try
|
||||||
while not Terminated do
|
while not Terminated do
|
||||||
|
@ -13,23 +13,30 @@ type
|
|||||||
TX2LogEventObserver = class(TX2LogCustomObserver)
|
TX2LogEventObserver = class(TX2LogCustomObserver)
|
||||||
private
|
private
|
||||||
FOnLog: TX2LogEvent;
|
FOnLog: TX2LogEvent;
|
||||||
|
FRunInMainThread: Boolean;
|
||||||
protected
|
protected
|
||||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); override;
|
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); override;
|
||||||
public
|
public
|
||||||
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
|
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
|
||||||
constructor Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
|
constructor Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
|
||||||
|
|
||||||
|
property RunInMainThread: Boolean read FRunInMainThread write FRunInMainThread default True;
|
||||||
|
|
||||||
property OnLog: TX2LogEvent read FOnLog write FOnLog;
|
property OnLog: TX2LogEvent read FOnLog write FOnLog;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
uses
|
||||||
|
System.Classes;
|
||||||
|
|
||||||
|
|
||||||
{ TX2LogEventObserver }
|
{ TX2LogEventObserver }
|
||||||
constructor TX2LogEventObserver.Create(ALogLevels: TX2LogLevels);
|
constructor TX2LogEventObserver.Create(ALogLevels: TX2LogLevels);
|
||||||
begin
|
begin
|
||||||
inherited Create(ALogLevels);
|
inherited Create(ALogLevels);
|
||||||
|
|
||||||
|
FRunInMainThread := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -42,9 +49,20 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
procedure TX2LogEventObserver.DoLog(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
procedure TX2LogEventObserver.DoLog(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||||
|
begin
|
||||||
|
if Assigned(FOnLog) then
|
||||||
|
begin
|
||||||
|
if RunInMainThread then
|
||||||
|
begin
|
||||||
|
TThread.Queue(nil,
|
||||||
|
procedure
|
||||||
begin
|
begin
|
||||||
if Assigned(FOnLog) then
|
if Assigned(FOnLog) then
|
||||||
FOnLog(Self, ALevel, AMessage, ADetails);
|
FOnLog(Self, ALevel, AMessage, ADetails);
|
||||||
|
end);
|
||||||
|
end else
|
||||||
|
FOnLog(Self, ALevel, AMessage, ADetails);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -35,7 +35,6 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
Align = alRight
|
Align = alRight
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
ExplicitHeight = 500
|
|
||||||
object tbDetails: TToolBar
|
object tbDetails: TToolBar
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
@ -69,7 +68,6 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
BevelKind = bkFlat
|
BevelKind = bkFlat
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
ExplicitHeight = 478
|
|
||||||
object HeaderControl1: THeaderControl
|
object HeaderControl1: THeaderControl
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
@ -101,7 +99,6 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
ReadOnly = True
|
ReadOnly = True
|
||||||
ScrollBars = ssBoth
|
ScrollBars = ssBoth
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
ExplicitHeight = 457
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -113,7 +110,6 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
Align = alClient
|
Align = alClient
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
ExplicitHeight = 500
|
|
||||||
object vstLog: TVirtualStringTree
|
object vstLog: TVirtualStringTree
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 22
|
Top = 22
|
||||||
@ -138,7 +134,6 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
OnGetImageIndex = vstLogGetImageIndex
|
OnGetImageIndex = vstLogGetImageIndex
|
||||||
OnGetHint = vstLogGetHint
|
OnGetHint = vstLogGetHint
|
||||||
OnInitNode = vstLogInitNode
|
OnInitNode = vstLogInitNode
|
||||||
ExplicitHeight = 478
|
|
||||||
Columns = <
|
Columns = <
|
||||||
item
|
item
|
||||||
Options = [coAllowClick, coDraggable, coEnabled, coParentBidiMode, coParentColor, coShowDropMark, coVisible, coAllowFocus]
|
Options = [coAllowClick, coDraggable, coEnabled, coParentBidiMode, coParentColor, coShowDropMark, coVisible, coAllowFocus]
|
||||||
@ -199,7 +194,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
Left = 584
|
Left = 584
|
||||||
Top = 48
|
Top = 48
|
||||||
Bitmap = {
|
Bitmap = {
|
||||||
494C0101090040007C0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
494C010109004000800010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||||
0000000000003600000028000000400000003000000001002000000000000030
|
0000000000003600000028000000400000003000000001002000000000000030
|
||||||
0000000000000000000000000000000000000000000000000000000000000000
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
0000000000000000000000000000000000000000000000000000000000000000
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -20,6 +20,7 @@ type
|
|||||||
|
|
||||||
implementation
|
implementation
|
||||||
uses
|
uses
|
||||||
|
System.Classes,
|
||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
System.SyncObjs,
|
System.SyncObjs,
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
@ -35,15 +36,18 @@ type
|
|||||||
TX2LogNamedPipeClient = class(TObject)
|
TX2LogNamedPipeClient = class(TObject)
|
||||||
private
|
private
|
||||||
FOverlapped: TOverlapped;
|
FOverlapped: TOverlapped;
|
||||||
FPipe: THandle;
|
FPipeHandle: THandle;
|
||||||
FState: TX2LogNamedPipeClientState;
|
FState: TX2LogNamedPipeClientState;
|
||||||
FOverlappedEvent: TEvent;
|
FOverlappedEvent: TEvent;
|
||||||
FWriteQueue: TObjectQueue<TX2LogQueueEntry>;
|
FWriteQueue: TObjectQueue<TX2LogQueueEntry>;
|
||||||
FWriteBuffer: Pointer;
|
FWriteBuffer: TMemoryStream;
|
||||||
FWriteBufferSize: Integer;
|
|
||||||
protected
|
protected
|
||||||
function DoSend(AEntry: TX2LogQueueEntry): Boolean;
|
function DoSend(AEntry: TX2LogQueueEntry): Boolean;
|
||||||
procedure ClearWriteBuffer;
|
procedure ClearWriteBuffer;
|
||||||
|
|
||||||
|
property PipeHandle: THandle read FPipeHandle;
|
||||||
|
property WriteBuffer: TMemoryStream read FWriteBuffer;
|
||||||
|
property WriteQueue: TObjectQueue<TX2LogQueueEntry> read FWriteQueue;
|
||||||
public
|
public
|
||||||
constructor Create(APipe: THandle);
|
constructor Create(APipe: THandle);
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -53,7 +57,7 @@ type
|
|||||||
|
|
||||||
procedure Disconnect;
|
procedure Disconnect;
|
||||||
|
|
||||||
property Pipe: THandle read FPipe;
|
property Pipe: THandle read FPipeHandle;
|
||||||
property Overlapped: TOverlapped read FOverlapped;
|
property Overlapped: TOverlapped read FOverlapped;
|
||||||
property OverlappedEvent: TEvent read FOverlappedEvent;
|
property OverlappedEvent: TEvent read FOverlappedEvent;
|
||||||
property State: TX2LogNamedPipeClientState read FState write FState;
|
property State: TX2LogNamedPipeClientState read FState write FState;
|
||||||
@ -65,7 +69,6 @@ type
|
|||||||
FClients: TObjectList<TX2LogNamedPipeClient>;
|
FClients: TObjectList<TX2LogNamedPipeClient>;
|
||||||
FPipeName: string;
|
FPipeName: string;
|
||||||
protected
|
protected
|
||||||
|
|
||||||
procedure WaitForEntry; override;
|
procedure WaitForEntry; override;
|
||||||
procedure ProcessEntry(AEntry: TX2LogQueueEntry); override;
|
procedure ProcessEntry(AEntry: TX2LogQueueEntry); override;
|
||||||
procedure ProcessClientEvent(AClientIndex: Integer);
|
procedure ProcessClientEvent(AClientIndex: Integer);
|
||||||
@ -103,7 +106,7 @@ constructor TX2LogNamedPipeClient.Create(APipe: THandle);
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
|
|
||||||
FPipe := APipe;
|
FPipeHandle := APipe;
|
||||||
FState := Listening;
|
FState := Listening;
|
||||||
|
|
||||||
FOverlappedEvent := TEvent.Create(nil, False, False, '');
|
FOverlappedEvent := TEvent.Create(nil, False, False, '');
|
||||||
@ -115,8 +118,8 @@ destructor TX2LogNamedPipeClient.Destroy;
|
|||||||
begin
|
begin
|
||||||
FreeAndNil(FOverlappedEvent);
|
FreeAndNil(FOverlappedEvent);
|
||||||
|
|
||||||
if FPipe <> INVALID_HANDLE_VALUE then
|
if PipeHandle <> INVALID_HANDLE_VALUE then
|
||||||
DisconnectNamedPipe(FPipe);
|
DisconnectNamedPipe(PipeHandle);
|
||||||
|
|
||||||
ClearWriteBuffer;
|
ClearWriteBuffer;
|
||||||
|
|
||||||
@ -126,14 +129,14 @@ end;
|
|||||||
|
|
||||||
procedure TX2LogNamedPipeClient.Send(AEntry: TX2LogQueueEntry);
|
procedure TX2LogNamedPipeClient.Send(AEntry: TX2LogQueueEntry);
|
||||||
begin
|
begin
|
||||||
if not Assigned(FWriteBuffer) then
|
if not Assigned(WriteBuffer) then
|
||||||
DoSend(AEntry)
|
DoSend(AEntry)
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
if not Assigned(FWriteQueue) then
|
if not Assigned(WriteQueue) then
|
||||||
FWriteQueue := TObjectQueue<TX2LogQueueEntry>.Create(True);
|
FWriteQueue := TObjectQueue<TX2LogQueueEntry>.Create(True);
|
||||||
|
|
||||||
FWriteQueue.Enqueue(TX2LogQueueEntry.Create(AEntry));
|
WriteQueue.Enqueue(TX2LogQueueEntry.Create(AEntry));
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -145,9 +148,11 @@ var
|
|||||||
begin
|
begin
|
||||||
ClearWriteBuffer;
|
ClearWriteBuffer;
|
||||||
|
|
||||||
while FWriteQueue.Count > 0 do
|
if Assigned(WriteQueue) then
|
||||||
begin
|
begin
|
||||||
entry := FWriteQueue.Extract;
|
while WriteQueue.Count > 0 do
|
||||||
|
begin
|
||||||
|
entry := WriteQueue.Extract;
|
||||||
try
|
try
|
||||||
{ Returns False when IO is pending }
|
{ Returns False when IO is pending }
|
||||||
if not DoSend(entry) then
|
if not DoSend(entry) then
|
||||||
@ -157,59 +162,54 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TX2LogNamedPipeClient.Disconnect;
|
procedure TX2LogNamedPipeClient.Disconnect;
|
||||||
begin
|
begin
|
||||||
if FPipe <> INVALID_HANDLE_VALUE then
|
if PipeHandle <> INVALID_HANDLE_VALUE then
|
||||||
begin
|
begin
|
||||||
CancelIo(FPipe);
|
CancelIo(PipeHandle);
|
||||||
DisconnectNamedPipe(FPipe);
|
DisconnectNamedPipe(PipeHandle);
|
||||||
|
|
||||||
FPipe := INVALID_HANDLE_VALUE;
|
FPipeHandle := INVALID_HANDLE_VALUE;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TX2LogNamedPipeClient.DoSend(AEntry: TX2LogQueueEntry): Boolean;
|
function TX2LogNamedPipeClient.DoSend(AEntry: TX2LogQueueEntry): Boolean;
|
||||||
|
|
||||||
procedure AppendToBuffer(var APointer: PByte; const ASource; ASize: Cardinal); overload; inline;
|
procedure WriteString(const ASource: WideString);
|
||||||
begin
|
|
||||||
Move(ASource, APointer^, ASize);
|
|
||||||
Inc(APointer, ASize);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure AppendToBuffer(var APointer: PByte; const ASource: string); overload; inline;
|
|
||||||
var
|
var
|
||||||
sourceLength: Cardinal;
|
sourceLength: Cardinal;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
sourceLength := Length(ASource);
|
sourceLength := Length(ASource);
|
||||||
AppendToBuffer(APointer, sourceLength, SizeOf(Cardinal));
|
WriteBuffer.WriteBuffer(sourceLength, SizeOf(Cardinal));
|
||||||
AppendToBuffer(APointer, PChar(ASource)^, sourceLength * SizeOf(Char));
|
WriteBuffer.WriteBuffer(PWideChar(ASource)^, sourceLength * SizeOf(WideChar));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
var
|
var
|
||||||
|
header: TX2LogMessageHeader;
|
||||||
bytesWritten: Cardinal;
|
bytesWritten: Cardinal;
|
||||||
bufferPointer: PByte;
|
|
||||||
lastError: Cardinal;
|
lastError: Cardinal;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
ClearWriteBuffer;
|
ClearWriteBuffer;
|
||||||
|
|
||||||
FWriteBufferSize := SizeOf(TX2LogLevel) +
|
FWriteBuffer := TMemoryStream.Create;
|
||||||
SizeOf(Cardinal) + (Length(AEntry.Message) * SizeOf(Char)) +
|
|
||||||
SizeOf(Cardinal) + (Length(AEntry.Details) * SizeOf(Char));
|
|
||||||
|
|
||||||
GetMem(FWriteBuffer, FWriteBufferSize);
|
header.ID := X2LogMessageHeader;
|
||||||
|
header.Version := X2LogMessageVersion;
|
||||||
|
header.Size := SizeOf(header);
|
||||||
|
header.Level := AEntry.Level;
|
||||||
|
|
||||||
bufferPointer := FWriteBuffer;
|
WriteBuffer.WriteBuffer(header, SizeOf(header));
|
||||||
AppendToBuffer(bufferPointer, AEntry.Level, SizeOf(TX2LogLevel));
|
WriteString(AEntry.Message);
|
||||||
AppendToBuffer(bufferPointer, AEntry.Message);
|
WriteString(AEntry.Details);
|
||||||
AppendToBuffer(bufferPointer, AEntry.Details);
|
|
||||||
|
|
||||||
Result := WriteFile(Pipe, FWriteBuffer^, FWriteBufferSize, bytesWritten, @Overlapped);
|
Result := WriteFile(Pipe, WriteBuffer.Memory^, WriteBuffer.Size, bytesWritten, @Overlapped);
|
||||||
if not Result then
|
if not Result then
|
||||||
begin
|
begin
|
||||||
lastError := GetLastError;
|
lastError := GetLastError;
|
||||||
@ -233,11 +233,7 @@ end;
|
|||||||
|
|
||||||
procedure TX2LogNamedPipeClient.ClearWriteBuffer;
|
procedure TX2LogNamedPipeClient.ClearWriteBuffer;
|
||||||
begin
|
begin
|
||||||
if Assigned(FWriteBuffer) then
|
FreeAndNil(FWriteBuffer);
|
||||||
begin
|
|
||||||
FreeMem(FWriteBuffer, FWriteBufferSize);
|
|
||||||
FWriteBuffer := nil;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
47
X2Log.pas
47
X2Log.pas
@ -6,30 +6,22 @@ uses
|
|||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
|
|
||||||
X2Log.Intf;
|
X2Log.Intf,
|
||||||
|
X2Log.Client.Base;
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TX2Log = class(TInterfacedObject, IX2Log, IX2LogMethods)
|
TX2Log = class(TX2LogBaseClient, IX2Log)
|
||||||
private
|
private
|
||||||
FExceptionStrategy: IX2LogExceptionStrategy;
|
FExceptionStrategy: IX2LogExceptionStrategy;
|
||||||
FObservers: TList<IX2LogObserver>;
|
|
||||||
private
|
private
|
||||||
property ExceptionStrategy: IX2LogExceptionStrategy read FExceptionStrategy;
|
property ExceptionStrategy: IX2LogExceptionStrategy read FExceptionStrategy;
|
||||||
property Observers: TList<IX2LogObserver> read FObservers;
|
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
|
||||||
|
|
||||||
{ IX2Log }
|
{ IX2Log }
|
||||||
procedure Attach(AObserver: IX2LogObserver);
|
|
||||||
procedure Detach(AObserver: IX2LogObserver);
|
|
||||||
|
|
||||||
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||||
|
|
||||||
{ IX2LogMethods }
|
|
||||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
|
||||||
|
|
||||||
procedure Verbose(const AMessage: string; const ADetails: string = '');
|
procedure Verbose(const AMessage: string; const ADetails: string = '');
|
||||||
procedure Info(const AMessage: string; const ADetails: string = '');
|
procedure Info(const AMessage: string; const ADetails: string = '');
|
||||||
procedure Warning(const AMessage: string; const ADetails: string = '');
|
procedure Warning(const AMessage: string; const ADetails: string = '');
|
||||||
@ -48,33 +40,10 @@ constructor TX2Log.Create;
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
|
|
||||||
FObservers := TList<IX2LogObserver>.Create;
|
|
||||||
SetExceptionStrategy(nil);
|
SetExceptionStrategy(nil);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
destructor TX2Log.Destroy;
|
|
||||||
begin
|
|
||||||
FreeAndNil(FObservers);
|
|
||||||
|
|
||||||
inherited Destroy;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TX2Log.Attach(AObserver: IX2LogObserver);
|
|
||||||
begin
|
|
||||||
{ Explicit cast ensures we're getting the same pointer in Attach and Detach
|
|
||||||
if, for example, the implementing interface is a descendant of IX2LogObserver }
|
|
||||||
Observers.Add(AObserver as IX2LogObserver);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TX2Log.Detach(AObserver: IX2LogObserver);
|
|
||||||
begin
|
|
||||||
Observers.Remove(AObserver as IX2LogObserver);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TX2Log.SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
procedure TX2Log.SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||||
begin
|
begin
|
||||||
if Assigned(AStrategy) then
|
if Assigned(AStrategy) then
|
||||||
@ -84,16 +53,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TX2Log.Log(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
|
||||||
var
|
|
||||||
observer: IX2LogObserver;
|
|
||||||
|
|
||||||
begin
|
|
||||||
for observer in Observers do
|
|
||||||
observer.Log(ALevel, AMessage, ADetails);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TX2Log.Verbose(const AMessage, ADetails: string);
|
procedure TX2Log.Verbose(const AMessage, ADetails: string);
|
||||||
begin
|
begin
|
||||||
Log(TX2LogLevel.Verbose, AMessage, ADetails);
|
Log(TX2LogLevel.Verbose, AMessage, ADetails);
|
||||||
|
Loading…
Reference in New Issue
Block a user