diff --git a/Test/X2LogTest.dpr b/Test/X2LogTest.dpr index 6fcbb59..7c82113 100644 --- a/Test/X2LogTest.dpr +++ b/Test/X2LogTest.dpr @@ -1,7 +1,8 @@ program X2LogTest; +{$R *.dres} + uses -// FastMM4, Forms, MainFrm in 'source\MainFrm.pas' {MainForm}, X2Log.Intf in '..\X2Log.Intf.pas', @@ -19,7 +20,9 @@ uses X2Log.Client.NamedPipe in '..\X2Log.Client.NamedPipe.pas', X2Log.Client.Base in '..\X2Log.Client.Base.pas', X2Log.Details.Default in '..\X2Log.Details.Default.pas', - X2Log.Details.Registry in '..\X2Log.Details.Registry.pas'; + X2Log.Details.Registry in '..\X2Log.Details.Registry.pas', + X2Log.Details.Intf in '..\X2Log.Details.Intf.pas', + X2Log.Util.Stream in '..\X2Log.Util.Stream.pas'; {$R *.res} diff --git a/Test/X2LogTest.dproj b/Test/X2LogTest.dproj index f7074ff..abc09a7 100644 --- a/Test/X2LogTest.dproj +++ b/Test/X2LogTest.dproj @@ -192,6 +192,12 @@ + + + + RCDATA + GraphicDetails + Cfg_2 Base diff --git a/Test/X2LogTest.dres b/Test/X2LogTest.dres new file mode 100644 index 0000000..b41465d Binary files /dev/null and b/Test/X2LogTest.dres differ diff --git a/Test/X2LogTestResource.rc b/Test/X2LogTestResource.rc new file mode 100644 index 0000000..337c6b6 --- /dev/null +++ b/Test/X2LogTestResource.rc @@ -0,0 +1 @@ +GraphicDetails RCDATA "resources\\Graphic.jpg" diff --git a/Test/resources/Graphic.jpg b/Test/resources/Graphic.jpg new file mode 100644 index 0000000..61ce5ab Binary files /dev/null and b/Test/resources/Graphic.jpg differ diff --git a/Test/source/MainFrm.dfm b/Test/source/MainFrm.dfm index 5ad1a81..330e329 100644 --- a/Test/source/MainFrm.dfm +++ b/Test/source/MainFrm.dfm @@ -211,7 +211,7 @@ object MainForm: TMainForm Margins.Top = 8 Margins.Right = 8 Margins.Bottom = 0 - ActivePage = tsText + ActivePage = tsBinary Align = alTop TabOrder = 2 object tsText: TTabSheet @@ -309,14 +309,23 @@ object MainForm: TMainForm object tsBinary: TTabSheet Caption = 'Binary' ImageIndex = 2 - object btnBinary: TButton + object btnBinaryRawByteString: TButton Left = 12 - Top = 23 - Width = 62 + Top = 15 + Width = 152 Height = 21 - Caption = 'Binary' + Caption = 'Binary (RawByteString)' TabOrder = 0 - OnClick = btnLogClick + OnClick = btnBinaryRawByteStringClick + end + object btnGraphic: TButton + Left = 170 + Top = 15 + Width = 79 + Height = 21 + Caption = 'Graphic' + TabOrder = 1 + OnClick = btnGraphicClick end end end @@ -384,7 +393,7 @@ object MainForm: TMainForm Left = 552 Top = 176 Bitmap = { - 494C0101020014003C000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 494C01010200140044000C000C00FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000300000000C00000001002000000000000009 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 diff --git a/Test/source/MainFrm.pas b/Test/source/MainFrm.pas index e7f2aa5..5a85870 100644 --- a/Test/source/MainFrm.pas +++ b/Test/source/MainFrm.pas @@ -45,7 +45,7 @@ type rbAbsolute: TRadioButton; edtPipeName: TEdit; lblPipeName: TLabel; - btnBinary: TButton; + btnBinaryRawByteString: TButton; pcDispatch: TPageControl; tsText: TTabSheet; tsException: TTabSheet; @@ -54,6 +54,7 @@ type bvlDispatch: TBevel; pnlObservers: TPanel; bvlObservers: TBevel; + btnGraphic: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); @@ -69,6 +70,8 @@ type procedure btnFileStopClick(Sender: TObject); procedure btnNamedPipeStartClick(Sender: TObject); procedure btnNamedPipeStopClick(Sender: TObject); + procedure btnBinaryRawByteStringClick(Sender: TObject); + procedure btnGraphicClick(Sender: TObject); private FLog: IX2Log; FEventObserver: IX2LogObserver; @@ -82,11 +85,13 @@ type implementation uses System.SysUtils, + Vcl.Imaging.Jpeg, Winapi.Windows, X2Log, X2Log.Constants, X2Log.Details.Default, + X2Log.Details.Intf, X2Log.Exception.madExcept, X2Log.Observer.Event, X2Log.Observer.LogFile, @@ -181,9 +186,35 @@ begin else if Sender = btnWarning then FLog.Warning(edtMessage.Text) else if Sender = btnError then - FLog.Error(edtMessage.Text) - else if Sender = btnBinary then - FLog.InfoEx(edtMessage.Text, TX2LogBinaryDetails.Create(#0#1#2#3'Test'#12'Some more data')); + FLog.Error(edtMessage.Text); +end; + + +procedure TMainForm.btnBinaryRawByteStringClick(Sender: TObject); +begin + FLog.InfoEx(edtMessage.Text, TX2LogBinaryDetails.Create(#0#1#2#3'Test'#12'Some more data')); +end; + + +procedure TMainForm.btnGraphicClick(Sender: TObject); +var + graphic: TJPEGImage; + resourceStream: TResourceStream; + +begin + graphic := TJPEGImage.Create; + try + resourceStream := TResourceStream.Create(SysInit.HInstance, 'GraphicDetails', RT_RCDATA); + try + graphic.LoadFromStream(resourceStream); + finally + FreeAndNil(resourceStream); + end; + + FLog.InfoEx('Graphic', TX2LogGraphicDetails.Create(graphic)); + finally + FreeAndNil(graphic); + end; end; diff --git a/X2Log.Client.NamedPipe.pas b/X2Log.Client.NamedPipe.pas index 3bfae15..3781cc0 100644 --- a/X2Log.Client.NamedPipe.pas +++ b/X2Log.Client.NamedPipe.pas @@ -29,7 +29,8 @@ uses Winapi.Windows, X2Log.Details.Default, - X2Log.Details.Registry; + X2Log.Details.Registry, + X2Log.Util.Stream; type @@ -271,22 +272,6 @@ 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; @@ -317,7 +302,7 @@ begin raise EReadError.Create('Header too small'); { Message } - msg := ReadString; + msg := TStreamUtil.ReadString(MessageData); { Details } details := nil; @@ -325,7 +310,7 @@ begin MessageData.ReadBuffer(serializerIID, SizeOf(TGUID)); if serializerIID <> GUID_NULL then begin - MessageData.ReadBuffer(detailsSize, SizeOf(Cardinal)); + detailsSize := TStreamUtil.ReadCardinal(MessageData); if detailsSize > 0 then begin if TX2LogDetailsRegistry.GetSerializer(serializerIID, serializer) then diff --git a/X2Log.Details.Default.pas b/X2Log.Details.Default.pas index 968dbed..7329e9d 100644 --- a/X2Log.Details.Default.pas +++ b/X2Log.Details.Default.pas @@ -3,7 +3,9 @@ unit X2Log.Details.Default; interface uses System.Classes, + Vcl.Graphics, + X2Log.Details.Intf, X2Log.Intf; @@ -55,18 +57,49 @@ type end; + + TX2LogGraphicDetails = class(TInterfacedObject, IX2LogDetails, IX2LogDetailsGraphic, + IX2LogDetailsCopyable, IX2LogDetailsStreamable) + private + FGraphic: TGraphic; + protected + { Dummy parameter to prevent 'Duplicate constructor inaccessible from C++' warning } + constructor CreateOwned(AGraphic: TGraphic; ADummy: Integer = 0); + public + class function CreateIfNotEmpty(AGraphic: TGraphic): TX2LogGraphicDetails; + + constructor Create(AGraphic: TGraphic); + destructor Destroy; override; + + { IX2LogDetails } + function GetSerializerIID: TGUID; + + { IX2LogDetailsGraphic } + function GetAsGraphic: TGraphic; + + { IX2LogDetailsCopyable } + procedure CopyToClipboard; + + { IX2LogDetailsStreamable } + procedure SaveToStream(AStream: TStream); + end; + + implementation uses System.SysUtils, Vcl.ClipBrd, + Winapi.Windows, X2Log.Constants, - X2Log.Details.Registry; + X2Log.Details.Registry, + X2Log.Util.Stream; const StringDetailsSerializerIID: TGUID = '{4223C30E-6E80-4D66-9EDC-F8688A7413D2}'; BinaryDetailsSerializerIID: TGUID = '{05F6E8BD-118E-41B3-B626-1F190CC2A7D3}'; + GraphicDetailsSerializerIID: TGUID = '{BD31E42A-83DC-4947-A862-79ABAE8D5056}'; @@ -87,6 +120,14 @@ type end; + TX2LogGraphicDetailsSerializer = class(TInterfacedObject, IX2LogDetailsSerializer) + public + { IX2LogDetailsSerializer } + procedure Serialize(ADetails: IX2LogDetails; AStream: TStream); + function Deserialize(AStream: TStream): IX2LogDetails; + end; + + { TX2LogStringDetails } class function TX2LogStringDetails.CreateIfNotEmpty(const AText: string): TX2LogStringDetails; begin @@ -190,39 +231,83 @@ begin end; -{ TX2LogStringDetailsSerializer } -procedure TX2LogStringDetailsSerializer.Serialize(ADetails: IX2LogDetails; AStream: TStream); +{ TX2LogGraphicDetails } +class function TX2LogGraphicDetails.CreateIfNotEmpty(AGraphic: TGraphic): TX2LogGraphicDetails; +begin + if Assigned(AGraphic) and (not AGraphic.Empty) then + Result := Self.Create(AGraphic) + else + Result := nil; +end; + + +constructor TX2LogGraphicDetails.Create(AGraphic: TGraphic); +begin + inherited Create; + + if not Assigned(AGraphic) then + raise EInvalidGraphic.Create('AGraphic can not be nil'); + + FGraphic := TGraphicClass(AGraphic.ClassType).Create; + FGraphic.Assign(AGraphic); +end; + + +constructor TX2LogGraphicDetails.CreateOwned(AGraphic: TGraphic; ADummy: Integer); +begin + inherited Create; + + FGraphic := AGraphic; +end; + + +destructor TX2LogGraphicDetails.Destroy; +begin + FreeAndNil(FGraphic); + + inherited; +end; + + +function TX2LogGraphicDetails.GetSerializerIID: TGUID; +begin + Result := GraphicDetailsSerializerIID; +end; + + +procedure TX2LogGraphicDetails.CopyToClipboard; var - bytes: TBytes; - bytesLength: Cardinal; + format: Word; + data: NativeUInt; + palette: HPALETTE; begin - bytes := TEncoding.UTF8.GetBytes((ADetails as IX2LogDetailsText).AsString); - bytesLength := Length(bytes); + GetAsGraphic.SaveToClipboardFormat(format, data, palette); +end; - AStream.WriteBuffer(bytesLength, SizeOf(Cardinal)); - if bytesLength > 0 then - AStream.WriteBuffer(bytes[0], bytesLength); + +procedure TX2LogGraphicDetails.SaveToStream(AStream: TStream); +begin + FGraphic.SaveToStream(AStream); +end; + + +function TX2LogGraphicDetails.GetAsGraphic: TGraphic; +begin + Result := FGraphic; +end; + + +{ TX2LogStringDetailsSerializer } +procedure TX2LogStringDetailsSerializer.Serialize(ADetails: IX2LogDetails; AStream: TStream); +begin + TStreamUtil.WriteString(AStream, (ADetails as IX2LogDetailsText).AsString); 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(''); + Result := TX2LogStringDetails.Create(TStreamUtil.ReadString(AStream)); end; @@ -230,15 +315,13 @@ end; 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); + TStreamUtil.WriteCardinal(AStream, stream.Size); + if stream.Size > 0 then + AStream.CopyFrom(stream, stream.Size); end; @@ -247,7 +330,7 @@ var streamSize: Cardinal; begin - AStream.ReadBuffer(streamSize, SizeOf(Cardinal)); + streamSize := TStreamUtil.ReadCardinal(AStream); if streamSize > 0 then Result := TX2LogBinaryDetails.Create(AStream, streamSize) else @@ -257,9 +340,44 @@ begin end; +{ TX2LogGraphicDetailsSerializer } +procedure TX2LogGraphicDetailsSerializer.Serialize(ADetails: IX2LogDetails; AStream: TStream); +var + graphic: TGraphic; + +begin + graphic := (ADetails as IX2LogDetailsGraphic).AsGraphic; + TStreamUtil.WriteString(AStream, graphic.ClassName); + graphic.SaveToStream(AStream); +end; + + +function TX2LogGraphicDetailsSerializer.Deserialize(AStream: TStream): IX2LogDetails; +var + graphicClass: TGraphicClass; + graphic: TGraphic; + +begin + Result := nil; + graphicClass := TGraphicClass(GetClass(TStreamUtil.ReadString(AStream))); + if Assigned(graphicClass) then + begin + graphic := graphicClass.Create; + try + graphic.LoadFromStream(AStream); + Result := TX2LogGraphicDetails.CreateOwned(graphic); + except + FreeAndNil(graphic); + raise; + end; + end; +end; + + initialization TX2LogDetailsRegistry.Register(StringDetailsSerializerIID, TX2LogStringDetailsSerializer.Create); TX2LogDetailsRegistry.Register(BinaryDetailsSerializerIID, TX2LogBinaryDetailsSerializer.Create); + TX2LogDetailsRegistry.Register(GraphicDetailsSerializerIID, TX2LogGraphicDetailsSerializer.Create); end. diff --git a/X2Log.Details.Intf.pas b/X2Log.Details.Intf.pas new file mode 100644 index 0000000..fff8720 --- /dev/null +++ b/X2Log.Details.Intf.pas @@ -0,0 +1,39 @@ +unit X2Log.Details.Intf; + +interface +uses + System.Classes, + Vcl.Graphics, + + X2Log.Intf; + + +type + IX2LogDetailsText = interface(IX2LogDetails) + ['{D5F194E9-8633-4575-801D-E8983124118F}'] + function GetAsString: string; + + property AsString: string read GetAsString; + end; + + + IX2LogDetailsBinary = interface(IX2LogDetails) + ['{265739E7-BB65-434B-BCD3-BB89B936A854}'] + function GetAsStream: TStream; + + { Note: Stream Position will be reset by GetAsStream } + property AsStream: TStream read GetAsStream; + end; + + + IX2LogDetailsGraphic = interface(IX2LogDetails) + ['{ED8200AA-0D0F-4D8D-BE7D-A32AC7D630AF}'] + function GetAsGraphic: TGraphic; + + property AsGraphic: TGraphic read GetAsGraphic; + end; + + +implementation + +end. diff --git a/X2Log.Intf.pas b/X2Log.Intf.pas index fceb355..6dc19d2 100644 --- a/X2Log.Intf.pas +++ b/X2Log.Intf.pas @@ -45,23 +45,6 @@ type end; - IX2LogDetailsText = interface(IX2LogDetails) - ['{D5F194E9-8633-4575-801D-E8983124118F}'] - function GetAsString: string; - - property AsString: string read GetAsString; - end; - - - IX2LogDetailsBinary = interface(IX2LogDetails) - ['{265739E7-BB65-434B-BCD3-BB89B936A854}'] - function GetAsStream: TStream; - - { Note: Stream Position will be reset by GetAsStream } - property AsStream: TStream read GetAsStream; - end; - - { Logging } IX2LogBase = interface ['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}'] diff --git a/X2Log.Observer.MonitorForm.dfm b/X2Log.Observer.MonitorForm.dfm index 1172a16..8400e58 100644 --- a/X2Log.Observer.MonitorForm.dfm +++ b/X2Log.Observer.MonitorForm.dfm @@ -73,7 +73,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm Left = 0 Top = 0 Width = 378 - Height = 17 + Height = 19 Sections = < item AutoSize = True @@ -85,9 +85,9 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm end object reDetails: TRichEdit Left = 0 - Top = 17 + Top = 19 Width = 378 - Height = 453 + Height = 451 Align = alClient BorderStyle = bsNone Font.Charset = ANSI_CHARSET @@ -100,6 +100,28 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm ReadOnly = True ScrollBars = ssBoth TabOrder = 1 + Visible = False + end + object sbDetailsImage: TScrollBox + Left = 0 + Top = 19 + Width = 378 + Height = 451 + HorzScrollBar.Tracking = True + VertScrollBar.Tracking = True + Align = alClient + BorderStyle = bsNone + DoubleBuffered = True + ParentDoubleBuffered = False + TabOrder = 2 + Visible = False + object imgDetailsImage: TImage + Left = 0 + Top = 0 + Width = 25 + Height = 25 + AutoSize = True + end end end end @@ -123,7 +145,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm Header.Font.Height = -11 Header.Font.Name = 'Tahoma' Header.Font.Style = [] - Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible, hoHeaderClickAutoSort] + Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoVisible] HintMode = hmHint Images = ilsLog TabOrder = 0 @@ -233,7 +255,7 @@ object X2LogObserverMonitorForm: TX2LogObserverMonitorForm Left = 448 Top = 48 Bitmap = { - 494C010109004000B80010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 494C010109004000BC0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000400000003000000001002000000000000030 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 diff --git a/X2Log.Observer.MonitorForm.pas b/X2Log.Observer.MonitorForm.pas index a293ed8..24bbf62 100644 --- a/X2Log.Observer.MonitorForm.pas +++ b/X2Log.Observer.MonitorForm.pas @@ -17,6 +17,7 @@ uses VirtualTrees, Winapi.Messages, + X2Log.Details.Intf, X2Log.Intf; @@ -60,6 +61,8 @@ type actShowWarning: TAction; actShowError: TAction; lblFilter: TLabel; + sbDetailsImage: TScrollBox; + imgDetailsImage: TImage; procedure FormShow(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); @@ -106,6 +109,9 @@ type procedure SetDetails(ADetails: IX2LogDetails); procedure SetBinaryDetails(ADetails: IX2LogDetailsBinary); + procedure SetGraphicDetails(ADetails: IX2LogDetailsGraphic); + + procedure SetVisibleDetails(AControl: TControl); property Details: IX2LogDetails read FDetails; property LogObservable: IX2LogObservable read FLogObservable; @@ -435,6 +441,7 @@ end; procedure TX2LogObserverMonitorForm.SetDetails(ADetails: IX2LogDetails); var + logDetailsGraphic: IX2LogDetailsGraphic; logDetailsBinary: IX2LogDetailsBinary; logDetailsText: IX2LogDetailsText; @@ -443,13 +450,19 @@ begin if Assigned(Details) then begin - if Supports(ADetails, IX2LogDetailsBinary, logDetailsBinary) then + if Supports(ADetails, IX2LogDetailsGraphic, logDetailsGraphic) then + SetGraphicDetails(logDetailsGraphic) + + else if Supports(ADetails, IX2LogDetailsBinary, logDetailsBinary) then SetBinaryDetails(logDetailsBinary) else if Supports(ADetails, IX2LogDetailsText, logDetailsText) then + begin reDetails.Text := logDetailsText.AsString; + SetVisibleDetails(reDetails); + end; end else - reDetails.Clear; + SetVisibleDetails(nil); actCopyDetails.Enabled := Supports(ADetails, IX2LogDetailsCopyable); @@ -549,10 +562,38 @@ begin reDetails.Lines.Add(line); finally reDetails.Lines.EndUpdate; + + SetVisibleDetails(reDetails); end; end; +procedure TX2LogObserverMonitorForm.SetGraphicDetails(ADetails: IX2LogDetailsGraphic); +begin + imgDetailsImage.Picture.Assign(ADetails.AsGraphic); + SetVisibleDetails(sbDetailsImage); +end; + + +procedure TX2LogObserverMonitorForm.SetVisibleDetails(AControl: TControl); +begin + if Assigned(AControl) then + begin + AControl.BringToFront; + AControl.Visible := True; + end; + + reDetails.Visible := (AControl = reDetails); + sbDetailsImage.Visible := (AControl = sbDetailsImage); + + if not reDetails.Visible then + reDetails.Clear; + + if not sbDetailsImage.Visible then + imgDetailsImage.Picture.Assign(nil); +end; + + procedure TX2LogObserverMonitorForm.vstLogInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); var @@ -648,6 +689,8 @@ end; procedure TX2LogObserverMonitorForm.actClearExecute(Sender: TObject); begin vstLog.Clear; + SetDetails(nil); + UpdateUI; end; diff --git a/X2Log.Observer.NamedPipe.pas b/X2Log.Observer.NamedPipe.pas index b1ecc22..045a8c2 100644 --- a/X2Log.Observer.NamedPipe.pas +++ b/X2Log.Observer.NamedPipe.pas @@ -27,7 +27,8 @@ uses System.Types, Winapi.Windows, - X2Log.Details.Registry; + X2Log.Details.Registry, + X2Log.Util.Stream; type @@ -194,23 +195,10 @@ end; function TX2LogNamedPipeClient.DoSend(AEntry: TX2LogQueueEntry): Boolean; - - procedure WriteString(const ASource: WideString); - var - sourceLength: Cardinal; - - begin - sourceLength := Length(ASource); - WriteBuffer.WriteBuffer(sourceLength, SizeOf(Cardinal)); - WriteBuffer.WriteBuffer(PWideChar(ASource)^, sourceLength * SizeOf(WideChar)); - end; - - var header: TX2LogMessageHeader; bytesWritten: Cardinal; lastError: Cardinal; - detailsSize: Cardinal; detailsStream: TMemoryStream; serializerIID: TGUID; serializer: IX2LogDetailsSerializer; @@ -229,7 +217,7 @@ begin WriteBuffer.WriteBuffer(header, SizeOf(header)); { Message } - WriteString(AEntry.Message); + TStreamUtil.WriteString(WriteBuffer, AEntry.Message); { Details } if TX2LogDetailsRegistry.GetSerializer(AEntry.Details, serializer) then @@ -241,8 +229,7 @@ begin serializerIID := AEntry.Details.SerializerIID; WriteBuffer.WriteBuffer(serializerIID, SizeOf(TGUID)); - detailsSize := detailsStream.Size; - WriteBuffer.WriteBuffer(detailsSize, SizeOf(Cardinal)); + TStreamUtil.WriteCardinal(WriteBuffer, detailsStream.Size); WriteBuffer.CopyFrom(detailsStream, 0); finally FreeAndNil(detailsStream); diff --git a/X2Log.Util.Stream.pas b/X2Log.Util.Stream.pas new file mode 100644 index 0000000..e0b70cd --- /dev/null +++ b/X2Log.Util.Stream.pas @@ -0,0 +1,77 @@ +unit X2Log.Util.Stream; + +interface +uses + System.Classes, + System.SysUtils; + +type + TStreamUtil = class(TObject) + protected + class function GetEncoding(AEncoding: TEncoding): TEncoding; + public + class function ReadCardinal(AStream: TStream): Cardinal; + class procedure WriteCardinal(AStream: TStream; AValue: Cardinal); + + class function ReadString(AStream: TStream; AEncoding: TEncoding = nil): string; + class procedure WriteString(AStream: TStream; const AValue: string; AEncoding: TEncoding = nil); + end; + +implementation + + +{ TStreamUtil } +class function TStreamUtil.GetEncoding(AEncoding: TEncoding): TEncoding; +begin + if Assigned(AEncoding) then + Result := AEncoding + else + Result := TEncoding.UTF8; +end; + + +class function TStreamUtil.ReadCardinal(AStream: TStream): Cardinal; +begin + AStream.ReadBuffer(Result, SizeOf(Cardinal)); +end; + + +class procedure TStreamUtil.WriteCardinal(AStream: TStream; AValue: Cardinal); +begin + AStream.WriteBuffer(AValue, SizeOf(Cardinal)); +end; + + +class function TStreamUtil.ReadString(AStream: TStream; AEncoding: TEncoding): string; +var + bytes: TBytes; + bytesLength: Cardinal; + +begin + bytesLength := ReadCardinal(AStream); + if bytesLength > 0 then + begin + SetLength(bytes, bytesLength); + AStream.ReadBuffer(bytes[0], bytesLength); + + Result := GetEncoding(AEncoding).GetString(bytes); + end else + Result := ''; +end; + + +class procedure TStreamUtil.WriteString(AStream: TStream; const AValue: string; AEncoding: TEncoding); +var + bytes: TBytes; + bytesLength: Cardinal; + +begin + bytes := GetEncoding(AEncoding).GetBytes(AValue); + bytesLength := Length(bytes); + + WriteCardinal(AStream, bytesLength); + if bytesLength > 0 then + AStream.WriteBuffer(bytes[0], bytesLength); +end; + +end.