tratar erro de comunicação com componente ComPort

18/07/2013

0

Olá pessoal,
tenho uma aplicação que se comunica com uma placa via serial rs232
estou usando o componente ComPort do pacote CPortLib para fazer essa comunicação, ate ai tudo bem

o problema é que preciso tratar o erro de comunicação, por exemplo, se eu desconectar o cabo sem desconectar da porta COM primeiro ou ocorrer qualquer outro erro de comunicação

atualmente se acontecer um erro desse, o programa trava, preciso que gere um erro e tente desconectar para não travar a aplicação.

Alguém pode me ajudar?

desde já agradeço.
Valdenir Matos

Valdenir Matos

Responder

Posts

18/07/2013

Marco Salles

vc tem que utilizar o Try except

tente inicialmente fazer assim

try
codigo
except
on e:Excpetion do
showmessage('Classe da exceção :'+e.classname+slinBreak+
            'mensagem original : '+e.message);
end;
end;
Responder

18/07/2013

Valdenir Matos

estou fazendo assim mesmo
  try
    if ComPort1.Connected then
      ComPort1.Close
    else
      ComPort1.Open;
  except on E: Exception do
    EnviaErro(dh+': Não foi possivel conectar a Placa MC PIC. '+E.Message);
  end;


o problema é que se ocorre erro na comunicação, o programa não detecta, ele entende que ainda está conectado e trava.

atualmente estou recebendo um caractere pela serial e na minha aplicação, fiz uma rotina no timer pra monitorar a frequencia de recebimento, se receber frequentemente a comunicação está OK, mas se puxo o cabo serial sem desconectar (ComPort1.Close) dar um erro e trava tudo.


Responder

18/07/2013

Marco Salles

Bem , mas ai não seria problema do componente ????

Vc tem o .pas dele ???

vc tem como comunicar isto ao criador do mesmo ou ao suporte ???
Responder

18/07/2013

Valdenir Matos

Talvez o componente até tenha essa função e não estou sabendo usar.

Vc tem o .pas dele ???


sim, segue

(******************************************************
* 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 := '<Ok>'
else
result := '<ComError:'+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.


Responder

11/04/2019

Sebastião Neto

Valdenir, conseguiu resolver esse problema?
Responder

Que tal ter acesso a um e-book gratuito que vai te ajudar muito nesse momento decisivo?

Ver ebook

Recomendado pra quem ainda não iniciou o estudos.

Eu quero
Ver ebook

Recomendado para quem está passando por dificuldades nessa etapa inicial

Eu quero

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar