📄 comm.cpp
字号:
///////////////////CommX.cpp///////////////////////////////////////////////////
//Author:agzxp@163.com
//Date:2008.3.18
//
//---------------------------------------------------------------------------
#include <basepch.h>
#pragma hdrstop
#include "comm.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//
static inline void ValidCtrCheck(TCommX *)
{
new TCommX(NULL);
}
//---------------------------------------------------------------------------
namespace Comm
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TCommX)};
RegisterComponents("System", classes, 0);
}
}
//---------------------------------------------------------------------------
/***************************************************************************/
// TComm PUBLIC METHODS
/***************************************************************************/
__fastcall TCommX::TCommX(TComponent* Owner)
: TComponent(Owner)
{
ReadThread = NULL;
WriteThread = NULL;
hCommFile = NULL;
hCloseEvent = NULL;
FSendDataEmpty = true;
FCommName = "COM1";
FBaudRate = 9600;
FParityCheck = false;
FOutx_CtsFlow = false;
FOutx_DsrFlow = false;
FDtrControl = DtrEnable;
FDsrSensitivity = false;
FTxContinueOnXoff = true;
FOutx_XonXoffFlow = true;
FInx_XonXoffFlow = true;
FReplaceWhenParityError = false;
FIgnoreNullChar = false;
FRtsControl = RtsEnable;
FXonLimit = 500;
FXoffLimit = 500;
FByteSize = _8;
FParity = None;
FStopBits = _1;
FXonChar = 0x11; // Ctrl-Q
FXoffChar = 0x13; // Ctrl-S
FReplacedChar = 0x00;
FReadIntervalTimeout = 100;
FReadTotalTimeoutMultiplier = 0;
FReadTotalTimeoutConstant = 0;
FWriteTotalTimeoutMultiplier = 0;
FWriteTotalTimeoutConstant = 0;
if(!ComponentState.Contains(csDesigning))
FHWnd = AllocateHWnd(CommWndProc);
}
//---------------------------------------------------------------------------
__fastcall TCommX::~TCommX(void)
{
if(!ComponentState.Contains(csDesigning))
DeallocateHWnd(FHWnd);
}
//---------------------------------------------------------------------------
//
// FUNCTION: StartComm
//
// PURPOSE: Starts communications over the comm port.
//
// PARAMETERS:
// hNewCommFile - This is the COMM File handle to communicate with.
// This handle is obtained from TAPI.
//
// Output:
// Successful: Startup the communications.
// Failure: Raise a exception
//
// COMMENTS:
//
// StartComm makes sure there isn't communication in progress already,
// creates a Comm file, and creates the read and write threads. It
// also configures the hNewCommFile for the appropriate COMM settings.
//
// If StartComm fails for any reason, it's up to the calling application
// to close the Comm file handle.
//
//
void __fastcall TCommX::StartComm(void)
{
HANDLE hNewCommFile;
// Are we already doing comm?
if (hCommFile != NULL)
throw ECommsError( "This serial port already opened!\n\n串口已经打开!");
hNewCommFile = CreateFile( FCommName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, //not shared
NULL, //no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL); //template
if (hNewCommFile == INVALID_HANDLE_VALUE)
throw ECommsError( "Error opening serial port!\n\n打开串口出现错误!");
// Is this a valid comm handle?
if (GetFileType( hNewCommFile ) != FILE_TYPE_CHAR)
{
CloseHandle( hNewCommFile );
throw ECommsError( "File handle is not a comm handle ! 打开的串口非通信串口!" );
}
if (! SetupComm( hNewCommFile, 4096, 4096 ) )
{
CloseHandle( hCommFile );
throw ECommsError( "Cannot setup comm buffer!" );
}
// It is ok to continue.
hCommFile = hNewCommFile;
// purge any information in the buffer
PurgeComm(hCommFile,PURGE_TXABORT |PURGE_RXABORT |PURGE_TXCLEAR |PURGE_RXCLEAR);
FSendDataEmpty =true;
// Setting the time-out value
_SetCommTimeout();
// Querying then setting the comm port configurations.
_SetCommState();
// Create the event that will signal the threads to close.
hCloseEvent = CreateEvent( NULL, true, false, NULL );
if (hCloseEvent == NULL)
{
CloseHandle( hCommFile );
hCommFile = NULL;
throw ECommsError( "Unable to create event" );
}
// Create the Read thread.
try
{
ReadThread = new TReadThread( true /* suspended */ );
}
catch(...)
{
ReadThread = NULL;
CloseHandle( hCloseEvent );
CloseHandle( hCommFile );
hCommFile = NULL;
throw ECommsError( "Unable to create read thread \n\n 无法创建读监控线程!" );
}
ReadThread->hCommFile = hCommFile;
ReadThread->hCloseEvent = hCloseEvent;
ReadThread->hComm32Window = FHWnd;
// Comm threads should have a higher base priority than the UI thread.
// If they don't, then any temporary priority boost the UI thread gains
// could cause the COMM threads to loose data.
ReadThread->Priority = tpHighest;
// Create the Write thread.
try
{
WriteThread = new TWriteThread( true /* suspended */ );
}
catch(...)
{
CloseReadThread();
WriteThread = NULL;
CloseHandle( hCloseEvent );
CloseHandle( hCommFile );
hCommFile = NULL;
throw ECommsError( "Unable to create write thread\n\n 无法创建写监控线程!" );
}
WriteThread->hCommFile = hCommFile;
WriteThread->hCloseEvent = hCloseEvent;
WriteThread->hComm32Window = FHWnd;
WriteThread->pFSendDataEmpty = &FSendDataEmpty;
WriteThread->Priority = tpHigher;
ReadThread->Resume();
WriteThread->Resume();
// Everything was created ok. Ready to go!
}
//---------------------------------------------------------------------------
//
// FUNCTION: StopComm
//
// PURPOSE: Stop and end all communication threads.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
// Tries to gracefully signal all communication threads to
// close, but terminates them if it has to.
//
//
void __fastcall TCommX::StopComm(void)
{
// No need to continue if we're not communicating.
if (hCommFile == NULL) return;
// Close the threads.
CloseReadThread();
CloseWriteThread();
// Not needed anymore.
CloseHandle( hCloseEvent );
// Now close the comm port handle.
CloseHandle( hCommFile );
hCommFile = NULL;
}
//---------------------------------------------------------------------------
//
// FUNCTION: WriteCommData(PChar, Word)
//
// PURPOSE: Send a String to the Write Thread to be written to the Comm.
//
// PARAMETERS:
// pszStringToWrite - String to Write to Comm port.
// nSizeofStringToWrite - length of pszStringToWrite.
//
// RETURN VALUE:
// Returns TRUE if the PostMessage is successful.
// Returns FALSE if PostMessage fails or Write thread doesn't exist.
//
// COMMENTS:
//
// This is a wrapper function so that other modules don't care that
// Comm writing is done via PostMessage to a Write thread. Note that
// using PostMessage speeds up response to the UI (very little delay to
// 'write' a string) and provides a natural buffer if the comm is slow
// (ie: the messages just pile up in the message queue).
//
// Note that it is assumed that pszStringToWrite is allocated with
// LocalAlloc, and that if WriteCommData succeeds, its the job of the
// Write thread to LocalFree it. If WriteCommData fails, then its
// the job of the calling function to free the string.
//
//
bool __fastcall TCommX::WriteCommData(char * pDataToWrite, Word dwSizeofDataToWrite)
{
Pointer Buffer;
if ((WriteThread != NULL) && (dwSizeofDataToWrite != 0))
{
Buffer = Pointer(LocalAlloc( LPTR, dwSizeofDataToWrite+1 ));
Move( pDataToWrite, Buffer, dwSizeofDataToWrite );
if (PostThreadMessage(WriteThread->ThreadID, PWM_COMMWRITE,
WPARAM(dwSizeofDataToWrite), LPARAM(Buffer)) )
{
FSendDataEmpty = false;
return true;
}
else
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INVALID_THREAD_ID)
{
throw ECommsError("Write thread is not a valid thread,\n\ or Write thread does not have a message queue.");
}
}
}
return false;
}
//---------------------------------------------------------------------------
//
// FUNCTION: GetModemState
//
// PURPOSE: Read the state of modem input pin right now
//
// PARAMETERS:
// none
//
// RETURN VALUE:
//
// A DWORD variable containing one or more of following codes:
//
// Value Meaning
// ---------- -----------------------------------------------------------
// MS_CTS_ON The CTS (clear-to-send) signal is on.
// MS_DSR_ON The DSR (data-set-ready) signal is on.
// MS_RING_ON The ring indicator signal is on.
// MS_RLSD_ON The RLSD (receive-line-signal-detect) signal is on.
//
// If this comm have bad handle or not yet opened, the return value is 0
//
// COMMENTS:
//
// This member function calls GetCommModemStatus and return its value.
// Before calling this member function, you must have a successful
// 'StartOpen' call.
//
//
DWORD __fastcall TCommX::GetModemState(void)
{
DWORD dwModemState;
if(! GetCommModemStatus( hCommFile, &dwModemState ) )
return 0;
else return dwModemState;
}
//---------------------------------------------------------------------------
/***************************************************************************/
// TComm PROTECTED METHODS
/***************************************************************************/
//---------------------------------------------------------------------------
//
// FUNCTION: CloseReadThread
//
// PURPOSE: Close the Read Thread.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
// Closes the Read thread by signaling the CloseEvent.
// Purges any outstanding reads on the comm port.
//
// Note that terminating a thread leaks memory.
// Besides the normal leak incurred, there is an event object
// that doesn't get closed. This isn't worth worrying about
// since it shouldn't happen anyway.
//
//
void __fastcall TCommX::CloseReadThread(void)
{
// If it exists...
if (ReadThread != NULL)
{
// Signal the event to close the worker threads.
SetEvent( hCloseEvent );
// Purge all outstanding reads
PurgeComm( hCommFile, PURGE_RXABORT | PURGE_RXCLEAR );
// Wait 10 seconds for it to exit. Shouldn't happen.
if (WaitForSingleObject((UINT*)ReadThread->Handle, 10000) == WAIT_TIMEOUT)
ReadThread->Terminate();
ReadThread->Free();
ReadThread = NULL;
}
}
//---------------------------------------------------------------------------
//
// FUNCTION: CloseWriteThread
//
// PURPOSE: Closes the Write Thread.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
// Closes the write thread by signaling the CloseEvent.
// Purges any outstanding writes on the comm port.
//
// Note that terminating a thread leaks memory.
// Besides the normal leak incurred, there is an event object
// that doesn't get closed. This isn't worth worrying about
// since it shouldn't happen anyway.
//
//
void __fastcall TCommX::CloseWriteThread(void)
{
// If it exists...
if (WriteThread != NULL)
{
// Signal the event to close the worker threads.
SetEvent(hCloseEvent);
// Purge all outstanding writes.
PurgeComm(hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);
FSendDataEmpty = true;
// Wait 10 seconds for it to exit. Shouldn't happen.
if (WaitForSingleObject( (UINT*)WriteThread->Handle, 10000 ) == WAIT_TIMEOUT)
WriteThread->Terminate();
WriteThread->Free();
WriteThread = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TCommX::ReceiveData(char * Buffer, Word BufferLength)
{
if (FOnReceiveData != NULL)
FOnReceiveData( this, Buffer, BufferLength );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -