diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4da76bc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+AssettoCorsa/source/__history
+AssettoCorsa/*.dproj.local
+AssettoCorsa/bin
+AssettoCorsa/lib
\ No newline at end of file
diff --git a/AssettoCorsa/AssettoCorsaSF.dpr b/AssettoCorsa/AssettoCorsaSF.dpr
new file mode 100644
index 0000000..5ebe640
--- /dev/null
+++ b/AssettoCorsa/AssettoCorsaSF.dpr
@@ -0,0 +1,19 @@
+program AssettoCorsaSF;
+
+uses
+ Vcl.Forms,
+ MainFrm in 'source\MainFrm.pas' {MainForm},
+ AssettoCorsa.SharedMemory in 'source\AssettoCorsa.SharedMemory.pas',
+ CPort in 'source\CPort.pas';
+
+{$R *.res}
+
+var
+ MainForm: TMainForm;
+
+begin
+ Application.Initialize;
+ Application.MainFormOnTaskbar := True;
+ Application.CreateForm(TMainForm, MainForm);
+ Application.Run;
+end.
diff --git a/AssettoCorsa/AssettoCorsaSF.dproj b/AssettoCorsa/AssettoCorsaSF.dproj
new file mode 100644
index 0000000..bc94af9
--- /dev/null
+++ b/AssettoCorsa/AssettoCorsaSF.dproj
@@ -0,0 +1,155 @@
+
+
+ {3CBA51E1-33C8-4599-8659-3A1F26AA5E70}
+ 13.4
+ VCL
+ AssettoCorsaSF.dpr
+ True
+ Debug
+ Win32
+ 1
+ Application
+
+
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Base
+ true
+
+
+ CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=
+ None
+ 1043
+ System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)
+ $(BDS)\bin\delphi_PROJECTICON.ico
+ bindcompfmx;fmx;rtl;dbrtl;IndySystem;DbxClientDriver;bindcomp;inetdb;DBXInterBaseDriver;DataSnapCommon;DataSnapClient;DataSnapServer;DataSnapProviderClient;xmlrtl;DbxCommonDriver;IndyProtocols;DBXMySQLDriver;dbxcds;soaprtl;FMXTee;bindengine;DBXOracleDriver;CustomIPTransport;dsnap;DBXInformixDriver;IndyCore;FmxTeeUI;DBXFirebirdDriver;inet;inetdbxpress;DBXSybaseASADriver;IPIndyImpl;dbexpress;DataSnapIndy10ServerTransport;$(DCC_UsePackage)
+ .\$(Platform)\$(Config)
+ .\$(Platform)\$(Config)
+ false
+ false
+ false
+ false
+ false
+
+
+ DBXOdbcDriver;DBXSybaseASEDriver;vclimg;vclactnband;vcldb;bindcompvcl;vcldsnap;vclie;vcltouch;DBXDb2Driver;websnap;VclSmp;vcl;DBXMSSQLDriver;dsnapcon;vclx;webdsnap;$(DCC_UsePackage)
+
+
+ .\bin\x86
+ true
+ Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
+ madBasic_;vcldbx;TeeDB;vclib;inetdbbde;Tee;DBXOdbcDriver;madDisAsm_;svnui;ibxpress;DBXSybaseASEDriver;vclimg;vclactnband;vcldb;TeeUI;bindcompvcl;vcldsnap;vclie;madExcept_;vcltouch;DBXDb2Driver;websnap;VclSmp;vcl;DataSnapConnectors;CloudService;DBXMSSQLDriver;dsnapcon;vclx;webdsnap;svn;bdertl;VirtualTreesR;$(DCC_UsePackage)
+ .\lib\x86
+ 1033
+ $(BDS)\bin\default_app.manifest
+
+
+ DEBUG;$(DCC_Define)
+ false
+ true
+ true
+ true
+
+
+ true
+ 1033
+ false
+
+
+ false
+ RELEASE;$(DCC_Define)
+ 0
+ false
+
+
+
+ MainSource
+
+
+
+ dfm
+
+
+
+
+ Cfg_2
+ Base
+
+
+ Base
+
+
+ Cfg_1
+ Base
+
+
+
+ Delphi.Personality.12
+
+
+
+
+
+ False
+ False
+ 1
+ 0
+ 0
+ 0
+ False
+ False
+ False
+ False
+ False
+ 1043
+ 1252
+
+
+
+
+ 1.0.0.0
+
+
+
+
+
+ 1.0.0.0
+
+
+
+
+
+ False
+ True
+
+
+ 12
+
+
+
+
diff --git a/AssettoCorsa/AssettoCorsaSF.res b/AssettoCorsa/AssettoCorsaSF.res
new file mode 100644
index 0000000..c287ee9
Binary files /dev/null and b/AssettoCorsa/AssettoCorsaSF.res differ
diff --git a/AssettoCorsa/source/AssettoCorsa.SharedMemory.pas b/AssettoCorsa/source/AssettoCorsa.SharedMemory.pas
new file mode 100644
index 0000000..bfa6424
--- /dev/null
+++ b/AssettoCorsa/source/AssettoCorsa.SharedMemory.pas
@@ -0,0 +1,333 @@
+unit AssettoCorsa.SharedMemory;
+
+interface
+type
+ AC_STATUS = Integer;
+ TACStatus = AC_STATUS;
+
+
+const
+ { AC_STATUS }
+ AC_OFF = 0;
+ AC_REPLAY = 1;
+ AC_LIVE = 2;
+ AC_PAUSE = 3;
+
+
+type
+ AC_SESSION_TYPE = Integer;
+ TACSessionType = AC_SESSION_TYPE;
+
+const
+ { AC_SESSION_TYPE }
+ AC_UNKNOWN = -1;
+ AC_PRACTICE = 0;
+ AC_QUALIFY = 1;
+ AC_RACE = 2;
+ AC_HOTLAP = 3;
+ AC_TIME_ATTACK = 4;
+ AC_DRIFT = 5;
+ AC_DRAG = 6;
+
+
+type
+ AC_FLAG_TYPE = Integer;
+ TACFlagType = AC_FLAG_TYPE;
+
+const
+ { AC_FLAG_TYPE }
+ AC_NO_FLAG = 0;
+ AC_BLUE_FLAG = 1;
+ AC_YELLOW_FLAG = 2;
+ AC_BLACK_FLAG = 3;
+ AC_WHITE_FLAG = 4;
+ AC_CHECKERED_FLAG = 5;
+ AC_PENALTY_FLAG = 6;
+
+
+type
+ TACSMPhysics = packed record
+ packetId: Integer;
+ gas: Single;
+ brake: Single;
+ fuel: Single;
+ gear: Integer;
+ rpms: Integer;
+ steerAngle: Single;
+ speedKmh: Single;
+ velocity: array[0..2] of Single;
+ accG: array[0..2] of Single;
+ wheelSlip: array[0..3] of Single;
+ wheelLoad: array[0..3] of Single;
+ wheelsPressure: array[0..3] of Single;
+ wheelAngularSpeed: array[0..3] of Single;
+ tyreWear: array[0..3] of Single;
+ tyreDirtyLevel: array[0..3] of Single;
+ tyreCoreTemperature: array[0..3] of Single;
+ camberRAD: array[0..3] of Single;
+ suspensionTravel: array[0..3] of Single;
+ drs: Single;
+ tc: Single;
+ heading: Single;
+ pitch: Single;
+ roll: Single;
+ cgHeight: Single;
+ carDamage: array[0..4] of Single;
+ umberOfTyresOut: Integer;
+ pitLimiterOn: Integer;
+ abs: Single;
+ kersCharge: Single;
+ kersInput: Single;
+ autoShifterOn: Integer;
+ rideHeigh: Single;
+ turboBoost: Single;
+ ballast: Single;
+ airDensity: Single;
+ airTemp: Single;
+ roadTemp: Single;
+ localAngularVelocity: array[0..2] of Single;
+ finalFF: Single;
+ performanceMeter: Single;
+
+ engineBrake: Integer;
+ ersRecoveryLevel: Integer;
+ ersPowerLevel: Integer;
+ ersHeatCharging: Integer;
+ ersIsCharging: Integer;
+ kersCurrentKJ: Single;
+
+ drsAvailable: Integer;
+ drsEnabled: Integer;
+
+ brakeTemp: array[0..3] of Single;
+ clutch: Single;
+
+ tyreTempI: array[0..3] of Single;
+ tyreTempM: array[0..3] of Single;
+ tyreTempO: array[0..3] of Single;
+
+ isAIControlled: Integer;
+
+ tyreContactPoint: array[0..3, 0..2] of Single;
+ tyreContactNormal: array[0..3, 0..2] of Single;
+ tyreContactHeading: array[0..3, 0..2] of Single;
+
+ brakeBias: Single;
+ end;
+ PACSMPhysics = ^TACSMPhysics;
+
+
+ TACSMGraphic = packed record
+ packetId: Integer;
+ status: TACStatus;
+ session: TACSessionType;
+ currentTime: array[0..14] of WideChar;
+ lastTime: array[0..14] of WideChar;
+ bestTime: array[0..14] of WideChar;
+ split: array[0..14] of WideChar;
+ completedLaps: Integer;
+ position: Integer;
+ iCurrentTime: Integer;
+ iLastTime: Integer;
+ iBestTime: Integer;
+ sessionTimeLeft: Single;
+ distanceTraveled: Single;
+ isInPit: Integer;
+ currentSectorIndex: Integer;
+ lastSectorTime: Integer;
+ numberOfLaps: Integer;
+ tyreCompound: array[0..32] of WideChar;
+
+ replayTimeMultiplier: Single;
+ normalizedCarPosition: Single;
+ carCoordinates: array[0..2] of Single;
+ penaltyTime: Single;
+ flag: TACFlagType;
+ idealLineOn: Integer;
+ isInPitLane: Integer;
+
+ surfaceGrip: Single;
+ end;
+ PACSMGraphic = ^TACSMGraphic;
+
+
+ TACSMStatic = packed record
+ smVersion: array[0..14] of WideChar;
+ acVersion: array[0..14] of WideChar;
+
+ // Session static info
+ numberOfSessions: Integer;
+ numCars: Integer;
+ carModel: array[0..32] of WideChar;
+ track: array[0..32] of WideChar;
+ playerName: array[0..32] of WideChar;
+ playerSurname: array[0..32] of WideChar;
+ playerNick: array[0..32] of WideChar;
+ sectorCount: Integer;
+
+ // Car static info
+ maxTorque: Single;
+ maxPower: Single;
+ maxRpm: Integer;
+ maxFuel: Single;
+ suspensionMaxTravel: array[0..3] of Single;
+ tyreRadius: array[0..3] of Single;
+ maxTurboBoost: Single;
+
+ deprecated_1: Single;
+ deprecated_2: Single;
+
+ penaltiesEnabled: Integer;
+
+ aidFuelRate: Single;
+ aidTireRate: Single;
+ aidMechanicalDamage: Single;
+ aidAllowTyreBlankets: Integer;
+ aidStability: Single;
+ aidAutoClutch: Integer;
+ aidAutoBlip: Integer;
+
+ hasDRS: Integer;
+ hasERS: Integer;
+ hasKERS: Integer;
+ kersMaxJ: Single;
+ engineBrakeSettingsCount: Integer;
+ ersPowerControllerCount: Integer;
+ trackSPlineLength: Single;
+ trackConfiguration: array[0..32] of WideChar;
+ ersMaxJ: Single;
+ end;
+ PACSMStatic = ^TACSMStatic;
+
+
+const
+ ACSM_PHYSICS = 'Local\acpmf_physics';
+ ACSM_GRAPHICS = 'Local\acpmf_graphics';
+ ACSM_STATIC = 'Local\acpmf_static';
+
+
+type
+ IAssettoCorsaSharedMemory = interface
+ ['{D7C0E678-CB11-4C34-81EB-4AAD5737C44F}']
+ function GetPhysics(out APhysics: TACSMPhysics): Boolean;
+ function GetGraphic(out AGraphic: TACSMGraphic): Boolean;
+ function GetStatic(out AStatic: TACSMStatic): Boolean;
+ end;
+
+
+ function GetAssettoCorsaSharedMemory(): IAssettoCorsaSharedMemory;
+
+
+implementation
+uses
+ WinApi.Windows, System.SysUtils, System.Classes;
+
+
+type
+ TACSMMapping = record
+ Handle: THandle;
+ Buffer: Pointer;
+ end;
+
+
+ TAssettoCorsaSharedMemory = class(TInterfacedObject, IAssettoCorsaSharedMemory)
+ private
+ FPhysicsMapping: TACSMMapping;
+ FGraphicMapping: TACSMMapping;
+ FStaticMapping: TACSMMapping;
+ protected
+ function OpenMapping(const AName: string; var AMapping: TACSMMapping; ASize: Integer): Boolean;
+ procedure CloseMapping(var AMapping: TACSMMapping);
+ public
+ destructor Destroy; override;
+
+ { IACSM }
+ function GetPhysics(out APhysics: TACSMPhysics): Boolean;
+ function GetGraphic(out AGraphic: TACSMGraphic): Boolean;
+ function GetStatic(out AStatic: TACSMStatic): Boolean;
+ end;
+
+
+
+function GetAssettoCorsaSharedMemory(): IAssettoCorsaSharedMemory;
+begin
+ Result := TAssettoCorsaSharedMemory.Create;
+end;
+
+
+{ TACSM }
+destructor TAssettoCorsaSharedMemory.Destroy;
+begin
+ CloseMapping(FPhysicsMapping);
+ CloseMapping(FGraphicMapping);
+ CloseMapping(FStaticMapping);
+
+ inherited;
+end;
+
+
+function TAssettoCorsaSharedMemory.GetPhysics(out APhysics: TACSMPhysics): Boolean;
+begin
+ Result := OpenMapping(ACSM_PHYSICS, FPhysicsMapping, SizeOf(TACSMPhysics));
+ if Result then
+ APhysics := PACSMPhysics(FPhysicsMapping.Buffer)^;
+end;
+
+
+function TAssettoCorsaSharedMemory.GetGraphic(out AGraphic: TACSMGraphic): Boolean;
+begin
+ Result := OpenMapping(ACSM_GRAPHICS, FGraphicMapping, SizeOf(TACSMGraphic));
+ if Result then
+ AGraphic := PACSMGraphic(FGraphicMapping.Buffer)^;
+end;
+
+
+function TAssettoCorsaSharedMemory.GetStatic(out AStatic: TACSMStatic): Boolean;
+begin
+ Result := OpenMapping(ACSM_STATIC, FStaticMapping, SizeOf(TACSMStatic));
+ if Result then
+ AStatic := PACSMStatic(FStaticMapping.Buffer)^;
+end;
+
+
+function TAssettoCorsaSharedMemory.OpenMapping(const AName: string; var AMapping: TACSMMapping; ASize: Integer): Boolean;
+var
+ handle: THandle;
+ buffer: Pointer;
+
+begin
+ if AMapping.Handle <> 0 then
+ begin
+ Result := True;
+ exit;
+ end;
+
+ Result := False;
+ handle := OpenFileMapping(FILE_MAP_READ, False, PChar(AName));
+ if handle = 0 then
+ Exit;
+
+ buffer := MapViewOfFile(handle, FILE_MAP_READ, 0, 0, ASize);
+ if Assigned(buffer) then
+ begin
+ AMapping.Handle := handle;
+ AMapping.Buffer := buffer;
+ Result := True;
+ end else
+ begin
+ CloseHandle(handle);
+ RaiseLastOSError;
+ end;
+end;
+
+
+procedure TAssettoCorsaSharedMemory.CloseMapping(var AMapping: TACSMMapping);
+begin
+ if AMapping.Handle <> 0 then
+ begin
+ UnmapViewOfFile(AMapping.Buffer);
+ CloseHandle(AMapping.Handle);
+ end;
+end;
+
+end.
diff --git a/AssettoCorsa/source/CPort.inc b/AssettoCorsa/source/CPort.inc
new file mode 100644
index 0000000..2c3e31f
--- /dev/null
+++ b/AssettoCorsa/source/CPort.inc
@@ -0,0 +1,227 @@
+{ ComPort Library global definitions }
+
+{ Fixed up for Delphi 2009 by W.Postma. }
+
+{$B-}
+{$X+}
+{$H+}
+
+{$IFDEF VER110} { C++ Builder 3 }
+ {$ObjExportAll On}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER120} { Delphi 4 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_4}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER125} { C++ Builder 4 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_4}
+ {$ObjExportAll On}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER130} { Delphi 5 and C++ Builder 5 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_5}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER140} { Delphi 6 and C++ Builder 6}
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_6}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER150} { Delphi 7 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_7}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER160} { Delphi 8 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_8}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER170} { Delphi 9 (2005) }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2005}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER180} { Delphi 10 (2006) }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2006_OR_HIGHER}
+ {$DEFINE DELPHI_2006}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+{$IFDEF VER185} { Delphi 11 - 2007 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2006_OR_HIGHER}
+ {$DEFINE DELPHI_2007_OR_HIGHER}
+ {$DEFINE DELPHI_2007}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$IFDEF BCB}
+ {$DEFINE BCB11}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+
+{$IFDEF VER190} { Delphi 12 2008 }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2006_OR_HIGHER}
+ {$DEFINE DELPHI_2007_OR_HIGHER}
+ {$DEFINE DELPHI_2008_OR_HIGHER}
+ {$DEFINE DELPHI_2008}
+ {$DEFINE DELPHI_UNICODE}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+
+
+{$IFDEF VER200} { Delphi 14 2009 UNICODE }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2006_OR_HIGHER}
+ {$DEFINE DELPHI_2007_OR_HIGHER}
+ {$DEFINE DELPHI_2008_OR_HIGHER}
+ {$DEFINE DELPHI_2009_OR_HIGHER}
+ {$DEFINE DELPHI_2009}
+ {$DEFINE DELPHI_UNICODE}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+
+
+
+{$IFDEF VER210} { Delphi 15 XE 2010 UNICODE }
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2006_OR_HIGHER}
+ {$DEFINE DELPHI_2007_OR_HIGHER}
+ {$DEFINE DELPHI_2008_OR_HIGHER}
+ {$DEFINE DELPHI_2009_OR_HIGHER}
+ {$DEFINE DELPHI_2010_OR_HIGHER}
+ {$DEFINE DELPHI_2010}
+ {$DEFINE DELPHI_UNICODE}
+ {$IFDEF BCBNOTDELPHI}
+ {$ObjExportAll On}
+ {$ENDIF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$DEFINE VER_RECOGNIZED}
+{$ENDIF}
+
+
+{... Lets try to make it work, for Delphi 2011 and later, right now...}
+{$IFNDEF VER_RECOGNIZED}
+ {$DEFINE DELPHI_4_OR_HIGHER}
+ {$DEFINE DELPHI_5_OR_HIGHER}
+ {$DEFINE DELPHI_6_OR_HIGHER}
+ {$DEFINE DELPHI_7_OR_HIGHER}
+ {$DEFINE DELPHI_8_OR_HIGHER}
+ {$DEFINE DELPHI_2005_OR_HIGHER}
+ {$DEFINE DELPHI_2006_OR_HIGHER}
+ {$DEFINE DELPHI_2007_OR_HIGHER}
+ {$DEFINE DELPHI_2009_OR_HIGHER}
+ {$DEFINE DELPHI_2010_OR_HIGHER}
+ {$DEFINE DELPHI_UNICODE}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+{$ENDIF}
+
+
+{$UNDEF VER_RECOGNIZED}
+
diff --git a/AssettoCorsa/source/CPort.pas b/AssettoCorsa/source/CPort.pas
new file mode 100644
index 0000000..dbc9482
--- /dev/null
+++ b/AssettoCorsa/source/CPort.pas
@@ -0,0 +1,3652 @@
+(******************************************************
+ * ComPort Library ver. 4.11 *
+ * for Delphi 5, 6, 7, 2007-2010,XE and *
+ * C++ Builder 3, 4, 5, 6 *
+ * written by Dejan Crnila, 1998 - 2002 *
+ * maintained by Lars B. Dybdahl, 2003 *
+ * Homepage: http://comport.sf.net/ *
+ * *
+ * Brian Gochnauer Oct 2010 *
+ * Removed ansi references for backward compat *
+ * Made unicode ready *
+ *****************************************************)
+
+
+unit CPort;
+{$Warnings OFF}
+{$I CPort.inc}
+{$DEFINE No_Dialogs} //removes forms setup/config code
+interface
+
+uses
+ Windows, Messages, Classes, SysUtils, IniFiles, Registry;
+
+type
+ TComExceptions = ( CE_OpenFailed , CE_WriteFailed ,
+ CE_ReadFailed , CE_InvalidAsync ,
+ CE_PurgeFailed , CE_AsyncCheck ,
+ CE_SetStateFailed , CE_TimeoutsFailed ,
+ CE_SetupComFailed , CE_ClearComFailed ,
+ CE_ModemStatFailed , CE_EscapeComFailed ,
+ CE_TransmitFailed , CE_ConnChangeProp ,
+ CE_EnumPortsFailed , CE_StoreFailed ,
+ CE_LoadFailed , CE_RegFailed ,
+ CE_LedStateFailed , CE_ThreadCreated ,
+ CE_WaitFailed , CE_HasLink ,
+ CE_RegError , CEPortNotOpen );
+
+
+
+
+ // various types
+ TPort = string;
+ TBaudRate = (brCustom, br110, br300, br600, br1200, br2400, br4800, br9600, br14400,
+ br19200, br38400, br56000, br57600, br115200, br128000, br256000);
+ TStopBits = (sbOneStopBit, sbOne5StopBits, sbTwoStopBits);
+ TDataBits = (dbFive, dbSix, dbSeven, dbEight);
+ TParityBits = (prNone, prOdd, prEven, prMark, prSpace);
+ TDTRFlowControl = (dtrDisable, dtrEnable, dtrHandshake);
+ TRTSFlowControl = (rtsDisable, rtsEnable, rtsHandshake, rtsToggle);
+ TFlowControl = (fcHardware, fcSoftware, fcNone, fcCustom);
+ TComEvent = (evRxChar, evTxEmpty, evRxFlag, evRing, evBreak, evCTS, evDSR, evError, evRLSD, evRx80Full);
+ TComEvents = set of TComEvent;
+ TComSignal = (csCTS, csDSR, csRing, csRLSD);
+ TComSignals = set of TComSignal;
+ TComError = (ceFrame, ceRxParity, ceOverrun, ceBreak, ceIO, ceMode, ceRxOver, ceTxFull);
+ TComErrors = set of TComError;
+ TSyncMethod = (smThreadSync, smWindowSync, smNone);
+ TStoreType = (stRegistry, stIniFile);
+ TStoredProp = (spBasic, spFlowControl, spBuffer, spTimeouts, spParity, spOthers);
+ TStoredProps = set of TStoredProp;
+ TComLinkEvent = (leConn, leCTS, leDSR, leRLSD, leRing, leRx, leTx, leTxEmpty, leRxFlag);
+ TRxCharEvent = procedure(Sender: TObject; Count: Integer) of object;
+ TRxBufEvent = procedure(Sender: TObject; const Buffer; Count: Integer) of object;
+ TComErrorEvent = procedure(Sender: TObject; Errors: TComErrors) of object;
+ TComSignalEvent = procedure(Sender: TObject; OnOff: Boolean) of object;
+ TComExceptionEvent = procedure(Sender:TObject;
+ TComException:TComExceptions; ComportMessage:String;
+ WinError:Int64; WinMessage:String) of object;
+
+ // types for asynchronous calls
+ TOperationKind = (okWrite, okRead);
+ TAsync = record
+ Overlapped: TOverlapped;
+ Kind: TOperationKind;
+ Data: Pointer;
+ Size: Integer;
+ end;
+ PAsync = ^TAsync;
+
+ {$IFNDEF Unicode}
+ UnicodeString = Widestring;
+ {$ENDIF}
+
+ // TComPort component and asistant classes
+ TCustomComPort = class; // forward declaration
+
+ // class that links TCustomComPort events to other components
+ TComLink = class
+ private
+ FOnConn: TComSignalEvent;
+ FOnRxBuf: TRxBufEvent;
+ FOnTxBuf: TRxBufEvent;
+ FOnTxEmpty: TNotifyEvent;
+ FOnRxFlag: TNotifyEvent;
+ FOnCTSChange: TComSignalEvent;
+ FOnDSRChange: TComSignalEvent;
+ FOnRLSDChange: TComSignalEvent;
+ FOnRing: TNotifyEvent;
+ FOnTx: TComSignalEvent;
+ FOnRx: TComSignalEvent;
+ public
+ property OnConn: TComSignalEvent read FOnConn write FOnConn;
+ property OnRxBuf: TRxBufEvent read FOnRxBuf write FOnRxBuf;
+ property OnTxBuf: TRxBufEvent read FOnTxBuf write FOnTxBuf;
+ property OnTxEmpty: TNotifyEvent read FOnTxEmpty write FOnTxEmpty;
+ property OnRxFlag: TNotifyEvent read FOnRxFlag write FOnRxFlag;
+ property OnCTSChange: TComSignalEvent read FOnCTSChange write FOnCTSChange;
+ property OnDSRChange: TComSignalEvent read FOnDSRChange write FOnDSRChange;
+ property OnRLSDChange: TComSignalEvent read FOnRLSDChange write FOnRLSDChange;
+ property OnRing: TNotifyEvent read FOnRing write FOnRing;
+ property OnTx: TComSignalEvent read FOnTx write FOnTx;
+ property OnRx: TComSignalEvent read FOnRx write FOnRx;
+ end;
+
+ // thread for background monitoring of port events
+ TComThread = class(TThread)
+ private
+ FComPort: TCustomComPort;
+ FStopEvent: THandle;
+ FEvents: TComEvents;
+ protected
+ procedure DispatchComMsg;
+ procedure DoEvents;
+ procedure Execute; override;
+ procedure SendEvents;
+ procedure Stop;
+ public
+ constructor Create(AComPort: TCustomComPort);
+ destructor Destroy; override;
+ end;
+
+ // timoeout properties for read/write operations
+ TComTimeouts = class(TPersistent)
+ private
+ FComPort: TCustomComPort;
+ FReadInterval: Integer;
+ FReadTotalM: Integer;
+ FReadTotalC: Integer;
+ FWriteTotalM: Integer;
+ FWriteTotalC: Integer;
+ procedure SetComPort(const AComPort: TCustomComPort);
+ procedure SetReadInterval(const Value: Integer);
+ procedure SetReadTotalM(const Value: Integer);
+ procedure SetReadTotalC(const Value: Integer);
+ procedure SetWriteTotalM(const Value: Integer);
+ procedure SetWriteTotalC(const Value: Integer);
+ protected
+ procedure AssignTo(Dest: TPersistent); override;
+ public
+ constructor Create;
+ property ComPort: TCustomComPort read FComPort;
+ published
+ property ReadInterval: Integer read FReadInterval write SetReadInterval default -1;
+ property ReadTotalMultiplier: Integer read FReadTotalM write SetReadTotalM default 0;
+ property ReadTotalConstant: Integer read FReadTotalC write SetReadTotalC default 0;
+ property WriteTotalMultiplier: Integer
+ read FWriteTotalM write SetWriteTotalM default 100;
+ property WriteTotalConstant: Integer
+ read FWriteTotalC write SetWriteTotalC default 1000;
+ end;
+
+ // flow control settings
+ TComFlowControl = class(TPersistent)
+ private
+ FComPort: TCustomComPort;
+ FOutCTSFlow: Boolean;
+ FOutDSRFlow: Boolean;
+ FControlDTR: TDTRFlowControl;
+ FControlRTS: TRTSFlowControl;
+ FXonXoffOut: Boolean;
+ FXonXoffIn: Boolean;
+ FDSRSensitivity: Boolean;
+ FTxContinueOnXoff: Boolean;
+ FXonChar: Char;
+ FXoffChar: Char;
+ procedure SetComPort(const AComPort: TCustomComPort);
+ procedure SetOutCTSFlow(const Value: Boolean);
+ procedure SetOutDSRFlow(const Value: Boolean);
+ procedure SetControlDTR(const Value: TDTRFlowControl);
+ procedure SetControlRTS(const Value: TRTSFlowControl);
+ procedure SetXonXoffOut(const Value: Boolean);
+ procedure SetXonXoffIn(const Value: Boolean);
+ procedure SetDSRSensitivity(const Value: Boolean);
+ procedure SetTxContinueOnXoff(const Value: Boolean);
+ procedure SetXonChar(const Value: Char);
+ procedure SetXoffChar(const Value: Char);
+ procedure SetFlowControl(const Value: TFlowControl);
+ function GetFlowControl: TFlowControl;
+ protected
+ procedure AssignTo(Dest: TPersistent); override;
+ public
+ constructor Create;
+ property ComPort: TCustomComPort read FComPort;
+ published
+ property FlowControl: TFlowControl read GetFlowControl write SetFlowControl stored False;
+ property OutCTSFlow: Boolean read FOutCTSFlow write SetOutCTSFlow;
+ property OutDSRFlow: Boolean read FOutDSRFlow write SetOutDSRFlow;
+ property ControlDTR: TDTRFlowControl read FControlDTR write SetControlDTR;
+ property ControlRTS: TRTSFlowControl read FControlRTS write SetControlRTS;
+ property XonXoffOut: Boolean read FXonXoffOut write SetXonXoffOut;
+ property XonXoffIn: Boolean read FXonXoffIn write SetXonXoffIn;
+ property DSRSensitivity: Boolean
+ read FDSRSensitivity write SetDSRSensitivity default False;
+ property TxContinueOnXoff: Boolean
+ read FTxContinueOnXoff write SetTxContinueOnXoff default False;
+ property XonChar: Char read FXonChar write SetXonChar default #17;
+ property XoffChar: Char read FXoffChar write SetXoffChar default #19;
+ end;
+
+ // parity settings
+ TComParity = class(TPersistent)
+ private
+ FComPort: TCustomComPort;
+ FBits: TParityBits;
+ FCheck: Boolean;
+ FReplace: Boolean;
+ FReplaceChar: Char;
+ procedure SetComPort(const AComPort: TCustomComPort);
+ procedure SetBits(const Value: TParityBits);
+ procedure SetCheck(const Value: Boolean);
+ procedure SetReplace(const Value: Boolean);
+ procedure SetReplaceChar(const Value: Char);
+ protected
+ procedure AssignTo(Dest: TPersistent); override;
+ public
+ constructor Create;
+ property ComPort: TCustomComPort read FComPort;
+ published
+ property Bits: TParityBits read FBits write SetBits;
+ property Check: Boolean read FCheck write SetCheck default False;
+ property Replace: Boolean read FReplace write SetReplace default False;
+ property ReplaceChar: Char read FReplaceChar write SetReplaceChar default #0;
+ end;
+
+ // buffer size settings
+ TComBuffer = class(TPersistent)
+ private
+ FComPort: TCustomComPort;
+ FInputSize: Integer;
+ FOutputSize: Integer;
+ procedure SetComPort(const AComPort: TCustomComPort);
+ procedure SetInputSize(const Value: Integer);
+ procedure SetOutputSize(const Value: Integer);
+ protected
+ procedure AssignTo(Dest: TPersistent); override;
+ public
+ constructor Create;
+ property ComPort: TCustomComPort read FComPort;
+ published
+ property InputSize: Integer read FInputSize write SetInputSize default 1024;
+ property OutputSize: Integer read FOutputSize write SetOutputSize default 1024;
+ end;
+
+ // main component
+ TCustomComPort = class(TComponent)
+ private
+ FEventThread: TComThread;
+ FThreadCreated: Boolean;
+ FHandle: THandle;
+ FWindow: THandle;
+ FUpdateCount: Integer;
+ FLinks: TList;
+ FTriggersOnRxChar: Boolean;
+ FEventThreadPriority: TThreadPriority;
+ FHasLink: Boolean;
+ FConnected: Boolean;
+ FBaudRate: TBaudRate;
+ FCustomBaudRate: Integer;
+ FPort: TPort;
+ FStopBits: TStopBits;
+ FDataBits: TDataBits;
+ FDiscardNull: Boolean;
+ FEventChar: Char;
+ FEvents: TComEvents;
+ FBuffer: TComBuffer;
+ FParity: TComParity;
+ FTimeouts: TComTimeouts;
+ FFlowControl: TComFlowControl;
+ FSyncMethod: TSyncMethod;
+ FStoredProps: TStoredProps;
+ FOnRxChar: TRxCharEvent;
+ FOnRxBuf: TRxBufEvent;
+ FOnTxEmpty: TNotifyEvent;
+ FOnBreak: TNotifyEvent;
+ FOnRing: TNotifyEvent;
+ FOnCTSChange: TComSignalEvent;
+ FOnDSRChange: TComSignalEvent;
+ FOnRLSDChange: TComSignalEvent;
+ FOnError: TComErrorEvent;
+ FOnRxFlag: TNotifyEvent;
+ FOnAfterOpen: TNotifyEvent;
+ FOnAfterClose: TNotifyEvent;
+ FOnBeforeOpen: TNotifyEvent;
+ FOnBeforeClose: TNotifyEvent;
+ FOnRx80Full : TNotifyEvent;
+ FOnException :TComExceptionEvent;
+ FCodePage : Cardinal;
+ function GetTriggersOnRxChar: Boolean;
+ procedure SetTriggersOnRxChar(const Value: Boolean);
+ procedure SetConnected(const Value: Boolean);
+ procedure SetBaudRate(const Value: TBaudRate);
+ procedure SetCustomBaudRate(const Value: Integer);
+ procedure SetPort(const Value: TPort);
+ procedure SetStopBits(const Value: TStopBits);
+ procedure SetDataBits(const Value: TDataBits);
+ procedure SetDiscardNull(const Value: Boolean);
+ procedure SetEventChar(const Value: Char);
+ procedure SetSyncMethod(const Value: TSyncMethod);
+ procedure SetEventThreadPriority(const Value: TThreadPriority);
+ procedure SetParity(const Value: TComParity);
+ procedure SetTimeouts(const Value: TComTimeouts);
+ procedure SetBuffer(const Value: TComBuffer);
+ procedure SetFlowControl(const Value: TComFlowControl);
+ function HasLink: Boolean;
+ procedure TxNotifyLink(const Buffer; Count: Integer);
+ procedure NotifyLink(FLinkEvent: TComLinkEvent);
+ procedure SendSignalToLink(Signal: TComLinkEvent; OnOff: Boolean);
+ procedure CheckSignals(Open: Boolean);
+ procedure WindowMethod(var Message: TMessage);
+ procedure CallAfterOpen;
+ procedure CallAfterClose;
+ procedure CallBeforeOpen;
+ procedure CallBeforeClose;
+ procedure CallRxChar;
+ procedure CallTxEmpty;
+ procedure CallBreak;
+ procedure CallRing;
+ procedure CallRxFlag;
+ procedure CallCTSChange;
+ procedure CallDSRChange;
+ procedure CallError;
+ procedure CallRLSDChange;
+ procedure CallRx80Full;
+ procedure CallException(AnException: Word; const WinError: Int64 =0);
+ protected
+ procedure Loaded; override;
+ procedure DoAfterClose; dynamic;
+ procedure DoAfterOpen; dynamic;
+ procedure DoBeforeClose; dynamic;
+ procedure DoBeforeOpen; dynamic;
+ procedure DoRxChar(Count: Integer); dynamic;
+ procedure DoRxBuf(const Buffer; Count: Integer); dynamic;
+ procedure DoTxEmpty; dynamic;
+ procedure DoBreak; dynamic;
+ procedure DoRing; dynamic;
+ procedure DoRxFlag; dynamic;
+ procedure DoCTSChange(OnOff: Boolean); dynamic;
+ procedure DoDSRChange(OnOff: Boolean); dynamic;
+ procedure DoError(Errors: TComErrors); dynamic;
+ procedure DoRLSDChange(OnOff: Boolean); dynamic;
+ procedure DoRx80Full; dynamic;
+ procedure StoreRegistry(Reg: TRegistry); virtual;
+ procedure StoreIniFile(IniFile: TIniFile); virtual;
+ procedure LoadRegistry(Reg: TRegistry); virtual;
+ procedure LoadIniFile(IniFile: TIniFile); virtual;
+ procedure CreateHandle; virtual;
+ procedure DestroyHandle; virtual;
+ procedure ApplyDCB; dynamic;
+ procedure ApplyTimeouts; dynamic;
+ procedure ApplyBuffer; dynamic;
+ procedure SetupComPort; virtual;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure BeginUpdate;
+ procedure EndUpdate;
+ procedure StoreSettings(StoreType: TStoreType; StoreTo: string);
+ procedure LoadSettings(StoreType: TStoreType; LoadFrom: string);
+ procedure Open;
+ procedure Close;
+ {$IFNDEF No_Dialogs}procedure ShowSetupDialog;{$ENDIF}
+ function InputCount: Integer;
+ function OutputCount: Integer;
+ function Signals: TComSignals;
+ function StateFlags: TComStateFlags;
+ procedure SetDTR(OnOff: Boolean);
+ procedure SetRTS(OnOff: Boolean);
+ procedure SetXonXoff(OnOff: Boolean);
+ procedure SetBreak(OnOff: Boolean);
+ procedure ClearBuffer(Input, Output: Boolean);
+ function LastErrors: TComErrors;
+
+ function Write(const Buffer; Count: Integer): Integer;
+ function WriteStr( Str: string): Integer;
+ function Read(var Buffer; Count: Integer): Integer;
+ function ReadStr(var Str: string; Count: Integer): Integer;
+ function WriteAsync(const Buffer; Count: Integer; var AsyncPtr: PAsync): Integer;
+ function WriteStrAsync(var Str: string; var AsyncPtr: PAsync): Integer;
+ function ReadAsync(var Buffer; Count: Integer; var AsyncPtr: PAsync): Integer;
+ function ReadStrAsync(var Str: Ansistring; Count: Integer; var AsyncPtr: PAsync): Integer;
+ function WriteUnicodeString(const Str: Unicodestring): Integer;
+ function ReadUnicodeString(var Str: UnicodeString; Count: Integer): Integer;
+
+ function WaitForAsync(var AsyncPtr: PAsync): Integer;
+ function IsAsyncCompleted(AsyncPtr: PAsync): Boolean;
+ procedure WaitForEvent(var Events: TComEvents; StopEvent: THandle; Timeout: Integer);
+ procedure AbortAllAsync;
+ procedure TransmitChar(Ch: Char);
+ procedure RegisterLink(AComLink: TComLink);
+ procedure UnRegisterLink(AComLink: TComLink);
+ property Handle: THandle read FHandle;
+ property TriggersOnRxChar: Boolean read GetTriggersOnRxChar write SetTriggersOnRxChar;
+ property EventThreadPriority: TThreadPriority read FEventThreadPriority write SetEventThreadPriority;
+ property StoredProps: TStoredProps read FStoredProps write FStoredProps;
+ property Connected: Boolean read FConnected write SetConnected default False;
+ property BaudRate: TBaudRate read FBaudRate write SetBaudRate;
+ property CustomBaudRate: Integer read FCustomBaudRate write SetCustomBaudRate;
+ property Port: TPort read FPort write SetPort;
+ property Parity: TComParity read FParity write SetParity;
+ property StopBits: TStopBits read FStopBits write SetStopBits;
+ property DataBits: TDataBits read FDataBits write SetDataBits;
+ property DiscardNull: Boolean read FDiscardNull write SetDiscardNull default False;
+ property EventChar: Char read FEventChar write SetEventChar default #0;
+ property Events: TComEvents read FEvents write FEvents;
+ property Buffer: TComBuffer read FBuffer write SetBuffer;
+ property FlowControl: TComFlowControl read FFlowControl write SetFlowControl;
+ property Timeouts: TComTimeouts read FTimeouts write SetTimeouts;
+ property SyncMethod: TSyncMethod read FSyncMethod write SetSyncMethod default smThreadSync;
+ property OnAfterOpen: TNotifyEvent read FOnAfterOpen write FOnAfterOpen;
+ property OnAfterClose: TNotifyEvent read FOnAfterClose write FOnAfterClose;
+ property OnBeforeOpen: TNotifyEvent read FOnBeforeOpen write FOnBeforeOpen;
+ property OnBeforeClose: TNotifyEvent read FOnBeforeClose write FOnBeforeClose;
+ property OnRxChar: TRxCharEvent read FOnRxChar write FOnRxChar;
+ property OnRxBuf: TRxBufEvent read FOnRxBuf write FOnRxBuf;
+ property OnTxEmpty: TNotifyEvent read FOnTxEmpty write FOnTxEmpty;
+ property OnBreak: TNotifyEvent read FOnBreak write FOnBreak;
+ property OnRing: TNotifyEvent read FOnRing write FOnRing;
+ property OnCTSChange: TComSignalEvent read FOnCTSChange write FOnCTSChange;
+ property OnDSRChange: TComSignalEvent read FOnDSRChange write FOnDSRChange;
+ property OnRLSDChange: TComSignalEvent read FOnRLSDChange write FOnRLSDChange;
+ property OnRxFlag: TNotifyEvent read FOnRxFlag write FOnRxFlag;
+ property OnError: TComErrorEvent read FOnError write FOnError;
+ property OnRx80Full: TNotifyEvent read FOnRx80Full write FOnRx80Full;
+ property OnException: TComExceptionEvent read FOnException write FOnException;
+ // Translate strings between ANSI charsets
+ property CodePage: Cardinal read FCodePage write FCodePage default 0;
+ end;
+
+ // publish the properties
+ TComPort = class(TCustomComPort)
+ property Connected;
+ property BaudRate;
+ property Port;
+ property Parity;
+ property StopBits;
+ property DataBits;
+ property DiscardNull;
+ property EventChar;
+ property Events;
+ property Buffer;
+ property FlowControl;
+ property Timeouts;
+ property StoredProps;
+ property TriggersOnRxChar;
+ property SyncMethod;
+ property OnAfterOpen;
+ property OnAfterClose;
+ property OnBeforeOpen;
+ property OnBeforeClose;
+ property OnRxChar;
+ property OnRxBuf;
+ property OnTxEmpty;
+ property OnBreak;
+ property OnRing;
+ property OnCTSChange;
+ property OnDSRChange;
+ property OnRLSDChange;
+ property OnRxFlag;
+ property OnError;
+ property OnRx80Full;
+ property OnException;
+ property CodePage;
+ end;
+
+ TComStrEvent = procedure(Sender: TObject; const Str: string) of object;
+ TCustPacketEvent = procedure(Sender: TObject; const Str: string;
+ var Pos: Integer) of object;
+
+ // component for reading data in packets
+ TComDataPacket = class(TComponent)
+ private
+ FComLink: TComLink;
+ FComPort: TCustomComPort;
+ FStartString: string;
+ FStopString: string;
+ FMaxBufferSize: Integer;
+ FSize: Integer;
+ FIncludeStrings: Boolean;
+ FCaseInsensitive: Boolean;
+ FInPacket: Boolean;
+ FBuffer: string;
+ FOnPacket: TComStrEvent;
+ FOnDiscard: TComStrEvent;
+ FOnCustomStart: TCustPacketEvent;
+ FOnCustomStop: TCustPacketEvent;
+ procedure SetComPort(const Value: TCustomComPort);
+ procedure SetCaseInsensitive(const Value: Boolean);
+ procedure SetSize(const Value: Integer);
+ procedure SetStartString(const Value: string);
+ procedure SetStopString(const Value: string);
+ procedure RxBuf(Sender: TObject; const Buffer; Count: Integer);
+ procedure CheckIncludeStrings(var Str: string);
+ function Upper(const Str: string): string;
+ procedure EmptyBuffer;
+ function ValidStop: Boolean;
+ protected
+ procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+ procedure DoDiscard(const Str: string); dynamic;
+ procedure DoPacket(const Str: string); dynamic;
+ procedure DoCustomStart(const Str: string; var Pos: Integer); dynamic;
+ procedure DoCustomStop(const Str: string; var Pos: Integer); dynamic;
+ procedure HandleBuffer; virtual;
+ property Buffer: string read FBuffer write FBuffer;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure AddData(const Str: string);
+ published
+ procedure ResetBuffer;
+ property ComPort: TCustomComPort read FComPort write SetComPort;
+ property CaseInsensitive: Boolean read FCaseInsensitive write SetCaseInsensitive default False;
+ property IncludeStrings: Boolean read FIncludeStrings write FIncludeStrings default False;
+ property MaxBufferSize: Integer read FMaxBufferSize write FMaxBufferSize default 1024;
+ property StartString: string read FStartString write SetStartString;
+ property StopString: string read FStopString write SetStopString;
+ property Size: Integer read FSize write SetSize default 0;
+ property OnDiscard: TComStrEvent read FOnDiscard write FOnDiscard;
+ property OnPacket: TComStrEvent read FOnPacket write FOnPacket;
+ property OnCustomStart: TCustPacketEvent read FOnCustomStart write FOnCustomStart;
+ property OnCustomStop: TCustPacketEvent read FOnCustomStop write FOnCustomStop;
+ end;
+
+ // com port stream
+ TComStream = class(TStream)
+ private
+ FComPort: TCustomComPort;
+ public
+ constructor Create(AComPort: TCustomComPort);
+ function Read(var Buffer; Count: Longint): Longint; override;
+ function Write(const Buffer; Count: Longint): Longint; override;
+ function Seek(Offset: Longint; Origin: Word): Longint; override;
+ end;
+
+ // exception class for ComPort Library errors
+ EComPort = class(Exception)
+ private
+ FWinCode: Integer;
+ FCode: Integer;
+ public
+ constructor Create(ACode: Integer; AWinCode: Integer);
+ constructor CreateNoWinCode(ACode: Integer);
+ property WinCode: Integer read FWinCode write FWinCode;
+ property Code: Integer read FCode write FCode;
+ end;
+
+// aditional procedures
+procedure InitAsync(var AsyncPtr: PAsync);
+procedure DoneAsync(var AsyncPtr: PAsync);
+procedure EnumComPorts(Ports: TStrings);
+
+// conversion functions
+function StrToBaudRate(Str: string): TBaudRate;
+function StrToStopBits(Str: string): TStopBits;
+function StrToDataBits(Str: string): TDataBits;
+function StrToParity(Str: string): TParityBits;
+function StrToFlowControl(Str: string): TFlowControl;
+function BaudRateToStr(BaudRate: TBaudRate): string;
+function StopBitsToStr(StopBits: TStopBits): string;
+function DataBitsToStr(DataBits: TDataBits): string;
+function ParityToStr(Parity: TParityBits): string;
+function FlowControlToStr(FlowControl: TFlowControl): string;
+function ComErrorsToStr(Errors:TComErrors):String;
+
+const
+ // infinite wait
+ WaitInfinite = Integer(INFINITE);
+
+ // error codes
+ CError_OpenFailed = 1;
+ CError_WriteFailed = 2;
+ CError_ReadFailed = 3;
+ CError_InvalidAsync = 4;
+ CError_PurgeFailed = 5;
+ CError_AsyncCheck = 6;
+ CError_SetStateFailed = 7;
+ CError_TimeoutsFailed = 8;
+ CError_SetupComFailed = 9;
+ CError_ClearComFailed = 10;
+ CError_ModemStatFailed = 11;
+ CError_EscapeComFailed = 12;
+ CError_TransmitFailed = 13;
+ CError_ConnChangeProp = 14;
+ CError_EnumPortsFailed = 15;
+ CError_StoreFailed = 16;
+ CError_LoadFailed = 17;
+ CError_RegFailed = 18;
+ CError_LedStateFailed = 19;
+ CError_ThreadCreated = 20;
+ CError_WaitFailed = 21;
+ CError_HasLink = 22;
+ CError_RegError = 23;
+ CError_PortNotOpen = 24;
+
+implementation
+
+uses
+ {$IFNDEF No_Dialogs} CPortSetup, {$ENDIF}
+ Controls, Forms, WinSpool;
+
+var
+ // error messages
+ ComErrorMessages: array[1..24] of widestring;
+
+const
+ // auxilary constants used not defined in windows.pas
+ dcb_Binary = $00000001;
+ dcb_Parity = $00000002;
+ dcb_OutxCTSFlow = $00000004;
+ dcb_OutxDSRFlow = $00000008;
+ dcb_DTRControl = $00000030;
+ dcb_DSRSensivity = $00000040;
+ dcb_TxContinueOnXoff = $00000080;
+ dcb_OutX = $00000100;
+ dcb_InX = $00000200;
+ dcb_ErrorChar = $00000400;
+ dcb_Null = $00000800;
+ dcb_RTSControl = $00003000;
+ dcb_AbortOnError = $00004000;
+
+ // com port window message
+ CM_COMPORT = WM_USER + 1;
+
+(*****************************************
+ * auxilary functions and procedures *
+ *****************************************)
+function ComErrorsToStr(Errors:TComErrors):String;
+ procedure e(msg:String);
+ begin
+ if result='' then
+ result := msg
+ else
+ result := result+','+msg;
+ end;
+begin
+ result := '';
+ if ceFrame in Errors then e('Frame');
+ if ceRxParity in Errors then e('Parity');
+ if ceOverrun in Errors then e('Overrun');
+ if ceBreak in Errors then e('Break');
+ if ceIO in Errors then e('IO');
+ if ceMode in Errors then e('Mode');
+ if ceRxOver in Errors then e('RxOver');
+ if ceTxFull in Errors then e('TxFull');
+ if result = '' then
+ result := ''
+ else
+ result := '';
+end;
+
+// converts TComEvents type to Integer
+function EventsToInt(const Events: TComEvents): Integer;
+begin
+ Result := 0;
+ if evRxChar in Events then
+ Result := Result or EV_RXCHAR;
+ if evRxFlag in Events then
+ Result := Result or EV_RXFLAG;
+ if evTxEmpty in Events then
+ Result := Result or EV_TXEMPTY;
+ if evRing in Events then
+ Result := Result or EV_RING;
+ if evCTS in Events then
+ Result := Result or EV_CTS;
+ if evDSR in Events then
+ Result := Result or EV_DSR;
+ if evRLSD in Events then
+ Result := Result or EV_RLSD;
+ if evError in Events then
+ Result := Result or EV_ERR;
+ if evBreak in Events then
+ Result := Result or EV_BREAK;
+ if evRx80Full in Events then
+ Result := Result or EV_RX80FULL;
+end;
+
+function IntToEvents(Mask: Integer): TComEvents;
+begin
+ Result := [];
+ if (EV_RXCHAR and Mask) <> 0 then
+ Result := Result + [evRxChar];
+ if (EV_TXEMPTY and Mask) <> 0 then
+ Result := Result + [evTxEmpty];
+ if (EV_BREAK and Mask) <> 0 then
+ Result := Result + [evBreak];
+ if (EV_RING and Mask) <> 0 then
+ Result := Result + [evRing];
+ if (EV_CTS and Mask) <> 0 then
+ Result := Result + [evCTS];
+ if (EV_DSR and Mask) <> 0 then
+ Result := Result + [evDSR];
+ if (EV_RXFLAG and Mask) <> 0 then
+ Result := Result + [evRxFlag];
+ if (EV_RLSD and Mask) <> 0 then
+ Result := Result + [evRLSD];
+ if (EV_ERR and Mask) <> 0 then
+ Result := Result + [evError];
+ if (EV_RX80FULL and Mask) <> 0 then
+ Result := Result + [evRx80Full];
+end;
+
+(*****************************************
+ * TComThread class *
+ *****************************************)
+
+// create thread
+constructor TComThread.Create(AComPort: TCustomComPort);
+begin
+ inherited Create(false);
+ FStopEvent := CreateEvent(nil, True, False, nil);
+ FComPort := AComPort;
+ // set thread priority
+ Priority := FComPort.EventThreadPriority;
+ // select which events are monitored
+ SetCommMask(FComPort.Handle, EventsToInt(FComPort.Events));
+ // execute thread
+ //{$IFDEF Unicode}Start; {$ELSE} Resume; {$ENDIF}
+end;
+
+// destroy thread
+destructor TComThread.Destroy;
+begin
+ Stop;
+ inherited Destroy;
+end;
+
+// thread action
+procedure TComThread.Execute;
+var
+ EventHandles: array[0..1] of THandle;
+ Overlapped: TOverlapped;
+ Signaled, BytesTrans, Mask: DWORD;
+begin
+ FillChar(Overlapped, SizeOf(Overlapped), 0);
+ Overlapped.hEvent := CreateEvent(nil, True, True, nil);
+ EventHandles[0] := FStopEvent;
+ EventHandles[1] := Overlapped.hEvent;
+ repeat
+ // wait for event to occur on serial port
+ WaitCommEvent(FComPort.Handle, Mask, @Overlapped);
+ Signaled := WaitForMultipleObjects(2, @EventHandles, False, INFINITE);
+ // if event occurs, dispatch it
+ if (Signaled = WAIT_OBJECT_0 + 1)
+ and GetOverlappedResult(FComPort.Handle, Overlapped, BytesTrans, False)
+ then
+ begin
+ FEvents := IntToEvents(Mask);
+ DispatchComMsg;
+ end;
+ until Signaled <> (WAIT_OBJECT_0 + 1);
+ // clear buffers
+ SetCommMask(FComPort.Handle, 0);
+ PurgeComm(FComPort.Handle, PURGE_TXCLEAR or PURGE_RXCLEAR);
+ CloseHandle(Overlapped.hEvent);
+ CloseHandle(FStopEvent);
+end;
+
+// stop thread
+procedure TComThread.Stop;
+begin
+ SetEvent(FStopEvent);
+ Sleep(0);
+end;
+
+// dispatch events
+procedure TComThread.DispatchComMsg;
+begin
+ case FComPort.SyncMethod of
+ smThreadSync: Synchronize(DoEvents); // call events in main thread
+ smWindowSync: SendEvents; // call events in thread that opened the port
+ smNone: DoEvents; // call events inside monitoring thread
+ end;
+end;
+
+// send events to TCustomComPort component using window message
+procedure TComThread.SendEvents;
+begin
+ if evError in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_ERR, 0);
+ if evRxChar in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_RXCHAR, 0);
+ if evTxEmpty in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_TXEMPTY, 0);
+ if evBreak in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_BREAK, 0);
+ if evRing in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_RING, 0);
+ if evCTS in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_CTS, 0);
+ if evDSR in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_DSR, 0);
+ if evRxFlag in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_RXFLAG, 0);
+ if evRing in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_RLSD, 0);
+ if evRx80Full in FEvents then
+ SendMessage(FComPort.FWindow, CM_COMPORT, EV_RX80FULL, 0);
+end;
+
+// call events
+procedure TComThread.DoEvents;
+begin
+ if evError in FEvents then
+ FComPort.CallError;
+ if evRxChar in FEvents then
+ FComPort.CallRxChar;
+ if evTxEmpty in FEvents then
+ FComPort.CallTxEmpty;
+ if evBreak in FEvents then
+ FComPort.CallBreak;
+ if evRing in FEvents then
+ FComPort.CallRing;
+ if evCTS in FEvents then
+ FComPort.CallCTSChange;
+ if evDSR in FEvents then
+ FComPort.CallDSRChange;
+ if evRxFlag in FEvents then
+ FComPort.CallRxFlag;
+ if evRLSD in FEvents then
+ FComPort.CallRLSDChange;
+ if evRx80Full in FEvents then
+ FComPort.CallRx80Full;
+end;
+
+(*****************************************
+ * TComTimeouts class *
+ *****************************************)
+
+// create class
+constructor TComTimeouts.Create;
+begin
+ inherited Create;
+ FReadInterval := -1;
+ FWriteTotalM := 100;
+ FWriteTotalC := 1000;
+end;
+
+// copy properties to other class
+procedure TComTimeouts.AssignTo(Dest: TPersistent);
+begin
+ if Dest is TComTimeouts then
+ begin
+ with TComTimeouts(Dest) do
+ begin
+ FReadInterval := Self.ReadInterval;
+ FReadTotalM := Self.ReadTotalMultiplier;
+ FReadTotalC := Self.ReadTotalConstant;
+ FWriteTotalM := Self.WriteTotalMultiplier;
+ FWriteTotalC := Self.WriteTotalConstant;
+ end
+ end
+ else
+ inherited AssignTo(Dest);
+end;
+
+// select TCustomComPort to own this class
+procedure TComTimeouts.SetComPort(const AComPort: TCustomComPort);
+begin
+ FComPort := AComPort;
+end;
+
+// set read interval
+procedure TComTimeouts.SetReadInterval(const Value: Integer);
+begin
+ if Value <> FReadInterval then
+ begin
+ FReadInterval := Value;
+ // if possible, apply the changes
+ if FComPort <> nil then
+ FComPort.ApplyTimeouts;
+ end;
+end;
+
+// set read total constant
+procedure TComTimeouts.SetReadTotalC(const Value: Integer);
+begin
+ if Value <> FReadTotalC then
+ begin
+ FReadTotalC := Value;
+ if FComPort <> nil then
+ FComPort.ApplyTimeouts;
+ end;
+end;
+
+// set read total multiplier
+procedure TComTimeouts.SetReadTotalM(const Value: Integer);
+begin
+ if Value <> FReadTotalM then
+ begin
+ FReadTotalM := Value;
+ if FComPort <> nil then
+ FComPort.ApplyTimeouts;
+ end;
+end;
+
+// set write total constant
+procedure TComTimeouts.SetWriteTotalC(const Value: Integer);
+begin
+ if Value <> FWriteTotalC then
+ begin
+ FWriteTotalC := Value;
+ if FComPort <> nil then
+ FComPort.ApplyTimeouts;
+ end;
+end;
+
+// set write total multiplier
+procedure TComTimeouts.SetWriteTotalM(const Value: Integer);
+begin
+ if Value <> FWriteTotalM then
+ begin
+ FWriteTotalM := Value;
+ if FComPort <> nil then
+ FComPort.ApplyTimeouts;
+ end;
+end;
+
+(*****************************************
+ * TComFlowControl class *
+ *****************************************)
+
+// create class
+constructor TComFlowControl.Create;
+begin
+ inherited Create;
+ FXonChar := #17;
+ FXoffChar := #19;
+end;
+
+// copy properties to other class
+procedure TComFlowControl.AssignTo(Dest: TPersistent);
+begin
+ if Dest is TComFlowControl then
+ begin
+ with TComFlowControl(Dest) do
+ begin
+ FOutCTSFlow := Self.OutCTSFlow;
+ FOutDSRFlow := Self.OutDSRFlow;
+ FControlDTR := Self.ControlDTR;
+ FControlRTS := Self.ControlRTS;
+ FXonXoffOut := Self.XonXoffOut;
+ FXonXoffIn := Self.XonXoffIn;
+ FTxContinueOnXoff := Self.TxContinueOnXoff;
+ FDSRSensitivity := Self.DSRSensitivity;
+ FXonChar := Self.XonChar;
+ FXoffChar := Self.XoffChar;
+ end
+ end
+ else
+ inherited AssignTo(Dest);
+end;
+
+// select TCustomComPort to own this class
+procedure TComFlowControl.SetComPort(const AComPort: TCustomComPort);
+begin
+ FComPort := AComPort;
+end;
+
+// set input flow control for DTR (data-terminal-ready)
+procedure TComFlowControl.SetControlDTR(const Value: TDTRFlowControl);
+begin
+ if Value <> FControlDTR then
+ begin
+ FControlDTR := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set input flow control for RTS (request-to-send)
+procedure TComFlowControl.SetControlRTS(const Value: TRTSFlowControl);
+begin
+ if Value <> FControlRTS then
+ begin
+ FControlRTS := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set ouput flow control for CTS (clear-to-send)
+procedure TComFlowControl.SetOutCTSFlow(const Value: Boolean);
+begin
+ if Value <> FOutCTSFlow then
+ begin
+ FOutCTSFlow := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set output flow control for DSR (data-set-ready)
+procedure TComFlowControl.SetOutDSRFlow(const Value: Boolean);
+begin
+ if Value <> FOutDSRFlow then
+ begin
+ FOutDSRFlow := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set software input flow control
+procedure TComFlowControl.SetXonXoffIn(const Value: Boolean);
+begin
+ if Value <> FXonXoffIn then
+ begin
+ FXonXoffIn := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set software ouput flow control
+procedure TComFlowControl.SetXonXoffOut(const Value: Boolean);
+begin
+ if Value <> FXonXoffOut then
+ begin
+ FXonXoffOut := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set DSR sensitivity
+procedure TComFlowControl.SetDSRSensitivity(const Value: Boolean);
+begin
+ if Value <> FDSRSensitivity then
+ begin
+ FDSRSensitivity := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set transfer continue when Xoff is sent
+procedure TComFlowControl.SetTxContinueOnXoff(const Value: Boolean);
+begin
+ if Value <> FTxContinueOnXoff then
+ begin
+ FTxContinueOnXoff := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set Xon char
+procedure TComFlowControl.SetXonChar(const Value: Char);
+begin
+ if Value <> FXonChar then
+ begin
+ FXonChar := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set Xoff char
+procedure TComFlowControl.SetXoffChar(const Value: Char);
+begin
+ if Value <> FXoffChar then
+ begin
+ FXoffChar := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// get common flow control
+function TComFlowControl.GetFlowControl: TFlowControl;
+begin
+ if (FControlRTS = rtsHandshake) and (FOutCTSFlow)
+ and (not FXonXoffIn) and (not FXonXoffOut)
+ then
+ Result := fcHardware
+ else
+ if (FControlRTS = rtsDisable) and (not FOutCTSFlow)
+ and (FXonXoffIn) and (FXonXoffOut)
+ then
+ Result := fcSoftware
+ else
+ if (FControlRTS = rtsDisable) and (not FOutCTSFlow)
+ and (not FXonXoffIn) and (not FXonXoffOut)
+ then
+ Result := fcNone
+ else
+ Result := fcCustom;
+end;
+
+// set common flow control
+procedure TComFlowControl.SetFlowControl(const Value: TFlowControl);
+begin
+ if Value <> fcCustom then
+ begin
+ FControlRTS := rtsDisable;
+ FOutCTSFlow := False;
+ FXonXoffIn := False;
+ FXonXoffOut := False;
+ case Value of
+ fcHardware:
+ begin
+ FControlRTS := rtsHandshake;
+ FOutCTSFlow := True;
+ end;
+ fcSoftware:
+ begin
+ FXonXoffIn := True;
+ FXonXoffOut := True;
+ end;
+ end;
+ end;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+end;
+
+(*****************************************
+ * TComParity class *
+ *****************************************)
+
+// create class
+constructor TComParity.Create;
+begin
+ inherited Create;
+ FBits := prNone;
+end;
+
+// copy properties to other class
+procedure TComParity.AssignTo(Dest: TPersistent);
+begin
+ if Dest is TComParity then
+ begin
+ with TComParity(Dest) do
+ begin
+ FBits := Self.Bits;
+ FCheck := Self.Check;
+ FReplace := Self.Replace;
+ FReplaceChar := Self.ReplaceChar;
+ end
+ end
+ else
+ inherited AssignTo(Dest);
+end;
+
+// select TCustomComPort to own this class
+procedure TComParity.SetComPort(const AComPort: TCustomComPort);
+begin
+ FComPort := AComPort;
+end;
+
+// set parity bits
+procedure TComParity.SetBits(const Value: TParityBits);
+begin
+ if Value <> FBits then
+ begin
+ FBits := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set check parity
+procedure TComParity.SetCheck(const Value: Boolean);
+begin
+ if Value <> FCheck then
+ begin
+ FCheck := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set replace on parity error
+procedure TComParity.SetReplace(const Value: Boolean);
+begin
+ if Value <> FReplace then
+ begin
+ FReplace := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+// set replace char
+procedure TComParity.SetReplaceChar(const Value: Char);
+begin
+ if Value <> FReplaceChar then
+ begin
+ FReplaceChar := Value;
+ if FComPort <> nil then
+ FComPort.ApplyDCB;
+ end;
+end;
+
+(*****************************************
+ * TComBuffer class *
+ *****************************************)
+
+// create class
+constructor TComBuffer.Create;
+begin
+ inherited Create;
+ FInputSize := 1024;
+ FOutputSize := 1024;
+end;
+
+// copy properties to other class
+procedure TComBuffer.AssignTo(Dest: TPersistent);
+begin
+ if Dest is TComBuffer then
+ begin
+ with TComBuffer(Dest) do
+ begin
+ FOutputSize := Self.OutputSize;
+ FInputSize := Self.InputSize;
+ end
+ end
+ else
+ inherited AssignTo(Dest);
+end;
+
+// select TCustomComPort to own this class
+procedure TComBuffer.SetComPort(const AComPort: TCustomComPort);
+begin
+ FComPort := AComPort;
+end;
+
+// set input size
+procedure TComBuffer.SetInputSize(const Value: Integer);
+begin
+ if Value <> FInputSize then
+ begin
+ FInputSize := Value;
+ if (FInputSize mod 2) = 1 then
+ Dec(FInputSize);
+ if FComPort <> nil then
+ FComPort.ApplyBuffer;
+ end;
+end;
+
+// set ouput size
+procedure TComBuffer.SetOutputSize(const Value: Integer);
+begin
+ if Value <> FOutputSize then
+ begin
+ FOutputSize := Value;
+ if (FOutputSize mod 2) = 1 then
+ Dec(FOutputSize);
+ if FComPort <> nil then
+ FComPort.ApplyBuffer;
+ end;
+end;
+
+(*****************************************
+ * TCustomComPort component *
+ *****************************************)
+
+// create component
+constructor TCustomComPort.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ // component cannot reside on inheritable forms
+ FComponentStyle := FComponentStyle - [csInheritable];
+ FLinks := TList.Create;
+ FTriggersOnRxChar := True;
+ FEventThreadPriority := tpNormal;
+ FBaudRate := br9600;
+ FCustomBaudRate := 9600;
+ FPort := 'COM1';
+ FStopBits := sbOneStopBit;
+ FDataBits := dbEight;
+ FEvents := [evRxChar, evTxEmpty, evRxFlag, evRing, evBreak,
+ evCTS, evDSR, evError, evRLSD, evRx80Full];
+ FHandle := INVALID_HANDLE_VALUE;
+ FStoredProps := [spBasic];
+ FParity := TComParity.Create;
+ FParity.SetComPort(Self);
+ FFlowControl := TComFlowControl.Create;
+ FFlowControl.SetComPort(Self);
+ FTimeouts := TComTimeouts.Create;
+ FTimeouts.SetComPort(Self);
+ FBuffer := TComBuffer.Create;
+ FBuffer.SetComPort(Self);
+ FCodePage := CP_ACP;//0; // uses default system codepage
+end;
+
+// destroy component
+destructor TCustomComPort.Destroy;
+begin
+ Close;
+ FBuffer.Free;
+ FFlowControl.Free;
+ FTimeouts.Free;
+ FParity.Free;
+ inherited Destroy;
+ FLinks.Free;
+end;
+
+//Handle Exceptions
+procedure TCustomComPort.CallException(AnException:Word; const WinError:Int64 =0);
+var winmessage:string;
+begin
+ if Assigned(FOnException) then
+ begin
+ if WinError > 0 then //get windows error string
+ try Win32Check(winerror = 0); except on E:Exception do WinMessage:=e.message; end;
+ FOnException(self,TComExceptions(AnException),ComErrorMessages[AnException],WinError, WinMessage);
+ end
+ else
+ if WinError > 0 then raise EComPort.Create(AnException, WinError)
+ else raise EComPort.CreateNoWinCode(AnException);
+
+end;
+// create handle to serial port
+procedure TCustomComPort.CreateHandle;
+begin
+ FHandle := CreateFile(
+ PChar('\\.\' + FPort),
+ GENERIC_READ or GENERIC_WRITE,
+ 0,
+ nil,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ 0);
+
+ if FHandle = INVALID_HANDLE_VALUE then
+ //raise EComPort.Create
+ CallException(CError_OpenFailed, GetLastError);
+end;
+
+// destroy serial port handle
+procedure TCustomComPort.DestroyHandle;
+begin
+ if FHandle <> INVALID_HANDLE_VALUE then
+ begin
+ if CloseHandle(FHandle) then
+ FHandle := INVALID_HANDLE_VALUE;
+ end;
+end;
+
+procedure TCustomComPort.Loaded;
+begin
+ inherited Loaded;
+ // open port if Connected is True at design-time
+ if FConnected and not (csDesigning in ComponentState) then
+ begin
+ FConnected := False;
+ try
+ Open;
+ except
+ Application.HandleException(Self);
+ end;
+ end;
+end;
+
+// call events which have been dispatch using window message
+procedure TCustomComPort.WindowMethod(var Message: TMessage);
+begin
+ with Message do
+ if Msg = CM_COMPORT then
+ try
+ if InSendMessage then
+ ReplyMessage(0);
+ if FConnected then
+ case wParam of
+ EV_RXCHAR: CallRxChar;
+ EV_TXEMPTY: CallTxEmpty;
+ EV_BREAK: CallBreak;
+ EV_RING: CallRing;
+ EV_CTS: CallCTSChange;
+ EV_DSR: CallDSRChange;
+ EV_RXFLAG: CallRxFlag;
+ EV_RLSD: CallRLSDChange;
+ EV_ERR: CallError;
+ EV_RX80FULL: CallRx80Full;
+ end
+ except
+ Application.HandleException(Self);
+ end
+ else
+ Result := DefWindowProc(FWindow, Msg, wParam, lParam);
+end;
+
+// prevent from applying changes at runtime
+procedure TCustomComPort.BeginUpdate;
+begin
+ FUpdateCount := FUpdateCount + 1;
+end;
+
+// apply the changes made since BeginUpdate call
+procedure TCustomComPort.EndUpdate;
+begin
+ if FUpdateCount > 0 then
+ begin
+ FUpdateCount := FUpdateCount - 1;
+ if FUpdateCount = 0 then
+ SetupComPort;
+ end;
+end;
+
+// open port
+procedure TCustomComPort.Open;
+begin
+ // if already connected, do nothing
+ if not FConnected and not (csDesigning in ComponentState) then
+ begin
+ CallBeforeOpen;
+ // open port
+ CreateHandle;
+ FConnected := True;
+ try
+ // initialize port
+ SetupComPort;
+ except
+ // error occured during initialization, destroy handle
+ DestroyHandle;
+ FConnected := False;
+ raise;
+ end;
+ // if at least one event is set, create special thread to monitor port
+ if (FEvents = []) then
+ FThreadCreated := False
+ else
+ begin
+ if (FSyncMethod = smWindowSync) then
+{$IFDEF DELPHI_6_OR_HIGHER}
+ {$WARN SYMBOL_DEPRECATED OFF}
+{$ENDIF}
+ FWindow := AllocateHWnd(WindowMethod);
+{$IFDEF DELPHI_6_OR_HIGHER}
+ {$WARN SYMBOL_DEPRECATED ON}
+{$ENDIF}
+ FEventThread := TComThread.Create(Self);
+ FThreadCreated := True;
+ end;
+ // port is succesfully opened, do any additional initialization
+ CallAfterOpen;
+ end;
+end;
+
+// close port
+procedure TCustomComPort.Close;
+begin
+ // if already closed, do nothing
+ if FConnected and not (csDesigning in ComponentState) then
+ begin
+ CallBeforeClose;
+ // abort all pending operations
+ AbortAllAsync;
+ // stop monitoring for events
+ if FThreadCreated then
+ begin
+ FEventThread.Free;
+ FThreadCreated := False;
+ if FSyncMethod = smWindowSync then
+{$IFDEF DELPHI_6_OR_HIGHER}
+ {$WARN SYMBOL_DEPRECATED OFF}
+{$ENDIF}
+ DeallocateHWnd(FWindow);
+{$IFDEF DELPHI_6_OR_HIGHER}
+ {$WARN SYMBOL_DEPRECATED ON}
+{$ENDIF}
+ end;
+ // close port
+ DestroyHandle;
+ FConnected := False;
+ // port is closed, do any additional finalization
+ CallAfterClose;
+ end;
+end;
+
+// apply port properties
+procedure TCustomComPort.ApplyDCB;
+const
+ CParityBits: array[TParityBits] of Integer =
+ (NOPARITY, ODDPARITY, EVENPARITY, MARKPARITY, SPACEPARITY);
+ CStopBits: array[TStopBits] of Integer =
+ (ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS);
+ CBaudRate: array[TBaudRate] of Integer =
+ (0, CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600,
+ CBR_14400, CBR_19200, CBR_38400, CBR_56000, CBR_57600, CBR_115200,
+ CBR_128000, CBR_256000);
+ CDataBits: array[TDataBits] of Integer = (5, 6, 7, 8);
+ CControlRTS: array[TRTSFlowControl] of Integer =
+ (RTS_CONTROL_DISABLE shl 12,
+ RTS_CONTROL_ENABLE shl 12,
+ RTS_CONTROL_HANDSHAKE shl 12,
+ RTS_CONTROL_TOGGLE shl 12);
+ CControlDTR: array[TDTRFlowControl] of Integer =
+ (DTR_CONTROL_DISABLE shl 4,
+ DTR_CONTROL_ENABLE shl 4,
+ DTR_CONTROL_HANDSHAKE shl 4);
+
+var
+ DCB: TDCB;
+
+begin
+ // if not connected or inside BeginUpdate/EndUpdate block, do nothing
+ if FConnected and (FUpdateCount = 0) and
+ not ((csDesigning in ComponentState) or (csLoading in ComponentState)) then
+ begin
+ DCB.DCBlength := SizeOf(TDCB);
+ DCB.XonLim := FBuffer.InputSize div 4;
+ DCB.XoffLim := DCB.XonLim;
+ DCB.EvtChar := AnsiChar(FEventChar);
+
+ DCB.Flags := dcb_Binary;
+ if FDiscardNull then
+ DCB.Flags := DCB.Flags or dcb_Null;
+
+ with FFlowControl do
+ begin
+ DCB.XonChar := AnsiChar(XonChar);
+ DCB.XoffChar := AnsiChar(XoffChar);
+ if OutCTSFlow then
+ DCB.Flags := DCB.Flags or dcb_OutxCTSFlow;
+ if OutDSRFlow then
+ DCB.Flags := DCB.Flags or dcb_OutxDSRFlow;
+ DCB.Flags := DCB.Flags or CControlDTR[ControlDTR]
+ or CControlRTS[ControlRTS];
+ if XonXoffOut then
+ DCB.Flags := DCB.Flags or dcb_OutX;
+ if XonXoffIn then
+ DCB.Flags := DCB.Flags or dcb_InX;
+ if DSRSensitivity then
+ DCB.Flags := DCB.Flags or dcb_DSRSensivity;
+ if TxContinueOnXoff then
+ DCB.Flags := DCB.Flags or dcb_TxContinueOnXoff;
+ end;
+
+ DCB.Parity := CParityBits[FParity.Bits];
+ DCB.StopBits := CStopBits[FStopBits];
+ if FBaudRate <> brCustom then
+ DCB.BaudRate := CBaudRate[FBaudRate]
+ else
+ DCB.BaudRate := FCustomBaudRate;
+ DCB.ByteSize := CDataBits[FDataBits];
+
+ if FParity.Check then
+ begin
+ DCB.Flags := DCB.Flags or dcb_Parity;
+ if FParity.Replace then
+ begin
+ DCB.Flags := DCB.Flags or dcb_ErrorChar;
+ DCB.ErrorChar := AnsiChar(FParity.ReplaceChar);
+ end;
+ end;
+
+ // apply settings
+ if not SetCommState(FHandle, DCB) then
+ //raise EComPort.Create
+ CallException(CError_SetStateFailed, GetLastError);
+ end;
+end;
+
+// apply timeout properties
+procedure TCustomComPort.ApplyTimeouts;
+var
+ Timeouts: TCommTimeouts;
+
+ function GetTOValue(const Value: Integer): DWORD;
+ begin
+ if Value = -1 then
+ Result := MAXDWORD
+ else
+ Result := Value;
+ end;
+
+begin
+ // if not connected or inside BeginUpdate/EndUpdate block, do nothing
+ if FConnected and (FUpdateCount = 0) and
+ not ((csDesigning in ComponentState) or (csLoading in ComponentState)) then
+ begin
+ Timeouts.ReadIntervalTimeout := GetTOValue(FTimeouts.ReadInterval);
+ Timeouts.ReadTotalTimeoutMultiplier := GetTOValue(FTimeouts.ReadTotalMultiplier);
+ Timeouts.ReadTotalTimeoutConstant := GetTOValue(FTimeouts.ReadTotalConstant);
+ Timeouts.WriteTotalTimeoutMultiplier := GetTOValue(FTimeouts.WriteTotalMultiplier);
+ Timeouts.WriteTotalTimeoutConstant := GetTOValue(FTimeouts.WriteTotalConstant);
+
+ // apply settings
+ if not SetCommTimeouts(FHandle, Timeouts) then
+ //raise EComPort.Create
+ CallException(CError_TimeoutsFailed, GetLastError);
+ end;
+end;
+
+// apply buffers
+procedure TCustomComPort.ApplyBuffer;
+begin
+ // if not connected or inside BeginUpdate/EndUpdate block, do nothing
+ if FConnected and (FUpdateCount = 0) and
+ not ((csDesigning in ComponentState) or (csLoading in ComponentState))
+ then
+ //apply settings
+ if not SetupComm(FHandle, FBuffer.InputSize, FBuffer.OutputSize) then
+ //raise EComPort.Create
+ CallException(CError_SetupComFailed, GetLastError);
+end;
+
+// initialize port
+procedure TCustomComPort.SetupComPort;
+begin
+ ApplyBuffer;
+ ApplyDCB;
+ ApplyTimeouts;
+end;
+
+// get number of bytes in input buffer
+function TCustomComPort.InputCount: Integer;
+var
+ Errors: DWORD;
+ ComStat: TComStat;
+begin
+ if not ClearCommError(FHandle, Errors, @ComStat) then
+ //raise EComPort.Create
+ CallException(CError_ClearComFailed, GetLastError);
+ Result := ComStat.cbInQue;
+end;
+
+// get number of bytes in output buffer
+function TCustomComPort.OutputCount: Integer;
+var
+ Errors: DWORD;
+ ComStat: TComStat;
+begin
+ if not ClearCommError(FHandle, Errors, @ComStat) then
+ //raise EComPort.Create
+ CallException(CError_ClearComFailed, GetLastError);
+ Result := ComStat.cbOutQue;
+end;
+
+// get signals which are in high state
+function TCustomComPort.Signals: TComSignals;
+var
+ Status: DWORD;
+begin
+ if not GetCommModemStatus(FHandle, Status) then
+ //raise EComPort.Create
+ CallException(CError_ModemStatFailed, GetLastError);
+ Result := [];
+
+ if (MS_CTS_ON and Status) <> 0 then
+ Result := Result + [csCTS];
+ if (MS_DSR_ON and Status) <> 0 then
+ Result := Result + [csDSR];
+ if (MS_RING_ON and Status) <> 0 then
+ Result := Result + [csRing];
+ if (MS_RLSD_ON and Status) <> 0 then
+ Result := Result + [csRLSD];
+end;
+
+// get port state flags
+function TCustomComPort.StateFlags: TComStateFlags;
+var
+ Errors: DWORD;
+ ComStat: TComStat;
+begin
+ if not ClearCommError(FHandle, Errors, @ComStat) then
+ //raise EComPort.Create
+ CallException(CError_ClearComFailed, GetLastError);
+ Result := ComStat.Flags;
+end;
+
+// set hardware line break
+procedure TCustomComPort.SetBreak(OnOff: Boolean);
+var
+ Act: Integer;
+begin
+ if OnOff then
+ Act := Windows.SETBREAK
+ else
+ Act := Windows.CLRBREAK;
+
+ if not EscapeCommFunction(FHandle, Act) then
+ //raise EComPort.Create
+ CallException(CError_EscapeComFailed, GetLastError);
+end;
+
+// set DTR signal
+procedure TCustomComPort.SetDTR(OnOff: Boolean);
+var
+ Act: DWORD;
+begin
+ if OnOff then
+ Act := Windows.SETDTR
+ else
+ Act := Windows.CLRDTR;
+
+ if not EscapeCommFunction(FHandle, Act) then
+ //raise EComPort.Create
+ CallException(CError_EscapeComFailed, GetLastError);
+end;
+
+// set RTS signals
+procedure TCustomComPort.SetRTS(OnOff: Boolean);
+var
+ Act: DWORD;
+begin
+ if OnOff then
+ Act := Windows.SETRTS
+ else
+ Act := Windows.CLRRTS;
+
+ if not EscapeCommFunction(FHandle, Act) then
+ //raise EComPort.Create
+ CallException(CError_EscapeComFailed, GetLastError);
+end;
+
+// set XonXoff state
+procedure TCustomComPort.SetXonXoff(OnOff: Boolean);
+var
+ Act: DWORD;
+begin
+ if OnOff then
+ Act := Windows.SETXON
+ else
+ Act := Windows.SETXOFF;
+
+ if not EscapeCommFunction(FHandle, Act) then
+ //raise EComPort.Create
+ CallException(CError_EscapeComFailed, GetLastError);
+end;
+
+// clear input and/or output buffer
+procedure TCustomComPort.ClearBuffer(Input, Output: Boolean);
+var
+ Flag: DWORD;
+begin
+ Flag := 0;
+ if Input then
+ Flag := PURGE_RXCLEAR;
+ if Output then
+ Flag := Flag or PURGE_TXCLEAR;
+
+ if not PurgeComm(FHandle, Flag) then
+ //raise EComPort.Create
+ CallException(CError_PurgeFailed, GetLastError);
+end;
+
+// return last errors on port
+function TCustomComPort.LastErrors: TComErrors;
+var
+ Errors: DWORD;
+ ComStat: TComStat;
+begin
+ if not ClearCommError(FHandle, Errors, @ComStat) then
+ //raise EComPort.Create
+ CallException(CError_ClearComFailed, GetLastError);
+ Result := [];
+
+ if (CE_FRAME and Errors) <> 0 then
+ Result := Result + [ceFrame];
+ if ((CE_RXPARITY and Errors) <> 0) and FParity.Check then // get around a bug
+ Result := Result + [ceRxParity];
+ if (CE_OVERRUN and Errors) <> 0 then
+ Result := Result + [ceOverrun];
+ if (CE_RXOVER and Errors) <> 0 then
+ Result := Result + [ceRxOver];
+ if (CE_TXFULL and Errors) <> 0 then
+ Result := Result + [ceTxFull];
+ if (CE_BREAK and Errors) <> 0 then
+ Result := Result + [ceBreak];
+ if (CE_IOE and Errors) <> 0 then
+ Result := Result + [ceIO];
+ if (CE_MODE and Errors) <> 0 then
+ Result := Result + [ceMode];
+end;
+
+// prepare PAsync variable for read/write operation
+procedure PrepareAsync(AKind: TOperationKind; const Buffer; Count: Integer; AsyncPtr: PAsync);
+begin
+ with AsyncPtr^ do
+ begin
+ Kind := AKind;
+ if Data <> nil then
+ FreeMem(Data);
+ GetMem(Data, Count);
+ Move(Buffer, Data^, Count);
+ Size := Count;
+ end;
+end;
+
+// perform asynchronous write operation
+function TCustomComPort.WriteAsync(const Buffer; Count: Integer; var AsyncPtr: PAsync): Integer;
+var
+ Success: Boolean;
+ BytesTrans: DWORD;
+begin
+ if AsyncPtr = nil then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_InvalidAsync);
+ if FHandle = INVALID_HANDLE_VALUE then
+ //raise EComPort.Create
+ CallException(CError_PortNotOpen, -24);
+ PrepareAsync(okWrite, Buffer, Count, AsyncPtr);
+
+ Success := WriteFile(FHandle, Buffer, Count, BytesTrans, @AsyncPtr^.Overlapped)
+ or (GetLastError = ERROR_IO_PENDING);
+
+ if not Success then
+ //raise EComPort.Create
+ CallException(CError_WriteFailed, GetLastError);
+
+ SendSignalToLink(leTx, True);
+ Result := BytesTrans;
+end;
+
+// perform synchronous write operation
+function TCustomComPort.Write(const Buffer; Count: Integer): Integer;
+var
+ AsyncPtr: PAsync;
+begin
+ InitAsync(AsyncPtr);
+ try
+ WriteAsync(Buffer, Count, AsyncPtr);
+ Result := WaitForAsync(AsyncPtr);
+ finally
+ DoneAsync(AsyncPtr);
+ end;
+end;
+
+// perform asynchronous write operation
+function TCustomComPort.WriteStrAsync(var Str: string; var AsyncPtr: PAsync): Integer;
+var sa : Ansistring; var i:integer;
+begin
+ if Length(Str) > 0 then
+ begin
+ setlength(sa,length(str));
+ {$IFDEF Unicode}
+ if length(sa)>0 then
+ begin
+ for i := 1 to length(str) do sa[i] := ansichar(byte(str[i]));
+ move(sa[1],str[1],length(sa));
+ end;
+ {$ENDIF}
+ Result := WriteAsync(Str[1], Length(Str), AsyncPtr)
+ end
+ else
+ Result := 0;
+end;
+// perform synchronous write operation
+function TCustomComPort.WriteStr(Str: string): Integer;
+var
+ AsyncPtr: PAsync;
+begin
+ InitAsync(AsyncPtr);
+ try
+ WriteStrAsync(Str, AsyncPtr);
+ Result := WaitForAsync(AsyncPtr);
+ finally
+ DoneAsync(AsyncPtr);
+ end;
+end;
+//Pierre Yager - includes codepage converstion of strings being sent
+function TCustomComPort.WriteUnicodeString(const Str: Unicodestring): Integer;
+var
+ l: Integer;
+ rb: AnsiString;
+begin
+ l := WideCharToMultiByte(FCodePage, 0, PWideChar(Str), Length(Str), nil, 0, nil, nil);
+ SetLength(rb, l);
+ WideCharToMultiByte(FCodePage, 0, PWideChar(Str), Length(Str), PAnsiChar(rb), l, nil, nil);
+ Result := WriteStr(string(rb));
+end;
+
+//Pierre Yager - includes codepage converstion of strings received
+function TCustomComPort.ReadUnicodeString(var Str: UnicodeString; Count: Integer): Integer;
+var
+ rb: AnsiString;
+ l: Integer;
+ AsyncPtr: PAsync;
+begin
+ InitAsync(AsyncPtr);
+ try
+ setLength(rb,count);
+ Result := ReadAsync(rb[1], Count, AsyncPtr); // ReadStr(s, Count);
+ //{$IFDEF Unicode}rb := UTF8Encode(s);{$ELSE} rb := s; {$ENDIF}
+ l := MultiByteToWideChar(FCodePage, 0, PAnsiChar(rb), Length(rb), nil, 0);
+ SetLength(Str, l);
+ Result := MultiByteToWideChar(FCodePage, 0, PAnsiChar(rb), Length(rb), PWideChar(Str), l);
+ finally
+ DoneAsync(AsyncPtr);
+ end;
+end;
+
+// perform asynchronous read operation
+function TCustomComPort.ReadAsync(var Buffer; Count: Integer; var AsyncPtr: PAsync): Integer;
+var
+ Success: Boolean;
+ BytesTrans: DWORD;
+begin
+ if AsyncPtr = nil then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_InvalidAsync);
+ AsyncPtr^.Kind := okRead;
+ if FHandle = INVALID_HANDLE_VALUE then
+ //raise EComPort.Create
+ CallException(CError_PortNotOpen, -24);
+
+ Success := ReadFile(FHandle, Buffer, Count, BytesTrans, @AsyncPtr^.Overlapped)
+ or (GetLastError = ERROR_IO_PENDING);
+
+ if not Success then
+ //raise EComPort.Create
+ CallException(CError_ReadFailed, GetLastError);
+
+ Result := BytesTrans;
+end;
+
+// perform synchronous read operation
+function TCustomComPort.Read(var Buffer; Count: Integer): Integer;
+var
+ AsyncPtr: PAsync;
+begin
+ InitAsync(AsyncPtr);
+ try
+ ReadAsync(Buffer, Count, AsyncPtr);
+ Result := WaitForAsync(AsyncPtr);
+ finally
+ DoneAsync(AsyncPtr);
+ end;
+end;
+
+// perform asynchronous read operation
+function TCustomComPort.ReadStrAsync(var Str: Ansistring; Count: Integer; var AsyncPtr: PAsync): Integer;
+begin
+ setlength(str,count);
+ if Count > 0 then
+ Result := ReadAsync(str[1], Count, AsyncPtr)
+ else
+ Result := 0;
+end;
+
+// perform synchronous read operation
+function TCustomComPort.ReadStr(var Str: string; Count: Integer): Integer;
+var
+ AsyncPtr: PAsync;
+ sa :ansistring;
+ i : integer;
+begin
+ InitAsync(AsyncPtr);
+ try
+ ReadStrAsync(sa, Count, AsyncPtr);
+ Result := WaitForAsync(AsyncPtr);
+ SetLength(sa, Result);
+ SetLength(str, Result);
+ {$IFDEF Unicode}
+ if length(sa)>0 then
+ for i := 1 to length(sa) do str[i] := char(byte(sa[i]))
+ {$ELSE}
+ str := sa;
+ {$ENDIF}
+ finally
+ DoneAsync(AsyncPtr);
+ end;
+end;
+
+function ErrorCode(AsyncPtr: PAsync): Integer;
+begin
+ Result := 0;
+ case AsyncPtr^.Kind of
+ okWrite: Result := CError_WriteFailed;
+ okRead: Result := CError_ReadFailed;
+ end;
+end;
+
+// wait for asynchronous operation to end
+function TCustomComPort.WaitForAsync(var AsyncPtr: PAsync): Integer;
+var
+ BytesTrans, Signaled: DWORD;
+ Success: Boolean;
+begin
+ if AsyncPtr = nil then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_InvalidAsync);
+
+ Signaled := WaitForSingleObject(AsyncPtr^.Overlapped.hEvent, INFINITE);
+ Success := (Signaled = WAIT_OBJECT_0) and
+ (GetOverlappedResult(FHandle, AsyncPtr^.Overlapped, BytesTrans, False));
+
+ if not Success then
+ //raise EComPort.Create
+ CallException(ErrorCode(AsyncPtr), GetLastError);
+
+ if (AsyncPtr^.Kind = okRead) and (InputCount = 0) then
+ SendSignalToLink(leRx, False)
+ else
+ if AsyncPtr^.Data <> nil then
+ TxNotifyLink(AsyncPtr^.Data^, AsyncPtr^.Size);
+
+ Result := BytesTrans;
+end;
+
+// abort all asynchronous operations
+procedure TCustomComPort.AbortAllAsync;
+begin
+ if not PurgeComm(FHandle, PURGE_TXABORT or PURGE_RXABORT) then
+ //raise EComPort.Create
+ CallException(CError_PurgeFailed, GetLastError);
+end;
+
+// detect whether asynchronous operation is completed
+function TCustomComPort.IsAsyncCompleted(AsyncPtr: PAsync): Boolean;
+var
+ BytesTrans: DWORD;
+begin
+ if AsyncPtr = nil then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_InvalidAsync);
+
+ Result := GetOverlappedResult(FHandle, AsyncPtr^.Overlapped, BytesTrans, False);
+ if not Result then
+ if (GetLastError <> ERROR_IO_PENDING) and (GetLastError <> ERROR_IO_INCOMPLETE) then
+ //raise EComPort.Create
+ CallException(CError_AsyncCheck, GetLastError);
+end;
+
+// waits for event to occur on serial port
+procedure TCustomComPort.WaitForEvent(var Events: TComEvents;
+ StopEvent: THandle; Timeout: Integer);
+var
+ Overlapped: TOverlapped;
+ Mask: DWORD;
+ Success: Boolean;
+ Signaled, EventHandleCount: Integer;
+ EventHandles: array[0..1] of THandle;
+begin
+ // cannot call method if event thread is running
+ if FThreadCreated then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_ThreadCreated);
+
+ FillChar(Overlapped, SizeOf(TOverlapped), 0);
+ Overlapped.hEvent := CreateEvent(nil, True, False, nil);
+ EventHandles[0] := Overlapped.hEvent;
+ if StopEvent <> 0 then
+ begin
+ EventHandles[1] := StopEvent;
+ EventHandleCount := 2;
+ end
+ else
+ EventHandleCount := 1;
+
+ try
+ SetCommMask(FHandle, EventsToInt(Events));
+ // let's wait for event or timeout
+ Success := WaitCommEvent(FHandle, Mask, @Overlapped);
+
+ if (Success) or (GetLastError = ERROR_IO_PENDING) then
+ begin
+ Signaled := WaitForMultipleObjects(EventHandleCount, @EventHandles,
+ False, Timeout);
+ Success := (Signaled = WAIT_OBJECT_0)
+ or (Signaled = WAIT_OBJECT_0 + 1) or (Signaled = WAIT_TIMEOUT);
+ SetCommMask(FHandle, 0);
+ end;
+
+ if not Success then
+ //raise EComPort.Create
+ CallException(CError_WaitFailed, GetLastError);
+
+ Events := IntToEvents(Mask);
+ finally
+ CloseHandle(Overlapped.hEvent);
+ end;
+end;
+
+// transmit char ahead of any pending data in ouput buffer
+procedure TCustomComPort.TransmitChar(Ch: Char);
+begin
+ if not TransmitCommChar(FHandle, AnsiChar(Ch)) then
+ //raise EComPort.Create
+ CallException(CError_TransmitFailed, GetLastError);
+end;
+
+// show port setup dialog
+{$IFNDEF No_Dialogs}
+procedure TCustomComPort.ShowSetupDialog;
+begin
+ EditComPort(Self);
+end;
+{$ENDIF}
+
+// some conversion routines
+function BoolToStr(const Value: Boolean): string;
+begin
+ if Value then
+ Result := 'Yes'
+ else
+ Result := 'No';
+end;
+
+function StrToBool(const Value: string): Boolean;
+begin
+ if UpperCase(Value) = 'YES' then
+ Result := True
+ else
+ Result := False;
+end;
+
+function DTRToStr(DTRFlowControl: TDTRFlowControl): string;
+const
+ DTRStrings: array[TDTRFlowControl] of string = ('Disable', 'Enable',
+ 'Handshake');
+begin
+ Result := DTRStrings[DTRFlowControl];
+end;
+
+function RTSToStr(RTSFlowControl: TRTSFlowControl): string;
+const
+ RTSStrings: array[TRTSFlowControl] of string = ('Disable', 'Enable',
+ 'Handshake', 'Toggle');
+begin
+ Result := RTSStrings[RTSFlowControl];
+end;
+
+function StrToRTS(Str: string): TRTSFlowControl;
+var
+ I: TRTSFlowControl;
+begin
+ I := Low(TRTSFlowControl);
+ while (I <= High(TRTSFlowControl)) do
+ begin
+ if UpperCase(Str) = UpperCase(RTSToStr(I)) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TRTSFlowControl) then
+ Result := rtsDisable
+ else
+ Result := I;
+end;
+
+function StrToDTR(Str: string): TDTRFlowControl;
+var
+ I: TDTRFlowControl;
+begin
+ I := Low(TDTRFlowControl);
+ while (I <= High(TDTRFlowControl)) do
+ begin
+ if UpperCase(Str) = UpperCase(DTRToStr(I)) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TDTRFlowControl) then
+ Result := dtrDisable
+ else
+ Result := I;
+end;
+
+function StrToChar(Str: string): Char;
+var
+ A: Integer;
+begin
+ if Length(Str) > 0 then
+ begin
+ if (Str[1] = '#') and (Length(Str) > 1) then
+ begin
+ try
+ A := StrToInt(Copy(Str, 2, Length(Str) - 1));
+ except
+ A := 0;
+ end;
+ Result := Chr(Byte(A));
+ end
+ else
+ Result := Str[1];
+ end
+ else
+ Result := #0;
+end;
+
+function CharToStr(Ch: Char): string;
+begin
+ {$IFDEF Unicode}
+ if CharInSet(ch,[#33..#127]) then
+ {$ELSE}
+ if Ch in [#33..#127] then {$ENDIF}
+ Result := Ch
+ else
+ Result := '#' + IntToStr(Ord(Ch));
+end;
+
+// store settings to ini file
+procedure TCustomComPort.StoreIniFile(IniFile: TIniFile);
+begin
+ if spBasic in FStoredProps then
+ begin
+ IniFile.WriteString(Name, 'Port', Port);
+ IniFile.WriteString(Name, 'BaudRate', BaudRateToStr(BaudRate));
+ if BaudRate = brCustom then
+ IniFile.WriteInteger(Name, 'CustomBaudRate', CustomBaudRate);
+ IniFile.WriteString(Name, 'StopBits', StopBitsToStr(StopBits));
+ IniFile.WriteString(Name, 'DataBits', DataBitsToStr(DataBits));
+ IniFile.WriteString(Name, 'Parity', ParityToStr(Parity.Bits));
+ IniFile.WriteString(Name, 'FlowControl', FlowControlToStr(FlowControl.FlowControl));
+ end;
+ if spOthers in FStoredProps then
+ begin
+ IniFile.WriteString(Name, 'EventChar', CharToStr(EventChar));
+ IniFile.WriteString(Name, 'DiscardNull', BoolToStr(DiscardNull));
+ end;
+ if spParity in FStoredProps then
+ begin
+ IniFile.WriteString(Name, 'Parity.Check', BoolToStr(Parity.Check));
+ IniFile.WriteString(Name, 'Parity.Replace', BoolToStr(Parity.Replace));
+ IniFile.WriteString(Name, 'Parity.ReplaceChar', CharToStr(Parity.ReplaceChar));
+ end;
+ if spBuffer in FStoredProps then
+ begin
+ IniFile.WriteInteger(Name, 'Buffer.OutputSize', Buffer.OutputSize);
+ IniFile.WriteInteger(Name, 'Buffer.InputSize', Buffer.InputSize);
+ end;
+ if spTimeouts in FStoredProps then
+ begin
+ IniFile.WriteInteger(Name, 'Timeouts.ReadInterval', Timeouts.ReadInterval);
+ IniFile.WriteInteger(Name, 'Timeouts.ReadTotalConstant', Timeouts.ReadTotalConstant);
+ IniFile.WriteInteger(Name, 'Timeouts.ReadTotalMultiplier', Timeouts.ReadTotalMultiplier);
+ IniFile.WriteInteger(Name, 'Timeouts.WriteTotalConstant', Timeouts.WriteTotalConstant);
+ IniFile.WriteInteger(Name, 'Timeouts.WriteTotalMultiplier', Timeouts.WriteTotalMultiplier);
+ end;
+ if spFlowControl in FStoredProps then
+ begin
+ IniFile.WriteString(Name, 'FlowControl.ControlRTS', RTSToStr(FlowControl.ControlRTS));
+ IniFile.WriteString(Name, 'FlowControl.ControlDTR', DTRToStr(FlowControl.ControlDTR));
+ IniFile.WriteString(Name, 'FlowControl.DSRSensitivity', BoolToStr(FlowControl.DSRSensitivity));
+ IniFile.WriteString(Name, 'FlowControl.OutCTSFlow', BoolToStr(FlowControl.OutCTSFlow));
+ IniFile.WriteString(Name, 'FlowControl.OutDSRFlow', BoolToStr(FlowControl.OutDSRFlow));
+ IniFile.WriteString(Name, 'FlowControl.TxContinueOnXoff', BoolToStr(FlowControl.TxContinueOnXoff));
+ IniFile.WriteString(Name, 'FlowControl.XonXoffIn', BoolToStr(FlowControl.XonXoffIn));
+ IniFile.WriteString(Name, 'FlowControl.XonXoffOut', BoolToStr(FlowControl.XonXoffOut));
+ IniFile.WriteString(Name, 'FlowControl.XoffChar', CharToStr(FlowControl.XoffChar));
+ IniFile.WriteString(Name, 'FlowControl.XonChar', CharToStr(FlowControl.XonChar));
+ end;
+end;
+
+// store settings to registry
+procedure TCustomComPort.StoreRegistry(Reg: TRegistry);
+begin
+ if spBasic in FStoredProps then
+ begin
+ Reg.WriteString('Port', Port);
+ Reg.WriteString('BaudRate', BaudRateToStr(BaudRate));
+ if BaudRate = brCustom then
+ Reg.WriteInteger('CustomBaudRate', CustomBaudRate);
+ Reg.WriteString('StopBits', StopBitsToStr(StopBits));
+ Reg.WriteString('DataBits', DataBitsToStr(DataBits));
+ Reg.WriteString('Parity', ParityToStr(Parity.Bits));
+ Reg.WriteString('FlowControl', FlowControlToStr(FlowControl.FlowControl));
+ end;
+ if spOthers in FStoredProps then
+ begin
+ Reg.WriteString('EventChar', CharToStr(EventChar));
+ Reg.WriteString('DiscardNull', BoolToStr(DiscardNull));
+ end;
+ if spParity in FStoredProps then
+ begin
+ Reg.WriteString('Parity.Check', BoolToStr(Parity.Check));
+ Reg.WriteString('Parity.Replace', BoolToStr(Parity.Replace));
+ Reg.WriteString('Parity.ReplaceChar', CharToStr(Parity.ReplaceChar));
+ end;
+ if spBuffer in FStoredProps then
+ begin
+ Reg.WriteInteger('Buffer.OutputSize', Buffer.OutputSize);
+ Reg.WriteInteger('Buffer.InputSize', Buffer.InputSize);
+ end;
+ if spTimeouts in FStoredProps then
+ begin
+ Reg.WriteInteger('Timeouts.ReadInterval', Timeouts.ReadInterval);
+ Reg.WriteInteger('Timeouts.ReadTotalConstant', Timeouts.ReadTotalConstant);
+ Reg.WriteInteger('Timeouts.ReadTotalMultiplier', Timeouts.ReadTotalMultiplier);
+ Reg.WriteInteger('Timeouts.WriteTotalConstant', Timeouts.WriteTotalConstant);
+ Reg.WriteInteger('Timeouts.WriteTotalMultiplier', Timeouts.WriteTotalMultiplier);
+ end;
+ if spFlowControl in FStoredProps then
+ begin
+ Reg.WriteString('FlowControl.ControlRTS', RTSToStr(FlowControl.ControlRTS));
+ Reg.WriteString('FlowControl.ControlDTR', DTRToStr(FlowControl.ControlDTR));
+ Reg.WriteString('FlowControl.DSRSensitivity', BoolToStr(FlowControl.DSRSensitivity));
+ Reg.WriteString('FlowControl.OutCTSFlow', BoolToStr(FlowControl.OutCTSFlow));
+ Reg.WriteString('FlowControl.OutDSRFlow', BoolToStr(FlowControl.OutDSRFlow));
+ Reg.WriteString('FlowControl.TxContinueOnXoff', BoolToStr(FlowControl.TxContinueOnXoff));
+ Reg.WriteString('FlowControl.XonXoffIn', BoolToStr(FlowControl.XonXoffIn));
+ Reg.WriteString('FlowControl.XonXoffOut', BoolToStr(FlowControl.XonXoffOut));
+ Reg.WriteString('FlowControl.XoffChar', CharToStr(FlowControl.XoffChar));
+ Reg.WriteString('FlowControl.XonChar', CharToStr(FlowControl.XonChar));
+ end;
+end;
+
+// load settings from ini file
+procedure TCustomComPort.LoadIniFile(IniFile: TIniFile);
+begin
+ if spBasic in FStoredProps then
+ begin
+ Port := IniFile.ReadString(Name, 'Port', Port);
+ BaudRate := StrToBaudRate(IniFile.ReadString(Name, 'BaudRate', BaudRateToStr(BaudRate)));
+ if BaudRate = brCustom then
+ CustomBaudRate := IniFile.ReadInteger(Name, 'CustomBaudRate', 9600);
+ StopBits := StrToStopBits(IniFile.ReadString(Name, 'StopBits', StopBitsToStr(StopBits)));
+ DataBits := StrToDataBits(IniFile.ReadString(Name, 'DataBits', DataBitsToStr(DataBits)));
+ Parity.Bits := StrToParity(IniFile.ReadString(Name, 'Parity', ParityToStr(Parity.Bits)));
+ FlowControl.FlowControl := StrToFlowControl(
+ IniFile.ReadString(Name, 'FlowControl', FlowControlToStr(FlowControl.FlowControl)));
+ end;
+ if spOthers in FStoredProps then
+ begin
+ EventChar := StrToChar(IniFile.ReadString(Name, 'EventChar', CharToStr(EventChar)));
+ DiscardNull := StrToBool(IniFile.ReadString(Name, 'DiscardNull', BoolToStr(DiscardNull)));
+ end;
+ if spParity in FStoredProps then
+ begin
+ Parity.Check := StrToBool(IniFile.ReadString(Name, 'Parity.Check', BoolToStr(Parity.Check)));
+ Parity.Replace := StrToBool(IniFile.ReadString(Name, 'Parity.Replace', BoolToStr(Parity.Replace)));
+ Parity.ReplaceChar := StrToChar(IniFile.ReadString(Name, 'Parity.ReplaceChar', CharToStr(Parity.ReplaceChar)));
+ end;
+ if spBuffer in FStoredProps then
+ begin
+ Buffer.OutputSize := IniFile.ReadInteger(Name, 'Buffer.OutputSize', Buffer.OutputSize);
+ Buffer.InputSize := IniFile.ReadInteger(Name, 'Buffer.InputSize', Buffer.InputSize);
+ end;
+ if spTimeouts in FStoredProps then
+ begin
+ Timeouts.ReadInterval := IniFile.ReadInteger(Name, 'Timeouts.ReadInterval', Timeouts.ReadInterval);
+ Timeouts.ReadTotalConstant := IniFile.ReadInteger(Name, 'Timeouts.ReadTotalConstant', Timeouts.ReadTotalConstant);
+ Timeouts.ReadTotalMultiplier := IniFile.ReadInteger(Name, 'Timeouts.ReadTotalMultiplier', Timeouts.ReadTotalMultiplier);
+ Timeouts.WriteTotalConstant := IniFile.ReadInteger(Name, 'Timeouts.WriteTotalConstant', Timeouts.WriteTotalConstant);
+ Timeouts.WriteTotalMultiplier := IniFile.ReadInteger(Name, 'Timeouts.WriteTotalMultiplier', Timeouts.WriteTotalMultiplier);
+ end;
+ if spFlowControl in FStoredProps then
+ begin
+ FlowControl.ControlRTS := StrToRTS(IniFile.ReadString(Name, 'FlowControl.ControlRTS', RTSToStr(FlowControl.ControlRTS)));
+ FlowControl.ControlDTR := StrToDTR(IniFile.ReadString(Name, 'FlowControl.ControlDTR', DTRToStr(FlowControl.ControlDTR)));
+ FlowControl.DSRSensitivity := StrToBool(IniFile.ReadString(Name, 'FlowControl.DSRSensitivity', BoolToStr(FlowControl.DSRSensitivity)));
+ FlowControl.OutCTSFlow := StrToBool(IniFile.ReadString(Name, 'FlowControl.OutCTSFlow', BoolToStr(FlowControl.OutCTSFlow)));
+ FlowControl.OutDSRFlow := StrToBool(IniFile.ReadString(Name, 'FlowControl.OutDSRFlow', BoolToStr(FlowControl.OutCTSFlow)));
+ FlowControl.TxContinueOnXoff := StrToBool(IniFile.ReadString(Name, 'FlowControl.TxContinueOnXoff', BoolToStr(FlowControl.TxContinueOnXoff)));
+ FlowControl.XonXoffIn := StrToBool(IniFile.ReadString(Name, 'FlowControl.XonXoffIn', BoolToStr(FlowControl.XonXoffIn)));
+ FlowControl.XonXoffOut := StrToBool(IniFile.ReadString(Name, 'FlowControl.XonXoffOut', BoolToStr(FlowControl.XonXoffOut)));
+ FlowControl.XoffChar := StrToChar(IniFile.ReadString(Name, 'FlowControl.XoffChar', CharToStr(FlowControl.XoffChar)));
+ FlowControl.XonChar := StrToChar(IniFile.ReadString(Name, 'FlowControl.XonChar', CharToStr(FlowControl.XonChar)));
+ end;
+end;
+
+// load settings from registry
+procedure TCustomComPort.LoadRegistry(Reg: TRegistry);
+begin
+ if spBasic in FStoredProps then
+ begin
+ Port := Reg.ReadString('Port');
+ BaudRate := StrToBaudRate(Reg.ReadString('BaudRate'));
+ if BaudRate = brCustom then
+ CustomBaudRate := Reg.ReadInteger('CustomBaudRate');
+ StopBits := StrToStopBits(Reg.ReadString('StopBits'));
+ DataBits := StrToDataBits(Reg.ReadString('DataBits'));
+ Parity.Bits := StrToParity(Reg.ReadString('Parity'));
+ FlowControl.FlowControl := StrToFlowControl(Reg.ReadString('FlowControl'));
+ end;
+ if spOthers in FStoredProps then
+ begin
+ EventChar := StrToChar(Reg.ReadString('EventChar'));
+ DiscardNull := StrToBool(Reg.ReadString('DiscardNull'));
+ end;
+ if spParity in FStoredProps then
+ begin
+ Parity.Check := StrToBool(Reg.ReadString('Parity.Check'));
+ Parity.Replace := StrToBool(Reg.ReadString('Parity.Replace'));
+ Parity.ReplaceChar := StrToChar(Reg.ReadString('Parity.ReplaceChar'));
+ end;
+ if spBuffer in FStoredProps then
+ begin
+ Buffer.OutputSize := Reg.ReadInteger('Buffer.OutputSize');
+ Buffer.InputSize := Reg.ReadInteger('Buffer.InputSize');
+ end;
+ if spTimeouts in FStoredProps then
+ begin
+ Timeouts.ReadInterval := Reg.ReadInteger('Timeouts.ReadInterval');
+ Timeouts.ReadTotalConstant := Reg.ReadInteger('Timeouts.ReadTotalConstant');
+ Timeouts.ReadTotalMultiplier := Reg.ReadInteger('Timeouts.ReadTotalMultiplier');
+ Timeouts.WriteTotalConstant := Reg.ReadInteger('Timeouts.WriteTotalConstant');
+ Timeouts.WriteTotalMultiplier := Reg.ReadInteger('Timeouts.WriteTotalMultiplier');
+ end;
+ if spFlowControl in FStoredProps then
+ begin
+ FlowControl.ControlRTS := StrToRTS(Reg.ReadString('FlowControl.ControlRTS'));
+ FlowControl.ControlDTR := StrToDTR(Reg.ReadString('FlowControl.ControlDTR'));
+ FlowControl.DSRSensitivity := StrToBool(Reg.ReadString('FlowControl.DSRSensitivity'));
+ FlowControl.OutCTSFlow := StrToBool(Reg.ReadString('FlowControl.OutCTSFlow'));
+ FlowControl.OutDSRFlow := StrToBool(Reg.ReadString('FlowControl.OutDSRFlow'));
+ FlowControl.TxContinueOnXoff := StrToBool(Reg.ReadString('FlowControl.TxContinueOnXoff'));
+ FlowControl.XonXoffIn := StrToBool(Reg.ReadString('FlowControl.XonXoffIn'));
+ FlowControl.XonXoffOut := StrToBool(Reg.ReadString('FlowControl.XonXoffOut'));
+ FlowControl.XoffChar := StrToChar(Reg.ReadString('FlowControl.XoffChar'));
+ FlowControl.XonChar := StrToChar(Reg.ReadString('FlowControl.XonChar'));
+ end;
+end;
+
+// initialize registry
+procedure SetRegistry(Reg: TRegistry; Key: string; Name: string);
+var
+ I: Integer;
+ Temp: string;
+begin
+ I := Pos('\', Key);
+ if I > 0 then
+ begin
+ Temp := Copy(Key, 1, I - 1);
+ if UpperCase(Temp) = 'HKEY_LOCAL_MACHINE' then
+ Reg.RootKey := HKEY_LOCAL_MACHINE
+ else
+ if UpperCase(Temp) = 'HKEY_CURRENT_USER' then
+ Reg.RootKey := HKEY_CURRENT_USER;
+ Key := Copy(Key, I + 1, Length(Key) - I);
+ if Key[Length(Key)] <> '\' then
+ Key := Key + '\';
+ Key := Key + Name;
+ Reg.OpenKey(Key, True);
+ end;
+end;
+
+// store settings
+procedure TCustomComPort.StoreSettings(StoreType: TStoreType; StoreTo: string);
+var
+ IniFile: TIniFile;
+ Reg: TRegistry;
+begin
+ try
+ if StoreType = stRegistry then
+ begin
+ Reg := TRegistry.Create;
+ try
+ SetRegistry(Reg, StoreTo, Name);
+ StoreRegistry(Reg);
+ finally
+ Reg.Free;
+ end
+ end else
+ begin
+ IniFile := TIniFile.Create(StoreTo);
+ try
+ StoreIniFile(IniFile);
+ finally
+ IniFile.Free;
+ end
+ end;
+ except
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_StoreFailed);
+ end;
+end;
+
+// load settings
+procedure TCustomComPort.LoadSettings(StoreType: TStoreType; LoadFrom: string);
+var
+ IniFile: TIniFile;
+ Reg: TRegistry;
+begin
+ BeginUpdate;
+ try
+ try
+ if StoreType = stRegistry then
+ begin
+ Reg := TRegistry.Create;
+ try
+ SetRegistry(Reg, LoadFrom, Name);
+ LoadRegistry(Reg);
+ finally
+ Reg.Free;
+ end
+ end else
+ begin
+ IniFile := TIniFile.Create(LoadFrom);
+ try
+ LoadIniFile(IniFile);
+ finally
+ IniFile.Free;
+ end
+ end;
+ finally
+ EndUpdate;
+ end;
+ except
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_LoadFailed);
+ end;
+end;
+
+// register link from other component to TCustomComPort
+procedure TCustomComPort.RegisterLink(AComLink: TComLink);
+begin
+ if FLinks.IndexOf(Pointer(AComLink)) > -1 then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_RegFailed)
+ else
+ FLinks.Add(Pointer(AComLink));
+ FHasLink := HasLink;
+end;
+
+// unregister link from other component to TCustomComPort
+procedure TCustomComPort.UnRegisterLink(AComLink: TComLink);
+begin
+ if FLinks.IndexOf(Pointer(AComLink)) = -1 then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_RegFailed)
+ else
+ FLinks.Remove(Pointer(AComLink));
+ FHasLink := HasLink;
+end;
+
+// default actions on port events
+
+procedure TCustomComPort.DoBeforeClose;
+begin
+ if Assigned(FOnBeforeClose) then
+ FOnBeforeClose(Self);
+end;
+
+procedure TCustomComPort.DoBeforeOpen;
+begin
+ if Assigned(FOnBeforeOpen) then
+ FOnBeforeOpen(Self);
+end;
+
+procedure TCustomComPort.DoAfterOpen;
+begin
+ if Assigned(FOnAfterOpen) then
+ FOnAfterOpen(Self);
+end;
+
+procedure TCustomComPort.DoAfterClose;
+begin
+ if Assigned(FOnAfterClose) then
+ FOnAfterClose(Self);
+end;
+
+procedure TCustomComPort.DoRxChar(Count: Integer);
+begin
+ if Assigned(FOnRxChar) then
+ FOnRxChar(Self, Count);
+end;
+
+procedure TCustomComPort.DoRxBuf(const Buffer; Count: Integer);
+begin
+ if Assigned(FOnRxBuf) then
+ FOnRxBuf(Self, Buffer, Count);
+end;
+
+procedure TCustomComPort.DoBreak;
+begin
+ if Assigned(FOnBreak) then
+ FOnBreak(Self);
+end;
+
+procedure TCustomComPort.DoTxEmpty;
+begin
+ if Assigned(FOnTxEmpty)
+ then FOnTxEmpty(Self);
+end;
+
+procedure TCustomComPort.DoRing;
+begin
+ if Assigned(FOnRing) then
+ FOnRing(Self);
+end;
+
+procedure TCustomComPort.DoCTSChange(OnOff: Boolean);
+begin
+ if Assigned(FOnCTSChange) then
+ FOnCTSChange(Self, OnOff);
+end;
+
+procedure TCustomComPort.DoDSRChange(OnOff: Boolean);
+begin
+ if Assigned(FOnDSRChange) then
+ FOnDSRChange(Self, OnOff);
+end;
+
+procedure TCustomComPort.DoRLSDChange(OnOff: Boolean);
+begin
+ if Assigned(FOnRLSDChange) then
+ FOnRLSDChange(Self, OnOff);
+end;
+
+procedure TCustomComPort.DoError(Errors: TComErrors);
+begin
+ if Assigned(FOnError) then
+ FOnError(Self, Errors);
+end;
+
+procedure TCustomComPort.DoRxFlag;
+begin
+ if Assigned(FOnRxFlag) then
+ FOnRxFlag(Self);
+end;
+
+procedure TCustomComPort.DoRx80Full;
+begin
+ if Assigned(FOnRx80Full) then
+ FOnRx80Full(Self);
+end;
+
+// set signals to false on close, and to proper value on open,
+// because OnXChange events are not called automatically
+procedure TCustomComPort.CheckSignals(Open: Boolean);
+begin
+ if Open then
+ begin
+ CallCTSChange;
+ CallDSRChange;
+ CallRLSDChange;
+ end else
+ begin
+ SendSignalToLink(leCTS, False);
+ SendSignalToLink(leDSR, False);
+ SendSignalToLink(leRLSD, False);
+ DoCTSChange(False);
+ DoDSRChange(False);
+ DoRLSDChange(False);
+ end;
+end;
+
+// called in response to EV_X events, except CallXClose, CallXOpen
+
+procedure TCustomComPort.CallAfterClose;
+begin
+ SendSignalToLink(leConn, False);
+ DoAfterClose;
+end;
+
+procedure TCustomComPort.CallAfterOpen;
+begin
+ SendSignalToLink(leConn, True);
+ DoAfterOpen;
+ CheckSignals(True);
+end;
+
+procedure TCustomComPort.CallBeforeClose;
+begin
+ // shutdown com signals manually
+ CheckSignals(False);
+ DoBeforeClose;
+end;
+
+procedure TCustomComPort.CallBeforeOpen;
+begin
+ DoBeforeOpen;
+end;
+
+procedure TCustomComPort.CallBreak;
+begin
+ DoBreak;
+end;
+
+procedure TCustomComPort.CallCTSChange;
+var
+ OnOff: Boolean;
+begin
+ OnOff := csCTS in Signals;
+ // check for linked components
+ SendSignalToLink(leCTS, OnOff);
+ DoCTSChange(OnOff);
+end;
+
+procedure TCustomComPort.CallDSRChange;
+var
+ OnOff: Boolean;
+begin
+ OnOff := csDSR in Signals;
+ // check for linked components
+ SendSignalToLink(leDSR, OnOff);
+ DoDSRChange(OnOff);
+end;
+
+procedure TCustomComPort.CallRLSDChange;
+var
+ OnOff: Boolean;
+begin
+ OnOff := csRLSD in Signals;
+ // check for linked components
+ SendSignalToLink(leRLSD, OnOff);
+ DoRLSDChange(OnOff);
+end;
+
+procedure TCustomComPort.CallError;
+var
+ Errors: TComErrors;
+begin
+ Errors := LastErrors;
+ if Errors <> [] then
+ DoError(Errors);
+end;
+
+procedure TCustomComPort.CallRing;
+begin
+ NotifyLink(leRing);
+ DoRing;
+end;
+
+procedure TCustomComPort.CallRx80Full;
+begin
+ DoRx80Full;
+end;
+
+procedure TCustomComPort.CallRxChar;
+var
+ Count: Integer;
+
+ // read from input buffer
+ procedure PerformRead(var P: Pointer);
+ begin
+ GetMem(P, Count);
+ Read(P^, Count);
+ // call OnRxBuf event
+ DoRxBuf(P^, Count);
+ end;
+
+ // check if any component is linked, to OnRxChar event
+ procedure CheckLinks;
+ {$WARNINGS OFF}
+ var
+ I: Integer;
+ P: Pointer;
+ ComLink: TComLink;
+ ReadFromBuffer: Boolean;
+ begin
+ // examine links
+ if (Count > 0) and (not TriggersOnRxChar) then
+ begin
+ ReadFromBuffer := False;
+ try
+ // cycle through links
+ for I := 0 to FLinks.Count - 1 do
+ begin
+ ComLink := TComLink(FLinks[I]);
+ if Assigned(ComLink.OnRxBuf) then
+ begin
+ // link to OnRxChar event found
+ if not ReadFromBuffer then
+ begin
+ // TCustomComPort must read from comport, so OnRxChar event is
+ // not triggered
+ ReadFromBuffer := True;
+ PerformRead(P);
+ end;
+ // send data to linked component
+ ComLink.OnRxBuf(Self, P^, Count);
+ end
+ end;
+ if (not ReadFromBuffer) and (not FTriggersOnRxChar) then
+ begin
+ ReadFromBuffer := True;
+ PerformRead(P);
+ end;
+ finally
+ if ReadFromBuffer then
+ begin
+ FreeMem(P);
+ // data is already out of buffer, prevent from OnRxChar event to occur
+ Count := 0;
+ end;
+ end;
+ end;
+ end;
+
+begin
+ Count := InputCount;
+ if Count > 0 then
+ SendSignalToLink(leRx, True);
+ CheckLinks;
+ if Count > 0 then
+ DoRxChar(Count);
+end;
+
+procedure TCustomComPort.CallRxFlag;
+begin
+ NotifyLink(leRxFlag);
+ DoRxFlag;
+end;
+
+procedure TCustomComPort.CallTxEmpty;
+begin
+ SendSignalToLink(leTx, False);
+ NotifyLink(leTxEmpty);
+ DoTxEmpty;
+end;
+
+// returns true if it has least one component linked to OnRxBuf event
+function TCustomComPort.HasLink: Boolean;
+var
+ I: Integer;
+ ComLink: TComLink;
+begin
+ Result := False;
+ // examine links
+ if FLinks.Count > 0 then
+ for I := 0 to FLinks.Count - 1 do
+ begin
+ ComLink := TComLink(FLinks[I]);
+ if Assigned(ComLink.OnRxBuf) then
+ Result := True;
+ end;
+end;
+
+// send TxBuf notify to link
+procedure TCustomComPort.TxNotifyLink(const Buffer; Count: Integer);
+var
+ I: Integer;
+ ComLink: TComLink;
+begin
+ if (FLinks.Count > 0) then
+ for I := 0 to FLinks.Count - 1 do
+ begin
+ ComLink := TComLink(FLinks[I]);
+ if Assigned(ComLink.OnTxBuf) then
+ ComLink.OnTxBuf(Self, Buffer, Count);
+ end;
+end;
+
+// send event notification to link
+procedure TCustomComPort.NotifyLink(FLinkEvent: TComLinkEvent);
+var
+ I: Integer;
+ ComLink: TComLink;
+ Event: TNotifyEvent;
+begin
+ if (FLinks.Count > 0) then
+ for I := 0 to FLinks.Count - 1 do
+ begin
+ ComLink := TComLink(FLinks[I]);
+ Event := nil;
+ case FLinkEvent of
+ leRing: Event := ComLink.OnRing;
+ leTxEmpty: Event := ComLink.OnTxEmpty;
+ leRxFlag: Event := ComLink.OnRxFlag;
+ end;
+ if Assigned(Event) then
+ Event(Self);
+ end;
+end;
+
+// send signal to linked components
+procedure TCustomComPort.SendSignalToLink(Signal: TComLinkEvent; OnOff: Boolean);
+var
+ I: Integer;
+ ComLink: TComLink;
+ SignalEvent: TComSignalEvent;
+begin
+ if (FLinks.Count > 0) then
+ // cycle through links
+ for I := 0 to FLinks.Count - 1 do
+ begin
+ ComLink := TComLink(FLinks[I]);
+ SignalEvent := nil;
+ case Signal of
+ leCTS: SignalEvent := ComLink.OnCTSChange;
+ leDSR: SignalEvent := ComLink.OnDSRChange;
+ leRLSD: SignalEvent := ComLink.OnRLSDChange;
+ leTx: SignalEvent := ComLink.OnTx;
+ leRx: SignalEvent := ComLink.OnRx;
+ leConn: SignalEvent := ComLink.OnConn;
+ end;
+ // if linked, trigger event
+ if Assigned(SignalEvent) then
+ SignalEvent(Self, OnOff);
+ end;
+end;
+
+// set connected property, same as Open/Close methods
+procedure TCustomComPort.SetConnected(const Value: Boolean);
+begin
+ if not ((csDesigning in ComponentState) or (csLoading in ComponentState)) then
+ begin
+ if Value <> FConnected then
+ if Value then
+ Open
+ else
+ Close;
+ end
+ else
+ FConnected := Value;
+end;
+
+// set baud rate
+procedure TCustomComPort.SetBaudRate(const Value: TBaudRate);
+begin
+ if Value <> FBaudRate then
+ begin
+ FBaudRate := Value;
+ // if possible, apply settings
+ ApplyDCB;
+ end;
+end;
+
+// set custom baud rate
+procedure TCustomComPort.SetCustomBaudRate(const Value: Integer);
+begin
+ if Value <> FCustomBaudRate then
+ begin
+ FCustomBaudRate := Value;
+ ApplyDCB;
+ end;
+end;
+
+// set data bits
+procedure TCustomComPort.SetDataBits(const Value: TDataBits);
+begin
+ if Value <> FDataBits then
+ begin
+ FDataBits := Value;
+ ApplyDCB;
+ end;
+end;
+
+// set discard null characters
+procedure TCustomComPort.SetDiscardNull(const Value: Boolean);
+begin
+ if Value <> FDiscardNull then
+ begin
+ FDiscardNull := Value;
+ ApplyDCB;
+ end;
+end;
+
+// set event characters
+procedure TCustomComPort.SetEventChar(const Value: Char);
+begin
+ if Value <> FEventChar then
+ begin
+ FEventChar := Value;
+ ApplyDCB;
+ end;
+end;
+
+// set port
+procedure TCustomComPort.SetPort(const Value: TPort);
+begin
+ // 11.1.2001 Ch. Kaufmann; removed function ComString, because there can be com ports
+ // with names other than COMn.
+ if Value <> FPort then
+ begin
+ FPort := Value;
+ if FConnected and not ((csDesigning in ComponentState) or
+ (csLoading in ComponentState)) then
+ begin
+ Close;
+ Open;
+ end;
+ end;
+end;
+
+// set stop bits
+procedure TCustomComPort.SetStopBits(const Value: TStopBits);
+begin
+ if Value <> FStopBits then
+ begin
+ FStopBits := Value;
+ ApplyDCB;
+ end;
+end;
+
+// set event synchronization method
+procedure TCustomComPort.SetSyncMethod(const Value: TSyncMethod);
+begin
+ if Value <> FSyncMethod then
+ begin
+ if FConnected and not ((csDesigning in ComponentState) or
+ (csLoading in ComponentState))
+ then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_ConnChangeProp)
+ else
+ FSyncMethod := Value;
+ end;
+end;
+
+// sets RxChar triggering
+procedure TCustomComPort.SetTriggersOnRxChar(const Value: Boolean);
+begin
+ if FHasLink then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_HasLink);
+ FTriggersOnRxChar := Value;
+end;
+
+// sets event thread priority
+procedure TCustomComPort.SetEventThreadPriority(const Value: TThreadPriority);
+begin
+ if Value <> FEventThreadPriority then
+ begin
+ if FConnected and not ((csDesigning in ComponentState) or
+ (csLoading in ComponentState))
+ then
+ //raise EComPort.CreateNoWinCode
+ CallException(CError_ConnChangeProp)
+ else
+ FEventThreadPriority := Value;
+ end;
+end;
+
+// returns true if RxChar is triggered when data arrives input buffer
+function TCustomComPort.GetTriggersOnRxChar: Boolean;
+begin
+ Result := FTriggersOnRxChar and (not FHasLink);
+end;
+
+// set flow control
+procedure TCustomComPort.SetFlowControl(const Value: TComFlowControl);
+begin
+ FFlowControl.Assign(Value);
+ ApplyDCB;
+end;
+
+// set parity
+procedure TCustomComPort.SetParity(const Value: TComParity);
+begin
+ FParity.Assign(Value);
+ ApplyDCB;
+end;
+
+// set timeouts
+procedure TCustomComPort.SetTimeouts(const Value: TComTimeouts);
+begin
+ FTimeouts.Assign(Value);
+ ApplyTimeouts;
+end;
+
+// set buffer
+procedure TCustomComPort.SetBuffer(const Value: TComBuffer);
+begin
+ FBuffer.Assign(Value);
+ ApplyBuffer;
+end;
+
+(*****************************************
+ * TComDataPacket component *
+ *****************************************)
+
+// create component
+constructor TComDataPacket.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FComLink := TComLink.Create;
+ FComLink.OnRxBuf := RxBuf;
+ FMaxBufferSize := 1024;
+end;
+
+// destroy component
+destructor TComDataPacket.Destroy;
+begin
+ ComPort := nil;
+ FComLink.Free;
+ inherited Destroy;
+end;
+
+// add custom data to packet buffer
+procedure TComDataPacket.AddData(const Str: string);
+begin
+ if ValidStop then
+ begin
+ Buffer := Buffer + Str;
+ HandleBuffer;
+ end
+ else
+ DoPacket(Str);
+end;
+
+// remove ComPort property if being destroyed
+procedure TComDataPacket.Notification(AComponent: TComponent;
+ Operation: TOperation);
+begin
+ inherited Notification(AComponent, Operation);
+ if (AComponent = FComPort) and (Operation = opRemove) then
+ ComPort := nil;
+end;
+
+// call OnDiscard
+procedure TComDataPacket.DoDiscard(const Str: string);
+begin
+ if Assigned(FOnDiscard) then
+ FOnDiscard(Self, Str);
+end;
+
+// call OnPacket
+procedure TComDataPacket.DoPacket(const Str: string);
+begin
+ if Assigned(FOnPacket) then
+ FOnPacket(Self, Str);
+end;
+
+// call OnCustomStart
+procedure TComDataPacket.DoCustomStart(const Str: string;
+ var Pos: Integer);
+begin
+ if Assigned(FOnCustomStart) then
+ FOnCustomStart(Self, Str, Pos);
+end;
+
+// call OnCustomStop
+procedure TComDataPacket.DoCustomStop(const Str: string; var Pos: Integer);
+begin
+ if Assigned(FOnCustomStop) then
+ FOnCustomStop(Self, Str, Pos);
+end;
+
+// discard start and stop strings
+procedure TComDataPacket.CheckIncludeStrings(var Str: string);
+var
+ LenStart, LenStop: Integer;
+begin
+ if FIncludeStrings then
+ Exit;
+ LenStart := Length(FStartString);
+ LenStop := Length(FStopString);
+ // remove start string
+ if Pos(Upper(FStartString), Upper(Str)) = 1 then
+ Str := Copy(Str, LenStart + 1, Length(Str) - LenStart);
+ // remove stop string
+ if Pos(Upper(FStopString), Upper(Str)) = (Length(Str) - LenStop + 1) then
+ Str := Copy(Str, 1, Length(Str) - LenStop);
+end;
+
+// upper case
+function TComDataPacket.Upper(const Str: string): string;
+begin
+ if FCaseInsensitive then
+ Result := UpperCase(Str)
+ else
+ Result := Str;
+end;
+
+// split buffer in packets
+procedure TComDataPacket.HandleBuffer;
+
+ procedure DiscardPacketToPos(Pos: Integer);
+ var
+ Str: string;
+ begin
+ FInPacket := True;
+ if Pos > 1 then
+ begin
+ Str := Copy(Buffer, 1, Pos - 1); // some discarded data
+ Buffer := Copy(Buffer, Pos, Length(Buffer) - Pos + 1);
+ DoDiscard(Str);
+ end;
+ end;
+
+ procedure FormPacket(CutSize: Integer);
+ var
+ Str: string;
+ begin
+ Str := Copy(Buffer, 1, CutSize);
+ Buffer := Copy(Buffer, CutSize + 1, Length(Buffer) - CutSize);
+ CheckIncludeStrings(Str);
+ DoPacket(Str);
+ end;
+
+ procedure StartPacket;
+ var
+ Found: Integer;
+ begin
+ // check for custom start condition
+ Found := -1;
+ DoCustomStart(Buffer, Found);
+ if Found > 0 then
+ DiscardPacketToPos(Found);
+ if Found = -1 then
+ begin
+ if Length(FStartString) > 0 then // start string valid
+ begin
+ Found := Pos(Upper(FStartString), Upper(Buffer));
+ if Found > 0 then
+ DiscardPacketToPos(Found);
+ end
+ else
+ FInPacket := True;
+ end;
+ end;
+
+ procedure EndPacket;
+ var
+ Found, CutSize, Len: Integer;
+ begin
+ // check for custom stop condition
+ Found := -1;
+ DoCustomStop(Buffer, Found);
+ if Found > 0 then
+ begin
+ // custom stop condition detected
+ CutSize := Found;
+ FInPacket := False;
+ end
+ else
+ if Found = -1 then
+ begin
+ Len := Length(Buffer);
+ if (FSize > 0) and (Len >= FSize) then
+ begin
+ // size stop condition detected
+ FInPacket := False;
+ CutSize := FSize;
+ end
+ else
+ begin
+ Len := Length(FStartString);
+ Found := Pos(Upper(FStopString),
+ Upper(Copy(Buffer, Len + 1, Length(Buffer) - Len)));
+ if Found > 0 then
+ begin
+ // stop string stop condition detected
+ CutSize := Found + Length(FStopString) + Len - 1;
+ FInPacket := False;
+ end;
+ end;
+ end;
+ if not FInPacket then
+ FormPacket(CutSize); // create packet
+ end;
+
+ function IsBufferTooLarge: Boolean;
+ begin
+ Result := (Length(Buffer) >= FMaxBufferSize) and (FMaxBufferSize > 0);
+ end;
+
+begin
+ try
+ if not FInPacket then
+ StartPacket;
+ if FInPacket then
+ begin
+ EndPacket;
+ if not FInPacket then
+ HandleBuffer;
+ end;
+ finally
+ if IsBufferTooLarge then
+ EmptyBuffer;
+ end;
+end;
+
+// is stop condition valid?
+function TComDataPacket.ValidStop: Boolean;
+begin
+ Result := (FSize > 0) or (Length(FStopString) > 0)
+ or (Assigned(FOnCustomStop));
+end;
+
+// receive data
+procedure TComDataPacket.ResetBuffer;
+begin
+ EmptyBuffer;
+end;
+
+procedure TComDataPacket.RxBuf(Sender: TObject; const Buffer; Count: Integer);
+var sa:AnsiString; Str: string;
+ i:integer;
+begin
+ SetLength(Str, Count);
+ SetLength(Sa, Count);
+ Move(Buffer, Sa[1], Count);
+ {$IFDEF Unicode}
+ if length(sa)>0 then
+ for i := 1 to length(sa) do str[i] := char(byte(sa[i]));
+ {$ELSE} str := sa; {$ENDIF}
+ AddData(Str);
+end;
+
+// empty buffer
+procedure TComDataPacket.EmptyBuffer;
+begin
+ if Buffer <> '' then
+ begin
+ try
+ DoDiscard(Buffer);
+ finally
+ Buffer := '';
+ FInPacket := False;
+ end;
+ end;
+end;
+
+// set com port
+procedure TComDataPacket.SetComPort(const Value: TCustomComPort);
+begin
+ if Value <> FComPort then
+ begin
+ if FComPort <> nil then
+ FComPort.UnRegisterLink(FComLink);
+ FComPort := Value;
+ if FComPort <> nil then
+ begin
+ FComPort.FreeNotification(Self);
+ FComPort.RegisterLink(FComLink);
+ end;
+ end;
+end;
+
+// set case sensitivity
+procedure TComDataPacket.SetCaseInsensitive(const Value: Boolean);
+begin
+ if FCaseInsensitive <> Value then
+ begin
+ FCaseInsensitive := Value;
+ if not (csLoading in ComponentState) then
+ EmptyBuffer;
+ end;
+end;
+
+// set packet size
+procedure TComDataPacket.SetSize(const Value: Integer);
+begin
+ if FSize <> Value then
+ begin
+ FSize := Value;
+ if not (csLoading in ComponentState) then
+ EmptyBuffer;
+ end;
+end;
+
+// set start string
+procedure TComDataPacket.SetStartString(const Value: string);
+begin
+ if FStartString <> Value then
+ begin
+ FStartString := Value;
+ if not (csLoading in ComponentState) then
+ EmptyBuffer;
+ end;
+end;
+
+// set stop string
+procedure TComDataPacket.SetStopString(const Value: string);
+begin
+ if FStopString <> Value then
+ begin
+ FStopString := Value;
+ if not (csLoading in ComponentState) then
+ EmptyBuffer;
+ end;
+end;
+
+(*****************************************
+ * EComPort exception *
+ *****************************************)
+
+// create stream
+constructor TComStream.Create(AComPort: TCustomComPort);
+begin
+ inherited Create;
+ FComPort := AComPort;
+end;
+
+// read from stream
+function TComStream.Read(var Buffer; Count: Integer): Longint;
+begin
+ FComPort.Read(Buffer, Count);
+end;
+
+// write to stream
+function TComStream.Write(const Buffer; Count: Integer): Longint;
+begin
+ FComPort.Write(Buffer, Count);
+end;
+
+// seek always to 0
+function TComStream.Seek(Offset: Integer; Origin: Word): Longint;
+begin
+ Result := 0;
+end;
+
+(*****************************************
+ * EComPort exception *
+ *****************************************)
+
+// create exception with windows error code
+constructor EComPort.Create(ACode: Integer; AWinCode: Integer);
+begin
+ FWinCode := AWinCode;
+ FCode := ACode;
+ inherited CreateFmt(ComErrorMessages[ACode] + ' (Error: %d)', [AWinCode]);
+end;
+
+// create exception
+constructor EComPort.CreateNoWinCode(ACode: Integer);
+begin
+ FWinCode := -1;
+ FCode := ACode;
+ inherited Create(ComErrorMessages[ACode]);
+end;
+
+(*****************************************
+ * other procedures/functions *
+ *****************************************)
+
+// initialization of PAsync variables used in asynchronous calls
+procedure InitAsync(var AsyncPtr: PAsync);
+begin
+ New(AsyncPtr);
+ with AsyncPtr^ do
+ begin
+ FillChar(Overlapped, SizeOf(TOverlapped), 0);
+ Overlapped.hEvent := CreateEvent(nil, True, True, nil);
+ Data := nil;
+ Size := 0;
+ end;
+end;
+
+// clean-up of PAsync variable
+procedure DoneAsync(var AsyncPtr: PAsync);
+begin
+ with AsyncPtr^ do
+ begin
+ CloseHandle(Overlapped.hEvent);
+ if Data <> nil then
+ FreeMem(Data);
+ end;
+ Dispose(AsyncPtr);
+ AsyncPtr := nil;
+end;
+
+procedure EnumComPorts(Ports: TStrings);
+var
+ KeyHandle: HKEY;
+ ErrCode, Index: Integer;
+ ValueName, Data: string;
+ ValueLen, DataLen, ValueType: DWORD;
+ TmpPorts: TStringList;
+begin
+ ErrCode := RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ 'HARDWARE\DEVICEMAP\SERIALCOMM',
+ 0,
+ KEY_READ,
+ KeyHandle);
+
+ if ErrCode <> ERROR_SUCCESS then
+ begin
+ //raise EComPort.Create(CError_RegError, ErrCode);
+ exit;
+ end;
+
+ TmpPorts := TStringList.Create;
+ try
+ Index := 0;
+ repeat
+ ValueLen := 256;
+ DataLen := 256;
+ SetLength(ValueName, ValueLen);
+ SetLength(Data, DataLen);
+ ErrCode := RegEnumValue(
+ KeyHandle,
+ Index,
+ PChar(ValueName),
+ {$IFDEF DELPHI_4_OR_HIGHER}
+ Cardinal(ValueLen),
+ {$ELSE}
+ ValueLen,
+ {$ENDIF}
+ nil,
+ @ValueType,
+ PByte(PChar(Data)),
+ @DataLen);
+
+ if ErrCode = ERROR_SUCCESS then
+ begin
+ SetLength(Data, DataLen - 1);
+ TmpPorts.Add(Data);
+ Inc(Index);
+ end
+ else
+ if ErrCode <> ERROR_NO_MORE_ITEMS then break;
+ //raise EComPort.Create(CError_RegError, ErrCode);
+
+ until (ErrCode <> ERROR_SUCCESS) ;
+
+ TmpPorts.Sort;
+ Ports.Assign(TmpPorts);
+ finally
+ RegCloseKey(KeyHandle);
+ TmpPorts.Free;
+ end;
+
+end;
+
+// string to baud rate
+function StrToBaudRate(Str: string): TBaudRate;
+var
+ I: TBaudRate;
+begin
+ I := Low(TBaudRate);
+ while (I <= High(TBaudRate)) do
+ begin
+ if UpperCase(Str) = UpperCase(BaudRateToStr(TBaudRate(I))) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TBaudRate) then
+ Result := br9600
+ else
+ Result := I;
+end;
+
+// string to stop bits
+function StrToStopBits(Str: string): TStopBits;
+var
+ I: TStopBits;
+begin
+ I := Low(TStopBits);
+ while (I <= High(TStopBits)) do
+ begin
+ if UpperCase(Str) = UpperCase(StopBitsToStr(TStopBits(I))) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TStopBits) then
+ Result := sbOneStopBit
+ else
+ Result := I;
+end;
+
+// string to data bits
+function StrToDataBits(Str: string): TDataBits;
+var
+ I: TDataBits;
+begin
+ I := Low(TDataBits);
+ while (I <= High(TDataBits)) do
+ begin
+ if UpperCase(Str) = UpperCase(DataBitsToStr(I)) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TDataBits) then
+ Result := dbEight
+ else
+ Result := I;
+end;
+
+// string to parity
+function StrToParity(Str: string): TParityBits;
+var
+ I: TParityBits;
+begin
+ I := Low(TParityBits);
+ while (I <= High(TParityBits)) do
+ begin
+ if UpperCase(Str) = UpperCase(ParityToStr(I)) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TParityBits) then
+ Result := prNone
+ else
+ Result := I;
+end;
+
+// string to flow control
+function StrToFlowControl(Str: string): TFlowControl;
+var
+ I: TFlowControl;
+begin
+ I := Low(TFlowControl);
+ while (I <= High(TFlowControl)) do
+ begin
+ if UpperCase(Str) = UpperCase(FlowControlToStr(I)) then
+ Break;
+ I := Succ(I);
+ end;
+ if I > High(TFlowControl) then
+ Result := fcCustom
+ else
+ Result := I;
+end;
+
+// baud rate to string
+function BaudRateToStr(BaudRate: TBaudRate): string;
+const
+ BaudRateStrings: array[TBaudRate] of string = ('Custom', '110', '300', '600',
+ '1200', '2400', '4800', '9600', '14400', '19200', '38400', '56000', '57600',
+ '115200', '128000', '256000');
+begin
+ Result := BaudRateStrings[BaudRate];
+end;
+
+// stop bits to string
+function StopBitsToStr(StopBits: TStopBits): string;
+const
+ StopBitsStrings: array[TStopBits] of string = ('1', '1.5', '2');
+begin
+ Result := StopBitsStrings[StopBits];
+end;
+
+// data bits to string
+function DataBitsToStr(DataBits: TDataBits): string;
+const
+ DataBitsStrings: array[TDataBits] of string = ('5', '6', '7', '8');
+begin
+ Result := DataBitsStrings[DataBits];
+end;
+
+// parity to string
+function ParityToStr(Parity: TParityBits): string;
+const
+ ParityBitsStrings: array[TParityBits] of string = ('None', 'Odd', 'Even',
+ 'Mark', 'Space');
+begin
+ Result := ParityBitsStrings[Parity];
+end;
+
+// flow control to string
+function FlowControlToStr(FlowControl: TFlowControl): string;
+const
+ FlowControlStrings: array[TFlowControl] of string = ('Hardware',
+ 'Software', 'None', 'Custom');
+begin
+ Result := FlowControlStrings[FlowControl];
+end;
+
+initialization
+ ComErrorMessages[1]:='Unable to open com port';
+ ComErrorMessages[2]:='WriteFile function failed';
+ ComErrorMessages[3]:='ReadFile function failed';
+ ComErrorMessages[4]:='Invalid Async parameter';
+ ComErrorMessages[5]:='PurgeComm function failed';
+ ComErrorMessages[6]:='Unable to get async status';
+ ComErrorMessages[7]:='SetCommState function failed';
+ ComErrorMessages[8]:='SetCommTimeouts failed';
+ ComErrorMessages[9]:='SetupComm function failed';
+ ComErrorMessages[10]:='ClearCommError function failed';
+ ComErrorMessages[11]:='GetCommModemStatus function failed';
+ ComErrorMessages[12]:='EscapeCommFunction function failed';
+ ComErrorMessages[13]:='TransmitCommChar function failed';
+ ComErrorMessages[14]:='Cannot set property while connected';
+ ComErrorMessages[15]:='EnumPorts function failed';
+ ComErrorMessages[16]:='Failed to store settings';
+ ComErrorMessages[17]:='Failed to load settings';
+ ComErrorMessages[18]:='Link (un)registration failed';
+ ComErrorMessages[19]:='Cannot change led state if ComPort is selected';
+ ComErrorMessages[20]:='Cannot wait for event if event thread is created';
+ ComErrorMessages[21]:='WaitForEvent method failed';
+ ComErrorMessages[22]:='A component is linked to OnRxBuf event';
+ ComErrorMessages[23]:='Registry error';
+ ComErrorMessages[24]:='Port Not Open';// CError_PortNotOpen
+
+
+end.
diff --git a/AssettoCorsa/source/MainFrm.dfm b/AssettoCorsa/source/MainFrm.dfm
new file mode 100644
index 0000000..79701d3
--- /dev/null
+++ b/AssettoCorsa/source/MainFrm.dfm
@@ -0,0 +1,90 @@
+object MainForm: TMainForm
+ Left = 0
+ Top = 0
+ BorderIcons = [biSystemMenu, biMinimize]
+ BorderStyle = bsSingle
+ Caption = 'SimulatorFans - Assetto Corsa'
+ ClientHeight = 309
+ ClientWidth = 645
+ 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 PortComboBox: TComboBox
+ Left = 24
+ Top = 24
+ Width = 201
+ Height = 21
+ Style = csDropDownList
+ TabOrder = 0
+ end
+ object RefreshPortsButton: TButton
+ Left = 231
+ Top = 23
+ Width = 75
+ Height = 23
+ Caption = 'Refresh'
+ TabOrder = 1
+ OnClick = RefreshPortsButtonClick
+ end
+ object Button1: TButton
+ Left = 192
+ Top = 176
+ Width = 75
+ Height = 25
+ Caption = 'Connect'
+ TabOrder = 2
+ OnClick = Button1Click
+ end
+ object Button2: TButton
+ Left = 288
+ Top = 176
+ Width = 75
+ Height = 25
+ Caption = 'Fan 1 full'
+ TabOrder = 3
+ OnClick = Button2Click
+ end
+ object Button3: TButton
+ Left = 384
+ Top = 176
+ Width = 75
+ Height = 25
+ Caption = 'Fan 2 full'
+ TabOrder = 4
+ OnClick = Button3Click
+ end
+ object Button4: TButton
+ Left = 480
+ Top = 176
+ Width = 75
+ Height = 25
+ Caption = 'Off'
+ TabOrder = 5
+ OnClick = Button4Click
+ end
+ object Button5: TButton
+ Left = 288
+ Top = 207
+ Width = 75
+ Height = 25
+ Caption = 'Fan 1 half'
+ TabOrder = 6
+ OnClick = Button5Click
+ end
+ object Button6: TButton
+ Left = 384
+ Top = 207
+ Width = 75
+ Height = 25
+ Caption = 'Fan 2 half'
+ TabOrder = 7
+ OnClick = Button6Click
+ end
+end
diff --git a/AssettoCorsa/source/MainFrm.pas b/AssettoCorsa/source/MainFrm.pas
new file mode 100644
index 0000000..0b164be
--- /dev/null
+++ b/AssettoCorsa/source/MainFrm.pas
@@ -0,0 +1,187 @@
+unit MainFrm;
+
+interface
+uses
+ System.Classes,
+ System.SysUtils,
+ Vcl.Controls,
+ Vcl.ExtCtrls,
+ Vcl.Forms,
+ Vcl.StdCtrls,
+
+ CPort;
+
+
+type
+ TMainForm = class(TForm)
+ PortComboBox: TComboBox;
+ RefreshPortsButton: TButton;
+ Button1: TButton;
+ Button2: TButton;
+ Button3: TButton;
+ Button4: TButton;
+ Button5: TButton;
+ Button6: TButton;
+
+ procedure FormCreate(Sender: TObject);
+ procedure RefreshPortsButtonClick(Sender: TObject);
+ procedure Button1Click(Sender: TObject);
+ procedure Button2Click(Sender: TObject);
+ procedure Button3Click(Sender: TObject);
+ procedure Button4Click(Sender: TObject);
+ procedure Button5Click(Sender: TObject);
+ procedure Button6Click(Sender: TObject);
+ private
+ FComPort: TComPort;
+ FReceived: string;
+ FOnResponse: TProc;
+
+ procedure OnReceiveChar(Sender: TObject; Count: Integer);
+
+ procedure RefreshPorts;
+ procedure SendCommand(const ACommand: string; AOnResponse: TProc);
+
+ property ComPort: TComPort read FComPort;
+ end;
+
+
+implementation
+
+uses
+ System.RegularExpressions, Vcl.Dialogs;
+
+{$R *.dfm}
+
+procedure TMainForm.Button1Click(Sender: TObject);
+begin
+ SendCommand('>Info',
+ procedure(Response: string)
+ var
+ match: TMatch;
+
+ begin
+ match := TRegEx.Match(Response, 'SetFans:255,0',
+ procedure(Response: string)
+ begin
+ if Response <> 'SetFans:0,255',
+ procedure(Response: string)
+ begin
+ if Response <> 'SetFans:0,0',
+ procedure(Response: string)
+ begin
+ if Response <> 'SetFans:128,0',
+ procedure(Response: string)
+ begin
+ if Response <> 'SetFans:0,128',
+ procedure(Response: string)
+ begin
+ if Response <> ' -1 then
+ currentPort := PortComboBox.Items[PortComboBox.ItemIndex];
+
+ EnumComPorts(PortComboBox.Items);
+
+ if Length(currentPort) > 0 then
+ PortComboBox.ItemIndex := PortComboBox.Items.IndexOf(currentPort);
+end;
+
+
+procedure TMainForm.OnReceiveChar(Sender: TObject; Count: Integer);
+var
+ data: string;
+ terminatorPos: Integer;
+
+begin
+ (Sender as TComPort).ReadStr(data, Count);
+ FReceived := FReceived + data;
+
+ terminatorPos := AnsiPos(#10, FReceived);
+ if terminatorPos > 0 then
+ begin
+ // Since the protocol is quite synchronous, this is good enough for now
+ SetLength(FReceived, terminatorPos - 1);
+ if Assigned(FOnResponse) then
+ FOnResponse(FReceived);
+
+ FReceived := '';
+ FOnResponse := nil;
+ end;
+end;
+
+
+procedure TMainForm.SendCommand(const ACommand: string; AOnResponse: TProc);
+begin
+ if not Assigned(FComPort) then
+ begin
+ FComPort := TComPort.Create(Self);
+ FComPort.Port := PortComboBox.Items[PortComboBox.ItemIndex];
+ FComPort.BaudRate := br19200;
+ FComPort.OnRxChar := OnReceiveChar;
+ FComPort.Open;
+ end;
+
+ FOnResponse := AOnResponse;
+ ComPort.WriteStr(ACommand + #10);
+end;
+
+end.