📄 comm.cpp
字号:
// From Win32 SDK's TTY Sample & TAPICOMM Sample
// Modify by JHCC, 1996-1997
#include "stdafx.h"
#include "comm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_SERIAL(CCommInfo, CObject, 0)
// constant definitions
const int cRXQueue = 4096;
const int cTXQueue = 4096;
CString CCommInfo::m_strCommNameFmt = _T("COM%d");
CCommInfo::CCommInfo()
{
m_hCommFile = 0;
m_bRTS = FALSE;
}
CCommInfo::~CCommInfo()
{
if (m_bConnected)
CloseConnection();
}
// for the Comm Device already opened, like TAPI
BOOL CCommInfo::Create(CWnd* pNotifyWnd, DWORD dwCommBaseCommandID,
BYTE* pReadBuf, int nReadBufLen, HANDLE hCommFile)
{
// Are we already doing comm?
if (m_hCommFile != NULL)
{
TRACE0("Already have a comm file open\n");
return FALSE;
}
m_bConnected = FALSE;
m_pNotifyWnd = pNotifyWnd;
m_dwCommBaseCommandID = dwCommBaseCommandID;
m_pReadBuf = pReadBuf;
m_nReadBufLen = nReadBufLen;
// Is this a valid comm handle?
if (hCommFile && ::GetFileType(hCommFile) != FILE_TYPE_CHAR)
{
TRACE0("File handle is not a comm handle.\n");
return FALSE;
}
m_hCommFile = hCommFile;
return TRUE;
}
// for manual opened Comm Device
BOOL CCommInfo::Create(CWnd* pNotifyWnd, DWORD dwCommBaseCommandID,
BYTE* pReadBuf, int nReadBufLen, int nPort,
DWORD dwBaudRate, BYTE byteFlowCtrl, BYTE byteDataBits,
BYTE byteStopBits, BYTE byteParity, BOOL bRTS)
{
// Are we already doing comm?
if (m_hCommFile != NULL)
{
TRACE0("Already have a comm file open\n");
return FALSE;
}
m_bConnected = FALSE;
// special for connection with DIANTAI ?
m_bRTS = bRTS;
m_pNotifyWnd = pNotifyWnd;
m_dwCommBaseCommandID = dwCommBaseCommandID;
m_pReadBuf = pReadBuf;
m_nReadBufLen = nReadBufLen;
m_nPort = nPort;
m_dwBaudRate = dwBaudRate;
m_byteFlowCtrl = byteFlowCtrl;
// for most comm application
m_byteDataBits = byteDataBits;
m_byteParity = byteParity;
m_byteStopBits = byteStopBits;
return TRUE;
}
// Stop and end all communication threads.
// Tries to gracefully signal all communication threads to
// close, but terminates them if it has to.
BOOL CCommInfo::CloseConnection(void)
{
// No need to continue if we're not communicating.
if (m_hCommFile == 0)
{
TRACE0("The Comm File is not opened !\n");
return TRUE;
}
TRACE0("Stopping the Comm\n");
// set connected flag to FALSE
m_bConnected = FALSE; // halt the thread
// disable event notification and wait for thread to halt
::SetCommMask(m_hCommFile, 0);
// Close the threads.
CloseReadThread();
CloseWriteThread();
// drop DTR
::EscapeCommFunction(m_hCommFile, CLRDTR); // ???
// Close the threads.
// CloseReadThread();
// CloseWriteThread();
// Not needed anymore.
::CloseHandle(m_hCloseEvent);
::CloseHandle(m_hPostEvent);
// Now close the comm port handle.
if (m_hCommFile)
{
::CloseHandle(m_hCommFile);
m_hCommFile = 0;
}
return TRUE;
}
// Win-32 Porting Issues:
// - OpenComm() is not supported under Win-32.
// Use CreateFile() and setup for OVERLAPPED_IO.
// - Win-32 has specific communication timeout parameters.
// - Created the secondary thread for event notification.
BOOL CCommInfo::OpenConnection(void)
{ian
DCB dcb;
COMMTIMEOUTS CommTimeOuts;
if (m_hCommFile == 0)
{
CString strPortName;
strPortName.Format(m_strCommNameFmt, m_nPort);
// open COMM device
if ((m_hCommFile =
::CreateFile(
strPortName, GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // overlapped I/O
NULL)) == INVALID_HANDLE_VALUE)
{
TRACE0("Open the Comm Port Eror !\n");
return FALSE;
}
CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 1000;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 1000;
// get any early notifications
::SetCommMask(m_hCommFile, EV_RXCHAR);
// setup device buffers
::SetupComm(m_hCommFile, cRXQueue, cTXQueue);
// purge any information in the buffer
::PurgeComm(m_hCommFile,
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
dcb.DCBlength = sizeof(DCB);
::GetCommState(m_hCommFile, &dcb);
dcb.BaudRate = m_dwBaudRate;
dcb.ByteSize = m_byteDataBits;
dcb.Parity = m_byteParity;
dcb.StopBits = m_byteStopBits;
// setup hardware flow control
// configuration of the hardware handshaking lines.
BYTE bSet = (BYTE)((m_byteFlowCtrl & PCF_DTRDSR) != 0);
dcb.fOutxDsrFlow = bSet;
if (bSet)
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
else
dcb.fDtrControl = DTR_CONTROL_ENABLE;
bSet = (BYTE)((m_byteFlowCtrl & PCF_RTSCTS) != 0);
dcb.fOutxCtsFlow = bSet;
if (bSet)
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
else
dcb.fRtsControl = RTS_CONTROL_ENABLE;
// setup software flow control
bSet = (BYTE)((m_byteFlowCtrl & PCF_XONXOFF) != 0);
dcb.fInX = dcb.fOutX = bSet;
dcb.XonChar = ASCII_XON;
dcb.XoffChar = ASCII_XOFF;
dcb.XonLim = 100;
dcb.XoffLim = 100;
// other various settings
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
}
else // already opened !
{
// The CommTimeout numbers will very likely change if you are
// coding to meet some kind of specification where
// you need to reply within a certain amount of time after
// recieving the last byte. However, If 1/4th of a second
// goes by between recieving two characters, its a good
// indication that the transmitting end has finished, even
// assuming a 1200 baud modem.
GetCommTimeouts(m_hCommFile, &CommTimeOuts);
CommTimeOuts.ReadIntervalTimeout = 250;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
// fAbortOnError is the only DCB dependancy in Tapi.
// Can't guarentee that the SP will set this to what we expect.
GetCommState(m_hCommFile, &dcb);
dcb.fAbortOnError = FALSE;
// Setting and querying the comm port configurations.
{
// Configure the comm settings.
COMMPROP commprop;
DWORD fdwEvtMask;
// These are here just so you can set a breakpoint
// and see what the comm settings are. Most Comm settings
// are already set through TAPI.
GetCommProperties(m_hCommFile, &commprop);
GetCommMask(m_hCommFile, &fdwEvtMask);
}
}
if (!::SetCommState(m_hCommFile, &dcb))
return FALSE;
// set up for overlapped I/O
::SetCommTimeouts(m_hCommFile, &CommTimeOuts);
m_bConnected = TRUE;
// Create the event that will signal the threads to close.
m_hCloseEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hCloseEvent == NULL)
{
TRACE0("Unable to CreateEvent m_hCloseEvent\n");
m_hCommFile = NULL;
return FALSE;
}
m_hPostEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
if (m_hPostEvent == NULL)
{
TRACE0("Unable to CreateEvent m_hPostEvent\n");
m_hCommFile = NULL;
return FALSE;
}
// Create the Read thread.
m_pReadThread =
::AfxBeginThread((AFX_THREADPROC)CCommInfo::ReadThreadProc, // pfnThreadProc
(LPVOID)this, // pParam
THREAD_PRIORITY_HIGHEST, // nPriority
(UINT)0, // nStackSize
(DWORD)CREATE_SUSPENDED, // dwCreateFlags,
(LPSECURITY_ATTRIBUTES)NULL // lpSecurityAttrs
);
m_pReadThread->m_bAutoDelete = FALSE;
/* m_hReadThread =
::CreateThread(
(LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)CCommInfo::ReadThreadProc,
(LPVOID)this,
0,
&m_dwReadThreadID);
if (m_hReadThread == NULL)*/
if (m_pReadThread == NULL)
{
TRACE0("Unable to create Read thread \n");
// m_dwReadThreadID = 0;
::CloseHandle(m_hCommFile);
m_hCommFile = 0;
return FALSE;
}
// assert DTR
::EscapeCommFunction(m_hCommFile, SETDTR);
// Comm threads should to 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.
// ::SetThreadPriority(m_hReadThread, THREAD_PRIORITY_HIGHEST);
m_pReadThread->ResumeThread();
// Create the Write thread.
m_pWriteThread =
::AfxBeginThread((AFX_THREADPROC)CCommInfo::WriteThreadProc, // pfnThreadProc
(LPVOID)this, // pParam
THREAD_PRIORITY_HIGHEST, // nPriority
(UINT)0, // nStackSize
(DWORD)CREATE_SUSPENDED, // dwCreateFlags,
(LPSECURITY_ATTRIBUTES)NULL // lpSecurityAttrs
);
m_pWriteThread->m_bAutoDelete = FALSE;
/*
m_hWriteThread =
::CreateThread(
(LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)CCommInfo::WriteThreadProc,
(LPVOID)this,
0,
&m_dwWriteThreadID);
if (m_hWriteThread == NULL)*/
if (m_pWriteThread == NULL)
{
TRACE0("Unable to create Write thread \n");
CloseReadThread();
// m_dwWriteThreadID = 0;
::CloseHandle(m_hCommFile);
m_hCommFile = 0;
return FALSE;
}
// ::SetThreadPriority(m_hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL);
m_pWriteThread->ResumeThread();
// special for connection with DIANTAI ?
if (m_bRTS)
{
::EscapeCommFunction(m_hCommFile, CLRRTS);
::EscapeCommFunction(m_hCommFile, CLRDTR);
}
// Everything was created ok. Ready to go!
return TRUE;
}
// Send a String to the Write Thread to be written to the Comm.
// 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).
BOOL CCommInfo::WriteCommBlock(LPCTSTR lpByte, DWORD dwBytesToWrite)
{
if (m_pWriteThread)
{
if (m_pWriteThread->PostThreadMessage(/*m_dwWriteThreadID, */
PWM_COMMWRITE,(WPARAM)dwBytesToWrite, (LPARAM)lpByte))
{
return TRUE;
}
else
{
switch (::GetLastError())
{
case ERROR_INVALID_THREAD_ID:
TRACE0("idThread is not a valid thread identifier, "
"or if the thread specified by idThread does not have a message queue !\n");
break;
default:
TRACE0("Failed to Post to Write thread.\n");
}
}
}
else
TRACE0("Write thread not created\n");
return FALSE;
}
// Closes the Read thread by signaling the CloseEvent.
// Purges any outstanding reads on the comm port.
//
// Note that terminating a thread leaks memory (read the docs).
// 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 CCommInfo::CloseReadThread(void)
{
// If it exists...
// if (m_hReadThread)
if (m_pReadThread)
{
TRACE0("Closing Read Thread\n");
// Signal the event to close the worker threads.
::SetEvent(m_hCloseEvent);
// Purge all outstanding reads
::PurgeComm(m_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
// Wait 10 seconds for it to exit. Shouldn't happen.
// if (::WaitForSingleObject(m_hReadThread, 10000) == WAIT_TIMEOUT)
if (::WaitForSingleObject(m_pReadThread->m_hThread, 10000) == WAIT_TIMEOUT)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -