1
0
mirror of synced 2024-11-21 10:53:50 +00:00

Added: FS#7 - Add support for graphics as details

Added: TStreamUtil to clean up the code a bit
This commit is contained in:
Mark van Renswoude 2014-06-01 13:32:21 +00:00
parent bf0bb05983
commit 3cbf8c520f
15 changed files with 408 additions and 104 deletions

View File

@ -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}

View File

@ -192,6 +192,12 @@
<DCCReference Include="..\X2Log.Client.Base.pas"/>
<DCCReference Include="..\X2Log.Details.Default.pas"/>
<DCCReference Include="..\X2Log.Details.Registry.pas"/>
<DCCReference Include="..\X2Log.Details.Intf.pas"/>
<DCCReference Include="..\X2Log.Util.Stream.pas"/>
<RcItem Include="resources\Graphic.jpg">
<ResourceType>RCDATA</ResourceType>
<ResourceId>GraphicDetails</ResourceId>
</RcItem>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>

BIN
Test/X2LogTest.dres Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
GraphicDetails RCDATA "resources\\Graphic.jpg"

BIN
Test/resources/Graphic.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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.

39
X2Log.Details.Intf.pas Normal file
View File

@ -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.

View File

@ -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}']

View File

@ -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

View File

@ -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;

View File

@ -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);

77
X2Log.Util.Stream.pas Normal file
View File

@ -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.