Fixed: FS#4 - Binary details are lost with the Named Pipe observer
Fixed: FS#5 - Reconnecting can cause pipe to be busy Fixed: FS#6 - Process hangs when closing after named pipe client has disconnected Fixed: themed drawing of toolbar in MonitorForm Fixed: various memory leaks Changed: NamedPipeClient test application uses MonitorForm observer to display log
This commit is contained in:
parent
a3fd535be7
commit
a9cf1b75f6
@ -1,17 +1,30 @@
|
|||||||
program X2LogNamedPipeClient;
|
program X2LogNamedPipeClient;
|
||||||
|
|
||||||
uses
|
uses
|
||||||
|
// FastMM4,
|
||||||
Vcl.Forms,
|
Vcl.Forms,
|
||||||
MainFrm in 'source\MainFrm.pas' {MainForm};
|
X2Log.Intf,
|
||||||
|
X2Log.Client.NamedPipe,
|
||||||
|
X2Log.Observer.MonitorForm;
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
var
|
var
|
||||||
MainForm: TMainForm;
|
client: IX2LogObservable;
|
||||||
|
observerForm: TX2LogObserverMonitorForm;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
ReportMemoryLeaksOnShutdown := True;
|
||||||
|
|
||||||
Application.Initialize;
|
Application.Initialize;
|
||||||
Application.MainFormOnTaskbar := True;
|
Application.MainFormOnTaskbar := True;
|
||||||
Application.CreateForm(TMainForm, MainForm);
|
Application.Title := 'X²Log Named Pipe Client';
|
||||||
Application.Run;
|
|
||||||
|
client := TX2LogNamedPipeClient.Create('X2LogTest');
|
||||||
|
try
|
||||||
|
observerForm := TX2LogObserverMonitorForm.Instance(client);
|
||||||
|
observerForm.ShowModal;
|
||||||
|
finally
|
||||||
|
client := nil;
|
||||||
|
end;
|
||||||
end.
|
end.
|
||||||
|
@ -84,10 +84,6 @@
|
|||||||
<DelphiCompile Include="$(MainSource)">
|
<DelphiCompile Include="$(MainSource)">
|
||||||
<MainSource>MainSource</MainSource>
|
<MainSource>MainSource</MainSource>
|
||||||
</DelphiCompile>
|
</DelphiCompile>
|
||||||
<DCCReference Include="source\MainFrm.pas">
|
|
||||||
<Form>MainForm</Form>
|
|
||||||
<FormType>dfm</FormType>
|
|
||||||
</DCCReference>
|
|
||||||
<BuildConfiguration Include="Release">
|
<BuildConfiguration Include="Release">
|
||||||
<Key>Cfg_2</Key>
|
<Key>Cfg_2</Key>
|
||||||
<CfgParent>Base</CfgParent>
|
<CfgParent>Base</CfgParent>
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
object MainForm: TMainForm
|
|
||||||
Left = 0
|
|
||||||
Top = 0
|
|
||||||
Caption = 'X'#178'Log Named Pipe Client'
|
|
||||||
ClientHeight = 443
|
|
||||||
ClientWidth = 552
|
|
||||||
Color = clBtnFace
|
|
||||||
Font.Charset = DEFAULT_CHARSET
|
|
||||||
Font.Color = clWindowText
|
|
||||||
Font.Height = -11
|
|
||||||
Font.Name = 'Tahoma'
|
|
||||||
Font.Style = []
|
|
||||||
OldCreateOrder = False
|
|
||||||
OnCreate = FormCreate
|
|
||||||
PixelsPerInch = 96
|
|
||||||
TextHeight = 13
|
|
||||||
object mmoLog: TMemo
|
|
||||||
Left = 0
|
|
||||||
Top = 0
|
|
||||||
Width = 552
|
|
||||||
Height = 443
|
|
||||||
Align = alClient
|
|
||||||
ReadOnly = True
|
|
||||||
ScrollBars = ssVertical
|
|
||||||
TabOrder = 0
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,52 +0,0 @@
|
|||||||
unit MainFrm;
|
|
||||||
|
|
||||||
interface
|
|
||||||
uses
|
|
||||||
System.Classes,
|
|
||||||
Vcl.Controls,
|
|
||||||
Vcl.Forms,
|
|
||||||
Vcl.StdCtrls,
|
|
||||||
|
|
||||||
X2Log.Intf;
|
|
||||||
|
|
||||||
|
|
||||||
type
|
|
||||||
TMainForm = class(TForm)
|
|
||||||
mmoLog: TMemo;
|
|
||||||
|
|
||||||
procedure FormCreate(Sender: TObject);
|
|
||||||
private
|
|
||||||
FClient: IX2LogObservable;
|
|
||||||
FObserver: IX2LogObserver;
|
|
||||||
protected
|
|
||||||
procedure DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
implementation
|
|
||||||
uses
|
|
||||||
System.SysUtils,
|
|
||||||
|
|
||||||
X2Log.Constants,
|
|
||||||
X2Log.Client.NamedPipe,
|
|
||||||
X2Log.Observer.Event;
|
|
||||||
|
|
||||||
|
|
||||||
{$R *.dfm}
|
|
||||||
|
|
||||||
|
|
||||||
{ TMainForm }
|
|
||||||
procedure TMainForm.FormCreate(Sender: TObject);
|
|
||||||
begin
|
|
||||||
FClient := TX2LogNamedPipeClient.Create('X2LogTest');
|
|
||||||
FObserver := TX2LogEventObserver.Create(DoLog);
|
|
||||||
FClient.Attach(FObserver);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
|
||||||
begin
|
|
||||||
mmoLog.Lines.Add(DateTimeToStr(Now) + ' ' + GetLogLevelText(Level) + ': ' + Msg + ' (' + Details + ')');
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
@ -1,6 +1,7 @@
|
|||||||
program X2LogTest;
|
program X2LogTest;
|
||||||
|
|
||||||
uses
|
uses
|
||||||
|
// FastMM4,
|
||||||
Forms,
|
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',
|
||||||
@ -17,7 +18,8 @@ uses
|
|||||||
X2Log.Global in '..\X2Log.Global.pas',
|
X2Log.Global in '..\X2Log.Global.pas',
|
||||||
X2Log.Client.NamedPipe in '..\X2Log.Client.NamedPipe.pas',
|
X2Log.Client.NamedPipe in '..\X2Log.Client.NamedPipe.pas',
|
||||||
X2Log.Client.Base in '..\X2Log.Client.Base.pas',
|
X2Log.Client.Base in '..\X2Log.Client.Base.pas',
|
||||||
X2Log.Details.Default in '..\X2Log.Details.Default.pas';
|
X2Log.Details.Default in '..\X2Log.Details.Default.pas',
|
||||||
|
X2Log.Details.Registry in '..\X2Log.Details.Registry.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
@ -186,12 +186,12 @@
|
|||||||
<DCCReference Include="..\X2Log.Observer.CustomThreaded.pas"/>
|
<DCCReference Include="..\X2Log.Observer.CustomThreaded.pas"/>
|
||||||
<DCCReference Include="..\X2Log.Observer.MonitorForm.pas">
|
<DCCReference Include="..\X2Log.Observer.MonitorForm.pas">
|
||||||
<Form>X2LogObserverMonitorForm</Form>
|
<Form>X2LogObserverMonitorForm</Form>
|
||||||
<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.NamedPipe.pas"/>
|
||||||
<DCCReference Include="..\X2Log.Client.Base.pas"/>
|
<DCCReference Include="..\X2Log.Client.Base.pas"/>
|
||||||
<DCCReference Include="..\X2Log.Details.Default.pas"/>
|
<DCCReference Include="..\X2Log.Details.Default.pas"/>
|
||||||
|
<DCCReference Include="..\X2Log.Details.Registry.pas"/>
|
||||||
<BuildConfiguration Include="Debug">
|
<BuildConfiguration Include="Debug">
|
||||||
<Key>Cfg_2</Key>
|
<Key>Cfg_2</Key>
|
||||||
<CfgParent>Base</CfgParent>
|
<CfgParent>Base</CfgParent>
|
||||||
|
@ -349,7 +349,7 @@ object MainForm: TMainForm
|
|||||||
Shape = bsTopLine
|
Shape = bsTopLine
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
object Panel1: TPanel
|
object pnlObservers: TPanel
|
||||||
AlignWithMargins = True
|
AlignWithMargins = True
|
||||||
Left = 8
|
Left = 8
|
||||||
Top = 144
|
Top = 144
|
||||||
@ -370,7 +370,7 @@ object MainForm: TMainForm
|
|||||||
Font.Style = [fsBold]
|
Font.Style = [fsBold]
|
||||||
ParentFont = False
|
ParentFont = False
|
||||||
TabOrder = 4
|
TabOrder = 4
|
||||||
object Bevel1: TBevel
|
object bvlObservers: TBevel
|
||||||
Left = 80
|
Left = 80
|
||||||
Top = 12
|
Top = 12
|
||||||
Width = 513
|
Width = 513
|
||||||
@ -384,7 +384,7 @@ object MainForm: TMainForm
|
|||||||
Left = 552
|
Left = 552
|
||||||
Top = 176
|
Top = 176
|
||||||
Bitmap = {
|
Bitmap = {
|
||||||
494C01010200140034000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
494C01010200140038000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||||
0000000000003600000028000000300000000C00000001002000000000000009
|
0000000000003600000028000000300000000C00000001002000000000000009
|
||||||
0000000000000000000000000000000000000000000000000000000000000000
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
0000000000000000000000000000000000000000000000000000000000000000
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -52,8 +52,8 @@ type
|
|||||||
tsBinary: TTabSheet;
|
tsBinary: TTabSheet;
|
||||||
pnlDispatch: TPanel;
|
pnlDispatch: TPanel;
|
||||||
bvlDispatch: TBevel;
|
bvlDispatch: TBevel;
|
||||||
Panel1: TPanel;
|
pnlObservers: TPanel;
|
||||||
Bevel1: TBevel;
|
bvlObservers: TBevel;
|
||||||
|
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
procedure FormDestroy(Sender: TObject);
|
procedure FormDestroy(Sender: TObject);
|
||||||
|
@ -17,6 +17,7 @@ type
|
|||||||
property WorkerThread: TThread read FWorkerThread;
|
property WorkerThread: TThread read FWorkerThread;
|
||||||
public
|
public
|
||||||
constructor Create(const APipeName: string);
|
constructor Create(const APipeName: string);
|
||||||
|
destructor Destroy; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -24,18 +25,21 @@ implementation
|
|||||||
uses
|
uses
|
||||||
System.SyncObjs,
|
System.SyncObjs,
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
|
System.Types,
|
||||||
Winapi.Windows,
|
Winapi.Windows,
|
||||||
|
|
||||||
X2Log.Details.Default;
|
X2Log.Details.Default,
|
||||||
|
X2Log.Details.Registry;
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TX2LogNamedPipeClientWorkerThread = class(TThread)
|
TX2LogNamedPipeClientWorkerThread = class(TThread)
|
||||||
private
|
private
|
||||||
FLog: IX2LogBase;
|
FClient: TX2LogNamedPipeClient;
|
||||||
FPipeName: string;
|
FPipeName: string;
|
||||||
|
|
||||||
FTerminateEvent: TEvent;
|
FTerminateEvent: TEvent;
|
||||||
|
FReadEvent: TEvent;
|
||||||
FPipeHandle: THandle;
|
FPipeHandle: THandle;
|
||||||
FOverlappedRead: TOverlapped;
|
FOverlappedRead: TOverlapped;
|
||||||
FReadBuffer: array[0..4095] of Byte;
|
FReadBuffer: array[0..4095] of Byte;
|
||||||
@ -51,14 +55,16 @@ type
|
|||||||
procedure ReadMessage;
|
procedure ReadMessage;
|
||||||
procedure HandleMessage;
|
procedure HandleMessage;
|
||||||
|
|
||||||
property Log: IX2LogBase read FLog;
|
property Client: TX2LogNamedPipeClient read FClient;
|
||||||
property PipeName: string read FPipeName;
|
property PipeName: string read FPipeName;
|
||||||
|
|
||||||
|
property ReadEvent: TEvent read FReadEvent;
|
||||||
property TerminateEvent: TEvent read FTerminateEvent;
|
property TerminateEvent: TEvent read FTerminateEvent;
|
||||||
property PipeHandle: THandle read FPipeHandle;
|
property PipeHandle: THandle read FPipeHandle;
|
||||||
property MessageData: TMemoryStream read FMessageData;
|
property MessageData: TMemoryStream read FMessageData;
|
||||||
public
|
public
|
||||||
constructor Create(ALog: IX2LogBase; const APipeName: string);
|
constructor Create(AClient: TX2LogNamedPipeClient; const APipeName: string);
|
||||||
|
destructor Destroy; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -80,19 +86,38 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
destructor TX2LogNamedPipeClient.Destroy;
|
||||||
|
begin
|
||||||
|
FreeAndNil(FWorkerThread);
|
||||||
|
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TX2LogNamedPipeClientWorkerThread }
|
{ TX2LogNamedPipeClientWorkerThread }
|
||||||
constructor TX2LogNamedPipeClientWorkerThread.Create(ALog: IX2LogBase; const APipeName: string);
|
constructor TX2LogNamedPipeClientWorkerThread.Create(AClient: TX2LogNamedPipeClient; const APipeName: string);
|
||||||
begin
|
begin
|
||||||
FTerminateEvent := TEvent.Create(nil, True, False, '');
|
FTerminateEvent := TEvent.Create(nil, True, False, '');
|
||||||
|
FReadEvent := TEvent.Create(nil, True, False, '');
|
||||||
FMessageData := TMemoryStream.Create;
|
FMessageData := TMemoryStream.Create;
|
||||||
|
|
||||||
FLog := ALog;
|
FClient := AClient;
|
||||||
FPipeName := APipeName;
|
FPipeName := APipeName;
|
||||||
|
|
||||||
inherited Create(False);
|
inherited Create(False);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
destructor TX2LogNamedPipeClientWorkerThread.Destroy;
|
||||||
|
begin
|
||||||
|
inherited Destroy;
|
||||||
|
|
||||||
|
FreeAndNil(FMessageData);
|
||||||
|
FreeAndNil(FReadEvent);
|
||||||
|
FreeAndNil(FTerminateEvent);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TX2LogNamedPipeClientWorkerThread.Execute;
|
procedure TX2LogNamedPipeClientWorkerThread.Execute;
|
||||||
begin
|
begin
|
||||||
FPipeHandle := INVALID_HANDLE_VALUE;
|
FPipeHandle := INVALID_HANDLE_VALUE;
|
||||||
@ -159,15 +184,13 @@ end;
|
|||||||
|
|
||||||
procedure TX2LogNamedPipeClientWorkerThread.ReadPipe;
|
procedure TX2LogNamedPipeClientWorkerThread.ReadPipe;
|
||||||
var
|
var
|
||||||
readEvent: TEvent;
|
|
||||||
events: array[0..1] of THandle;
|
events: array[0..1] of THandle;
|
||||||
waitResult: Cardinal;
|
waitResult: Cardinal;
|
||||||
bytesTransferred: Cardinal;
|
bytesTransferred: Cardinal;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
readEvent := TEvent.Create(nil, False, False, '');
|
|
||||||
events[0] := TerminateEvent.Handle;
|
events[0] := TerminateEvent.Handle;
|
||||||
events[1] := readEvent.Handle;
|
events[1] := ReadEvent.Handle;
|
||||||
|
|
||||||
FOverlappedRead.hEvent := readEvent.Handle;
|
FOverlappedRead.hEvent := readEvent.Handle;
|
||||||
ReadMessage;
|
ReadMessage;
|
||||||
@ -268,12 +291,17 @@ var
|
|||||||
header: TX2LogMessageHeaderV1;
|
header: TX2LogMessageHeaderV1;
|
||||||
headerDiff: Integer;
|
headerDiff: Integer;
|
||||||
msg: string;
|
msg: string;
|
||||||
details: string;
|
details: IX2LogDetails;
|
||||||
|
serializerIID: TGUID;
|
||||||
|
detailsSize: Cardinal;
|
||||||
|
detailsStream: TMemoryStream;
|
||||||
|
serializer: IX2LogDetailsSerializer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if MessageData.Size > 0 then
|
if MessageData.Size > 0 then
|
||||||
begin
|
begin
|
||||||
try
|
try
|
||||||
|
{ Header }
|
||||||
MessageData.Position := 0;
|
MessageData.Position := 0;
|
||||||
MessageData.ReadBuffer(header, SizeOf(header));
|
MessageData.ReadBuffer(header, SizeOf(header));
|
||||||
|
|
||||||
@ -288,11 +316,35 @@ begin
|
|||||||
end else if headerDiff < 0 then
|
end else if headerDiff < 0 then
|
||||||
raise EReadError.Create('Header too small');
|
raise EReadError.Create('Header too small');
|
||||||
|
|
||||||
|
{ Message }
|
||||||
msg := ReadString;
|
msg := ReadString;
|
||||||
details := ReadString;
|
|
||||||
|
|
||||||
// #ToDo1 named pipe support for non-string details
|
{ Details }
|
||||||
Log.Log(header.Level, msg, TX2LogStringDetails.CreateIfNotEmpty(details));
|
details := nil;
|
||||||
|
|
||||||
|
MessageData.ReadBuffer(serializerIID, SizeOf(TGUID));
|
||||||
|
if serializerIID <> GUID_NULL then
|
||||||
|
begin
|
||||||
|
MessageData.ReadBuffer(detailsSize, SizeOf(Cardinal));
|
||||||
|
if detailsSize > 0 then
|
||||||
|
begin
|
||||||
|
if TX2LogDetailsRegistry.GetSerializer(serializerIID, serializer) then
|
||||||
|
begin
|
||||||
|
detailsStream := TMemoryStream.Create;
|
||||||
|
try
|
||||||
|
detailsStream.CopyFrom(MessageData, detailsSize);
|
||||||
|
detailsStream.Position := 0;
|
||||||
|
|
||||||
|
details := serializer.Deserialize(detailsStream);
|
||||||
|
finally
|
||||||
|
FreeAndNil(detailsStream);
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
MessageData.Seek(detailsSize, soFromCurrent);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Client.Log(header.Level, msg, details);
|
||||||
except
|
except
|
||||||
on E:EReadError do
|
on E:EReadError do
|
||||||
ClosePipe;
|
ClosePipe;
|
||||||
|
@ -46,10 +46,10 @@ resourcestring
|
|||||||
{ Status messages }
|
{ Status messages }
|
||||||
LogMonitorFormStatusPaused = 'Paused: %d log message(s) skipped';
|
LogMonitorFormStatusPaused = 'Paused: %d log message(s) skipped';
|
||||||
|
|
||||||
|
{ Filter for Save details buttons }
|
||||||
LogMonitorFormSaveDetailsFilter = 'All files (*.*)|*.*';
|
LogMonitorFormSaveDetailsFilter = 'All files (*.*)|*.*';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function GetLogLevelText(ALogLevel: TX2LogLevel): string;
|
function GetLogLevelText(ALogLevel: TX2LogLevel): string;
|
||||||
|
|
||||||
function GetLogResourceString(AResourceString: Pointer): string;
|
function GetLogResourceString(AResourceString: Pointer): string;
|
||||||
@ -62,8 +62,11 @@ uses
|
|||||||
System.SysUtils;
|
System.SysUtils;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TResourceStringDictionary = TDictionary<Pointer,string>;
|
||||||
|
|
||||||
var
|
var
|
||||||
LogResourceStringMap: TDictionary<Pointer,string>;
|
LogResourceStringMap: TResourceStringDictionary;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -104,7 +107,7 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
LogResourceStringMap := TDictionary<Pointer,string>.Create;
|
LogResourceStringMap := TResourceStringDictionary.Create;
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
FreeAndNil(LogResourceStringMap);
|
FreeAndNil(LogResourceStringMap);
|
||||||
|
@ -17,6 +17,9 @@ type
|
|||||||
|
|
||||||
constructor Create(const AText: string);
|
constructor Create(const AText: string);
|
||||||
|
|
||||||
|
{ IX2LogDetails }
|
||||||
|
function GetSerializerIID: TGUID;
|
||||||
|
|
||||||
{ IX2LogDetailsText }
|
{ IX2LogDetailsText }
|
||||||
function GetAsString: string;
|
function GetAsString: string;
|
||||||
|
|
||||||
@ -36,10 +39,14 @@ type
|
|||||||
protected
|
protected
|
||||||
property Data: TStream read FData;
|
property Data: TStream read FData;
|
||||||
public
|
public
|
||||||
constructor Create(ACopyFrom: TStream); overload;
|
constructor Create; overload;
|
||||||
|
constructor Create(ACopyFrom: TStream; ACount: Integer = 0); overload;
|
||||||
constructor Create(AData: RawByteString); overload;
|
constructor Create(AData: RawByteString); overload;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
|
{ IX2LogDetails }
|
||||||
|
function GetSerializerIID: TGUID;
|
||||||
|
|
||||||
{ IX2LogDetailsBinary }
|
{ IX2LogDetailsBinary }
|
||||||
function GetAsStream: TStream;
|
function GetAsStream: TStream;
|
||||||
|
|
||||||
@ -51,7 +58,33 @@ type
|
|||||||
implementation
|
implementation
|
||||||
uses
|
uses
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
Vcl.ClipBrd;
|
Vcl.ClipBrd,
|
||||||
|
|
||||||
|
X2Log.Constants,
|
||||||
|
X2Log.Details.Registry;
|
||||||
|
|
||||||
|
|
||||||
|
const
|
||||||
|
StringDetailsSerializerIID: TGUID = '{4223C30E-6E80-4D66-9EDC-F8688A7413D2}';
|
||||||
|
BinaryDetailsSerializerIID: TGUID = '{05F6E8BD-118E-41B3-B626-1F190CC2A7D3}';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TX2LogStringDetailsSerializer = class(TInterfacedObject, IX2LogDetailsSerializer)
|
||||||
|
public
|
||||||
|
{ IX2LogDetailsSerializer }
|
||||||
|
procedure Serialize(ADetails: IX2LogDetails; AStream: TStream);
|
||||||
|
function Deserialize(AStream: TStream): IX2LogDetails;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
TX2LogBinaryDetailsSerializer = class(TInterfacedObject, IX2LogDetailsSerializer)
|
||||||
|
public
|
||||||
|
{ IX2LogDetailsSerializer }
|
||||||
|
procedure Serialize(ADetails: IX2LogDetails; AStream: TStream);
|
||||||
|
function Deserialize(AStream: TStream): IX2LogDetails;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TX2LogStringDetails }
|
{ TX2LogStringDetails }
|
||||||
@ -72,6 +105,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TX2LogStringDetails.GetSerializerIID: TGUID;
|
||||||
|
begin
|
||||||
|
Result := StringDetailsSerializerIID;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TX2LogStringDetails.GetAsString: string;
|
function TX2LogStringDetails.GetAsString: string;
|
||||||
begin
|
begin
|
||||||
Result := FText;
|
Result := FText;
|
||||||
@ -86,25 +125,33 @@ end;
|
|||||||
|
|
||||||
procedure TX2LogStringDetails.SaveToStream(AStream: TStream);
|
procedure TX2LogStringDetails.SaveToStream(AStream: TStream);
|
||||||
var
|
var
|
||||||
textStream: TStringStream;
|
writer: TStreamWriter;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
textStream := TStringStream.Create(GetAsString, TEncoding.ANSI, False);
|
writer := TStreamWriter.Create(AStream, TEncoding.UTF8);
|
||||||
try
|
try
|
||||||
AStream.CopyFrom(textStream, 0);
|
writer.Write(GetAsString);
|
||||||
finally
|
finally
|
||||||
FreeAndNil(textStream);
|
FreeAndNil(writer);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TX2LogBinaryDetails }
|
{ TX2LogBinaryDetails }
|
||||||
constructor TX2LogBinaryDetails.Create(ACopyFrom: TStream);
|
constructor TX2LogBinaryDetails.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
|
|
||||||
FData := TMemoryStream.Create;
|
FData := TMemoryStream.Create;
|
||||||
FData.CopyFrom(ACopyFrom, ACopyFrom.Size - ACopyFrom.Position);
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
constructor TX2LogBinaryDetails.Create(ACopyFrom: TStream; ACount: Integer);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
|
||||||
|
FData := TMemoryStream.Create;
|
||||||
|
FData.CopyFrom(ACopyFrom, ACount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -124,6 +171,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TX2LogBinaryDetails.GetSerializerIID: TGUID;
|
||||||
|
begin
|
||||||
|
Result := BinaryDetailsSerializerIID;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TX2LogBinaryDetails.GetAsStream: TStream;
|
function TX2LogBinaryDetails.GetAsStream: TStream;
|
||||||
begin
|
begin
|
||||||
Data.Position := 0;
|
Data.Position := 0;
|
||||||
@ -136,4 +189,77 @@ begin
|
|||||||
AStream.CopyFrom(Data, 0);
|
AStream.CopyFrom(Data, 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2LogStringDetailsSerializer }
|
||||||
|
procedure TX2LogStringDetailsSerializer.Serialize(ADetails: IX2LogDetails; AStream: TStream);
|
||||||
|
var
|
||||||
|
bytes: TBytes;
|
||||||
|
bytesLength: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
bytes := TEncoding.UTF8.GetBytes((ADetails as IX2LogDetailsText).AsString);
|
||||||
|
bytesLength := Length(bytes);
|
||||||
|
|
||||||
|
AStream.WriteBuffer(bytesLength, SizeOf(Cardinal));
|
||||||
|
if bytesLength > 0 then
|
||||||
|
AStream.WriteBuffer(bytes[0], bytesLength);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TX2LogStringDetailsSerializer.Deserialize(AStream: TStream): IX2LogDetails;
|
||||||
|
var
|
||||||
|
bytes: TBytes;
|
||||||
|
bytesLength: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
AStream.ReadBuffer(bytesLength, SizeOf(Cardinal));
|
||||||
|
if bytesLength > 0 then
|
||||||
|
begin
|
||||||
|
SetLength(bytes, bytesLength);
|
||||||
|
AStream.ReadBuffer(bytes[0], bytesLength);
|
||||||
|
|
||||||
|
Result := TX2LogStringDetails.Create(TEncoding.UTF8.GetString(bytes));
|
||||||
|
end else
|
||||||
|
{ Do not return nil; the fact that Deserialize is called means an
|
||||||
|
empty Details was serialized. }
|
||||||
|
Result := TX2LogStringDetails.Create('');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2LogBinaryDetailsSerializer }
|
||||||
|
procedure TX2LogBinaryDetailsSerializer.Serialize(ADetails: IX2LogDetails; AStream: TStream);
|
||||||
|
var
|
||||||
|
stream: TStream;
|
||||||
|
streamSize: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
stream := (ADetails as IX2LogDetailsBinary).AsStream;
|
||||||
|
streamSize := stream.Size;
|
||||||
|
|
||||||
|
AStream.WriteBuffer(streamSize, SizeOf(Cardinal));
|
||||||
|
if streamSize > 0 then
|
||||||
|
AStream.CopyFrom(stream, streamSize);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TX2LogBinaryDetailsSerializer.Deserialize(AStream: TStream): IX2LogDetails;
|
||||||
|
var
|
||||||
|
streamSize: Cardinal;
|
||||||
|
|
||||||
|
begin
|
||||||
|
AStream.ReadBuffer(streamSize, SizeOf(Cardinal));
|
||||||
|
if streamSize > 0 then
|
||||||
|
Result := TX2LogBinaryDetails.Create(AStream, streamSize)
|
||||||
|
else
|
||||||
|
{ Do not return nil; the fact that Deserialize is called means an
|
||||||
|
empty Details was serialized. }
|
||||||
|
Result := TX2LogBinaryDetails.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
initialization
|
||||||
|
TX2LogDetailsRegistry.Register(StringDetailsSerializerIID, TX2LogStringDetailsSerializer.Create);
|
||||||
|
TX2LogDetailsRegistry.Register(BinaryDetailsSerializerIID, TX2LogBinaryDetailsSerializer.Create);
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
81
X2Log.Details.Registry.pas
Normal file
81
X2Log.Details.Registry.pas
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
unit X2Log.Details.Registry;
|
||||||
|
|
||||||
|
interface
|
||||||
|
uses
|
||||||
|
System.Generics.Collections,
|
||||||
|
|
||||||
|
X2Log.Intf;
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TSerializerDictionary = TDictionary<TGUID, IX2LogDetailsSerializer>;
|
||||||
|
|
||||||
|
TX2LogDetailsRegistry = class(TObject)
|
||||||
|
private
|
||||||
|
class var FSerializers: TSerializerDictionary;
|
||||||
|
protected
|
||||||
|
class function Serializers: TSerializerDictionary;
|
||||||
|
|
||||||
|
class procedure CleanupSerializers;
|
||||||
|
public
|
||||||
|
class procedure Register(AIID: TGUID; ASerializer: IX2LogDetailsSerializer);
|
||||||
|
class procedure Unregister(AIID: TGUID);
|
||||||
|
|
||||||
|
class function GetSerializer(ASerializerIID: TGUID; out ASerializer: IX2LogDetailsSerializer): Boolean; overload;
|
||||||
|
class function GetSerializer(ADetails: IX2LogDetails; out ASerializer: IX2LogDetailsSerializer): Boolean; overload;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
implementation
|
||||||
|
uses
|
||||||
|
System.SysUtils,
|
||||||
|
System.Types;
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2LogDetailsRegistry }
|
||||||
|
class procedure TX2LogDetailsRegistry.Register(AIID: TGUID; ASerializer: IX2LogDetailsSerializer);
|
||||||
|
begin
|
||||||
|
Serializers.Add(AIID, ASerializer);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
class function TX2LogDetailsRegistry.GetSerializer(ADetails: IX2LogDetails; out ASerializer: IX2LogDetailsSerializer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Assigned(ADetails) and
|
||||||
|
(ADetails.SerializerIID <> GUID_NULL) and
|
||||||
|
GetSerializer(ADetails.SerializerIID, ASerializer);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
class procedure TX2LogDetailsRegistry.Unregister(AIID: TGUID);
|
||||||
|
begin
|
||||||
|
Serializers.Remove(AIID);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
class function TX2LogDetailsRegistry.GetSerializer(ASerializerIID: TGUID; out ASerializer: IX2LogDetailsSerializer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Serializers.TryGetValue(ASerializerIID, ASerializer);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
class function TX2LogDetailsRegistry.Serializers: TSerializerDictionary;
|
||||||
|
begin
|
||||||
|
if not Assigned(FSerializers) then
|
||||||
|
FSerializers := TSerializerDictionary.Create;
|
||||||
|
|
||||||
|
Result := FSerializers;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
class procedure TX2LogDetailsRegistry.CleanupSerializers;
|
||||||
|
begin
|
||||||
|
FreeAndNil(FSerializers);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
initialization
|
||||||
|
finalization
|
||||||
|
TX2LogDetailsRegistry.CleanupSerializers;
|
||||||
|
|
||||||
|
end.
|
@ -10,8 +10,8 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
TX2GlobalLog = class(TObject)
|
TX2GlobalLog = class(TObject)
|
||||||
private class var
|
private
|
||||||
FInstance: IX2Log;
|
class var FInstance: IX2Log;
|
||||||
protected
|
protected
|
||||||
class procedure CleanupInstance;
|
class procedure CleanupInstance;
|
||||||
public
|
public
|
||||||
|
@ -19,6 +19,16 @@ type
|
|||||||
{ Details }
|
{ Details }
|
||||||
IX2LogDetails = interface
|
IX2LogDetails = interface
|
||||||
['{86F24F52-CE1F-4A79-936F-A5805D84E18A}']
|
['{86F24F52-CE1F-4A79-936F-A5805D84E18A}']
|
||||||
|
function GetSerializerIID: TGUID;
|
||||||
|
|
||||||
|
property SerializerIID: TGUID read GetSerializerIID;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
IX2LogDetailsSerializer = interface
|
||||||
|
['{E75F6F4E-A671-4622-878C-F59C64FB1320}']
|
||||||
|
procedure Serialize(ADetails: IX2LogDetails; AStream: TStream);
|
||||||
|
function Deserialize(AStream: TStream): IX2LogDetails;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
List = True
|
List = True
|
||||||
ShowCaptions = True
|
ShowCaptions = True
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
|
OnCustomDraw = ToolbarCustomDraw
|
||||||
object tbCopyDetails: TToolButton
|
object tbCopyDetails: TToolButton
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
@ -163,6 +164,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
List = True
|
List = True
|
||||||
ShowCaptions = True
|
ShowCaptions = True
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
|
OnCustomDraw = ToolbarCustomDraw
|
||||||
object tbClear: TToolButton
|
object tbClear: TToolButton
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
@ -194,7 +196,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm
|
|||||||
Left = 448
|
Left = 448
|
||||||
Top = 48
|
Top = 48
|
||||||
Bitmap = {
|
Bitmap = {
|
||||||
494C010109004000940010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
494C0101090040009C0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||||
0000000000003600000028000000400000003000000001002000000000000030
|
0000000000003600000028000000400000003000000001002000000000000030
|
||||||
0000000000000000000000000000000000000000000000000000000000000000
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
0000000000000000000000000000000000000000000000000000000000000000
|
0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -4,6 +4,7 @@ interface
|
|||||||
uses
|
uses
|
||||||
System.Classes,
|
System.Classes,
|
||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
|
System.Types,
|
||||||
Vcl.ActnList,
|
Vcl.ActnList,
|
||||||
Vcl.ComCtrls,
|
Vcl.ComCtrls,
|
||||||
Vcl.Controls,
|
Vcl.Controls,
|
||||||
@ -24,6 +25,10 @@ const
|
|||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
TX2LogObserverMonitorForm = class;
|
||||||
|
TMonitorFormDictionary = TObjectDictionary<IX2LogObservable,TX2LogObserverMonitorForm>;
|
||||||
|
|
||||||
|
|
||||||
TX2LogObserverMonitorForm = class(TForm, IX2LogObserver)
|
TX2LogObserverMonitorForm = class(TForm, IX2LogObserver)
|
||||||
vstLog: TVirtualStringTree;
|
vstLog: TVirtualStringTree;
|
||||||
ilsLog: TImageList;
|
ilsLog: TImageList;
|
||||||
@ -59,18 +64,19 @@ type
|
|||||||
procedure actCopyDetailsExecute(Sender: TObject);
|
procedure actCopyDetailsExecute(Sender: TObject);
|
||||||
procedure actSaveDetailsExecute(Sender: TObject);
|
procedure actSaveDetailsExecute(Sender: TObject);
|
||||||
procedure actPauseExecute(Sender: TObject);
|
procedure actPauseExecute(Sender: TObject);
|
||||||
|
procedure ToolbarCustomDraw(Sender: TToolBar; const ARect: TRect; var DefaultDraw: Boolean);
|
||||||
private class var
|
private class var
|
||||||
FInstances: TObjectDictionary<IX2Log,TX2LogObserverMonitorForm>;
|
FInstances: TMonitorFormDictionary;
|
||||||
private
|
private
|
||||||
FFreeOnClose: Boolean;
|
FFreeOnClose: Boolean;
|
||||||
FLogToAttach: IX2Log;
|
FLogObservable: IX2LogObservable;
|
||||||
FLogAttached: Boolean;
|
FLogAttached: Boolean;
|
||||||
FPausedLogCount: Integer;
|
FPausedLogCount: Integer;
|
||||||
FDetails: IX2LogDetails;
|
FDetails: IX2LogDetails;
|
||||||
|
|
||||||
function GetPaused: Boolean;
|
function GetPaused: Boolean;
|
||||||
protected
|
protected
|
||||||
class function GetInstance(ALog: IX2Log; out AForm: TX2LogObserverMonitorForm): Boolean;
|
class function GetInstance(ALog: IX2LogObservable; out AForm: TX2LogObserverMonitorForm): Boolean;
|
||||||
class procedure RemoveInstance(AForm: TX2LogObserverMonitorForm);
|
class procedure RemoveInstance(AForm: TX2LogObserverMonitorForm);
|
||||||
class procedure CleanupInstances;
|
class procedure CleanupInstances;
|
||||||
|
|
||||||
@ -86,17 +92,17 @@ type
|
|||||||
procedure SetBinaryDetails(ADetails: IX2LogDetailsBinary);
|
procedure SetBinaryDetails(ADetails: IX2LogDetailsBinary);
|
||||||
|
|
||||||
property Details: IX2LogDetails read FDetails;
|
property Details: IX2LogDetails read FDetails;
|
||||||
property LogToAttach: IX2Log read FLogToAttach;
|
property LogObservable: IX2LogObservable read FLogObservable;
|
||||||
property LogAttached: Boolean read FLogAttached;
|
property LogAttached: Boolean read FLogAttached;
|
||||||
property Paused: Boolean read GetPaused;
|
property Paused: Boolean read GetPaused;
|
||||||
property PausedLogCount: Integer read FPausedLogCount write FPausedLogCount;
|
property PausedLogCount: Integer read FPausedLogCount write FPausedLogCount;
|
||||||
public
|
public
|
||||||
class function Instance(ALog: IX2Log): TX2LogObserverMonitorForm;
|
class function Instance(ALog: IX2LogObservable): TX2LogObserverMonitorForm;
|
||||||
|
|
||||||
class procedure ShowInstance(ALog: IX2Log);
|
class procedure ShowInstance(ALog: IX2LogObservable);
|
||||||
class procedure CloseInstance(ALog: IX2Log);
|
class procedure CloseInstance(ALog: IX2LogObservable);
|
||||||
|
|
||||||
constructor Create(AOwner: TComponent; ALogToAttach: IX2Log = nil); reintroduce;
|
constructor Create(AOwner: TComponent; ALogObservable: IX2LogObservable = nil); reintroduce;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
{ IX2LogObserver }
|
{ IX2LogObserver }
|
||||||
@ -112,6 +118,7 @@ uses
|
|||||||
System.Math,
|
System.Math,
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
Vcl.Clipbrd,
|
Vcl.Clipbrd,
|
||||||
|
Vcl.Themes,
|
||||||
Winapi.Windows,
|
Winapi.Windows,
|
||||||
|
|
||||||
X2Log.Constants;
|
X2Log.Constants;
|
||||||
@ -150,17 +157,17 @@ end;
|
|||||||
|
|
||||||
|
|
||||||
{ TX2LogObserverMonitorForm }
|
{ TX2LogObserverMonitorForm }
|
||||||
class function TX2LogObserverMonitorForm.Instance(ALog: IX2Log): TX2LogObserverMonitorForm;
|
class function TX2LogObserverMonitorForm.Instance(ALog: IX2LogObservable): TX2LogObserverMonitorForm;
|
||||||
var
|
var
|
||||||
log: IX2Log;
|
log: IX2LogObservable;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
{ Explicit cast ensures we're getting the same pointer every time if, for example,
|
{ Explicit cast ensures we're getting the same pointer every time if, for example,
|
||||||
the implementing interface is a descendant of IX2Log }
|
the implementing interface is a descendant of IX2Log }
|
||||||
log := (ALog as IX2Log);
|
log := (ALog as IX2LogObservable);
|
||||||
|
|
||||||
if not Assigned(FInstances) then
|
if not Assigned(FInstances) then
|
||||||
FInstances := TObjectDictionary<IX2Log,TX2LogObserverMonitorForm>.Create([doOwnsValues]);
|
FInstances := TMonitorFormDictionary.Create([doOwnsValues]);
|
||||||
|
|
||||||
if not FInstances.TryGetValue(log, Result) then
|
if not FInstances.TryGetValue(log, Result) then
|
||||||
begin
|
begin
|
||||||
@ -172,13 +179,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
class procedure TX2LogObserverMonitorForm.ShowInstance(ALog: IX2Log);
|
class procedure TX2LogObserverMonitorForm.ShowInstance(ALog: IX2LogObservable);
|
||||||
begin
|
begin
|
||||||
Instance(ALog).Show;
|
Instance(ALog).Show;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
class procedure TX2LogObserverMonitorForm.CloseInstance(ALog: IX2Log);
|
class procedure TX2LogObserverMonitorForm.CloseInstance(ALog: IX2LogObservable);
|
||||||
var
|
var
|
||||||
monitorForm: TX2LogObserverMonitorForm;
|
monitorForm: TX2LogObserverMonitorForm;
|
||||||
|
|
||||||
@ -188,7 +195,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
class function TX2LogObserverMonitorForm.GetInstance(ALog: IX2Log; out AForm: TX2LogObserverMonitorForm): Boolean;
|
class function TX2LogObserverMonitorForm.GetInstance(ALog: IX2LogObservable; out AForm: TX2LogObserverMonitorForm): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
|
|
||||||
@ -199,7 +206,7 @@ end;
|
|||||||
|
|
||||||
class procedure TX2LogObserverMonitorForm.RemoveInstance(AForm: TX2LogObserverMonitorForm);
|
class procedure TX2LogObserverMonitorForm.RemoveInstance(AForm: TX2LogObserverMonitorForm);
|
||||||
var
|
var
|
||||||
log: IX2Log;
|
log: IX2LogObservable;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if Assigned(FInstances) then
|
if Assigned(FInstances) then
|
||||||
@ -223,14 +230,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
constructor TX2LogObserverMonitorForm.Create(AOwner: TComponent; ALogToAttach: IX2Log);
|
constructor TX2LogObserverMonitorForm.Create(AOwner: TComponent; ALogObservable: IX2LogObservable);
|
||||||
var
|
var
|
||||||
captionFormat: string;
|
captionFormat: string;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
|
|
||||||
FLogToAttach := ALogToAttach;
|
FLogObservable := ALogObservable;
|
||||||
|
|
||||||
captionFormat := GetLogResourceString(@LogMonitorFormCaption);
|
captionFormat := GetLogResourceString(@LogMonitorFormCaption);
|
||||||
if Pos('%s', captionFormat) > 0 then
|
if Pos('%s', captionFormat) > 0 then
|
||||||
@ -262,8 +269,8 @@ end;
|
|||||||
|
|
||||||
destructor TX2LogObserverMonitorForm.Destroy;
|
destructor TX2LogObserverMonitorForm.Destroy;
|
||||||
begin
|
begin
|
||||||
if Assigned(FLogToAttach) and FLogAttached then
|
if Assigned(FLogObservable) and FLogAttached then
|
||||||
FLogToAttach.Detach(Self);
|
FLogObservable.Detach(Self);
|
||||||
|
|
||||||
RemoveInstance(Self);
|
RemoveInstance(Self);
|
||||||
|
|
||||||
@ -273,9 +280,9 @@ end;
|
|||||||
|
|
||||||
procedure TX2LogObserverMonitorForm.FormShow(Sender: TObject);
|
procedure TX2LogObserverMonitorForm.FormShow(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
if Assigned(FLogToAttach) and (not FLogAttached) then
|
if Assigned(FLogObservable) and (not FLogAttached) then
|
||||||
begin
|
begin
|
||||||
FLogToAttach.Attach(Self);
|
FLogObservable.Attach(Self);
|
||||||
FLogAttached := True;
|
FLogAttached := True;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -283,9 +290,9 @@ end;
|
|||||||
|
|
||||||
procedure TX2LogObserverMonitorForm.FormClose(Sender: TObject; var Action: TCloseAction);
|
procedure TX2LogObserverMonitorForm.FormClose(Sender: TObject; var Action: TCloseAction);
|
||||||
begin
|
begin
|
||||||
if Assigned(FLogToAttach) and FLogAttached then
|
if Assigned(FLogObservable) and FLogAttached then
|
||||||
begin
|
begin
|
||||||
FLogToAttach.Detach(Self);
|
FLogObservable.Detach(Self);
|
||||||
FLogAttached := False;
|
FLogAttached := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -628,6 +635,24 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2LogObserverMonitorForm.ToolbarCustomDraw(Sender: TToolBar; const ARect: TRect; var DefaultDraw: Boolean);
|
||||||
|
var
|
||||||
|
element: TThemedElementDetails;
|
||||||
|
rect: TRect;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if StyleServices.Enabled then
|
||||||
|
begin
|
||||||
|
rect := Sender.ClientRect;
|
||||||
|
if Assigned(Self.Menu) then
|
||||||
|
Dec(rect.Top, GetSystemMetrics(SM_CYMENU));
|
||||||
|
|
||||||
|
element := StyleServices.GetElementDetails(trRebarRoot);
|
||||||
|
StyleServices.DrawElement(Sender.Canvas.Handle, element, rect);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
finalization
|
finalization
|
||||||
TX2LogObserverMonitorForm.CleanupInstances;
|
TX2LogObserverMonitorForm.CleanupInstances;
|
||||||
|
@ -24,7 +24,10 @@ uses
|
|||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
System.SyncObjs,
|
System.SyncObjs,
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
Winapi.Windows;
|
System.Types,
|
||||||
|
Winapi.Windows,
|
||||||
|
|
||||||
|
X2Log.Details.Registry;
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -128,11 +131,9 @@ end;
|
|||||||
|
|
||||||
destructor TX2LogNamedPipeClient.Destroy;
|
destructor TX2LogNamedPipeClient.Destroy;
|
||||||
begin
|
begin
|
||||||
|
Disconnect;
|
||||||
|
|
||||||
FreeAndNil(FOverlappedEvent);
|
FreeAndNil(FOverlappedEvent);
|
||||||
|
|
||||||
if PipeHandle <> INVALID_HANDLE_VALUE then
|
|
||||||
DisconnectNamedPipe(PipeHandle);
|
|
||||||
|
|
||||||
ClearWriteBuffer;
|
ClearWriteBuffer;
|
||||||
|
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
@ -185,6 +186,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
CancelIo(PipeHandle);
|
CancelIo(PipeHandle);
|
||||||
DisconnectNamedPipe(PipeHandle);
|
DisconnectNamedPipe(PipeHandle);
|
||||||
|
CloseHandle(PipeHandle);
|
||||||
|
|
||||||
FPipeHandle := INVALID_HANDLE_VALUE;
|
FPipeHandle := INVALID_HANDLE_VALUE;
|
||||||
end;
|
end;
|
||||||
@ -208,26 +210,49 @@ var
|
|||||||
header: TX2LogMessageHeader;
|
header: TX2LogMessageHeader;
|
||||||
bytesWritten: Cardinal;
|
bytesWritten: Cardinal;
|
||||||
lastError: Cardinal;
|
lastError: Cardinal;
|
||||||
logDetailsText: IX2LogDetailsText;
|
detailsSize: Cardinal;
|
||||||
|
detailsStream: TMemoryStream;
|
||||||
|
serializerIID: TGUID;
|
||||||
|
serializer: IX2LogDetailsSerializer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
ClearWriteBuffer;
|
ClearWriteBuffer;
|
||||||
|
|
||||||
FWriteBuffer := TMemoryStream.Create;
|
FWriteBuffer := TMemoryStream.Create;
|
||||||
|
|
||||||
|
{ Header }
|
||||||
header.ID := X2LogMessageHeader;
|
header.ID := X2LogMessageHeader;
|
||||||
header.Version := X2LogMessageVersion;
|
header.Version := X2LogMessageVersion;
|
||||||
header.Size := SizeOf(header);
|
header.Size := SizeOf(header);
|
||||||
header.Level := AEntry.Level;
|
header.Level := AEntry.Level;
|
||||||
|
|
||||||
WriteBuffer.WriteBuffer(header, SizeOf(header));
|
WriteBuffer.WriteBuffer(header, SizeOf(header));
|
||||||
|
|
||||||
|
{ Message }
|
||||||
WriteString(AEntry.Message);
|
WriteString(AEntry.Message);
|
||||||
|
|
||||||
// #ToDo1 support for non-string details
|
{ Details }
|
||||||
if Supports(AEntry.Details, IX2LogDetailsText, logDetailsText) then
|
if TX2LogDetailsRegistry.GetSerializer(AEntry.Details, serializer) then
|
||||||
WriteString(logDetailsText.AsString)
|
begin
|
||||||
else
|
detailsStream := TMemoryStream.Create;
|
||||||
WriteString('');
|
try
|
||||||
|
serializer.Serialize(AEntry.Details, detailsStream);
|
||||||
|
|
||||||
|
serializerIID := AEntry.Details.SerializerIID;
|
||||||
|
WriteBuffer.WriteBuffer(serializerIID, SizeOf(TGUID));
|
||||||
|
|
||||||
|
detailsSize := detailsStream.Size;
|
||||||
|
WriteBuffer.WriteBuffer(detailsSize, SizeOf(Cardinal));
|
||||||
|
WriteBuffer.CopyFrom(detailsStream, 0);
|
||||||
|
finally
|
||||||
|
FreeAndNil(detailsStream);
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
serializerIID := GUID_NULL;
|
||||||
|
WriteBuffer.WriteBuffer(serializerIID, SizeOf(TGUID));
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
Result := WriteFile(Pipe, WriteBuffer.Memory^, WriteBuffer.Size, bytesWritten, @Overlapped);
|
Result := WriteFile(Pipe, WriteBuffer.Memory^, WriteBuffer.Size, bytesWritten, @Overlapped);
|
||||||
if not Result then
|
if not Result then
|
||||||
@ -412,18 +437,19 @@ begin
|
|||||||
{ Thanks to: http://www.osronline.com/showthread.cfm?link=204207
|
{ Thanks to: http://www.osronline.com/showthread.cfm?link=204207
|
||||||
and: http://www.netid.washington.edu/documentation/domains/sddl.aspx
|
and: http://www.netid.washington.edu/documentation/domains/sddl.aspx
|
||||||
|
|
||||||
0x12018d =
|
0x12019f =
|
||||||
0x00100000 - SYNCHRONIZE
|
0x00100000 - SYNCHRONIZE
|
||||||
0x00020000 - READ_CONTROL
|
0x00020000 - READ_CONTROL
|
||||||
0x00000100 - FILE_WRITE_ATTRIBUTES
|
0x00000100 - FILE_WRITE_ATTRIBUTES
|
||||||
0x00000080 - FILE_READ_ATTRIBUTES
|
0x00000080 - FILE_READ_ATTRIBUTES
|
||||||
|
0x00000010 - FILE_WRITE_EA
|
||||||
0x00000008 - FILE_READ_EA
|
0x00000008 - FILE_READ_EA
|
||||||
0x00000004 - FILE_CREATE_PIPE_INSTANCE
|
0x00000004 - FILE_CREATE_PIPE_INSTANCE
|
||||||
|
0x00000002 - FILE_WRITE_DATA
|
||||||
0x00000001 - FILE_READ_DATA }
|
0x00000001 - FILE_READ_DATA }
|
||||||
if ConvertStringSecurityDescriptorToSecurityDescriptorW('D:' + // Discretionary ACL
|
if ConvertStringSecurityDescriptorToSecurityDescriptorW('D:' + // Discretionary ACL
|
||||||
'(D;;FA;;;NU)' + // Deny file all access (FA) to network user access (NU)
|
'(D;;FA;;;NU)' + // Deny file all access (FA) to network user access (NU)
|
||||||
'(A;;0x12018d;;;WD)' + // Allow specific permissions for everyone (WD)
|
'(A;;0x12019f;;;WD)', // Allow permissions for everyone (WD)
|
||||||
'(A;;0x12018d;;;CO)', // Allow specific permissions for creator owner (CO)
|
|
||||||
SDDL_REVISION_1,
|
SDDL_REVISION_1,
|
||||||
@security.lpSecurityDescriptor,
|
@security.lpSecurityDescriptor,
|
||||||
nil) then
|
nil) then
|
||||||
@ -452,6 +478,8 @@ begin
|
|||||||
begin
|
begin
|
||||||
client.State := Connected;
|
client.State := Connected;
|
||||||
Clients.Add(client);
|
Clients.Add(client);
|
||||||
|
|
||||||
|
AddListener;
|
||||||
end;
|
end;
|
||||||
else
|
else
|
||||||
{ Error occured }
|
{ Error occured }
|
||||||
|
Loading…
Reference in New Issue
Block a user