 |
#include <stdio.h> #include <Windows.h>
int main(void) { char buffer[100]; char *p; DCB dcb; DWORD dwSize; HANDLE hPort = CreateFile(TEXT("\\\\.\\COM6"), GENERIC_READ | GENERIC_WRITE, (DWORD)NULL, NULL, OPEN_EXISTING, (DWORD)NULL, NULL);
if (hPort != INVALID_HANDLE_VALUE) { ZeroMemory(&dcb, sizeof(dcb)); // 由於dcb結構中還有很多其他成員, 所以使用前必須清零 dcb.BaudRate = CBR_4800; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; if (SetCommState(hPort, &dcb)) puts("串口打開成功");
p = "這是一段中文字元串"; WriteFile(hPort, p, strlen(p), &dwSize, NULL); printf("\n發送: %d 字節, 錯誤編號: %d\n", dwSize, GetLastError()); ReadFile(hPort, buffer, sizeof(buffer), &dwSize, NULL); printf("接收: %d 字節, 錯誤編號: %d\n", dwSize, GetLastError()); if (dwSize > 0) { buffer[dwSize] = '\0'; printf("接收到的資料: %s\n", buffer); }
if (CloseHandle(hPort)) puts("串口已關閉"); } else printf("串口打開失敗, 錯誤碼: %d\n", GetLastError()); return 0; }
|
 |
|
 |
COM6無法直接在運行裏面打開:  
|
 |
就連文件名都不能是com6,即便是有.png的擴展名: 
|
 |
【注意】 dcb.BaudRate = CBR_9600; 常量是以CBR_開頭的,不是以BAUD_開頭的 錯誤的寫法: dcb.BaudRate = BAUD_9600;
|
 |
另外,dcb裏面還有一個DCBlength成員,必須設置為DCB結構的大小。不過API函數似乎不檢查這個成員的值。不過為了兼容性最好寫上: DCB dcb; ZeroMemory(&dcb, sizeof(dcb)); dcb.DCBlength = sizeof(DCB); GetCommState(hComm, &dcb); // 獲取該串口的默認配置 ..... SetCommState(hComm, &dcb);
|
 |
【程序2】 #include <iostream> #include <Windows.h> #include <strsafe.h> using namespace std; #define DEBCD(n) ((((n) & 0xf0) >> 4) * 10 + ((n) & 0x0f)) #ifdef _UNICODE ostream &operator << (ostream &os, const wchar_t *wstr) { if (os == cout) WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), wstr, wcslen(wstr), NULL, NULL); return os; } #endif int main(void) { HANDLE hComm = CreateFile(TEXT("\\\\.\\COM10"), GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, NULL, NULL); if (hComm == INVALID_HANDLE_VALUE) { cout << TEXT("打開串口失敗") << endl; return 0; } try { DCB dcb; ZeroMemory(&dcb, sizeof(dcb)); dcb.DCBlength = sizeof(DCB); GetCommState(hComm, &dcb); dcb.BaudRate = CBR_9600; // 注意是CBR_不是BAUD_ dcb.ByteSize = 8; dcb.fParity = NOPARITY; dcb.StopBits = ONESTOPBIT; if (!SetCommState(hComm, &dcb)) throw 0; COMMTIMEOUTS tmout; ZeroMemory(&tmout, sizeof(tmout)); tmout.ReadTotalTimeoutConstant = 1000; if (!SetCommTimeouts(hComm, &tmout)) throw 2; BYTE buf[100]; DWORD dwSize; buf[0] = 0x05; WriteFile(hComm, buf, 1, &dwSize, NULL); ReadFile(hComm, buf, _countof(buf), &dwSize, NULL); if (buf[0] == 0x20) { int hour, wday; TCHAR h[4]; LPTSTR list = TEXT("日一二三四五六"); TCHAR str[100]; if (buf[3] & 0x80) { if (buf[3] & 0x20) h[0] = 'P'; else h[0] = 'A'; h[1] = 'M'; h[2] = ' '; h[3] = '\0'; hour = buf[3] & 0x1f; hour = DEBCD(hour); } else { h[0] = '\0'; hour = DEBCD(buf[3]); } wday = buf[6] & 0x07; if (wday == 7) wday = 0; cout << TEXT("時間為: ") << endl; StringCbPrintf(str, sizeof(str), TEXT("20%02d年%02d月%02d日\n%02d:%02d:%02d %s星期%c"), DEBCD(buf[7]), DEBCD(buf[5]), DEBCD(buf[4]), hour, DEBCD(buf[2]), DEBCD(buf[1]), h, list[wday]); cout << str << endl; } else throw 1; } catch (const int iErrId) { DWORD dwErr = GetLastError(); switch (iErrId) { case 0: cout << TEXT("設置波特率失敗, 錯誤碼: ") << dwErr << endl; if (dwErr == ERROR_INVALID_PARAMETER) cout << TEXT("參數不正確") << endl; break; case 1: cout << TEXT("讀取時間失敗") << endl; break; case 2: cout << TEXT("設置超時時間失敗") << endl; } } if (hComm != INVALID_HANDLE_VALUE) CloseHandle(hComm); return 0; } 【運行結果】 時間為: 2016年07月17日 19:04:25 星期日
|
 |

這是每次開機後第一次打開串口時默認的串口配置,可見設備管理器中的串口波特率的設置是形同虛設的,雖然裏面設置的是9600,但是默認打開的還是1200,而且每字節的位數默認是7而不是8。 執行SetCommState後,該串口的配置會自動保存,直到關機或註銷。所以,如果你在程序A中將串口的波特率設為了9600的話,再在程序B中重新打開串口,無需執行SetCommState函數,波特率仍然是9600,而不是剛開機時的1200。因此有時候即使SetCommState函數執行失敗了,後面的串口通訊代碼也能正常執行,就是因為可能在運行之前用其他軟件正確打開了串口而遺留了正確的串口配置,如果關機重啟或註銷,問題就會重新出現。
|
 |
因此,7樓的代碼還有一個很明顯的漏洞。程序中只設置了DCB結構體中的DCBlength、BaudRate、ByteSize、fParity和StopBits這五個成員變量。如果有搞破壞的程序惡意設置了DCB中的其他成員變量並保存了,那麼再次運行這個程序,串口通訊可能就不能正常進行。
|
 |
11樓
巨大八爪鱼
2016-7-17 22:47
【獲取設備管理器中設置的串口波特率的方法(包括完整的DCB結構體)】 COMMCONFIG cfg; ZeroMemory(&cfg, sizeof(cfg)); DWORD dwSize = sizeof(cfg); GetDefaultCommConfig(TEXT("COM10"), &cfg, &dwSize); // 這裏不能寫成\\\\.\\COM10 cout << "波特率: " << cfg.dcb.BaudRate << endl;
|