From 3cbf8c520f1a38b6f53a55f9cbae418fc836c5a1 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 1 Jun 2014 13:32:21 +0000 Subject: [PATCH] Added: FS#7 - Add support for graphics as details Added: TStreamUtil to clean up the code a bit --- Test/X2LogTest.dpr | 7 +- Test/X2LogTest.dproj | 6 ++ Test/X2LogTest.dres | Bin 0 -> 22272 bytes Test/X2LogTestResource.rc | 1 + Test/resources/Graphic.jpg | Bin 0 -> 22177 bytes Test/source/MainFrm.dfm | 23 +++-- Test/source/MainFrm.pas | 39 ++++++- X2Log.Client.NamedPipe.pas | 23 +---- X2Log.Details.Default.pas | 180 +++++++++++++++++++++++++++------ X2Log.Details.Intf.pas | 39 +++++++ X2Log.Intf.pas | 17 ---- X2Log.Observer.MonitorForm.dfm | 32 +++++- X2Log.Observer.MonitorForm.pas | 47 ++++++++- X2Log.Observer.NamedPipe.pas | 21 +--- X2Log.Util.Stream.pas | 77 ++++++++++++++ 15 files changed, 408 insertions(+), 104 deletions(-) create mode 100644 Test/X2LogTest.dres create mode 100644 Test/X2LogTestResource.rc create mode 100644 Test/resources/Graphic.jpg create mode 100644 X2Log.Details.Intf.pas create mode 100644 X2Log.Util.Stream.pas 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 0000000000000000000000000000000000000000..b41465dc9343ed0d3b303c1a038cdba769e9c055 GIT binary patch literal 22272 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 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.