📄 serialport.cpp
字号:
#include "stdafx.h"
#include "SerialPort.h"
#include <objbase.h>
#include <initguid.h>
#ifndef GUID_CLASS_COMPORT
DEFINE_GUID(GUID_CLASS_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4,0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);
#endif
/*
int g_nBaudrates[]={CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200,
CBR_75,CBR_110,CBR_134,CBR_150,CBR_300,CBR_1800,CBR_7200,
CBR_12000,CBR_14400,CBR_28800,CBR_33600,CBR_38400,CBR_56000,
CBR_57600,CBR_115200,CBR_128000,CBR_256000};
*/
//==========================
CSerialPort::CSerialPort()
{
m_hFile = NULL;
m_pThreadRece=NULL;
m_pWndReceMsg=NULL;
m_nCOMNo=0;
m_bThreadAlive=FALSE;
m_hEventCloseThread=NULL;
m_bValid=0;
m_nRTO=20;
m_nParityBit=NOPARITY;
m_nStopBit=ONESTOPBIT;
m_nDataBit=8;
m_nFlowCtrl=0;
m_nBaudRate=9600;
}
CSerialPort::~CSerialPort()
{
Close();
}
BOOL CSerialPort::Open(CWnd *pWndReceMsg, UINT uiPortNo,BOOL bConfig,BOOL bStart)
{
// _ASSERTE(uiPortNo>=0 && uiPortNo<MAX_SCC_NUM);
_ASSERTE(uiPortNo>=0 && uiPortNo<255);
_ASSERTE(pWndReceMsg!=NULL);
if(m_bThreadAlive||m_hFile) return FALSE;
char str[100];
sprintf(str,"COM%d",uiPortNo+1);
m_hFile = ::CreateFile(str,GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,0);
if (m_hFile == INVALID_HANDLE_VALUE)
{
switch (::GetLastError())
{
case ERROR_FILE_NOT_FOUND:
strcat(str," 为无效端口!");
AfxMessageBox(str);
break;
case ERROR_ACCESS_DENIED:
strcat(str," 已被其他程序打开!");
AfxMessageBox(str);
break;
default:
strcat(str," 打开失败!未知错误!");
AfxMessageBox(str);
break;
}
m_hFile = NULL;
return FALSE;
}
m_pWndReceMsg=pWndReceMsg;
m_nCOMNo=uiPortNo;
m_bValid=1;
if (m_hEventCloseThread != NULL) ResetEvent(m_hEventCloseThread);
m_hEventCloseThread = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hEventCloseThread == 0)
{
::CloseHandle(m_hFile);
m_hFile = NULL;
return FALSE;
}
//====
if(bConfig)
if(!Config()) return FALSE;
if(bStart)
if(!StartThread()) return FALSE;
return TRUE;
}
BOOL CSerialPort::Close()
{
DWORD dwR=0;
for(;;)
{
if(m_hFile&&m_bThreadAlive)
{
dwR=m_pThreadRece->ResumeThread();
if((dwR>1)&&(dwR!=0xFFFFFFFF)) continue;
}
SetEvent(m_hEventCloseThread);
if(!m_bThreadAlive) break;
}
m_pThreadRece=NULL;
m_pWndReceMsg=NULL;
::CloseHandle(m_hEventCloseThread);
m_hEventCloseThread = 0;
if (m_hFile == 0) return TRUE;
::CloseHandle(m_hFile);
m_hFile = 0;
return TRUE;
}
UINT CSerialPort::CommThread(LPVOID pParam)
{
CSerialPort *port = (CSerialPort*)pParam;
OVERLAPPED olRead;
::ZeroMemory(&olRead, sizeof(OVERLAPPED));
olRead.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
if(olRead.hEvent==NULL) return (UINT)-1;
port->m_bThreadAlive = TRUE;
DWORD Event = 0;
DWORD CommEvent = 0;
DWORD dwError = 0;
COMSTAT comstat;
BOOL bResult = TRUE;
DWORD length=0;
char *RXBuffer;
RXBuffer=new char[MAX_SCC_RECV_LEN];
HANDLE hEventArray[2];
hEventArray[0] = port->m_hEventCloseThread;
hEventArray[1] = olRead.hEvent;
if (port->m_hFile) // check if the port is opened
PurgeComm(port->m_hFile, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
BOOL bError=0;
for(;;)
{
bResult = WaitCommEvent(port->m_hFile, &Event, &olRead);
if(!bResult)
{
if(GetLastError()!=ERROR_IO_PENDING) bError=1;
}
else
{
bResult = ClearCommError(port->m_hFile, &dwError, &comstat);
if (comstat.cbInQue == 0) continue;
}
if(bError) break;
Event = WaitForMultipleObjects(2, hEventArray, FALSE, INFINITE);
switch (Event)
{
case 0:
port->m_bThreadAlive = FALSE;
delete [] RXBuffer;
RXBuffer=NULL;
::ZeroMemory(&comstat, sizeof(COMSTAT));
CloseHandle(olRead.hEvent);
CloseHandle(hEventArray[0]);
CloseHandle(hEventArray[1]);
AfxEndThread(100);
break;
case 1: //读数据
GetCommMask(port->m_hFile, &CommEvent);
if (CommEvent & EV_RXCHAR) port->ReadData(RXBuffer, &olRead);
break;
default:
bError=1;
break;
}
if(bError) break;
}
port->m_bThreadAlive = FALSE;
delete [] RXBuffer;
RXBuffer=NULL;
::ZeroMemory(&comstat, sizeof(COMSTAT));
CloseHandle(olRead.hEvent);
CloseHandle(hEventArray[0]);
CloseHandle(hEventArray[1]);
return 0;
}
BOOL CSerialPort::StartThread()
{
if (m_hFile == 0) return FALSE;
if (!(m_pThreadRece = AfxBeginThread(CommThread, this))) return FALSE;
return TRUE;
}
DWORD CSerialPort::ReadData(LPVOID cpRXBuffer, OVERLAPPED* olRead)
{
BOOL bResult = TRUE;
DWORD dwError = 0;
DWORD dwBytesRead = 0;
COMSTAT comstat;
bResult = ClearCommError(m_hFile, &dwError, &comstat);
if (comstat.cbInQue == 0) return 0;
bResult = ReadFile(m_hFile,cpRXBuffer,MAX_SCC_RECV_LEN,&dwBytesRead,olRead);
if(!bResult)
{
if(GetLastError()==ERROR_IO_PENDING)
GetOverlappedResult(m_hFile,olRead,&dwBytesRead,1);
else
return 0;
}
WORD wBytesRead=(WORD)dwBytesRead;
::PostMessage(m_pWndReceMsg->m_hWnd, WM_SCC_MSG, (WPARAM)cpRXBuffer, MAKELPARAM(m_nCOMNo,wBytesRead));
::ZeroMemory(&comstat, sizeof(COMSTAT));
return dwBytesRead;
}
BOOL CSerialPort::Config()
{
if (m_hFile == 0) return FALSE;
DCB dcb;
dcb.DCBlength = sizeof(DCB);
if (!::GetCommState(m_hFile,&dcb)) return FALSE;
dcb.BaudRate = (DWORD) m_nBaudRate;
dcb.ByteSize = (BYTE) m_nDataBit;
dcb.Parity = (BYTE) m_nParityBit;
dcb.StopBits = (BYTE) m_nStopBit;
dcb.fParity = (m_nParityBit != NOPARITY);
if (!::SetCommState(m_hFile,&dcb))
{
char cherror[100];
sprintf(cherror,"COM%d设置错误:\n可能不支持所选波特率,或停止位,或数据位",m_nCOMNo+1);
AfxMessageBox(cherror);
return FALSE;
}
if (!::SetupComm(m_hFile,MAX_SCC_RECV_LEN,MAX_SCC_SEND_LEN)) return FALSE;
if (!::SetCommMask(m_hFile,EV_RXCHAR)) return FALSE;
COMMTIMEOUTS cto;
if (!::GetCommTimeouts(m_hFile,&cto)) return FALSE;
cto.ReadIntervalTimeout = m_nRTO;
cto.ReadTotalTimeoutConstant = 0;
cto.ReadTotalTimeoutMultiplier = 0;
cto.WriteTotalTimeoutConstant =0;
cto.WriteTotalTimeoutMultiplier =0;
if (!::SetCommTimeouts(m_hFile,&cto)) return FALSE;
::ZeroMemory(&dcb, sizeof(DCB));
::ZeroMemory(&cto, sizeof(COMMTIMEOUTS));
return TRUE;
}
DWORD CSerialPort::WriteData(LPVOID lpBuffer, DWORD dwLength)
{
if(!dwLength) return 0;
BOOL bResult=TRUE;
DWORD length=dwLength;
COMSTAT ComStat;
DWORD dwErrorFlags;
OVERLAPPED olWrite;
::ZeroMemory(&olWrite, sizeof(OVERLAPPED));
olWrite.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);
if(olWrite.hEvent==NULL) return 0;
ClearCommError(m_hFile,&dwErrorFlags,&ComStat);
bResult=WriteFile(m_hFile,lpBuffer,length,&length,&olWrite);
if(!bResult)
{
if(GetLastError()==ERROR_IO_PENDING)
GetOverlappedResult(m_hFile,&olWrite,&length,TRUE);// 等待
else length=0;
}
::ZeroMemory(&ComStat, sizeof(COMSTAT));
::CloseHandle (olWrite.hEvent);
return length;
}
//===============================
#ifdef ADDSCCENUM
CSCCEnum::CSCCEnum()
{
}
CSCCEnum::~CSCCEnum()
{
}
void CSCCEnum::EnumPortsWdm(CArray<SSerInfo,SSerInfo&> &asi)
{
CString strErr;
// Create a device information set that will be the container for
// the device interfaces.
GUID *guidDev = (GUID*) &GUID_CLASS_COMPORT;
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
try {
hDevInfo = SetupDiGetClassDevs( guidDev,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
);
if(hDevInfo == INVALID_HANDLE_VALUE)
{
strErr.Format("SetupDiGetClassDevs failed. (err=%lx)",
GetLastError());
throw strErr;
}
// Enumerate the serial ports
BOOL bOk = TRUE;
SP_DEVICE_INTERFACE_DATA ifcData;
DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*) new char[dwDetDataSize];
// This is required, according to the documentation. Yes,
// it's weird.
ifcData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
for (DWORD ii=0; bOk; ii++) {
bOk = SetupDiEnumDeviceInterfaces(hDevInfo,
NULL, guidDev, ii, &ifcData);
if (bOk) {
// Got a device. Get the details.
SP_DEVINFO_DATA devdata = {sizeof(SP_DEVINFO_DATA)};
bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo,
&ifcData, pDetData, dwDetDataSize, NULL, &devdata);
if (bOk) {
CString strDevPath(pDetData->DevicePath);
// Got a path to the device. Try to get some more info.
TCHAR fname[256];
TCHAR desc[256];
BOOL bSuccess = SetupDiGetDeviceRegistryProperty(
hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL,
(PBYTE)fname, sizeof(fname), NULL);
bSuccess = bSuccess && SetupDiGetDeviceRegistryProperty(
hDevInfo, &devdata, SPDRP_DEVICEDESC, NULL,
(PBYTE)desc, sizeof(desc), NULL);
BOOL bUsbDevice = FALSE;
TCHAR locinfo[256];
if (SetupDiGetDeviceRegistryProperty(
hDevInfo, &devdata, SPDRP_LOCATION_INFORMATION, NULL,
(PBYTE)locinfo, sizeof(locinfo), NULL))
{
// Just check the first three characters to determine
// if the port is connected to the USB bus. This isn't
// an infallible method; it would be better to use the
// BUS GUID. Currently, Windows doesn't let you query
// that though (SPDRP_BUSTYPEGUID seems to exist in
// documentation only).
bUsbDevice = (strncmp(locinfo, "USB", 3)==0);
}
if (bSuccess) {
// Add an entry to the array
SSerInfo si;
si.strDevPath = strDevPath;
si.strFriendlyName = fname;
si.strPortDesc = desc;
si.bUsbDevice = bUsbDevice;
asi.Add(si);
}
}
else {
strErr.Format("SetupDiGetDeviceInterfaceDetail failed. (err=%lx)",
GetLastError());
throw strErr;
}
}
else {
DWORD err = GetLastError();
if (err != ERROR_NO_MORE_ITEMS) {
strErr.Format("SetupDiEnumDeviceInterfaces failed. (err=%lx)", err);
throw strErr;
}
}
}
}
catch (CString strCatchErr) {
strErr = strCatchErr;
}
if (pDetData != NULL)
delete [] (char*)pDetData;
if (hDevInfo != INVALID_HANDLE_VALUE)
SetupDiDestroyDeviceInfoList(hDevInfo);
if (!strErr.IsEmpty())
throw strErr;
}
void CSCCEnum::EnumPortsWNt4(CArray<SSerInfo,SSerInfo&> &asi)
{
// NT4's driver model is totally different, and not that
// many people use NT4 anymore. Just try all the COM ports
// between 1 and 16
SSerInfo si;
for (int ii=1; ii<=16; ii++) {
CString strPort;
strPort.Format("COM%d",ii);
si.strDevPath = CString("\\\\.\\") + strPort;
si.strPortName = strPort;
asi.Add(si);
}
}
void CSCCEnum::EnumPortsW9x(CArray<SSerInfo,SSerInfo&> &asi)
{
// Look at all keys in HKLM\Enum, searching for subkeys named
// *PNP0500 and *PNP0501. Within these subkeys, search for
// sub-subkeys containing value entries with the name "PORTNAME"
// Search all subkeys of HKLM\Enum\USBPORTS for PORTNAME entries.
// First, open HKLM\Enum
HKEY hkEnum = NULL;
HKEY hkSubEnum = NULL;
HKEY hkSubSubEnum = NULL;
try {
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Enum", 0, KEY_READ,
&hkEnum) != ERROR_SUCCESS)
throw CString("Could not read from HKLM\\Enum");
// Enumerate the subkeys of HKLM\Enum
char acSubEnum[128];
DWORD dwSubEnumIndex = 0;
DWORD dwSize = sizeof(acSubEnum);
while (RegEnumKeyEx(hkEnum, dwSubEnumIndex++, acSubEnum, &dwSize,
NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
HKEY hkSubEnum = NULL;
if (RegOpenKeyEx(hkEnum, acSubEnum, 0, KEY_READ,
&hkSubEnum) != ERROR_SUCCESS)
throw CString("Could not read from HKLM\\Enum\\")+acSubEnum;
// Enumerate the subkeys of HKLM\Enum\*\, looking for keys
// named *PNP0500 and *PNP0501 (or anything in USBPORTS)
BOOL bUsbDevice = (strcmp(acSubEnum,"USBPORTS")==0);
char acSubSubEnum[128];
dwSize = sizeof(acSubSubEnum); // set the buffer size
DWORD dwSubSubEnumIndex = 0;
while (RegEnumKeyEx(hkSubEnum, dwSubSubEnumIndex++, acSubSubEnum,
&dwSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
BOOL bMatch = (strcmp(acSubSubEnum,"*PNP0500")==0 ||
strcmp(acSubSubEnum,"*PNP0501")==0 ||
bUsbDevice);
if (bMatch) {
HKEY hkSubSubEnum = NULL;
if (RegOpenKeyEx(hkSubEnum, acSubSubEnum, 0, KEY_READ,
&hkSubSubEnum) != ERROR_SUCCESS)
throw CString("Could not read from HKLM\\Enum\\") +
acSubEnum + "\\" + acSubSubEnum;
SearchPnpKeyW9x(hkSubSubEnum, bUsbDevice, asi);
RegCloseKey(hkSubSubEnum);
hkSubSubEnum = NULL;
}
dwSize = sizeof(acSubSubEnum); // restore the buffer size
}
RegCloseKey(hkSubEnum);
hkSubEnum = NULL;
dwSize = sizeof(acSubEnum); // restore the buffer size
}
}
catch (CString strError) {
if (hkEnum != NULL)
RegCloseKey(hkEnum);
if (hkSubEnum != NULL)
RegCloseKey(hkSubEnum);
if (hkSubSubEnum != NULL)
RegCloseKey(hkSubSubEnum);
throw strError;
}
RegCloseKey(hkEnum);
}
void CSCCEnum::SearchPnpKeyW9x(HKEY hkPnp, BOOL bUsbDevice,CArray<SSerInfo,SSerInfo&> &asi)
{
// Enumerate the subkeys of the given PNP key, looking for values with
// the name "PORTNAME"
// First, open HKLM\Enum
HKEY hkSubPnp = NULL;
try {
// Enumerate the subkeys of HKLM\Enum\*\PNP050[01]
char acSubPnp[128];
DWORD dwSubPnpIndex = 0;
DWORD dwSize = sizeof(acSubPnp);
while (RegEnumKeyEx(hkPnp, dwSubPnpIndex++, acSubPnp, &dwSize,
NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
HKEY hkSubPnp = NULL;
if (RegOpenKeyEx(hkPnp, acSubPnp, 0, KEY_READ,
&hkSubPnp) != ERROR_SUCCESS)
throw CString("Could not read from HKLM\\Enum\\...\\")
+ acSubPnp;
// Look for the PORTNAME value
char acValue[128];
dwSize = sizeof(acValue);
if (RegQueryValueEx(hkSubPnp, "PORTNAME", NULL, NULL, (BYTE*)acValue,
&dwSize) == ERROR_SUCCESS)
{
CString strPortName(acValue);
// Got the portname value. Look for a friendly name.
CString strFriendlyName;
dwSize = sizeof(acValue);
if (RegQueryValueEx(hkSubPnp, "FRIENDLYNAME", NULL, NULL, (BYTE*)acValue,
&dwSize) == ERROR_SUCCESS)
strFriendlyName = acValue;
// Prepare an entry for the output array.
SSerInfo si;
si.strDevPath = CString("\\\\.\\") + strPortName;
si.strPortName = strPortName;
si.strFriendlyName = strFriendlyName;
si.bUsbDevice = bUsbDevice;
// Overwrite duplicates.
BOOL bDup = FALSE;
for (int ii=0; ii<asi.GetSize() && !bDup; ii++)
{
if (asi[ii].strPortName == strPortName) {
bDup = TRUE;
asi[ii] = si;
}
}
if (!bDup) {
// Add an entry to the array
asi.Add(si);
}
}
RegCloseKey(hkSubPnp);
hkSubPnp = NULL;
dwSize = sizeof(acSubPnp); // restore the buffer size
}
}
catch (CString strError) {
if (hkSubPnp != NULL)
RegCloseKey(hkSubPnp);
throw strError;
}
}
void CSCCEnum::EnumSerialPorts(CArray<SSerInfo,SSerInfo&> &asi, BOOL bIgnoreBusyPorts)
{
asi.RemoveAll();
// Use different techniques to enumerate the available serial
// ports, depending on the OS we're using
OSVERSIONINFO vi;
vi.dwOSVersionInfoSize = sizeof(vi);
if (!::GetVersionEx(&vi)) {
CString str;
str.Format("Could not get OS version. (err=%lx)",
GetLastError());
throw str;
}
// Handle windows 9x and NT4 specially
if (vi.dwMajorVersion < 5) {
if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
EnumPortsWNt4(asi);
else
EnumPortsW9x(asi);
}
else {
// Win2k and later support a standard API for
// enumerating hardware devices.
EnumPortsWdm(asi);
}
for (int ii=0; ii<asi.GetSize(); ii++)
{
SSerInfo& rsi = asi[ii];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -