使用Windows API操作串口有两种方式:同步方式和异步方式(又称为重叠方式)。同步方式是指API函数会阻塞直到操作完成才返回;在多线程下,虽然不会阻塞主线程,但是仍然会阻塞监听线程。而异步方式下,API函数会立即返回,操作在后台进行,避免线程的阻塞。
操作串口一般有以下几步骤:
1)获取串口
2)打开串口
3)配置串口
4)读写串口
5)关闭串口
一、获取串口
1)下面是项目中使用到的方法,使用QueryDosDevice函数查找COM口
BOOL CActiveReaderDlg::GetLocalSerialPort(CUIntArray &ports)
{
//Make sure we clear out any elements which may already be in the array
ports.RemoveAll();
//Determine what OS we are running on
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
BOOL bGetVer = GetVersionEx(&osvi);
//On NT use the QueryDosDevice API
if (bGetVer && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT))
{
//Use QueryDosDevice to look for all devices of the form COMx. This is a better
//solution as it means that no ports have to be opened at all.
TCHAR szDevices[65535];
DWORD dwChars = QueryDosDevice(NULL, szDevices, 65535);
if (dwChars)
{
int i=0;
for (;;)
{
//Get the current device name
TCHAR* pszCurrentDevice = &szDevices[i];
//If it looks like "COMX" then
//add it to the array which will be returned
int nLen = _tcslen(pszCurrentDevice);
if (nLen > 3 && _tcsncmp(pszCurrentDevice, _T("COM"), 3) == 0)
{
//Work out the port number
int nPort = _ttoi(&pszCurrentDevice[3]);
ports.Add(nPort);
}
// Go to next NULL character
while(szDevices[i] != _T('\0'))
i++;
// Bump pointer to the next string
i++;
// The list is double-NULL terminated, so if the character is
// now NULL, we're at the end
if (szDevices[i] == _T('\0'))
break;
}
}
else
TRACE(_T("Failed in call to QueryDosDevice, GetLastError:%d\n"), GetLastError());
}
else
{
//On 95/98 open up each port to determine their existence
//Up to 255 COM ports are supported so we iterate through all of them seeing
//if we can open them or if we fail to open them, get an access denied or general error error.
//Both of these cases indicate that there is a COM port at that number.
for (UINT i=1; i<256; i++)
{
//Form the Raw device name
CString sPort;
sPort.Format(_T("\\\\.\\COM%d"), i);
//Try to open the port
BOOL bSuccess = FALSE;
HANDLE hPort = ::CreateFile(sPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (hPort == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
//Check to see if the error was because some other app had the port open or a general failure
if (dwError == ERROR_ACCESS_DENIED || dwError == ERROR_GEN_FAILURE)
bSuccess = TRUE;
}
else
{
//The port was opened successfully
bSuccess = TRUE;
//Don't forget to close the port, since we are going to do nothing with it anyway
CloseHandle(hPort);
}
//Add the port number to the array which will be returned
if (bSuccess)
ports.Add(i);
}
}
return true;
}
BOOL CActiveReaderDlg::OnInitDialog()
{
CDialog::OnInitDialog();
GetLocalSerialPort(ports);
for (int i=0; i<ports.GetSize(); i++)
{
CString str="";
CString port="COM";
str.Format("%d",ports.ElementAt(i));
port = port+str;
((CComboBox *)GetDlgItem(IDC_SP_NUM_COMBO))->AddString(port);
}
}
以下方法是网上摘录的:
2)打开注册表枚举串口的方法
void CPageSetCom::ShowComm()
{
long lReg;
HKEY hKey;
DWORD MaxValueLength;
DWORD dwValueNumber;
lReg=RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey);
if(lReg!=ERROR_SUCCESS)
{
AfxMessageBox(L"Open Registry Error!\n");
return;
}
lReg=RegQueryInfoKeyA(hKey,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&dwValueNumber, //返回和hKey关联的值
&MaxValueLength,
NULL,
NULL,
NULL);
if(lReg!=ERROR_SUCCESS) //没有成功
{
AfxMessageBox(L"Getting Info Error!\n");
return;
}
LPSTR pValueName,pCOMNumber;
DWORD cchValueName,dwValueSize=6;
for(DWORD i=0;i < dwValueNumber;i++)
{
cchValueName=MaxValueLength+1;
dwValueSize=6;
pValueName=(LPSTR)VirtualAlloc(NULL,cchValueName,MEM_COMMIT,PAGE_READWRITE);
lReg=RegEnumValueA(hKey,
i,
pValueName,
&cchValueName,
NULL,
NULL,
NULL,
NULL);
if((lReg!=ERROR_SUCCESS)&&(lReg!=ERROR_NO_MORE_ITEMS))
{
AfxMessageBox(L"Enum Registry Error or No More Items!\n");
continue;
}
pCOMNumber=(LPSTR)VirtualAlloc(NULL,6,MEM_COMMIT,PAGE_READWRITE);
lReg=RegQueryValueExA(hKey,
pValueName,
NULL,
NULL,
(LPBYTE)pCOMNumber,
&dwValueSize);
if(lReg!=ERROR_SUCCESS)
{
AfxMessageBox(L"Can not get the name of the port");
continue;
}
CString strCommList;
//AfxMessageBox(pCOMNumber);
CharToUnicode(pCOMNumber,&strCommList);
//m_ctlPort.AddString(strCommList);
BOOL m_bInsert=0;
if(((CComboBox*)GetDlgItem(IDC_CMBREADERCOM))->GetCount()==0)
((CComboBox*)GetDlgItem(IDC_CMBREADERCOM))->AddString(strCommList);
else
{
CString strTemp=strCommList;
strCommList.TrimLeft(L"COM");
for(int icurrent=0;icurrent<((CComboBox*)GetDlgItem(IDC_CMBREADERCOM))->GetCount();icurrent++)
{
CString strCurrent;
((CComboBox*)GetDlgItem(IDC_CMBREADERCOM))->GetLBText(icurrent,strCurrent);
strCurrent.TrimLeft(L"COM");
if(_ttol(strCurrent)>_ttol(strCommList))
{
((CComboBox*)GetDlgItem(IDC_CMBREADERCOM))->InsertString(icurrent,strTemp);
m_bInsert = 1;
break;
}
}
if(!m_bInsert)
((CComboBox*)GetDlgItem(IDC_CMBREADERCOM))->InsertString(icurrent,strTemp);
}
VirtualFree(pValueName,0,MEM_RELEASE);
VirtualFree(pCOMNumber,0,MEM_RELEASE);
}
}
int CPageSetCom::CharToUnicode(char *pchIn, CString *pstrOut)
{
int nLen;
WCHAR *ptch;
if(pchIn == NULL)
{
return 0;
}
nLen = MultiByteToWideChar(CP_ACP, 0, pchIn, -1, NULL, 0);
ptch = new WCHAR[nLen];
MultiByteToWideChar(CP_ACP, 0, pchIn, -1, ptch, nLen);
pstrOut->Format(_T("%s"), ptch);
delete [] ptch;
return nLen;
}
3)使用EnumPort方法
该方法调用EnumPort()函数,该函数本身就是枚举电脑端口用的,它枚举的并非只有串口,所以必须对其所得串口进行筛选。
</pre><pre name="code" class="cpp">int m_nSerialPortNum