⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serialport.cpp

📁 一个很好的串口监视程序
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#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 + -