勘誤
1、2009-5-14 據shunruo網友發現了一個重要的BUG,在此感謝shunruo網友,還有張冬冬網友
CnComm1.5第1180行 函數:DWORD Write(LPCVOID lpBuf, DWORD dwSize)
原有代碼:
DWORD Write(LPCVOID lpBuf, DWORD dwSize)
{
DWORD dwTemp = dwSize, dwFree = FreeSize();
if (dwFree)//! 首先查找末尾空閑,并寫入數據
{
DWORD dwCopy = dwFree > dwSize ? dwSize : dwFree;
memcpy(L_->P_ + L_->E_, lpBuf, dwCopy);
dwTemp -= dwCopy, L_->E_ += dwCopy;
}
if (dwTemp)//! 剩余的數據分配新的空間并寫入
{
memcpy(NewBlock(dwSize)->P_, lpBuf, dwTemp);//重要的BUG
L_->E_ += dwTemp;
}
S_ += dwSize;
return dwSize;
}
請將該函數內容修正如下:
DWORD Write(LPCVOID lpBuf, DWORD dwSize)
{
DWORD dwTemp = dwSize, dwFree = FreeSize(), dwCopy = 0;
if (dwFree)//! 首先查找末尾空閑,并寫入數據
{
dwCopy = dwFree > dwSize ? dwSize : dwFree;
memcpy(L_->P_ + L_->E_, lpBuf, dwCopy);
dwTemp -= dwCopy, L_->E_ += dwCopy;
}
if (dwTemp)//! 剩余的數據分配新的空間并寫入
{
memcpy(NewBlock(dwSize)->P_, ((LPBYTE)lpBuf )+ dwCopy, dwTemp);//該處原代碼未作偏移值修正,由于緩沖區一般較大,這一塊在測試中忽略了
L_->E_ += dwTemp;
}
S_ += dwSize;
return dwSize;
}
2、2009-4-29 根據網友的提醒,發現一個CnComm在WinCE下使用一個潛在的缺陷
CnComm1.5第1700行 函數: virtual bool SetupPort()
原有代碼:
virtual bool SetupPort()
{
if(!CN_ASSERT(IsOpen()))
return false;
if(!CN_ASSERT(::SetupComm(hComm_, 4096, 4096)))//! 配置端口發送接收隊列大小, 讀4096字節, 寫4096字節, 阻塞I/O模式發送隊列無意義
return false; //此處有些硬件不支持
if(!CN_ASSERT(::GetCommTimeouts(hComm_, &CO_)))
return false;
CO_.ReadIntervalTimeout = 100;//! 配置超時結構 字符最小間隔100ms
CO_.ReadTotalTimeoutMultiplier = 0;
CO_.ReadTotalTimeoutConstant = IsOverlappedMode() ? 500 : 250;//! 讀超時 重疊I/O模式下500毫秒 阻塞I/O模式下250毫秒
CO_.WriteTotalTimeoutMultiplier = IsOverlappedMode() ? 1 : 0;
CO_.WriteTotalTimeoutConstant = IsOverlappedMode() ? 10000 : 250;//! 寫超時 重疊I/O模式下(10000+1×字節數)毫秒 阻塞I/O模式下250毫秒
if(!CN_ASSERT(::SetCommTimeouts(hComm_, &CO_)))
return false;
if(!CN_ASSERT(::PurgeComm(hComm_, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR )))//! 清除端口
return false;
return true;
}
將該函數修正如下:
virtual bool SetupPort()
{
if(!CN_ASSERT(IsOpen()))
return false;
#if defined(CN_COMM_FOR_CE)
::SetupComm(hComm_, 4096, 4096);
#else
if(!CN_ASSERT(::SetupComm(hComm_, 4096, 4096)))//! 配置端口發送接收隊列大小, 讀4096字節, 寫4096字節, 阻塞I/O模式發送隊列無意義
return false;
#endif
if(!CN_ASSERT(::GetCommTimeouts(hComm_, &CO_)))
return false;
CO_.ReadIntervalTimeout = 100;//! 配置超時結構 字符最小間隔100ms
CO_.ReadTotalTimeoutMultiplier = 0;
CO_.ReadTotalTimeoutConstant = IsOverlappedMode() ? 500 : 250;//! 讀超時 重疊I/O模式下500毫秒 阻塞I/O模式下250毫秒
CO_.WriteTotalTimeoutMultiplier = IsOverlappedMode() ? 1 : 0;
CO_.WriteTotalTimeoutConstant = IsOverlappedMode() ? 10000 : 250;//! 寫超時 重疊I/O模式下(10000+1×字節數)毫秒 阻塞I/O模式下250毫秒
if(!CN_ASSERT(::SetCommTimeouts(hComm_, &CO_)))
return false;
if(!CN_ASSERT(::PurgeComm(hComm_, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR )))//! 清除端口
return false;
return true;
}
3 、2009-5-18 根據網友騰空的提示,發現一個關于內置緩沖區的BUG
CnComm1.5第1726行 函數: DWORD PortToBuffer(DWORD dwPortByteNum)
原有代碼:
//! 將端口數據讀入緩沖區的
DWORD PortToBuffer(DWORD dwPortByteNum)
{
return dwPortByteNum ? I_.Release(ReadPort(I_.GetFreePtr(dwPortByteNum), dwPortByteNum)) : 0;
}
將該函數修正如下:
//! 將端口數據讀入緩沖區的
DWORD PortToBuffer(DWORD dwPortByteNum)
{
BlockBuffer::InnerLock locker(&I_);
return dwPortByteNum ? I_.Release(ReadPort(I_.GetFreePtr(dwPortByteNum), dwPortByteNum)) : 0;
}
4、2009-6-11 根據網友deke_chen的提示,修改兩個BUG
CnComm1.5 第487行 wchar_t * ReadString(wchar_t *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
原有代碼:
//! 讀取串口 dwLength - 1 個UNICODE字符到 szBuffer 返回 C 模式字符串指針 適合一般字符通訊
wchar_t * ReadString(wchar_t *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
{
CN_ASSERT(szBuffer);
szBuffer[Read(szBuffer, dwLength - 1, dwWaitTime)] = L'\0';
return szBuffer;
}
將該函數修正如下:
//! 讀取串口 dwLength - 1 個UNICODE字符到 szBuffer 返回 C 模式字符串指針 適合一般字符通訊
wchar_t * ReadString(wchar_t *szBuffer, DWORD dwLength, DWORD dwWaitTime = INFINITE)
{
CN_ASSERT(szBuffer);
szBuffer[(Read(szBuffer, (dwLength - 1)*sizeof(wchar_t), dwWaitTime) +1)/ sizeof(wchar_t)] = L'\0';
return szBuffer;
}
注:不推薦使用UNICODE字符串通訊,因為串口是字符通訊,不能保障2個字節的UNICODE字符一次性被接收到。
CnComm1.5 第1307行 wchar_t* ReadString(wchar_t* lpBuf, DWORD dMaxSize)
原有代碼:
//! 讀入UNICODE字符串緩沖區
wchar_t* ReadString(wchar_t* lpBuf, DWORD dMaxSize)
{
lpBuf[Read(lpBuf, dMaxSize)] = L'\0';
return lpBuf;
}
將該函數修正如下:
//! 讀入UNICODE字符串緩沖區
wchar_t* ReadString(wchar_t* lpBuf, DWORD dMaxSize)
{
lpBuf[(Read(lpBuf, dMaxSize*sizeof(wchar_t))+1) / sizeof(wchar_t)] = L'\0';
return lpBuf;
}
CnComm1.5 第1331行 void Clear(bool bDeleteAll = false)
原有代碼:
//! 清除 \param bDeleteAll 為true時釋放所有內存, 否則保留一個內存塊以提高效率
void Clear(bool bDeleteAll = false)
{
if (F_ && (F_==L_) && F_->S_>(M_<<2))
memset(F_, 0, sizeof(Block));
else
{
for (Block* t = F_; t; delete F_)
F_ = t, t = t->N_;
F_ = L_ = NULL, S_ = 0;
}
}
將該函數修正如下:
//! 清除 \param bDeleteAll 為true時釋放所有內存, 否則保留一個內存塊以提高效率
void Clear(bool bDeleteAll = false)
{
if (F_ && (F_==L_) && F_->S_>(M_<<2))
{
DWORD S = F_->S_;
memset(F_, 0, sizeof(Block)), F_->S_ = S;
}
else
{
for (Block* t = F_; t; delete F_)
F_ = t, t = t->N_;
F_ = L_ = NULL, S_ = 0;
}
}
Faq
1、用CnComm寫的程序發送不正常,但用其他程序打開端口后,再用CnComm寫的程序就好了?
分析:
具體原因是配置參數失敗。并且該網友沒有檢查Open()或者SetState()的返回值, 所以沒有發現配置參數失敗造成的。
比如設置波特率"9600,O,7,1",不小心把配置字符串寫成了"9600,7,O,1"。
使用CnComm配置后失敗,并沒有檢查返回值,發送數據就會是亂碼,或者不能正確接收。
這時很自然的,你會用其他工具程序打開端口,由于有可視界面,打開參數正確,又能正常發送。
然后你又想用CnComm再打開試試,結果發現又行了。
實際上配置串口失敗,會采用上一次正確配置,所以看上去正常了,實際上單獨運行就不行了。
這和我的筆誤,即有一回把"9600,N,8,1"誤寫成了"9600,8,N,1",客觀上造成去多網友照搬出錯,對于這一部分網友,真誠表示歉意。
不過我還有看到更夸張的寫法,但部分網友不檢查返回值,是一個很不好的現象。
if (!Com.Open(1, 9600))
assert(0);
if (!Com.Open(1, "9600,N,8,1"))
assert(0);
最起碼也要像上面這樣寫,當然這只是舉例,實際應用應該更嚴謹才對。
另外在CnComm1.5版本以后打開和配置出錯,調試版中都會產生錯誤提示,即便是你不檢查返回值。
CnComm配置字符串的標準寫法:
"BBBB,P,D,S" BBBB 為波特率,P 為奇偶校驗,D 為數據位數,S 為停止位數。
合法的奇偶校驗值:
E 偶數 (Even)
M 標記 (Mark)
N 缺省 (Default)
None
O 奇數 (Odd)
S 空格 (Space)
合法的數據位:
4
5
6
7
8 (缺?。?/span>
合法的停止位值:
1 (缺?。?br>1.5
2
2、發送10個字節數據,每次都分成兩部分,比如"0123456789", 被分成"01234"和"56789", 能不能一次讀取10個字節?
分析:這是個通訊的基本問題。
由于是10個字節,你才會問這個問題,如果是1百萬個字節,你就不會這樣問我了。
你肯定不敢開口要求一次性接受1百萬個字節。
通訊歸根結底是看似連續,實則斷續。
在CnComm1.5已經針對這個問題作了修改,對于百千個字節可以做到連續一次性接受
CnComm1.3可以采用Read(buf, 1000, false);
最好升級到CnComm1.5。這里參見我在CSDN博客中接收處理兩種用法
最規范的做法,是利用緩沖區緩存數據,然后再做處理。
3、調用發送語句后,馬上關閉串口,常常數據發不出。
分析:如果你采用重疊IO方式即CnComm默認方式打開串口,發送數據并不是在Write語句調用后馬上發生,而是系統在后臺幫你發送。
而你立即調用關閉,關閉里有清空隊列的代碼,就沒有系統運行的機會。
解決辦法:同步IO,不存在這個問題。
使用重疊IO,在CnComm1.5 ,可以按如下做法
CnComm com;
com.Open(1);
com.Write("test");
com.Flush();
com.Close();