适用于BCB6.0, CB2006/TC2006
用法:
TMyComm<ThreadRd,ThreadWrt> Comm; //异步模式Comm类, 含有读线程和写线程,有OnWritten,OnRead,OnSignal,OnError事件
TMyComm<BlockRd,BlockWrt> Comm; //同步模式Comm类(阻塞),无读线程和写线程,使用Write和Read方法类似于文件读写的方式
TMyComm<ThreadRd,BlockWrt> Comm; //有读线程无写线程, Write类似于普通文件写(写完才返回), 能过OnRead事件读到数据
TMyComm<BlockRd,ThreadWrt> Comm; //和上面相反, 有写线程无读线程, 不建议使用此模式, 除非只是用写功能或者你知道你在干什么.
另外使用BlockRd模式时建议使用SetTimeouts方法设置超时值,不然死在Read上可别怪我没说:)
//---------------------------------------------------------------------------
#ifndef TMyCommH
#define TMyCommH
#include <Classes.hpp>
#ifndef COMMREADBUFFERSIZE
#define COMMREADBUFFERSIZE 2048
#endif
#pragma option push -w-inl
enum TFlowControl {fcNull=0,fcHardware=1,fcXonXoff=2};
//---------------------------------------------------------------------------
struct TMyDCB :DCB{
TMyDCB(){
ZeroMemory(this,sizeof(DCB));
DCBlength=sizeof(TMyDCB);
}
TMyDCB(const DCB &dcb){
CopyMemory(this,&dcb,sizeof(DCB));
}
TMyDCB& operator=(const DCB &dcb){
CopyMemory(this,&dcb,sizeof(DCB));
return *this;
}
bool Build(const char *strDCB){ //eg."baud=1200 parity=N data=8 stop=1","9600,n,8,1"
return BuildCommDCB(strDCB,this);
}
void SetFlowControl(TFlowControl FlowControl){
fOutxCtsFlow=(FlowControl==fcHardware);
fInX=fOutX=(FlowControl==fcXonXoff);
fRtsControl=(FlowControl==fcHardware)?RTS_CONTROL_HANDSHAKE:RTS_CONTROL_ENABLE;
}
};
template<class p_rd,class p_wt> class TMyComm:
public p_rd,public p_wt
{
TMyComm(const TMyComm&);
TMyComm & operator=(const TMyComm&);
protected:
HANDLE hComm;
AnsiString FPort;
TMyDCB dcb;
void CheckOpened(){if(!Opened()) throw(Exception("Port do not opened!"));}
public:
TMyComm():hComm(INVALID_HANDLE_VALUE),p_rd(hComm),p_wt(hComm){;}
~TMyComm(){Close();}
// Open & Close
void Open(const AnsiString &Port);
void Close();
bool Opened(){return hComm!=INVALID_HANDLE_VALUE;}
AnsiString GetPort(){return FPort;}
// Read & Write
bool TransmitCommChar(char c){
CheckOpened();
return ::TransmitCommChar(hComm,c);
}
//dwFunc: CLRDTR,CLRRTS,SETDTR,SETRTS,SETXOFF,SETXON,SETBREAK,CLRBREAK,RESETDEV
bool EscapeCommFunction(DWORD dwFunc){
CheckOpened();
return ::EscapeCommFunction(hComm,dwFunc);
}
void Flush(){CheckOpened();FlushFileBuffers(hComm);}
void ClearRdBuf(){CheckOpened();PurgeComm(hComm,PURGE_RXABORT|PURGE_RXCLEAR);}
void ClearWrtBuf(){CheckOpened();PurgeComm(hComm,PURGE_TXABORT|PURGE_TXCLEAR);}
// Info
HANDLE GetHandle(){return hComm;}
DWORD GetCommModemStatus(){ //MS_CTS_ON,MS_DSR_ON,MS_RING_ON,MS_RLSD_ON
CheckOpened();
DWORD Result=0;
Win32Check(::GetCommModemStatus(hComm,&Result));
return Result;
}
// Setting DCB,Must be Opened;
void SetDCB(const DCB&);
TMyDCB GetDCB(){CheckOpened();return dcb;}
void SetBaudRate(DWORD);
void SetParity(BYTE);
void SetByteSize(BYTE);
void SetStopBits(BYTE);
DWORD GetBaudRate(){return GetDCB().BaudRate;} /* Baudrate at which running */
BYTE GetParity(){return GetDCB().Parity;} /* 0-4=None,Odd,Even,Mark,Space */
BYTE GetByteSize(){return GetDCB().ByteSize;} /* Number of bits/byte, 4-8 */
BYTE GetStopBits(){return GetDCB().StopBits;} /* 0,1,2 = 1, 1.5, 2 */
/*//winbase.h defined/
#define NOPARITY 0
#define ODDPARITY 1
#define EVENPARITY 2
#define MARKPARITY 3
#define SPACEPARITY 4
#define ONESTOPBIT 0
#define ONE5STOPBITS 1
#define TWOSTOPBITS 2
/*///
// Setting TimeOut
void SetTimeouts(const COMMTIMEOUTS&);
void SetTimeouts(DWORD RdItv,DWORD RdMult=0,DWORD RdConst=0,
DWORD WrtMult=0,DWORD WrtConst=0);
COMMTIMEOUTS GetTimeouts();
// Setting Buffer
void SetBuffers(DWORD In,DWORD Out)
{
CheckOpened();
Win32Check(SetupComm(hComm,In,Out));
}
};
// BlockWrt-------------------------------------------
struct BlockWrt{
BlockWrt(HANDLE &_hComm):hComm(_hComm),Opened(false){;}
DWORD Write(void *Buf,DWORD Len)
{
if(!Opened)return 0;
DWORD Result=Len;
if(Result && !WriteFile(hComm, Buf, Result,&Result,&osWrite))
{
Result=0;
if (GetLastError() == ERROR_IO_PENDING)
GetOverlappedResult(hComm,&osWrite,&Result,true);
}
return Result;
}
void Open()
{
if(Opened)return;
ZeroMemory(&osWrite,sizeof(osWrite));
osWrite.hEvent=CreateEvent(NULL, true, false, NULL);
Opened=true;
}
void Close()
{
if(!Opened)return;
PurgeComm(hComm,PURGE_TXABORT|PURGE_TXCLEAR);
CloseHandle(osWrite.hEvent);
Opened=false;
}
private:
HANDLE &hComm;
bool Opened;
OVERLAPPED osWrite;
};
// BlockRd-------------------------------------------
struct BlockRd{
BlockRd(HANDLE &_hComm):hComm(_hComm),Opened(false){;}
DWORD Read(void *Buf,DWORD Len)
{
if(!Opened)return 0;
DWORD Result=Len;
if (Result && !ReadFile(hComm, Buf, Result, &Result, &osRead))
{
Result=0;
if (GetLastError() == ERROR_IO_PENDING)
GetOverlappedResult(hComm,&osRead,&Result,true);
}
return Result;
}
void Open()
{
if(Opened)return;
ZeroMemory(&osRead,sizeof(osRead));
osRead.hEvent=CreateEvent(NULL, true, false, NULL);
Opened=true;
}
void Close()
{
if(!Opened)return;
PurgeComm(hComm,PURGE_RXABORT|PURGE_RXCLEAR);
CloseHandle(osRead.hEvent);
Opened=false;
}
private:
HANDLE &hComm;
bool Opened;
OVERLAPPED osRead;
};
// ThreadWrt-------------------------------------------
typedef void (__closure *TWrtEvent)(HANDLE,void*,DWORD);
class TCommWriter : public TThread
{
private:
struct TWrtItem{
DWORD Len;
char Buf[0];
};
HANDLE &hComm;
OVERLAPPED osWrite;
TThreadList *_WriteQueue;
HANDLE evWrite;
TWrtEvent OnWritten;
protected:
void __fastcall Execute()
{
while(!Terminated)
{
WaitForSingleObject(evWrite,INFINITE);
TWrtItem *Item=NULL;
TList *pList = _WriteQueue->LockList();
try
{
if(pList->Count>0)
{
Item=(TWrtItem*)pList->First();
pList->Delete(0);
}
else
{
ResetEvent(evWrite);
}
}
__finally
{
_WriteQueue->UnlockList();
}
if(Item)
{
DWORD Result=0;
try{
if(Item->Len&&!WriteFile(hComm, Item->Buf ,Item->Len,&Result,&osWrite))
{
if (GetLastError() == ERROR_IO_PENDING)
GetOverlappedResult(hComm,&osWrite,&Result,true);
}
if(Result&&OnWritten) OnWritten(hComm,Item->Buf,Item->Len);
}
__finally
{
free(Item);
}
}
}
}
public:
__fastcall TCommWriter(HANDLE &handle,TWrtEvent WrtEvent)
:TThread(true),hComm(handle),OnWritten(WrtEvent)
{
_WriteQueue=new TThreadList;
evWrite=CreateEvent(NULL,true,false,NULL);
ZeroMemory(&osWrite,sizeof(osWrite));
osWrite.hEvent=CreateEvent(NULL,true,false,NULL);
}
__fastcall ~TCommWriter()
{
Clear();
CloseHandle(evWrite);
CloseHandle(osWrite.hEvent);
delete _WriteQueue;
}
void __fastcall Terminate(void)
{
TThread::Terminate();
SetEvent(osWrite.hEvent);
SetEvent(evWrite);
PurgeComm(hComm,PURGE_TXABORT|PURGE_TXCLEAR);
}
void Write(void *Buf,DWORD Len)
{
TWrtItem *Data=(TWrtItem *)malloc(sizeof(TWrtItem)+Len);
Data->Len=Len;
CopyMemory(Data->Buf,Buf,Len);
_WriteQueue->LockList();
_WriteQueue->Add(Data);
SetEvent(evWrite);
_WriteQueue->UnlockList();
}
void Clear()
{
TList *pList = _WriteQueue->LockList();
try
{
for (int i = 0; i < pList->Count; i++)
{
free(pList->Items[i]);
}
pList->Clear();
}
__finally
{
_WriteQueue->UnlockList();
}
}
};
struct ThreadWrt{
ThreadWrt(HANDLE &_hComm):hComm(_hComm),Wrt(NULL),OnWritten(NULL){;}
~ThreadWrt(){Close();}
TWrtEvent OnWritten;
DWORD Write(void *Buf,DWORD Len)
{
if(Wrt==NULL||Len==0)return 0;
Wrt->Write(Buf,Len);
return Len;
}
void Open()
{
if(Wrt)return;
Wrt=new TCommWriter(hComm,OnWritten);
Wrt->Resume();
}
void Close()
{
if(Wrt==NULL)return;
Wrt->Terminate();
Wrt->WaitFor();
delete Wrt;
Wrt=NULL;
}
private:
TCommWriter *Wrt;
HANDLE &hComm;
};
// ThreadRd-------------------------------------------
typedef void (__closure *TRdEvent)(HANDLE,void*,DWORD);
typedef void (__closure *TRdError)(HANDLE,DWORD ErrorCode);
typedef void (__closure *TRdSignal)(HANDLE,DWORD EvtMask);
class TCommReader : public TThread
{
private:
HANDLE &hComm;
TRdEvent OnRead;
OVERLAPPED osRead;
OVERLAPPED osStatus;
HANDLE hExit;
protected:
void __fastcall Execute()
{
char Buffer[COMMREADBUFFERSIZE];
bool WaitingRead=false;
bool WaitingStat=false;
DWORD Len=0;
DWORD Evt;
HANDLE hArray[3]={osRead.hEvent,osStatus.hEvent,hExit};
SetCommMask(hComm,EV_CTS|EV_DSR|EV_ERR|EV_RING|EV_RLSD);
while(!Terminated)
{
if (!WaitingRead)
{
if (!ReadFile(hComm, Buffer, COMMREADBUFFERSIZE, &Len, &osRead))
{
if (GetLastError() == ERROR_IO_PENDING) WaitingRead = true;
}
else
{ // read completed immediately
if (Len>0)
{
if(OnRead) OnRead(hComm,Buffer,Len);
}
else
Sleep(1);
}
}
if (!WaitingStat)
{
if(!WaitCommEvent(hComm, &Evt, &osStatus))
{
if (GetLastError() == ERROR_IO_PENDING) WaitingStat = true;
}
else
{
if(OnSignal) OnSignal(hComm,Evt);
}
}
if(WaitingRead&&WaitingStat)
{
DWORD dwRes = WaitForMultipleObjects(3,hArray,false,INFINITE);
switch(dwRes)
{
case WAIT_OBJECT_0: //Read
if(GetOverlappedResult(hComm, &osRead, &Len, false))
{
if(Len&&OnRead) OnRead(hComm,Buffer,Len);
}
WaitingRead = false;
break;
case WAIT_OBJECT_0+1: //State
if(GetOverlappedResult(hComm, &osStatus, &Len, false))
{
if(Evt&EV_ERR)
{
DWORD ErrorCode=0;
if(ClearCommError(hComm, &ErrorCode,NULL)&&OnError)
OnError(hComm,ErrorCode);
}
else
if(OnSignal) OnSignal(hComm,Evt);
}
WaitingStat=false;
break;
case WAIT_OBJECT_0+2: //Exit
goto end;
}
}
}
end:
}
public:
TRdError OnError;
TRdSignal OnSignal;
__fastcall TCommReader(HANDLE &handle,TRdEvent RdEvent)
:TThread(true),hComm(handle),OnRead(RdEvent),OnSignal(NULL),OnError(NULL)
{
ZeroMemory(&osRead,sizeof(osRead));
osRead.hEvent=CreateEvent(NULL,true,false,NULL);
osStatus.hEvent=CreateEvent(NULL,true,false,NULL);
hExit=CreateEvent(NULL,false,false,NULL);
}
__fastcall ~TCommReader()
{
CloseHandle(osRead.hEvent);
CloseHandle(osStatus.hEvent);
CloseHandle(hExit);
}
void __fastcall Terminate(void)
{
TThread::Terminate();
PurgeComm(hComm,PURGE_RXABORT|PURGE_RXCLEAR);
SetEvent(hExit);
}
};
struct ThreadRd{
ThreadRd(HANDLE &_hComm)
:hComm(_hComm),Rd(NULL),OnRead(NULL),OnSignal(NULL),OnError(NULL)
{;}
~ThreadRd(){Close();}
TRdEvent OnRead;
TRdError OnError;
TRdSignal OnSignal;
DWORD Read(void *Buf,DWORD Len)
{
return 0;
}
void Open()
{
if(Rd)return;
Rd=new TCommReader(hComm,OnRead);
Rd->OnSignal=OnSignal;
Rd->OnError=OnError;
Rd->Resume();
}
void Close()
{
if(Rd==NULL)return;
Rd->Terminate();
Rd->WaitFor();
delete Rd;
Rd=NULL;
}
private:
TCommReader *Rd;
HANDLE &hComm;
};
#define Micro_DefDCB(v) /
if(dcb.##v##==##v##)return; /
TMyDCB tmpDcb=dcb; /
tmpDcb.##v##=##v##; /
SetDCB(tmpDcb);
//---------------------------------------------------------------------------
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::Open(const AnsiString &Port)
{
if(Opened())
{
if(SameText(Port,FPort))
{
return;
}
else
{
Close();
}
}
FPort=Port;
hComm = CreateFile( FPort.c_str(),GENERIC_READ | GENERIC_WRITE,
0,0,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
if (!Opened()) RaiseLastOSError();
PurgeComm(hComm,PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
ZeroMemory(&dcb,sizeof(dcb));
if (!GetCommState(hComm, &dcb))
{
Close();
RaiseLastOSError();
}
p_rd::Open();
p_wt::Open();
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::Close()
{
if(Opened())
{
p_rd::Close();
p_wt::Close();
CloseHandle(hComm);
hComm=INVALID_HANDLE_VALUE;
}
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetDCB(const DCB &_dcb)
{
CheckOpened();
DCB tmpDcb=_dcb;
Win32Check(SetCommState(hComm, &tmpDcb));
dcb=tmpDcb;
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetBaudRate(DWORD BaudRate)
{
Micro_DefDCB(BaudRate)
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetParity(BYTE Parity)
{
if(dcb.Parity==Parity)return;
TMyDCB tmpDcb=dcb;
tmpDcb.Parity=Parity;
tmpDcb.fParity=Parity?1:0;
SetDCB(tmpDcb);
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetByteSize(BYTE ByteSize)
{
Micro_DefDCB(ByteSize)
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetStopBits(BYTE StopBits)
{
Micro_DefDCB(StopBits)
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetTimeouts(const COMMTIMEOUTS& TimeOuts)
{
CheckOpened();
COMMTIMEOUTS _TimeOuts=TimeOuts;
Win32Check(SetCommTimeouts(hComm,&_TimeOuts));
}
template<class p_rd,class p_wt>
void TMyComm<p_rd,p_wt>::SetTimeouts(
DWORD RdItv,DWORD RdMult,DWORD RdConst,DWORD WrtMult,DWORD WrtConst)
{
COMMTIMEOUTS _TimeOuts={RdItv,RdMult,RdConst,WrtMult,WrtConst};
SetTimeouts(_TimeOuts);
}
template<class p_rd,class p_wt>
COMMTIMEOUTS TMyComm<p_rd,p_wt>::GetTimeouts()
{
CheckOpened();
COMMTIMEOUTS _TimeOuts={0,0,0,0,0};
Win32Check(GetCommTimeouts(hComm,&_TimeOuts));
return _TimeOuts;
}
#pragma option pop
#endif