Initial commit
This commit is contained in:
parent
c08ec03460
commit
895f496507
18
NamedPipeClient/X2LogNamedPipeClient.dpr
Normal file
18
NamedPipeClient/X2LogNamedPipeClient.dpr
Normal file
@ -0,0 +1,18 @@
|
||||
program X2LogNamedPipeClient;
|
||||
|
||||
uses
|
||||
Vcl.Forms,
|
||||
MainFrm in 'source\MainFrm.pas' {MainForm},
|
||||
X2Log.Intf in '..\X2Log.Intf.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
var
|
||||
MainForm: TMainForm;
|
||||
|
||||
begin
|
||||
Application.Initialize;
|
||||
Application.MainFormOnTaskbar := True;
|
||||
Application.CreateForm(TMainForm, MainForm);
|
||||
Application.Run;
|
||||
end.
|
178
NamedPipeClient/X2LogNamedPipeClient.dproj
Normal file
178
NamedPipeClient/X2LogNamedPipeClient.dproj
Normal file
@ -0,0 +1,178 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{883FC03C-9DB1-43A5-8053-5C920FDBCCAC}</ProjectGuid>
|
||||
<ProjectVersion>13.4</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>X2LogNamedPipeClient.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||
<Platform Condition="'$(Platform)'==''">Win32</Platform>
|
||||
<TargetedPlatforms>1</TargetedPlatforms>
|
||||
<AppType>Application</AppType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
|
||||
<Base_Win64>true</Base_Win64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
|
||||
<Base_Win32>true</Base_Win32>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
|
||||
<Cfg_1>true</Cfg_1>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
|
||||
<Cfg_1_Win32>true</Cfg_1_Win32>
|
||||
<CfgParent>Cfg_1</CfgParent>
|
||||
<Cfg_1>true</Cfg_1>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
|
||||
<Cfg_2>true</Cfg_2>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base)'!=''">
|
||||
<DCC_UsePackage>fmx;IndySystem;DBXInterBaseDriver;DataSnapClient;DataSnapCommon;DataSnapServer;DataSnapProviderClient;DbxCommonDriver;dbxcds;DBXOracleDriver;CustomIPTransport;dsnap;fmxase;IndyCore;inetdbxpress;IPIndyImpl;bindcompfmx;rtl;dbrtl;DbxClientDriver;bindcomp;inetdb;xmlrtl;ibxpress;IndyProtocols;DBXMySQLDriver;soaprtl;bindengine;DBXInformixDriver;DBXFirebirdDriver;inet;fmxobj;DBXSybaseASADriver;fmxdae;dbexpress;DataSnapIndy10ServerTransport;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
|
||||
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
|
||||
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
|
||||
<VerInfo_Locale>1043</VerInfo_Locale>
|
||||
<Manifest_File>None</Manifest_File>
|
||||
<DCC_DcuOutput>lib</DCC_DcuOutput>
|
||||
<DCC_ExeOutput>bin</DCC_ExeOutput>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win64)'!=''">
|
||||
<DCC_UsePackage>dxdborRS16;cxLibraryRS16;dxLayoutControlRS16;dxPScxPivotGridLnkRS16;dxCoreRS16;cxExportRS16;dxBarRS16;cxSpreadSheetRS16;cxTreeListdxBarPopupMenuRS16;TeeDB;dxDBXServerModeRS16;dxPsPrVwAdvRS16;vclib;dxPSCoreRS16;cxPivotGridOLAPRS16;dxPScxTLLnkRS16;dxPScxGridLnkRS16;cxPageControlRS16;dxRibbonRS16;DBXSybaseASEDriver;vclimg;cxTreeListRS16;dxComnRS16;vcldb;dxADOServerModeRS16;vcldsnap;dxBarExtDBItemsRS16;DBXDb2Driver;vcl;DBXMSSQLDriver;cxDataRS16;cxBarEditItemRS16;dxDockingRS16;dxPSDBTeeChartRS16;cxPageControldxBarPopupMenuRS16;webdsnap;dxBarExtItemsRS16;dxPSLnksRS16;dxPSTeeChartRS16;adortl;dxPSdxLCLnkRS16;dxorgcRS16;dxWizardControlRS16;dxPScxExtCommonRS16;dxNavBarRS16;dxPSdxDBOCLnkRS16;cxSchedulerTreeBrowserRS16;Tee;DBXOdbcDriver;dxdbtrRS16;dxPScxSSLnkRS16;dxPScxCommonRS16;dxmdsRS16;dxPSPrVwRibbonRS16;cxGridRS16;cxEditorsRS16;TeeUI;vclactnband;dxServerModeRS16;bindcompvcl;cxPivotGridRS16;dxPScxSchedulerLnkRS16;dxPSdxDBTVLnkRS16;vclie;cxSchedulerRibbonStyleEventEditorRS16;cxSchedulerRS16;vcltouch;websnap;VclSmp;dxTabbedMDIRS16;DataSnapConnectors;dxPSdxOCLnkRS16;dsnapcon;dxPSdxFCLnkRS16;dxThemeRS16;dxPScxPCProdRS16;vclx;dxFlowChartRS16;dxGDIPlusRS16;dxBarDBNavRS16;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||
<DCC_UsePackage>dxdborRS16;cxLibraryRS16;dxLayoutControlRS16;dxPScxPivotGridLnkRS16;dxCoreRS16;cxExportRS16;dxBarRS16;cxSpreadSheetRS16;cxTreeListdxBarPopupMenuRS16;TeeDB;dxDBXServerModeRS16;dxPsPrVwAdvRS16;vclib;inetdbbde;dxPSCoreRS16;cxPivotGridOLAPRS16;dxPScxTLLnkRS16;dxPScxGridLnkRS16;cxPageControlRS16;dxRibbonRS16;DBXSybaseASEDriver;vclimg;fmi;cxTreeListRS16;dxComnRS16;vcldb;dxADOServerModeRS16;vcldsnap;dxBarExtDBItemsRS16;X2CLGL;DBXDb2Driver;vcl;CloudService;DBXMSSQLDriver;CodeSiteExpressPkg;FmxTeeUI;cxDataRS16;cxBarEditItemRS16;dxDockingRS16;dxPSDBTeeChartRS16;cxPageControldxBarPopupMenuRS16;cxSchedulerGridRS16;webdsnap;X2CLMB;dxBarExtItemsRS16;dxPSLnksRS16;OmniThreadLibraryRuntimeXE2;dxtrmdRS16;dxPSTeeChartRS16;adortl;dxPSdxLCLnkRS16;madBasic_;dxorgcRS16;dxWizardControlRS16;dxPScxExtCommonRS16;vcldbx;dxNavBarRS16;dxPSdxDBOCLnkRS16;cxSchedulerTreeBrowserRS16;Tee;DBXOdbcDriver;dxdbtrRS16;madDisAsm_;svnui;dxPScxSSLnkRS16;dxPScxCommonRS16;dxmdsRS16;dxPSPrVwRibbonRS16;cxPivotGridChartRS16;cxGridRS16;cxEditorsRS16;FMXTee;TeeUI;vclactnband;dxServerModeRS16;bindcompvcl;cxPivotGridRS16;dxPScxSchedulerLnkRS16;dxPSdxDBTVLnkRS16;vclie;cxSchedulerRibbonStyleEventEditorRS16;cxSchedulerRS16;madExcept_;vcltouch;websnap;VclSmp;dxTabbedMDIRS16;DataSnapConnectors;dxPSdxOCLnkRS16;dsnapcon;dxPSdxFCLnkRS16;dxThemeRS16;dxPScxPCProdRS16;vclx;svn;dxFlowChartRS16;bdertl;VirtualTreesR;dxGDIPlusRS16;dxBarDBNavRS16;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
||||
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
|
||||
<DCC_Optimize>false</DCC_Optimize>
|
||||
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
|
||||
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
|
||||
<DCC_RemoteDebug>true</DCC_RemoteDebug>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
<DCC_RemoteDebug>false</DCC_RemoteDebug>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
||||
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
|
||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
||||
<DCC_DebugInformation>false</DCC_DebugInformation>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<DelphiCompile Include="$(MainSource)">
|
||||
<MainSource>MainSource</MainSource>
|
||||
</DelphiCompile>
|
||||
<DCCReference Include="source\MainFrm.pas">
|
||||
<Form>MainForm</Form>
|
||||
<FormType>dfm</FormType>
|
||||
</DCCReference>
|
||||
<DCCReference Include="..\X2Log.Intf.pas"/>
|
||||
<BuildConfiguration Include="Release">
|
||||
<Key>Cfg_2</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Debug">
|
||||
<Key>Cfg_1</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
</ItemGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
||||
<Borland.ProjectType/>
|
||||
<BorlandProject>
|
||||
<Delphi.Personality>
|
||||
<VersionInfo>
|
||||
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
|
||||
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
|
||||
<VersionInfo Name="MajorVer">1</VersionInfo>
|
||||
<VersionInfo Name="MinorVer">0</VersionInfo>
|
||||
<VersionInfo Name="Release">0</VersionInfo>
|
||||
<VersionInfo Name="Build">0</VersionInfo>
|
||||
<VersionInfo Name="Debug">False</VersionInfo>
|
||||
<VersionInfo Name="PreRelease">False</VersionInfo>
|
||||
<VersionInfo Name="Special">False</VersionInfo>
|
||||
<VersionInfo Name="Private">False</VersionInfo>
|
||||
<VersionInfo Name="DLL">False</VersionInfo>
|
||||
<VersionInfo Name="Locale">1043</VersionInfo>
|
||||
<VersionInfo Name="CodePage">1252</VersionInfo>
|
||||
</VersionInfo>
|
||||
<VersionInfoKeys>
|
||||
<VersionInfoKeys Name="CompanyName"/>
|
||||
<VersionInfoKeys Name="FileDescription"/>
|
||||
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
|
||||
<VersionInfoKeys Name="InternalName"/>
|
||||
<VersionInfoKeys Name="LegalCopyright"/>
|
||||
<VersionInfoKeys Name="LegalTrademarks"/>
|
||||
<VersionInfoKeys Name="OriginalFilename"/>
|
||||
<VersionInfoKeys Name="ProductName"/>
|
||||
<VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
|
||||
<VersionInfoKeys Name="Comments"/>
|
||||
</VersionInfoKeys>
|
||||
<Source>
|
||||
<Source Name="MainSource">X2LogNamedPipeClient.dpr</Source>
|
||||
</Source>
|
||||
<Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvAppFrmDesign160.bpl">JVCL Application and Form Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvBandsDesign160.bpl">JVCL Band Objects</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvBDEDesign160.bpl">JVCL BDE Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCmpDesign160.bpl">JVCL Non-Visual Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvControlsDesign160.bpl">JVCL Visual Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCoreDesign160.bpl">JVCL Core Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCryptDesign160.bpl">JVCL Encryption and Compression</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCustomDesign160.bpl">JVCL Custom Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDBDesign160.bpl">JVCL Database Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDlgsDesign160.bpl">JVCL Dialog Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDockingDesign160.bpl">JVCL Docking Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDotNetCtrlsDesign160.bpl">JVCL DotNet Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvGlobusDesign160.bpl">JVCL Globus Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvHMIDesign160.bpl">JVCL HMI Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvJansDesign160.bpl">JVCL Jans Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvManagedThreadsDesign160.bpl">JVCL Managed Threads</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvMMDesign160.bpl">JVCL Multimedia and Image Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvNetDesign160.bpl">JVCL Network Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPageCompsDesign160.bpl">JVCL Page Style Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPascalInterpreterDesign160.bpl">JVCL Interpreter Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPluginSystemDesign160.bpl">JVCL Plugin Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPrintPreviewDesign160.bpl">JVCL Print Preview Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvRuntimeDesignDesign160.bpl">JVCL Runtime Design Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvStdCtrlsDesign160.bpl">JVCL Standard Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvSystemDesign160.bpl">JVCL System Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvTimeFrameworkDesign160.bpl">JVCL Time Framework</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvWizardsDesign160.bpl">JVCL Wizard</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvXPCtrlsDesign160.bpl">JVCL XP Controls</Excluded_Packages>
|
||||
</Excluded_Packages>
|
||||
</Delphi.Personality>
|
||||
<Deployment/>
|
||||
<Platforms>
|
||||
<Platform value="Win64">False</Platform>
|
||||
<Platform value="Win32">True</Platform>
|
||||
</Platforms>
|
||||
</BorlandProject>
|
||||
<ProjectFileVersion>12</ProjectFileVersion>
|
||||
</ProjectExtensions>
|
||||
<Import Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')" Project="$(BDS)\Bin\CodeGear.Delphi.Targets"/>
|
||||
<Import Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')" Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj"/>
|
||||
</Project>
|
BIN
NamedPipeClient/X2LogNamedPipeClient.res
Normal file
BIN
NamedPipeClient/X2LogNamedPipeClient.res
Normal file
Binary file not shown.
27
NamedPipeClient/source/MainFrm.dfm
Normal file
27
NamedPipeClient/source/MainFrm.dfm
Normal file
@ -0,0 +1,27 @@
|
||||
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
|
243
NamedPipeClient/source/MainFrm.pas
Normal file
243
NamedPipeClient/source/MainFrm.pas
Normal file
@ -0,0 +1,243 @@
|
||||
unit MainFrm;
|
||||
|
||||
interface
|
||||
uses
|
||||
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
|
||||
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
|
||||
|
||||
|
||||
type
|
||||
TMainForm = class(TForm)
|
||||
mmoLog: TMemo;
|
||||
|
||||
procedure FormCreate(Sender: TObject);
|
||||
private
|
||||
FClientThread: TThread;
|
||||
|
||||
procedure DoMessage(Sender: TObject; Msg: TStream);
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.SyncObjs,
|
||||
|
||||
X2Log.Intf;
|
||||
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
type
|
||||
TClientMessageEvent = procedure(Sender: TObject; Msg: TStream) of object;
|
||||
|
||||
TClientThread = class(TThread)
|
||||
private
|
||||
FTerminateEvent: TEvent;
|
||||
FPipe: THandle;
|
||||
FOverlappedRead: TOverlapped;
|
||||
FReadBuffer: array[0..4095] of Byte;
|
||||
FMessage: TMemoryStream;
|
||||
FOnMessage: TClientMessageEvent;
|
||||
protected
|
||||
procedure Execute; override;
|
||||
procedure TerminatedSet; override;
|
||||
|
||||
procedure ReadMessage;
|
||||
procedure HandleMessage;
|
||||
|
||||
procedure DoMessage;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
property OnMessage: TClientMessageEvent read FOnMessage write FOnMessage;
|
||||
end;
|
||||
|
||||
|
||||
{ TMainForm }
|
||||
procedure TMainForm.FormCreate(Sender: TObject);
|
||||
begin
|
||||
FClientThread := TClientThread.Create;
|
||||
(FClientThread as TClientThread).OnMessage := DoMessage;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.DoMessage(Sender: TObject; Msg: TStream);
|
||||
|
||||
function ReadString: string;
|
||||
var
|
||||
size: Cardinal;
|
||||
|
||||
begin
|
||||
Msg.ReadBuffer(size, SizeOf(cardinal));
|
||||
if size > 0 then
|
||||
begin
|
||||
SetLength(Result, size);
|
||||
Msg.ReadBuffer(Result[1], size * SizeOf(Char));
|
||||
end else
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
var
|
||||
level: TX2LogLevel;
|
||||
logMsg: string;
|
||||
detail: string;
|
||||
|
||||
begin
|
||||
Msg.ReadBuffer(level, SizeOf(TX2LogLevel));
|
||||
logMsg := ReadString;
|
||||
detail := ReadString;
|
||||
|
||||
mmoLog.Lines.Add(logMsg + ' (' + detail + ')');
|
||||
end;
|
||||
|
||||
|
||||
const
|
||||
FILE_WRITE_ATTRIBUTES = $0100;
|
||||
|
||||
{ TClientThread }
|
||||
constructor TClientThread.Create;
|
||||
begin
|
||||
FTerminateEvent := TEvent.Create(nil, True, False, '');
|
||||
FMessage := TMemoryStream.Create;
|
||||
|
||||
inherited Create(False);
|
||||
end;
|
||||
|
||||
destructor TClientThread.Destroy;
|
||||
begin
|
||||
FreeAndNil(FMessage);
|
||||
FreeAndNil(FTerminateEvent);
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
||||
procedure TClientThread.Execute;
|
||||
var
|
||||
mode: Cardinal;
|
||||
readEvent: TEvent;
|
||||
events: array[0..1] of THandle;
|
||||
waitResult: Cardinal;
|
||||
bytesTransferred: Cardinal;
|
||||
|
||||
begin
|
||||
while not Terminated do
|
||||
begin
|
||||
FPipe := CreateFile('\\.\pipe\X2LogTest', GENERIC_READ or FILE_WRITE_ATTRIBUTES,
|
||||
0, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
|
||||
|
||||
if FPipe = INVALID_HANDLE_VALUE then
|
||||
begin
|
||||
if GetLastError = ERROR_PIPE_BUSY then
|
||||
begin
|
||||
if not WaitNamedPipe('\\.\pipe\X2LogTest', 5000) then
|
||||
exit;
|
||||
end else
|
||||
RaiseLastOSError;
|
||||
end else
|
||||
break;
|
||||
end;
|
||||
|
||||
if Terminated then
|
||||
exit;
|
||||
|
||||
mode := PIPE_READMODE_MESSAGE;
|
||||
if not SetNamedPipeHandleState(FPipe, mode, nil, nil) then
|
||||
exit;
|
||||
|
||||
readEvent := TEvent.Create(nil, False, False, '');
|
||||
events[0] := FTerminateEvent.Handle;
|
||||
events[1] := readEvent.Handle;
|
||||
|
||||
FOverlappedRead.hEvent := readEvent.Handle;
|
||||
ReadMessage;
|
||||
|
||||
while not Terminated do
|
||||
begin
|
||||
waitResult := WaitForMultipleObjects(Length(events), @events, False, INFINITE);
|
||||
|
||||
case waitResult of
|
||||
WAIT_OBJECT_0:
|
||||
{ Terminated }
|
||||
break;
|
||||
|
||||
WAIT_OBJECT_0 + 1:
|
||||
{ Read event completed }
|
||||
if GetOverlappedResult(FPipe, FOverlappedRead, bytesTransferred, False) then
|
||||
begin
|
||||
FMessage.WriteBuffer(FReadBuffer[0], bytesTransferred);
|
||||
HandleMessage;
|
||||
ReadMessage;
|
||||
end else
|
||||
begin
|
||||
if GetLastError = ERROR_MORE_DATA then
|
||||
begin
|
||||
FMessage.WriteBuffer(FReadBuffer[0], bytesTransferred);
|
||||
ReadMessage;
|
||||
end else
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
CloseHandle(FPipe);
|
||||
end;
|
||||
|
||||
|
||||
procedure TClientThread.ReadMessage;
|
||||
var
|
||||
bytesRead: Cardinal;
|
||||
lastError: Cardinal;
|
||||
|
||||
begin
|
||||
while True do
|
||||
begin
|
||||
if ReadFile(FPipe, FReadBuffer, SizeOf(FReadBuffer), bytesRead, @FOverlappedRead) then
|
||||
begin
|
||||
{ Immediate result }
|
||||
FMessage.WriteBuffer(FReadBuffer[0], bytesRead);
|
||||
HandleMessage;
|
||||
end else
|
||||
begin
|
||||
{ More data, pending I/O or an actual error }
|
||||
lastError := GetLastError;
|
||||
|
||||
if lastError = ERROR_IO_PENDING then
|
||||
break
|
||||
else if lastError = ERROR_MORE_DATA then
|
||||
FMessage.WriteBuffer(FReadBuffer[0], SizeOf(FReadBuffer))
|
||||
else
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TClientThread.HandleMessage;
|
||||
begin
|
||||
if FMessage.Size > 0 then
|
||||
begin
|
||||
FMessage.Position := 0;
|
||||
Synchronize(DoMessage);
|
||||
FMessage.Clear;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TClientThread.TerminatedSet;
|
||||
begin
|
||||
inherited TerminatedSet;
|
||||
|
||||
FTerminateEvent.SetEvent;
|
||||
end;
|
||||
|
||||
|
||||
procedure TClientThread.DoMessage;
|
||||
begin
|
||||
if Assigned(FOnMessage) then
|
||||
FOnMessage(Self, FMessage);
|
||||
end;
|
||||
|
||||
end.
|
27
Test/X2LogTest.dpr
Normal file
27
Test/X2LogTest.dpr
Normal file
@ -0,0 +1,27 @@
|
||||
program X2LogTest;
|
||||
|
||||
uses
|
||||
Forms,
|
||||
MainFrm in 'source\MainFrm.pas' {MainForm},
|
||||
X2Log.Intf in '..\X2Log.Intf.pas',
|
||||
X2Log in '..\X2Log.pas',
|
||||
X2Log.Observer.Event in '..\X2Log.Observer.Event.pas',
|
||||
X2Log.Observer.Custom in '..\X2Log.Observer.Custom.pas',
|
||||
X2Log.Exception.Default in '..\X2Log.Exception.Default.pas',
|
||||
X2Log.Exception.madExcept in '..\X2Log.Exception.madExcept.pas',
|
||||
X2Log.Observer.LogFile in '..\X2Log.Observer.LogFile.pas',
|
||||
X2Log.Constants in '..\X2Log.Constants.pas',
|
||||
X2Log.Observer.NamedPipe in '..\X2Log.Observer.NamedPipe.pas',
|
||||
X2Log.Observer.CustomThreaded in '..\X2Log.Observer.CustomThreaded.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
var
|
||||
MainForm: TMainForm;
|
||||
|
||||
begin
|
||||
Application.Initialize;
|
||||
Application.MainFormOnTaskbar := True;
|
||||
Application.CreateForm(TMainForm, MainForm);
|
||||
Application.Run;
|
||||
end.
|
201
Test/X2LogTest.dproj
Normal file
201
Test/X2LogTest.dproj
Normal file
@ -0,0 +1,201 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{e601c684-e576-44d0-b94c-9a32de0c82c4}</ProjectGuid>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
|
||||
<DCC_DependencyCheckOutputName>X2LogTest.exe</DCC_DependencyCheckOutputName>
|
||||
<MainSource>X2LogTest.dpr</MainSource>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<ProjectVersion>13.4</ProjectVersion>
|
||||
<Base>True</Base>
|
||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||
<Platform Condition="'$(Platform)'==''">Win32</Platform>
|
||||
<TargetedPlatforms>1</TargetedPlatforms>
|
||||
<AppType>Application</AppType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
|
||||
<Base_Win64>true</Base_Win64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
|
||||
<Base_Win32>true</Base_Win32>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
|
||||
<Cfg_1>true</Cfg_1>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
|
||||
<Cfg_1_Win32>true</Cfg_1_Win32>
|
||||
<CfgParent>Cfg_1</CfgParent>
|
||||
<Cfg_1>true</Cfg_1>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
|
||||
<Cfg_2>true</Cfg_2>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
|
||||
<Cfg_2_Win32>true</Cfg_2_Win32>
|
||||
<CfgParent>Cfg_2</CfgParent>
|
||||
<Cfg_2>true</Cfg_2>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base)'!=''">
|
||||
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
|
||||
<DCC_ExeOutput>bin</DCC_ExeOutput>
|
||||
<Manifest_File>None</Manifest_File>
|
||||
<DCC_DcuOutput>lib</DCC_DcuOutput>
|
||||
<DCC_Namespace>Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)</DCC_Namespace>
|
||||
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
|
||||
<VerInfo_Locale>1043</VerInfo_Locale>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win64)'!=''">
|
||||
<Icon_MainIcon>X2LogTest_Icon.ico</Icon_MainIcon>
|
||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_1)'!=''">
|
||||
<Version>7.0</Version>
|
||||
<DCC_DebugInformation>False</DCC_DebugInformation>
|
||||
<DCC_LocalDebugSymbols>False</DCC_LocalDebugSymbols>
|
||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
||||
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
||||
<Version>7.0</Version>
|
||||
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
|
||||
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
||||
<Borland.ProjectType/>
|
||||
<BorlandProject>
|
||||
<Delphi.Personality>
|
||||
<Parameters>
|
||||
<Parameters Name="UseLauncher">False</Parameters>
|
||||
<Parameters Name="LoadAllSymbols">True</Parameters>
|
||||
<Parameters Name="LoadUnspecifiedSymbols">False</Parameters>
|
||||
</Parameters>
|
||||
<VersionInfo>
|
||||
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
|
||||
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
|
||||
<VersionInfo Name="MajorVer">1</VersionInfo>
|
||||
<VersionInfo Name="MinorVer">0</VersionInfo>
|
||||
<VersionInfo Name="Release">0</VersionInfo>
|
||||
<VersionInfo Name="Build">0</VersionInfo>
|
||||
<VersionInfo Name="Debug">False</VersionInfo>
|
||||
<VersionInfo Name="PreRelease">False</VersionInfo>
|
||||
<VersionInfo Name="Special">False</VersionInfo>
|
||||
<VersionInfo Name="Private">False</VersionInfo>
|
||||
<VersionInfo Name="DLL">False</VersionInfo>
|
||||
<VersionInfo Name="Locale">1043</VersionInfo>
|
||||
<VersionInfo Name="CodePage">1252</VersionInfo>
|
||||
</VersionInfo>
|
||||
<VersionInfoKeys>
|
||||
<VersionInfoKeys Name="CompanyName"/>
|
||||
<VersionInfoKeys Name="FileDescription"/>
|
||||
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
|
||||
<VersionInfoKeys Name="InternalName"/>
|
||||
<VersionInfoKeys Name="LegalCopyright"/>
|
||||
<VersionInfoKeys Name="LegalTrademarks"/>
|
||||
<VersionInfoKeys Name="OriginalFilename"/>
|
||||
<VersionInfoKeys Name="ProductName"/>
|
||||
<VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
|
||||
<VersionInfoKeys Name="Comments"/>
|
||||
</VersionInfoKeys>
|
||||
<Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvAppFrmDesign160.bpl">JVCL Application and Form Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvBandsDesign160.bpl">JVCL Band Objects</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvBDEDesign160.bpl">JVCL BDE Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCmpDesign160.bpl">JVCL Non-Visual Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvControlsDesign160.bpl">JVCL Visual Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCoreDesign160.bpl">JVCL Core Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCryptDesign160.bpl">JVCL Encryption and Compression</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvCustomDesign160.bpl">JVCL Custom Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDBDesign160.bpl">JVCL Database Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDlgsDesign160.bpl">JVCL Dialog Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDockingDesign160.bpl">JVCL Docking Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvDotNetCtrlsDesign160.bpl">JVCL DotNet Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvGlobusDesign160.bpl">JVCL Globus Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvHMIDesign160.bpl">JVCL HMI Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvJansDesign160.bpl">JVCL Jans Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvManagedThreadsDesign160.bpl">JVCL Managed Threads</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvMMDesign160.bpl">JVCL Multimedia and Image Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvNetDesign160.bpl">JVCL Network Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPageCompsDesign160.bpl">JVCL Page Style Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPascalInterpreterDesign160.bpl">JVCL Interpreter Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPluginSystemDesign160.bpl">JVCL Plugin Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvPrintPreviewDesign160.bpl">JVCL Print Preview Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvRuntimeDesignDesign160.bpl">JVCL Runtime Design Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvStdCtrlsDesign160.bpl">JVCL Standard Controls</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvSystemDesign160.bpl">JVCL System Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvTimeFrameworkDesign160.bpl">JVCL Time Framework</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvWizardsDesign160.bpl">JVCL Wizard</Excluded_Packages>
|
||||
<Excluded_Packages Name="F:\Components\bin\DXE2\win32\JvXPCtrlsDesign160.bpl">JVCL XP Controls</Excluded_Packages>
|
||||
</Excluded_Packages>
|
||||
<Source>
|
||||
<Source Name="MainSource">X2LogTest.dpr</Source>
|
||||
</Source>
|
||||
</Delphi.Personality>
|
||||
<Platforms>
|
||||
<Platform value="Win64">False</Platform>
|
||||
<Platform value="Win32">True</Platform>
|
||||
</Platforms>
|
||||
</BorlandProject>
|
||||
<ProjectFileVersion>12</ProjectFileVersion>
|
||||
</ProjectExtensions>
|
||||
<ItemGroup>
|
||||
<DelphiCompile Include="$(MainSource)">
|
||||
<MainSource>MainSource</MainSource>
|
||||
</DelphiCompile>
|
||||
<DCCReference Include="source\MainFrm.pas">
|
||||
<Form>MainForm</Form>
|
||||
</DCCReference>
|
||||
<DCCReference Include="..\X2Log.Intf.pas"/>
|
||||
<DCCReference Include="..\X2Log.pas"/>
|
||||
<DCCReference Include="..\X2Log.Observer.Event.pas"/>
|
||||
<DCCReference Include="..\X2Log.Observer.Custom.pas"/>
|
||||
<DCCReference Include="..\X2Log.Exception.Default.pas"/>
|
||||
<DCCReference Include="..\X2Log.Exception.madExcept.pas"/>
|
||||
<DCCReference Include="..\X2Log.Observer.LogFile.pas"/>
|
||||
<DCCReference Include="..\X2Log.Constants.pas"/>
|
||||
<DCCReference Include="..\X2Log.Observer.NamedPipe.pas"/>
|
||||
<DCCReference Include="..\X2Log.Observer.CustomThreaded.pas"/>
|
||||
<BuildConfiguration Include="Debug">
|
||||
<Key>Cfg_2</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Release">
|
||||
<Key>Cfg_1</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup/>
|
||||
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets"/>
|
||||
<Import Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')" Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj"/>
|
||||
</Project>
|
BIN
Test/X2LogTest.res
Normal file
BIN
Test/X2LogTest.res
Normal file
Binary file not shown.
155
Test/source/MainFrm.dfm
Normal file
155
Test/source/MainFrm.dfm
Normal file
@ -0,0 +1,155 @@
|
||||
object MainForm: TMainForm
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = 'X'#178'Log Test'
|
||||
ClientHeight = 515
|
||||
ClientWidth = 611
|
||||
Color = clBtnFace
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -11
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
OldCreateOrder = False
|
||||
Position = poScreenCenter
|
||||
OnCreate = FormCreate
|
||||
OnDestroy = FormDestroy
|
||||
PixelsPerInch = 96
|
||||
TextHeight = 13
|
||||
object pcObservers: TPageControl
|
||||
AlignWithMargins = True
|
||||
Left = 8
|
||||
Top = 113
|
||||
Width = 595
|
||||
Height = 361
|
||||
Margins.Left = 8
|
||||
Margins.Top = 8
|
||||
Margins.Right = 8
|
||||
Margins.Bottom = 8
|
||||
ActivePage = tsNamedPipe
|
||||
Align = alClient
|
||||
TabOrder = 0
|
||||
OnChange = pcObserversChange
|
||||
object tsEvent: TTabSheet
|
||||
Caption = 'Event Observer '
|
||||
object mmoEvent: TMemo
|
||||
AlignWithMargins = True
|
||||
Left = 8
|
||||
Top = 8
|
||||
Width = 571
|
||||
Height = 317
|
||||
Margins.Left = 8
|
||||
Margins.Top = 8
|
||||
Margins.Right = 8
|
||||
Margins.Bottom = 8
|
||||
Align = alClient
|
||||
ReadOnly = True
|
||||
ScrollBars = ssVertical
|
||||
TabOrder = 0
|
||||
end
|
||||
end
|
||||
object tsFile: TTabSheet
|
||||
Caption = 'File Observer'
|
||||
ImageIndex = 1
|
||||
end
|
||||
object tsNamedPipe: TTabSheet
|
||||
Caption = 'Named Pipe Observer'
|
||||
ImageIndex = 2
|
||||
end
|
||||
end
|
||||
object pnlButtons: TPanel
|
||||
AlignWithMargins = True
|
||||
Left = 8
|
||||
Top = 482
|
||||
Width = 595
|
||||
Height = 25
|
||||
Margins.Left = 8
|
||||
Margins.Top = 0
|
||||
Margins.Right = 8
|
||||
Margins.Bottom = 8
|
||||
Align = alBottom
|
||||
BevelOuter = bvNone
|
||||
TabOrder = 1
|
||||
object btnClose: TButton
|
||||
Left = 520
|
||||
Top = 0
|
||||
Width = 75
|
||||
Height = 25
|
||||
Align = alRight
|
||||
Cancel = True
|
||||
Caption = 'Close'
|
||||
TabOrder = 0
|
||||
end
|
||||
end
|
||||
object GroupBox1: TGroupBox
|
||||
AlignWithMargins = True
|
||||
Left = 8
|
||||
Top = 8
|
||||
Width = 595
|
||||
Height = 97
|
||||
Margins.Left = 8
|
||||
Margins.Top = 8
|
||||
Margins.Right = 8
|
||||
Margins.Bottom = 0
|
||||
Align = alTop
|
||||
Caption = ' Dispatch '
|
||||
TabOrder = 2
|
||||
DesignSize = (
|
||||
595
|
||||
97)
|
||||
object lblMessage: TLabel
|
||||
Left = 16
|
||||
Top = 32
|
||||
Width = 46
|
||||
Height = 13
|
||||
Caption = 'Message:'
|
||||
end
|
||||
object lblException: TLabel
|
||||
Left = 16
|
||||
Top = 59
|
||||
Width = 51
|
||||
Height = 13
|
||||
Caption = 'Exception:'
|
||||
end
|
||||
object edtMessage: TEdit
|
||||
Left = 92
|
||||
Top = 29
|
||||
Width = 402
|
||||
Height = 21
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
TabOrder = 0
|
||||
Text = 'Hello world!'
|
||||
OnKeyDown = edtMessageKeyDown
|
||||
end
|
||||
object btnSend: TButton
|
||||
Left = 500
|
||||
Top = 29
|
||||
Width = 75
|
||||
Height = 21
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = '&Send'
|
||||
TabOrder = 1
|
||||
OnClick = btnSendClick
|
||||
end
|
||||
object edtException: TEdit
|
||||
Left = 92
|
||||
Top = 56
|
||||
Width = 402
|
||||
Height = 21
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
TabOrder = 2
|
||||
Text = 'Horrible things are happening.'
|
||||
OnKeyDown = edtExceptionKeyDown
|
||||
end
|
||||
object btnException: TButton
|
||||
Left = 500
|
||||
Top = 56
|
||||
Width = 75
|
||||
Height = 21
|
||||
Anchors = [akLeft, akTop, akRight]
|
||||
Caption = '&Send'
|
||||
TabOrder = 3
|
||||
OnClick = btnExceptionClick
|
||||
end
|
||||
end
|
||||
end
|
167
Test/source/MainFrm.pas
Normal file
167
Test/source/MainFrm.pas
Normal file
@ -0,0 +1,167 @@
|
||||
unit MainFrm;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.Classes,
|
||||
Vcl.ComCtrls,
|
||||
Vcl.Controls,
|
||||
Vcl.ExtCtrls,
|
||||
Vcl.Forms,
|
||||
Vcl.StdCtrls,
|
||||
|
||||
X2Log.Intf;
|
||||
|
||||
|
||||
type
|
||||
TMainForm = class(TForm)
|
||||
btnClose: TButton;
|
||||
btnSend: TButton;
|
||||
edtMessage: TEdit;
|
||||
GroupBox1: TGroupBox;
|
||||
lblMessage: TLabel;
|
||||
mmoEvent: TMemo;
|
||||
pcObservers: TPageControl;
|
||||
pnlButtons: TPanel;
|
||||
tsEvent: TTabSheet;
|
||||
tsFile: TTabSheet;
|
||||
lblException: TLabel;
|
||||
edtException: TEdit;
|
||||
btnException: TButton;
|
||||
tsNamedPipe: TTabSheet;
|
||||
|
||||
procedure FormCreate(Sender: TObject);
|
||||
procedure FormDestroy(Sender: TObject);
|
||||
procedure pcObserversChange(Sender: TObject);
|
||||
procedure btnSendClick(Sender: TObject);
|
||||
procedure edtExceptionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||
procedure edtMessageKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||
procedure btnExceptionClick(Sender: TObject);
|
||||
private
|
||||
FLog: IX2Log;
|
||||
FObserver: IX2LogObserver;
|
||||
protected
|
||||
procedure InitObserver;
|
||||
|
||||
procedure DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.SysUtils,
|
||||
Winapi.Windows,
|
||||
|
||||
X2Log,
|
||||
X2Log.Constants,
|
||||
X2Log.Exception.madExcept,
|
||||
X2Log.Observer.Event,
|
||||
X2Log.Observer.LogFile,
|
||||
X2Log.Observer.NamedPipe;
|
||||
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
{ TMainForm }
|
||||
procedure TMainForm.FormCreate(Sender: TObject);
|
||||
begin
|
||||
SetLogResourceString(@LogLevelVerbose, 'Uitgebreid');
|
||||
SetLogResourceString(@LogLevelInfo, 'Informatie');
|
||||
SetLogResourceString(@LogLevelWarning, 'Waarschuwing');
|
||||
SetLogResourceString(@LogLevelError, 'Fout');
|
||||
|
||||
FLog := TX2Log.Create;
|
||||
FLog.SetExceptionStrategy(TX2LogmadExceptExceptionStrategy.Create);
|
||||
|
||||
pcObservers.ActivePageIndex := 0;
|
||||
InitObserver;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.FormDestroy(Sender: TObject);
|
||||
begin
|
||||
FLog := nil;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.InitObserver;
|
||||
var
|
||||
activePage: TTabSheet;
|
||||
|
||||
begin
|
||||
if Assigned(FObserver) then
|
||||
begin
|
||||
FLog.Detach(FObserver);
|
||||
FObserver := nil;
|
||||
end;
|
||||
|
||||
activePage := pcObservers.ActivePage;
|
||||
|
||||
if activePage = tsEvent then
|
||||
FObserver := TX2LogEventObserver.Create(DoLog)
|
||||
else if activePage = tsFile then
|
||||
FObserver := TX2LogFileObserver.CreateInProgramData('X2LogTest\Test.log');
|
||||
|
||||
if activePage = tsNamedPipe then
|
||||
begin
|
||||
FObserver := TX2LogNamedPipeObserver.Create('X2LogTest');
|
||||
end else
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
if Assigned(FObserver) then
|
||||
FLog.Attach(FObserver);
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.DoLog(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string);
|
||||
begin
|
||||
mmoEvent.Lines.Add(GetLogLevelText(Level) + ': ' + Msg + ' (' + Details + ')');
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.edtMessageKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||
begin
|
||||
if Key = VK_RETURN then
|
||||
begin
|
||||
btnException.Click;
|
||||
Key := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.edtExceptionKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||
begin
|
||||
if Key = VK_RETURN then
|
||||
begin
|
||||
btnException.Click;
|
||||
Key := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.pcObserversChange(Sender: TObject);
|
||||
begin
|
||||
InitObserver;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.btnSendClick(Sender: TObject);
|
||||
begin
|
||||
FLog.Info(edtMessage.Text);
|
||||
end;
|
||||
|
||||
|
||||
procedure TMainForm.btnExceptionClick(Sender: TObject);
|
||||
begin
|
||||
try
|
||||
{ Throw an actual exception, don't just create it, to allow
|
||||
strategies like madExcept to do their stack trace }
|
||||
raise EAbort.Create(edtException.Text);
|
||||
except
|
||||
on E:Exception do
|
||||
FLog.Exception(E);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
89
X2Log.Constants.pas
Normal file
89
X2Log.Constants.pas
Normal file
@ -0,0 +1,89 @@
|
||||
unit X2Log.Constants;
|
||||
|
||||
interface
|
||||
uses
|
||||
X2Log.Intf;
|
||||
|
||||
|
||||
resourcestring
|
||||
LogLevelVerbose = 'Verbose';
|
||||
LogLevelInfo = 'Info';
|
||||
LogLevelWarning = 'Warning';
|
||||
LogLevelError = 'Error';
|
||||
|
||||
|
||||
{
|
||||
X2Log.Observer.LogFile
|
||||
}
|
||||
|
||||
{ Date format used to determine the file name of detail files }
|
||||
LogFileNameDateFormat = 'ddmmyyyy_hhnn';
|
||||
|
||||
{ Date format used in log files }
|
||||
LogFileLineDateFormat = 'dd-mm-yy hh:nn';
|
||||
|
||||
{ The text added to the message if details are stored externally }
|
||||
LogFileLineDetails = ' (details: %s)';
|
||||
|
||||
|
||||
|
||||
function GetLogLevelText(ALogLevel: TX2LogLevel): string;
|
||||
|
||||
function GetLogResourceString(AResourceString: Pointer): string;
|
||||
procedure SetLogResourceString(AResourceString: Pointer; const AValue: string);
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.Generics.Collections,
|
||||
System.SysUtils;
|
||||
|
||||
|
||||
var
|
||||
LogResourceStringMap: TDictionary<Pointer,string>;
|
||||
|
||||
|
||||
|
||||
function GetLogLevelText(ALogLevel: TX2LogLevel): string;
|
||||
begin
|
||||
case ALogLevel of
|
||||
TX2LogLevel.Verbose: Result := GetLogResourceString(@LogLevelVerbose);
|
||||
TX2LogLevel.Info: Result := GetLogResourceString(@LogLevelInfo);
|
||||
TX2LogLevel.Warning: Result := GetLogResourceString(@LogLevelWarning);
|
||||
TX2LogLevel.Error: Result := GetLogResourceString(@LogLevelError);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function GetLogResourceString(AResourceString: Pointer): string;
|
||||
begin
|
||||
TMonitor.Enter(LogResourceStringMap);
|
||||
try
|
||||
if LogResourceStringMap.ContainsKey(AResourceString) then
|
||||
Result := LogResourceStringMap[AResourceString]
|
||||
else
|
||||
Result := LoadResString(AResourceString);
|
||||
finally
|
||||
TMonitor.Exit(LogResourceStringMap);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure SetLogResourceString(AResourceString: Pointer; const AValue: string);
|
||||
begin
|
||||
TMonitor.Enter(LogResourceStringMap);
|
||||
try
|
||||
LogResourceStringMap.AddOrSetValue(AResourceString, AValue);
|
||||
finally
|
||||
TMonitor.Exit(LogResourceStringMap);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
LogResourceStringMap := TDictionary<Pointer,string>.Create;
|
||||
|
||||
finalization
|
||||
FreeAndNil(LogResourceStringMap);
|
||||
|
||||
end.
|
30
X2Log.Exception.Default.pas
Normal file
30
X2Log.Exception.Default.pas
Normal file
@ -0,0 +1,30 @@
|
||||
unit X2Log.Exception.Default;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.SysUtils,
|
||||
|
||||
X2Log.Intf;
|
||||
|
||||
|
||||
type
|
||||
TX2LogDefaultExceptionStrategy = class(TInterfacedObject, IX2LogExceptionStrategy)
|
||||
public
|
||||
{ IX2LogExceptionStrategy }
|
||||
procedure Execute(AException: Exception; var AMessage: string; var ADetails: string); virtual;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
{ TX2LogDefaultExceptionStrategy }
|
||||
procedure TX2LogDefaultExceptionStrategy.Execute(AException: Exception; var AMessage, ADetails: string);
|
||||
begin
|
||||
if Length(AMessage) > 0 then
|
||||
AMessage := AMessage + ': ';
|
||||
|
||||
AMessage := AMessage + AException.Message;
|
||||
end;
|
||||
|
||||
end.
|
35
X2Log.Exception.madExcept.pas
Normal file
35
X2Log.Exception.madExcept.pas
Normal file
@ -0,0 +1,35 @@
|
||||
unit X2Log.Exception.madExcept;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.SysUtils,
|
||||
|
||||
X2Log.Intf,
|
||||
X2Log.Exception.Default;
|
||||
|
||||
|
||||
type
|
||||
TX2LogmadExceptExceptionStrategy = class(TX2LogDefaultExceptionStrategy)
|
||||
public
|
||||
{ IX2LogExceptionStrategy }
|
||||
procedure Execute(AException: Exception; var AMessage: string; var ADetails: string); override;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
madExcept;
|
||||
|
||||
|
||||
{ TX2LogmadExceptExceptionStrategy }
|
||||
procedure TX2LogmadExceptExceptionStrategy.Execute(AException: Exception; var AMessage, ADetails: string);
|
||||
begin
|
||||
inherited Execute(AException, AMessage, ADetails);
|
||||
|
||||
if Length(ADetails) > 0 then
|
||||
ADetails := ADetails + #13#10;
|
||||
|
||||
ADetails := ADetails + madExcept.CreateBugReport(etNormal, AException);
|
||||
end;
|
||||
|
||||
end.
|
45
X2Log.Intf.pas
Normal file
45
X2Log.Intf.pas
Normal file
@ -0,0 +1,45 @@
|
||||
unit X2Log.Intf;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.SysUtils;
|
||||
|
||||
type
|
||||
TX2LogLevel = (Verbose, Info, Warning, Error);
|
||||
|
||||
|
||||
IX2LogMethods = interface
|
||||
['{1949E8DC-6DC5-43DC-B678-55CF8274E79D}']
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
||||
end;
|
||||
|
||||
|
||||
IX2LogObserver = interface(IX2LogMethods)
|
||||
['{CBC5C18E-84EE-43F4-8DBE-C66D06FCDE74}']
|
||||
end;
|
||||
|
||||
|
||||
IX2LogExceptionStrategy = interface
|
||||
['{C0B7950E-BE0A-4A21-A7C5-F8322FD4E205}']
|
||||
procedure Execute(AException: Exception; var AMessage: string; var ADetails: string);
|
||||
end;
|
||||
|
||||
|
||||
IX2Log = interface(IX2LogMethods)
|
||||
['{A6FF38F9-EDA8-4C76-9C95-2C0317560D78}']
|
||||
procedure Attach(AObserver: IX2LogObserver);
|
||||
procedure Detach(AObserver: IX2LogObserver);
|
||||
|
||||
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||
|
||||
procedure Verbose(const AMessage: string; const ADetails: string = '');
|
||||
procedure Info(const AMessage: string; const ADetails: string = '');
|
||||
procedure Warning(const AMessage: string; const ADetails: string = '');
|
||||
procedure Error(const AMessage: string; const ADetails: string = '');
|
||||
procedure Exception(AException: Exception; const AMessage: string = ''; const ADetails: string = '');
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
end.
|
52
X2Log.Observer.Custom.pas
Normal file
52
X2Log.Observer.Custom.pas
Normal file
@ -0,0 +1,52 @@
|
||||
unit X2Log.Observer.Custom;
|
||||
|
||||
interface
|
||||
uses
|
||||
Classes,
|
||||
SysUtils,
|
||||
|
||||
X2Log.Intf;
|
||||
|
||||
|
||||
const
|
||||
X2LogLevelsAll = [Low(TX2LogLevel)..High(TX2LogLevel)];
|
||||
X2LogLevelsDefault = X2LogLevelsAll - [Verbose];
|
||||
|
||||
type
|
||||
TX2LogLevels = set of TX2LogLevel;
|
||||
|
||||
TX2LogCustomObserver = class(TInterfacedObject, IX2LogObserver)
|
||||
private
|
||||
FLogLevels: TX2LogLevels;
|
||||
protected
|
||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); virtual; abstract;
|
||||
|
||||
{ IX2LogObserver }
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); virtual;
|
||||
|
||||
property LogLevels: TX2LogLevels read FLogLevels;
|
||||
public
|
||||
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
{ TX2LogCustomObserver }
|
||||
constructor TX2LogCustomObserver.Create(ALogLevels: TX2LogLevels);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FLogLevels := ALogLevels;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogCustomObserver.Log(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||
begin
|
||||
if ALevel in LogLevels then
|
||||
DoLog(ALevel, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
235
X2Log.Observer.CustomThreaded.pas
Normal file
235
X2Log.Observer.CustomThreaded.pas
Normal file
@ -0,0 +1,235 @@
|
||||
unit X2Log.Observer.CustomThreaded;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
System.SyncObjs,
|
||||
|
||||
X2Log.Intf,
|
||||
X2Log.Observer.Custom;
|
||||
|
||||
|
||||
type
|
||||
TX2LogObserverWorkerThread = class;
|
||||
|
||||
|
||||
TX2LogCustomThreadedObserver = class(TX2LogCustomObserver)
|
||||
private
|
||||
FWorkerThread: TX2LogObserverWorkerThread;
|
||||
protected
|
||||
function CreateWorkerThread: TX2LogObserverWorkerThread; virtual; abstract;
|
||||
|
||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); override;
|
||||
|
||||
property WorkerThread: TX2LogObserverWorkerThread read FWorkerThread;
|
||||
public
|
||||
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
|
||||
TX2LogQueueEntry = class(TPersistent)
|
||||
private
|
||||
FDetails: string;
|
||||
FLevel: TX2LogLevel;
|
||||
FMessage: string;
|
||||
public
|
||||
constructor Create(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string); overload;
|
||||
constructor Create(AEntry: TX2LogQueueEntry); overload;
|
||||
|
||||
procedure Assign(Source: TPersistent); override;
|
||||
|
||||
property Details: string read FDetails;
|
||||
property Level: TX2LogLevel read FLevel;
|
||||
property Message: string read FMessage;
|
||||
end;
|
||||
|
||||
|
||||
TX2LogObserverWorkerThread = class(TThread)
|
||||
private
|
||||
FFileName: string;
|
||||
FLogQueue: TObjectQueue<TX2LogQueueEntry>;
|
||||
FLogQueueSignal: TEvent;
|
||||
protected
|
||||
procedure Execute; override;
|
||||
procedure TerminatedSet; override;
|
||||
|
||||
procedure Setup; virtual;
|
||||
procedure Cleanup; virtual;
|
||||
|
||||
procedure WaitForEntry; virtual;
|
||||
procedure ProcessEntry(AEntry: TX2LogQueueEntry); virtual; abstract;
|
||||
|
||||
property FileName: string read FFileName;
|
||||
property LogQueue: TObjectQueue<TX2LogQueueEntry> read FLogQueue;
|
||||
property LogQueueSignal: TEvent read FLogQueueSignal;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.SysUtils;
|
||||
|
||||
|
||||
{ TX2LogCustomThreadedObserver }
|
||||
constructor TX2LogCustomThreadedObserver.Create(ALogLevels: TX2LogLevels);
|
||||
begin
|
||||
inherited Create(ALogLevels);
|
||||
|
||||
FWorkerThread := CreateWorkerThread;
|
||||
end;
|
||||
|
||||
|
||||
destructor TX2LogCustomThreadedObserver.Destroy;
|
||||
begin
|
||||
FreeAndNil(FWorkerThread);
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogCustomThreadedObserver.DoLog(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||
begin
|
||||
WorkerThread.Log(ALevel, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{ TX2LogQueueEntry }
|
||||
constructor TX2LogQueueEntry.Create(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FLevel := ALevel;
|
||||
FMessage := AMessage;
|
||||
FDetails := ADetails;
|
||||
end;
|
||||
|
||||
|
||||
constructor TX2LogQueueEntry.Create(AEntry: TX2LogQueueEntry);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
Assign(AEntry);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogQueueEntry.Assign(Source: TPersistent);
|
||||
var
|
||||
entrySource: TX2LogQueueEntry;
|
||||
|
||||
begin
|
||||
if Source is TX2LogQueueEntry then
|
||||
begin
|
||||
entrySource := TX2LogQueueEntry(Source);
|
||||
|
||||
FLevel := entrySource.Level;
|
||||
FMessage := entrySource.Message;
|
||||
FDetails := entrySource.Details;
|
||||
end else
|
||||
inherited Assign(Source);
|
||||
end;
|
||||
|
||||
|
||||
{ TX2LogObserverWorkerThread }
|
||||
constructor TX2LogObserverWorkerThread.Create;
|
||||
begin
|
||||
FLogQueueSignal := TEvent.Create(nil, False, False, '');
|
||||
FLogQueue := TObjectQueue<TX2LogQueueEntry>.Create(True);
|
||||
|
||||
inherited Create(False);
|
||||
end;
|
||||
|
||||
|
||||
destructor TX2LogObserverWorkerThread.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
|
||||
FreeAndNil(FLogQueue);
|
||||
FreeAndNil(FLogQueueSignal);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.Log(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||
begin
|
||||
TMonitor.Enter(LogQueue);
|
||||
try
|
||||
LogQueue.Enqueue(TX2LogQueueEntry.Create(ALevel, AMessage, ADetails));
|
||||
finally
|
||||
TMonitor.Exit(LogQueue);
|
||||
end;
|
||||
|
||||
LogQueueSignal.SetEvent;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.Execute;
|
||||
var
|
||||
entry: TX2LogQueueEntry;
|
||||
|
||||
begin
|
||||
Setup;
|
||||
try
|
||||
while not Terminated do
|
||||
begin
|
||||
WaitForEntry;
|
||||
|
||||
if Terminated then
|
||||
break;
|
||||
|
||||
entry := nil;
|
||||
TMonitor.Enter(LogQueue);
|
||||
try
|
||||
if LogQueue.Count > 0 then
|
||||
entry := LogQueue.Extract;
|
||||
finally
|
||||
TMonitor.Exit(LogQueue);
|
||||
end;
|
||||
|
||||
if Assigned(entry) then
|
||||
try
|
||||
ProcessEntry(entry);
|
||||
finally
|
||||
FreeAndNil(entry);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
Cleanup;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.Setup;
|
||||
begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.Cleanup;
|
||||
begin
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.WaitForEntry;
|
||||
begin
|
||||
case LogQueueSignal.WaitFor(INFINITE) of
|
||||
wrAbandoned,
|
||||
wrError:
|
||||
Terminate;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogObserverWorkerThread.TerminatedSet;
|
||||
begin
|
||||
LogQueueSignal.SetEvent;
|
||||
|
||||
inherited TerminatedSet;
|
||||
end;
|
||||
|
||||
end.
|
50
X2Log.Observer.Event.pas
Normal file
50
X2Log.Observer.Event.pas
Normal file
@ -0,0 +1,50 @@
|
||||
unit X2Log.Observer.Event;
|
||||
|
||||
interface
|
||||
uses
|
||||
X2Log.Intf,
|
||||
X2Log.Observer.Custom;
|
||||
|
||||
|
||||
type
|
||||
TX2LogEvent = procedure(Sender: TObject; Level: TX2LogLevel; const Msg, Details: string) of object;
|
||||
|
||||
|
||||
TX2LogEventObserver = class(TX2LogCustomObserver)
|
||||
private
|
||||
FOnLog: TX2LogEvent;
|
||||
protected
|
||||
procedure DoLog(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = ''); override;
|
||||
public
|
||||
constructor Create(ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
|
||||
constructor Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels = X2LogLevelsDefault); overload;
|
||||
|
||||
property OnLog: TX2LogEvent read FOnLog write FOnLog;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
|
||||
{ TX2LogEventObserver }
|
||||
constructor TX2LogEventObserver.Create(ALogLevels: TX2LogLevels);
|
||||
begin
|
||||
inherited Create(ALogLevels);
|
||||
end;
|
||||
|
||||
|
||||
constructor TX2LogEventObserver.Create(AOnLog: TX2LogEvent; ALogLevels: TX2LogLevels);
|
||||
begin
|
||||
Create(ALogLevels);
|
||||
|
||||
FOnLog := AOnLog;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogEventObserver.DoLog(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||
begin
|
||||
if Assigned(FOnLog) then
|
||||
FOnLog(Self, ALevel, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
end.
|
185
X2Log.Observer.LogFile.pas
Normal file
185
X2Log.Observer.LogFile.pas
Normal file
@ -0,0 +1,185 @@
|
||||
unit X2Log.Observer.LogFile;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
System.SyncObjs,
|
||||
|
||||
X2Log.Intf,
|
||||
X2Log.Observer.Custom,
|
||||
X2Log.Observer.CustomThreaded;
|
||||
|
||||
|
||||
type
|
||||
TX2LogFileObserver = class(TX2LogCustomThreadedObserver)
|
||||
private
|
||||
FFileName: string;
|
||||
protected
|
||||
function CreateWorkerThread: TX2LogObserverWorkerThread; override;
|
||||
public
|
||||
constructor Create(const AFileName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
||||
constructor CreateInProgramData(const AFileName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
||||
constructor CreateInUserAppData(const AFileName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.IOUtils,
|
||||
System.SysUtils,
|
||||
System.Win.ComObj,
|
||||
Winapi.SHFolder,
|
||||
Winapi.Windows,
|
||||
|
||||
X2Log.Constants;
|
||||
|
||||
|
||||
type
|
||||
TX2LogFileWorkerThread = class(TX2LogObserverWorkerThread)
|
||||
private
|
||||
FFileName: string;
|
||||
protected
|
||||
procedure ProcessEntry(AEntry: TX2LogQueueEntry); override;
|
||||
|
||||
property FileName: string read FFileName;
|
||||
public
|
||||
constructor Create(const AFileName: string);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{ TX2LogFileObserver }
|
||||
constructor TX2LogFileObserver.Create(const AFileName: string; ALogLevels: TX2LogLevels);
|
||||
begin
|
||||
FFileName := AFileName;
|
||||
|
||||
inherited Create(ALogLevels);
|
||||
end;
|
||||
|
||||
|
||||
constructor TX2LogFileObserver.CreateInProgramData(const AFileName: string; ALogLevels: TX2LogLevels);
|
||||
var
|
||||
path: PWideChar;
|
||||
|
||||
begin
|
||||
GetMem(path, MAX_PATH);
|
||||
try
|
||||
OleCheck(SHGetFolderPath(0, CSIDL_COMMON_APPDATA, 0, SHGFP_TYPE_CURRENT, path));
|
||||
Create(IncludeTrailingPathDelimiter(path) + AFileName, ALogLevels);
|
||||
finally
|
||||
FreeMem(path);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
constructor TX2LogFileObserver.CreateInUserAppData(const AFileName: string; ALogLevels: TX2LogLevels);
|
||||
var
|
||||
path: PWideChar;
|
||||
|
||||
begin
|
||||
GetMem(path, MAX_PATH);
|
||||
try
|
||||
OleCheck(SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, path));
|
||||
Create(IncludeTrailingPathDelimiter(path) + AFileName, ALogLevels);
|
||||
finally
|
||||
FreeMem(path);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TX2LogFileObserver.CreateWorkerThread: TX2LogObserverWorkerThread;
|
||||
begin
|
||||
Result := TX2LogFileWorkerThread.Create(FFileName);
|
||||
end;
|
||||
|
||||
|
||||
{ TX2LogFileWorkerThread }
|
||||
constructor TX2LogFileWorkerThread.Create(const AFileName: string);
|
||||
begin
|
||||
FFileName := AFileName;
|
||||
|
||||
inherited Create;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogFileWorkerThread.ProcessEntry(AEntry: TX2LogQueueEntry);
|
||||
var
|
||||
baseReportFileName: string;
|
||||
errorMsg: string;
|
||||
detailsExtension: string;
|
||||
detailsFile: THandle;
|
||||
detailsFileStream: THandleStream;
|
||||
detailsWriter: TStreamWriter;
|
||||
detailsFileName: string;
|
||||
detailsNumber: Integer;
|
||||
writer: TStreamWriter;
|
||||
|
||||
begin
|
||||
ForceDirectories(ExtractFilePath(FileName));
|
||||
errorMsg := AEntry.Message;
|
||||
|
||||
if Length(AEntry.Details) > 0 then
|
||||
begin
|
||||
detailsExtension := ExtractFileExt(FileName);
|
||||
baseReportFileName := ChangeFileExt(FileName, '_' + FormatDateTime(GetLogResourceString(@LogFileNameDateFormat), Now));
|
||||
detailsFileName := baseReportFileName + detailsExtension;
|
||||
detailsNumber := 0;
|
||||
|
||||
if ForceDirectories(ExtractFilePath(detailsFileName)) then
|
||||
begin
|
||||
repeat
|
||||
{ TFileStream lacks the ability to create a file only when it does not exist }
|
||||
detailsFile := CreateFile(PChar(detailsFileName), GENERIC_READ or GENERIC_WRITE,
|
||||
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL, 0);
|
||||
|
||||
if detailsFile = INVALID_HANDLE_VALUE then
|
||||
begin
|
||||
if GetLastError = ERROR_FILE_EXISTS then
|
||||
begin
|
||||
{ Generate a new file name }
|
||||
Inc(detailsNumber);
|
||||
detailsFileName := Format('%s_%d%s', [baseReportFileName, detailsNumber,
|
||||
detailsExtension]);
|
||||
end else
|
||||
break;
|
||||
end else
|
||||
begin
|
||||
{ Details file succesfully generated }
|
||||
try
|
||||
detailsFileStream := THandleStream.Create(detailsFile);
|
||||
try
|
||||
detailsWriter := TStreamWriter.Create(detailsFileStream, TEncoding.ANSI);
|
||||
try
|
||||
detailsWriter.Write(AEntry.Details);
|
||||
finally
|
||||
FreeAndNil(detailsWriter);
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(detailsFileStream);
|
||||
end;
|
||||
finally
|
||||
CloseHandle(detailsFile);
|
||||
end;
|
||||
|
||||
// ErrorLogs.Add(reportFileName);
|
||||
|
||||
errorMsg := errorMsg + Format(GetLogResourceString(@LogFileLineDetails), [ExtractFileName(detailsFileName)]);
|
||||
break;
|
||||
end;
|
||||
until False;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Append line to log file }
|
||||
writer := TFile.AppendText(FileName);
|
||||
try
|
||||
writer.WriteLine('[' + FormatDateTime(GetLogResourceString(@LogFileLineDateFormat), Now) + '] ' +
|
||||
GetLogLevelText(AEntry.Level) + ': ' + errorMsg);
|
||||
finally
|
||||
FreeAndNil(writer);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
422
X2Log.Observer.NamedPipe.pas
Normal file
422
X2Log.Observer.NamedPipe.pas
Normal file
@ -0,0 +1,422 @@
|
||||
unit X2Log.Observer.NamedPipe;
|
||||
|
||||
interface
|
||||
uses
|
||||
X2Log.Intf,
|
||||
X2Log.Observer.Custom,
|
||||
X2Log.Observer.CustomThreaded;
|
||||
|
||||
|
||||
type
|
||||
TX2LogNamedPipeObserver = class(TX2LogCustomThreadedObserver)
|
||||
private
|
||||
FPipeName: string;
|
||||
protected
|
||||
function CreateWorkerThread: TX2LogObserverWorkerThread; override;
|
||||
public
|
||||
constructor Create(const APipeName: string; ALogLevels: TX2LogLevels = X2LogLevelsDefault);
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
System.Generics.Collections,
|
||||
System.SyncObjs,
|
||||
System.SysUtils,
|
||||
Winapi.Windows;
|
||||
|
||||
|
||||
type
|
||||
EX2LogSilentException = class(Exception);
|
||||
EX2LogPipeDisconnected = class(EX2LogSilentException);
|
||||
|
||||
TX2LogNamedPipeClientState = (Listening, Connected, Writing);
|
||||
|
||||
TX2LogNamedPipeClient = class(TObject)
|
||||
private
|
||||
FOverlapped: TOverlapped;
|
||||
FPipe: THandle;
|
||||
FState: TX2LogNamedPipeClientState;
|
||||
FOverlappedEvent: TEvent;
|
||||
FWriteQueue: TObjectQueue<TX2LogQueueEntry>;
|
||||
FWriteBuffer: Pointer;
|
||||
FWriteBufferSize: Integer;
|
||||
protected
|
||||
function DoSend(AEntry: TX2LogQueueEntry): Boolean;
|
||||
procedure ClearWriteBuffer;
|
||||
public
|
||||
constructor Create(APipe: THandle);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Send(AEntry: TX2LogQueueEntry);
|
||||
procedure SendNext;
|
||||
|
||||
procedure Disconnect;
|
||||
|
||||
property Pipe: THandle read FPipe;
|
||||
property Overlapped: TOverlapped read FOverlapped;
|
||||
property OverlappedEvent: TEvent read FOverlappedEvent;
|
||||
property State: TX2LogNamedPipeClientState read FState write FState;
|
||||
end;
|
||||
|
||||
|
||||
TX2LogNamedPipeWorkerThread = class(TX2LogObserverWorkerThread)
|
||||
private
|
||||
FClients: TObjectList<TX2LogNamedPipeClient>;
|
||||
FPipeName: string;
|
||||
protected
|
||||
|
||||
procedure WaitForEntry; override;
|
||||
procedure ProcessEntry(AEntry: TX2LogQueueEntry); override;
|
||||
procedure ProcessClientEvent(AClientIndex: Integer);
|
||||
|
||||
procedure AddListener;
|
||||
procedure RemoveClient(AClientIndex: Integer);
|
||||
procedure Setup; override;
|
||||
procedure Cleanup; override;
|
||||
|
||||
property Clients: TObjectList<TX2LogNamedPipeClient> read FClients;
|
||||
property PipeName: string read FPipeName;
|
||||
public
|
||||
constructor Create(const APipeName: string);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
|
||||
{ TX2LogNamedPipeObserver }
|
||||
constructor TX2LogNamedPipeObserver.Create(const APipeName: string; ALogLevels: TX2LogLevels);
|
||||
begin
|
||||
FPipeName := APipeName;
|
||||
|
||||
inherited Create(ALogLevels);
|
||||
end;
|
||||
|
||||
|
||||
function TX2LogNamedPipeObserver.CreateWorkerThread: TX2LogObserverWorkerThread;
|
||||
begin
|
||||
Result := TX2LogNamedPipeWorkerThread.Create(FPipeName);
|
||||
end;
|
||||
|
||||
|
||||
{ TX2LogNamedPipeClient }
|
||||
constructor TX2LogNamedPipeClient.Create(APipe: THandle);
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FPipe := APipe;
|
||||
FState := Listening;
|
||||
|
||||
FOverlappedEvent := TEvent.Create(nil, False, False, '');
|
||||
FOverlapped.hEvent := FOverlappedEvent.Handle;
|
||||
end;
|
||||
|
||||
|
||||
destructor TX2LogNamedPipeClient.Destroy;
|
||||
begin
|
||||
FreeAndNil(FOverlappedEvent);
|
||||
|
||||
if FPipe <> INVALID_HANDLE_VALUE then
|
||||
DisconnectNamedPipe(FPipe);
|
||||
|
||||
ClearWriteBuffer;
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeClient.Send(AEntry: TX2LogQueueEntry);
|
||||
begin
|
||||
if not Assigned(FWriteBuffer) then
|
||||
DoSend(AEntry)
|
||||
else
|
||||
begin
|
||||
if not Assigned(FWriteQueue) then
|
||||
FWriteQueue := TObjectQueue<TX2LogQueueEntry>.Create(True);
|
||||
|
||||
FWriteQueue.Enqueue(TX2LogQueueEntry.Create(AEntry));
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeClient.SendNext;
|
||||
var
|
||||
entry: TX2LogQueueEntry;
|
||||
|
||||
begin
|
||||
ClearWriteBuffer;
|
||||
|
||||
while FWriteQueue.Count > 0 do
|
||||
begin
|
||||
entry := FWriteQueue.Extract;
|
||||
try
|
||||
{ Returns False when IO is pending }
|
||||
if not DoSend(entry) then
|
||||
break;
|
||||
finally
|
||||
FreeAndNil(entry);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeClient.Disconnect;
|
||||
begin
|
||||
if FPipe <> INVALID_HANDLE_VALUE then
|
||||
begin
|
||||
CancelIo(FPipe);
|
||||
DisconnectNamedPipe(FPipe);
|
||||
|
||||
FPipe := INVALID_HANDLE_VALUE;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TX2LogNamedPipeClient.DoSend(AEntry: TX2LogQueueEntry): Boolean;
|
||||
|
||||
procedure AppendToBuffer(var APointer: PByte; const ASource; ASize: Cardinal); overload; inline;
|
||||
begin
|
||||
Move(ASource, APointer^, ASize);
|
||||
Inc(APointer, ASize);
|
||||
end;
|
||||
|
||||
procedure AppendToBuffer(var APointer: PByte; const ASource: string); overload; inline;
|
||||
var
|
||||
sourceLength: Cardinal;
|
||||
|
||||
begin
|
||||
sourceLength := Length(ASource);
|
||||
AppendToBuffer(APointer, sourceLength, SizeOf(Cardinal));
|
||||
AppendToBuffer(APointer, PChar(ASource)^, sourceLength * SizeOf(Char));
|
||||
end;
|
||||
|
||||
|
||||
var
|
||||
bytesWritten: Cardinal;
|
||||
bufferPointer: PByte;
|
||||
lastError: Cardinal;
|
||||
|
||||
begin
|
||||
ClearWriteBuffer;
|
||||
|
||||
FWriteBufferSize := SizeOf(TX2LogLevel) +
|
||||
SizeOf(Cardinal) + (Length(AEntry.Message) * SizeOf(Char)) +
|
||||
SizeOf(Cardinal) + (Length(AEntry.Details) * SizeOf(Char));
|
||||
|
||||
GetMem(FWriteBuffer, FWriteBufferSize);
|
||||
|
||||
bufferPointer := FWriteBuffer;
|
||||
AppendToBuffer(bufferPointer, AEntry.Level, SizeOf(TX2LogLevel));
|
||||
AppendToBuffer(bufferPointer, AEntry.Message);
|
||||
AppendToBuffer(bufferPointer, AEntry.Details);
|
||||
|
||||
Result := WriteFile(Pipe, FWriteBuffer^, FWriteBufferSize, bytesWritten, @Overlapped);
|
||||
if not Result then
|
||||
begin
|
||||
lastError := GetLastError;
|
||||
if lastError in [ERROR_NO_DATA, ERROR_PIPE_NOT_CONNECTED] then
|
||||
raise EX2LogPipeDisconnected.Create('Client disconnected');
|
||||
|
||||
if lastError = ERROR_IO_PENDING then
|
||||
State := Writing
|
||||
else
|
||||
begin
|
||||
ClearWriteBuffer;
|
||||
RaiseLastOSError;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
ClearWriteBuffer;
|
||||
State := Connected;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeClient.ClearWriteBuffer;
|
||||
begin
|
||||
if Assigned(FWriteBuffer) then
|
||||
begin
|
||||
FreeMem(FWriteBuffer, FWriteBufferSize);
|
||||
FWriteBuffer := nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{ TX2LogNamedPipeWorkerThread }
|
||||
constructor TX2LogNamedPipeWorkerThread.Create(const APipeName: string);
|
||||
begin
|
||||
FPipeName := APipeName;
|
||||
FClients := TObjectList<TX2LogNamedPipeClient>.Create(True);
|
||||
|
||||
inherited Create;
|
||||
end;
|
||||
|
||||
|
||||
destructor TX2LogNamedPipeWorkerThread.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
|
||||
FreeAndNil(FClients);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.Setup;
|
||||
begin
|
||||
inherited Setup;
|
||||
|
||||
AddListener;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.Cleanup;
|
||||
var
|
||||
client: TX2LogNamedPipeClient;
|
||||
|
||||
begin
|
||||
for client in Clients do
|
||||
client.Disconnect;
|
||||
|
||||
inherited Cleanup;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.WaitForEntry;
|
||||
var
|
||||
eventHandles: array of THandle;
|
||||
clientIndex: Integer;
|
||||
waitResult: Cardinal;
|
||||
|
||||
begin
|
||||
repeat
|
||||
SetLength(eventHandles, Clients.Count + 1);
|
||||
for clientIndex := 0 to Pred(Clients.Count) do
|
||||
eventHandles[clientIndex] := Clients[clientIndex].OverlappedEvent.Handle;
|
||||
|
||||
eventHandles[Clients.Count] := LogQueueSignal.Handle;
|
||||
|
||||
waitResult := WaitForMultipleObjects(Length(eventHandles), @eventHandles[0], False, INFINITE);
|
||||
if waitResult in [WAIT_OBJECT_0..WAIT_OBJECT_0 + Pred(High(eventHandles))] then
|
||||
begin
|
||||
{ Connect or write I/O completed }
|
||||
clientIndex := waitResult - WAIT_OBJECT_0;
|
||||
if (clientIndex >= 0) and (clientIndex < Clients.Count) then
|
||||
ProcessClientEvent(clientIndex);
|
||||
end else if waitResult = Cardinal(WAIT_OBJECT_0 + High(eventHandles)) then
|
||||
begin
|
||||
{ Entry queued }
|
||||
break;
|
||||
end else if waitResult in [WAIT_ABANDONED_0..WAIT_ABANDONED_0 + High(eventHandles)] then
|
||||
begin
|
||||
{ Client event abandoned }
|
||||
clientIndex := waitResult - WAIT_ABANDONED_0;
|
||||
if (clientIndex >= 0) and (clientIndex < Clients.Count) then
|
||||
RemoveClient(clientIndex)
|
||||
else if clientIndex = Clients.Count then
|
||||
Terminate;
|
||||
end else if waitResult = WAIT_FAILED then
|
||||
RaiseLastOSError;
|
||||
until False;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.ProcessEntry(AEntry: TX2LogQueueEntry);
|
||||
var
|
||||
clientIndex: Integer;
|
||||
client: TX2LogNamedPipeClient;
|
||||
|
||||
begin
|
||||
{ Broadcast to connected clients }
|
||||
for clientIndex := Pred(Clients.Count) downto 0 do
|
||||
begin
|
||||
client := Clients[clientIndex];
|
||||
|
||||
if client.State <> Listening then
|
||||
try
|
||||
client.Send(AEntry);
|
||||
except
|
||||
on E:EX2LogPipeDisconnected do
|
||||
RemoveClient(clientIndex);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.ProcessClientEvent(AClientIndex: Integer);
|
||||
var
|
||||
client: TX2LogNamedPipeClient;
|
||||
bytesTransferred: Cardinal;
|
||||
|
||||
begin
|
||||
client := Clients[AClientIndex];
|
||||
|
||||
case client.State of
|
||||
Listening:
|
||||
{ Client connected }
|
||||
if GetOverlappedResult(client.Pipe, client.Overlapped, bytesTransferred, False) then
|
||||
begin
|
||||
client.State := Connected;
|
||||
AddListener;
|
||||
end else
|
||||
RemoveClient(AClientIndex);
|
||||
|
||||
Writing:
|
||||
{ Write operation completed }
|
||||
if GetOverlappedResult(client.Pipe, client.Overlapped, bytesTransferred, False) and
|
||||
(bytesTransferred > 0) then
|
||||
begin
|
||||
try
|
||||
client.SendNext;
|
||||
except
|
||||
on E:EX2LogPipeDisconnected do
|
||||
RemoveClient(AClientIndex);
|
||||
end;
|
||||
end else
|
||||
RemoveClient(AClientIndex);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.AddListener;
|
||||
const
|
||||
BufferSize = 4096;
|
||||
DefaultTimeout = 5000;
|
||||
|
||||
var
|
||||
pipe: THandle;
|
||||
client: TX2LogNamedPipeClient;
|
||||
|
||||
begin
|
||||
pipe := CreateNamedPipe(PChar('\\.\pipe\' + PipeName), PIPE_ACCESS_OUTBOUND or FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
|
||||
BufferSize, BufferSize, DefaultTimeout, nil);
|
||||
|
||||
if pipe <> INVALID_HANDLE_VALUE then
|
||||
begin
|
||||
client := TX2LogNamedPipeClient.Create(pipe);
|
||||
|
||||
if not ConnectNamedPipe(client.Pipe, @client.Overlapped) then
|
||||
begin
|
||||
case GetLastError of
|
||||
ERROR_IO_PENDING:
|
||||
Clients.Add(client);
|
||||
|
||||
ERROR_PIPE_CONNECTED:
|
||||
begin
|
||||
client.State := Connected;
|
||||
Clients.Add(client);
|
||||
end;
|
||||
else
|
||||
{ Error occured }
|
||||
FreeAndNil(client);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2LogNamedPipeWorkerThread.RemoveClient(AClientIndex: Integer);
|
||||
begin
|
||||
Clients.Delete(AClientIndex);
|
||||
end;
|
||||
|
||||
end.
|
48
X2Log.groupproj
Normal file
48
X2Log.groupproj
Normal file
@ -0,0 +1,48 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{66C17964-1E71-4A50-A5F4-D04EB6A833D3}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Projects Include="Test\X2LogTest.dproj">
|
||||
<Dependencies/>
|
||||
</Projects>
|
||||
<Projects Include="NamedPipeClient\X2LogNamedPipeClient.dproj">
|
||||
<Dependencies/>
|
||||
</Projects>
|
||||
</ItemGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>Default.Personality.12</Borland.Personality>
|
||||
<Borland.ProjectType/>
|
||||
<BorlandProject>
|
||||
<Default.Personality/>
|
||||
</BorlandProject>
|
||||
</ProjectExtensions>
|
||||
<Target Name="X2LogTest">
|
||||
<MSBuild Projects="Test\X2LogTest.dproj"/>
|
||||
</Target>
|
||||
<Target Name="X2LogTest:Clean">
|
||||
<MSBuild Projects="Test\X2LogTest.dproj" Targets="Clean"/>
|
||||
</Target>
|
||||
<Target Name="X2LogTest:Make">
|
||||
<MSBuild Projects="Test\X2LogTest.dproj" Targets="Make"/>
|
||||
</Target>
|
||||
<Target Name="X2LogNamedPipeClient">
|
||||
<MSBuild Projects="NamedPipeClient\X2LogNamedPipeClient.dproj"/>
|
||||
</Target>
|
||||
<Target Name="X2LogNamedPipeClient:Clean">
|
||||
<MSBuild Projects="NamedPipeClient\X2LogNamedPipeClient.dproj" Targets="Clean"/>
|
||||
</Target>
|
||||
<Target Name="X2LogNamedPipeClient:Make">
|
||||
<MSBuild Projects="NamedPipeClient\X2LogNamedPipeClient.dproj" Targets="Make"/>
|
||||
</Target>
|
||||
<Target Name="Build">
|
||||
<CallTarget Targets="X2LogTest;X2LogNamedPipeClient"/>
|
||||
</Target>
|
||||
<Target Name="Clean">
|
||||
<CallTarget Targets="X2LogTest:Clean;X2LogNamedPipeClient:Clean"/>
|
||||
</Target>
|
||||
<Target Name="Make">
|
||||
<CallTarget Targets="X2LogTest:Make;X2LogNamedPipeClient:Make"/>
|
||||
</Target>
|
||||
<Import Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')" Project="$(BDS)\Bin\CodeGear.Group.Targets"/>
|
||||
</Project>
|
134
X2Log.pas
Normal file
134
X2Log.pas
Normal file
@ -0,0 +1,134 @@
|
||||
unit X2Log;
|
||||
|
||||
interface
|
||||
uses
|
||||
System.Classes,
|
||||
System.Generics.Collections,
|
||||
System.SysUtils,
|
||||
|
||||
X2Log.Intf;
|
||||
|
||||
|
||||
type
|
||||
TX2Log = class(TInterfacedObject, IX2Log, IX2LogMethods)
|
||||
private
|
||||
FExceptionStrategy: IX2LogExceptionStrategy;
|
||||
FObservers: TList<IX2LogObserver>;
|
||||
private
|
||||
property ExceptionStrategy: IX2LogExceptionStrategy read FExceptionStrategy;
|
||||
property Observers: TList<IX2LogObserver> read FObservers;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
{ IX2Log }
|
||||
procedure Attach(AObserver: IX2LogObserver);
|
||||
procedure Detach(AObserver: IX2LogObserver);
|
||||
|
||||
procedure SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||
|
||||
{ IX2LogMethods }
|
||||
procedure Log(ALevel: TX2LogLevel; const AMessage: string; const ADetails: string = '');
|
||||
|
||||
procedure Verbose(const AMessage: string; const ADetails: string = '');
|
||||
procedure Info(const AMessage: string; const ADetails: string = '');
|
||||
procedure Warning(const AMessage: string; const ADetails: string = '');
|
||||
procedure Error(const AMessage: string; const ADetails: string = '');
|
||||
procedure Exception(AException: Exception; const AMessage: string = ''; const ADetails: string = '');
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
uses
|
||||
X2Log.Exception.Default;
|
||||
|
||||
|
||||
{ TX2Log }
|
||||
constructor TX2Log.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FObservers := TList<IX2LogObserver>.Create;
|
||||
SetExceptionStrategy(nil);
|
||||
end;
|
||||
|
||||
|
||||
destructor TX2Log.Destroy;
|
||||
begin
|
||||
FreeAndNil(FObservers);
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Attach(AObserver: IX2LogObserver);
|
||||
begin
|
||||
{ Explicit cast ensures we're getting the same pointer in Attach and Detach
|
||||
if, for example, the implementing interface is a descendant of IX2LogObserver }
|
||||
Observers.Add(AObserver as IX2LogObserver);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Detach(AObserver: IX2LogObserver);
|
||||
begin
|
||||
Observers.Remove(AObserver as IX2LogObserver);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.SetExceptionStrategy(AStrategy: IX2LogExceptionStrategy);
|
||||
begin
|
||||
if Assigned(AStrategy) then
|
||||
FExceptionStrategy := AStrategy
|
||||
else
|
||||
FExceptionStrategy := TX2LogDefaultExceptionStrategy.Create;
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Log(ALevel: TX2LogLevel; const AMessage, ADetails: string);
|
||||
var
|
||||
observer: IX2LogObserver;
|
||||
|
||||
begin
|
||||
for observer in Observers do
|
||||
observer.Log(ALevel, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Verbose(const AMessage, ADetails: string);
|
||||
begin
|
||||
Log(TX2LogLevel.Verbose, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Info(const AMessage, ADetails: string);
|
||||
begin
|
||||
Log(TX2LogLevel.Info, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Warning(const AMessage, ADetails: string);
|
||||
begin
|
||||
Log(TX2LogLevel.Warning, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Error(const AMessage, ADetails: string);
|
||||
begin
|
||||
Log(TX2LogLevel.Error, AMessage, ADetails);
|
||||
end;
|
||||
|
||||
|
||||
procedure TX2Log.Exception(AException: Exception; const AMessage, ADetails: string);
|
||||
var
|
||||
msg: string;
|
||||
details: string;
|
||||
|
||||
begin
|
||||
msg := AMessage;
|
||||
details := ADetails;
|
||||
|
||||
ExceptionStrategy.Execute(AException, msg, details);
|
||||
Log(TX2LogLevel.Error, msg, details);
|
||||
end;
|
||||
|
||||
end.
|
Loading…
Reference in New Issue
Block a user