// -------------------------------------------------------
// | TComportDriver - A Basic Driver for the serial port |
// -------------------------------------------------------
// | © 1997 by Marco Cocco |
// | © 1998 enhanced by Angerer Bernhard |
// | © 2001 enhanced by Christophe Geers |
// -------------------------------------------------------
//I removed the TTimer and inserted a thread (TTimerThread) to simulate
//the function formerly done by the TTimer.TimerEvent.
//Further more the Readstring procedure has been adjusted. As soon as
//some input on the input buffer from the serial port has been detected
//the TTimerThread is supsended until all the data from the input buffer is read
//using the ReadString procedure......well go ahead and check it out for
//yourself.
//Tested with Delphi 6 Profesionnal / Enterprise on Windows 2000.
{$A+,B-,C+,D-,E-,F-,G+,H+,I+,J+,K-, L-,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y-,Z1}
{$MINSTACKSIZE $00004000}
{$MAXSTACKSIZE $00100000}
{$IMAGEBASE $51000000}
{$APPTYPE GUI}
unit ComportDriverThread;
interface
uses
//Include "ExtCtrl" for the TTimer component.
Windows, Messages, SysUtils, Classes, Forms, ExtCtrls;
type
TComPortNumber = (pnCOM1,pnCOM2,pnCOM3,pnCOM4);
TComPortBaudRate = (br110,br300,br600,br1200,br2400,br4800,br9600,
br14400,br19200,br38400,br56000,br57600,br115200);
TComPortDataBits = (db5BITS,db6BITS,db7BITS,db8BITS);
TComPortStopBits = (sb1BITS,sb1HALFBITS,sb2BITS);
TComPortParity = (ptNONE,ptODD,ptEVEN,ptMARK,ptSPACE);
TComportHwHandshaking = (hhNONE,hhRTSCTS);
TComPortSwHandshaking = (shNONE,shXONXOFF);
TTimerThread = class(TThread)
private
{ Private declarations }
FOnTimer : TThreadMethod;
FEnabled: Boolean;
protected
{ Protected declarations }
procedure Execute; override;
procedure SupRes;
public
{ Public declarations }
published
{ Published declarations }
property Enabled: Boolean read FEnabled write FEnabled;
end;
TComportDriverThread = class(TComponent)
private
{ Private declarations }
FTimer : TTimerThread;
FOnReceiveData : TNotifyEvent;
FReceiving : Boolean;
protected
{ Protected declarations }
FComPortActive : Boolean;
FComportHandle : THandle;
FComportNumber : TComPortNumber;
FComportBaudRate : TComPortBaudRate;
FComportDataBits : TComPortDataBits;
FComportStopBits : TComPortStopBits;
FComportParity : TComPortParity;
FComportHwHandshaking : TComportHwHandshaking;
FComportSwHandshaking : TComPortSwHandshaking;
FComportInputBufferSize : Word;
FComportOutputBufferSize : Word;
FComportPollingDelay : Word;
FTimeOut : Integer;
FTempInputBuffer : Pointer;
procedure SetComPortActive(Value: Boolean);
procedure SetComPortNumber(Value: TComPortNumber);
procedure SetComPortBaudRate(Value: TComPortBaudRate);
procedure SetComPortDataBits(Value: TComPortDataBits);
procedure SetComPortStopBits(Value: TComPortStopBits);
procedure SetComPortParity(Value: TComPortParity);
procedure SetComPortHwHandshaking(Value: TComportHwHandshaking);
procedure SetComPortSwHandshaking(Value: TComPortSwHandshaking);
procedure SetComPortInputBufferSize(Value: Word);
procedure SetComPortOutputBufferSize(Value: Word);
procedure SetComPortPollingDelay(Value: Word);
procedure ApplyComPortSettings;
procedure TimerEvent; virtual;
procedure doDataReceived; virtual;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function Connect: Boolean;
function Disconnect: Boolean;
function Connected: Boolean;
function Disconnected: Boolean;
function SendData(DataPtr: Pointer; DataSize: Integer): Boolean;
function SendString(Input: string): Boolean;
function ReadString(var Str: string): Integer;
published
{ Published declarations }
property Active: Boolean read FComPortActive write SetComPortActive default False;
property ComPort: TComPortNumber read FComportNumber write SetComportNumber
default pnCOM1;
property ComPortSpeed: TComPortBaudRate read FComportBaudRate write
SetComportBaudRate default br9600;
property ComPortDataBits: TComPortDataBits read FComportDataBits write
SetComportDataBits default db8BITS;
property ComPortStopBits: TComPortStopBits read FComportStopBits write
SetComportStopBits default sb1BITS;
property ComPortParity: TComPortParity read FComportParity write
SetComportParity default ptNONE;
property ComPortHwHandshaking: TComportHwHandshaking read FComportHwHandshaking
write SetComportHwHandshaking default
hhNONE;
property ComPortSwHandshaking: TComPortSwHandshaking read FComportSwHandshaking
write SetComportSwHandshaking default
shNONE;
property ComPortInputBufferSize: Word read FComportInputBufferSize
write SetComportInputBufferSize default
2048;
property ComPortOutputBufferSize: Word read FComportOutputBufferSize
write SetComportOutputBufferSize default
2048;
property ComPortPollingDelay: Word read FComportPollingDelay write
SetComportPollingDelay default 100;
property OnReceiveData: TNotifyEvent read FOnReceiveData
write FOnReceiveData;
property TimeOut: Integer read FTimeOut write FTimeOut default 30;
end;
procedure register;
implementation
procedure register;
begin
RegisterComponents('Self-made Components', [TComportDriverThread]);
end;
{ TComportDriver }
constructor TComportDriverThread.Create(AOwner: TComponent);
begin
inherited;
FReceiving := False;
FComportHandle := 0;
FComportNumber := pnCOM1;
FComportBaudRate := br9600;
FComportDataBits := db8BITS;
FComportStopBits := sb1BITS;
FComportParity := ptNONE;
FComportHwHandshaking := hhNONE;
FComportSwHandshaking := shNONE;
FComportInputBufferSize := 2048;
FComportOutputBufferSize := 2048;
FOnReceiveData := nil;
FTimeOut := 30;
FComportPollingDelay := 500;
GetMem(FTempInputBuffer,FComportInputBufferSize);
if csDesigning in ComponentState then
Exit;
FTimer := TTimerThread.Create(False);
FTimer.FOnTimer := TimerEvent;
if FComPortActive then
FTimer.Enabled := True;
FTimer.SupRes;
end;
destructor TComportDriverThread.Destroy;
begin
Disconnect;
FreeMem(FTempInputBuffer,FComportInputBufferSize);
inherited Destroy;
end;
function TComportDriverThread.Connect: Boolean;
var
comName: array[0..4] of Char;
tms: TCommTimeouts;
begin
if Connected then
Exit;
StrPCopy(comName,'COM');
comName[3] := chr(ord('1') + ord(FComportNumber));
comName[4] := #0;
FComportHandle := CreateFile(comName,GENERIC_READ or GENERIC_WRITE,0,nil,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if not Connected then
Exit;
ApplyComPortSettings;
tms.ReadIntervalTimeout := 1;
tms.ReadTotalTimeoutMultiplier := 0;
tms.ReadTotalTimeoutConstant := 1;
tms.WriteTotalTimeoutMultiplier := 0;
tms.WriteTotalTimeoutConstant := 0;
SetCommTimeouts(FComportHandle,tms);
Sleep(1000);
end;
function TComportDriverThread.Connected: Boolean;
begin
Result := FComportHandle > 0;
end;
function TComportDriverThread.Disconnect: Boolean;
begin
Result := False;
if Connected then
begin
CloseHandle(FComportHandle);
FComportHandle := 0;
end;
Result := True;
end;
function TComportDriverThread.Disconnected: Boolean;
begin
if (FComportHandle <> 0) then
Result := False
else
Result := True;
end;
const
Win32BaudRates: array[br110..br115200] of DWORD =
(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);
const
dcb_Binary = $00000001;
dcb_ParityCheck = $00000002;
dcb_OutxCtsFlow = $00000004;
dcb_OutxDsrFlow = $00000008;
dcb_DtrControlMask = $00000030;
dcb_DtrControlDisable = $00000000;
dcb_DtrControlEnable = $00000010;
dcb_DtrControlHandshake = $00000020;
dcb_DsrSensitvity = $00000040;
dcb_TXContinueOnXoff = $00000080;
dcb_OutX = $00000100;
dcb_InX = $00000200;
dcb_ErrorChar = $00000400;
dcb_NullStrip = $00000800;
dcb_RtsControlMask = $00003000;
dcb_RtsControlDisable = $00000000;
dcb_RtsControlEnable = $00001000;
dcb_RtsControlHandshake = $00002000;
dcb_RtsControlToggle = $00003000;
dcb_AbortOnError = $00004000;
dcb_Reserveds = $FFFF8000;
procedure TComportDriverThread.ApplyComPortSettings;
var
//Device Control Block (= dcb)
dcb: TDCB;
begin
if not Connected then
Exit;
FillChar(dcb,sizeOf(dcb),0);
dcb.DCBlength := sizeOf(dcb);
dcb.Flags := dcb_Binary or dcb_RtsControlEnable;
dcb.BaudRate := Win32BaudRates[FComPortBaudRate];
case FComportHwHandshaking of
hhNONE : ;
hhRTSCTS:
dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake;
end;
case FComportSwHandshaking of
shNONE : ;
shXONXOFF:
dcb.Flags := dcb.Flags or dcb_OutX or dcb_Inx;
end;
dcb.XonLim := FComportInputBufferSize div 4;
dcb.XoffLim := 1;
dcb.ByteSize := 5 + ord(FComportDataBits);
dcb.Parity := ord(FComportParity);
dcb.StopBits := ord(FComportStopBits);
dcb.XonChar := #17;
dcb.XoffChar := #19;
SetCommState(FComportHandle,dcb);
SetupComm(FComportHandle, FComPortInputBufferSize,FComPortOutputBufferSize);
end;
function TComportDriverThread.ReadString(var Str: string): Integer;
var
BytesTrans, nRead: DWORD;
Buffer : string;
i : Integer;
temp : string;
begin
BytesTrans := 0;
Str := '';
SetLength(Buffer,1);
ReadFile(FComportHandle,PChar(Buffer)^, 1, nRead, nil);
while nRead > 0 do
begin
temp := temp + PChar(Buffer);
ReadFile(FComportHandle,PChar(Buffer)^, 1, nRead, nil);
end;
//Remove the end token.
BytesTrans := Length(temp);
SetLength(str,BytesTrans-2);
for i:=0 to BytesTrans-2 do
begin
str[i] := temp[i];
end;
Result := BytesTrans;
end;
function TComportDriverThread.SendData(DataPtr: Pointer;
DataSize: Integer): Boolean;
var
nsent : DWORD;
begin
Result := WriteFile(FComportHandle,DataPtr^,DataSize,nsent,nil);
Result := Result and (nsent = DataSize);
end;
function TComportDriverThread.SendString(Input: string): Boolean;
begin
if not Connected then
if not Connect then
raise Exception.CreateHelp('Could not connect to COM-port !',101);
Result := SendData(PChar(Input),Length(Input));
if not Result then
raise Exception.CreateHelp('Could not send to COM-port !',102);
end;
procedure TComportDriverThread.TimerEvent;
var
InQueue, OutQueue: Integer;
Buffer : string;
nRead : DWORD;
procedure DataInBuffer(Handle: THandle; var aInQueue, aOutQueue: Integer);
var
ComStat : TComStat;
e : Cardinal;
begin
aInQueue := 0;
aOutQueue := 0;
if ClearCommError(Handle,e,@ComStat) then
begin
aInQueue := ComStat.cbInQue;
aOutQueue := ComStat.cbOutQue;
end;
end;
begin
if csDesigning in ComponentState then
Exit;
if not Connected then
if not Connect then
raise Exception.CreateHelp('TimerEvent: Could not connect to COM-port !',101);
Application.ProcessMessages;
if Connected then
begin
DataInBuffer(FComportHandle,InQueue,OutQueue);
if InQueue > 0 then
begin
if (Assigned(FOnReceiveData) ) then
begin
FReceiving := True;
FOnReceiveData(Self);
end;
end;
end;
end;
procedure TComportDriverThread.SetComportBaudRate(Value: TComPortBaudRate);
begin
FComportBaudRate := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportDataBits(Value: TComPortDataBits);
begin
FComportDataBits := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportHwHandshaking(Value: TComportHwHandshaking);
begin
FComportHwHandshaking := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportInputBufferSize(Value: Word);
begin
FreeMem(FTempInputBuffer,FComportInputBufferSize);
FComportInputBufferSize := Value;
GetMem(FTempInputBuffer,FComportInputBufferSize);
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportNumber(Value: TComPortNumber);
begin
if Connected then
exit;
FComportNumber := Value;
end;
procedure TComportDriverThread.SetComportOutputBufferSize(Value: Word);
begin
FComportOutputBufferSize := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportParity(Value: TComPortParity);
begin
FComportParity := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportPollingDelay(Value: Word);
begin
FComportPollingDelay := Value;
end;
procedure TComportDriverThread.SetComportStopBits(Value: TComPortStopBits);
begin
FComportStopBits := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportSwHandshaking(Value: TComPortSwHandshaking);
begin
FComportSwHandshaking := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.DoDataReceived;
begin
if Assigned(FOnReceiveData) then
FOnReceiveData(Self);
end;
procedure TComportDriverThread.SetComPortActive(Value: Boolean);
var
DumpString : string;
begin
FComPortActive := Value;
if csDesigning in ComponentState then
Exit;
if FComPortActive then
begin
//Just dump the contents of the input buffer of the com-port.
ReadString(DumpString);
FTimer.Enabled := True;
end
else
FTimer.Enabled := False;
FTimer.SupRes;
end;
{ TTimerThread }
procedure TTimerThread.Execute;
begin
Priority := tpNormal;
repeat
Sleep(500);
if Assigned(FOnTimer) then
Synchronize(FOnTimer);
until
Terminated;
end;
procedure TTimerThread.SupRes;
begin
if not Suspended then
Suspend;
if FEnabled then
Resume;
end;
end.
|