近段在写个小程序时发现了CListCtrl::GetItemText的Bug,微软的源代码如下:
VC6对应文件: VC98\MFC\SRC\winctrl2.cpp
VC7.1对应文件: Vc7\atlmfc\src\mfc\winctrl2.cpp
CString CListCtrl::GetItemText(int nItem, int nSubItem) const
{
ASSERT(::IsWindow(m_hWnd));
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.iSubItem = nSubItem;
CString str;
int nLen = 128;
int nRes;
do
{
nLen *= 2;
lvi.cchTextMax = nLen;
lvi.pszText = str.GetBufferSetLength(nLen);
nRes = (int)::SendMessage(m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem,
(LPARAM)&lvi);
} while (nRes == nLen-1);
str.ReleaseBuffer();
return str;
}
问题出在标红色的句子,我发现如果读取的第(lvi.pszText + lvi.cchTextMax – 1)字节刚好是双字节字符的第一个字节时(比如中文汉字的第一个字节),该字节最后被替换为’\0’(这符合lvi.pszText以’\0’结尾的约定),但返回读取的字节数是lvi.cchTextMax而不是预想的(lvi.cchTextMax-1),导致(nRet == nLen-1)结果为FALSE而结束读取数据,但是数据仍没有读完,需要继续读取。
解决办法很简单,把(nRet == nLen-1)改成(nRet >= nLen – 1),真搞不懂为什么没有人向微软反应这个错误并促使他们改过来呢。
我自己写了一个外部函数:
CString GetItemText(CListCtrl& lvw, int nItem, int nSubItem)
{
ASSERT(::IsWindow(lvw.m_hWnd));
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.iSubItem = nSubItem;
CString str;
int nLen = 128;
int nRes;
do
{
nLen *= 2;
lvi.cchTextMax = nLen;
lvi.pszText = str.GetBufferSetLength(nLen);
nRes = (int)::SendMessage(lvw.m_hWnd, LVM_GETITEMTEXT, (WPARAM)nItem,
(LPARAM)&lvi);
} while (nRes >= nLen-1);
str.ReleaseBuffer();
return str;
}