Win32 C++ ListView LVITEM的 pszText字符串生命周期——为何会出现乱码字符
我的ListView项标签保存在一个 std::vector 的 std::wstring 中。关于我在一个工具函数中将来自 std::wstring 的字符串转换为 LVITEM 的 pszText 的生存期问题,导致出现乱码字符,尽管调试器在构造时显示 LVITEM 已经完全正确。
我在我的 MyListView 对象的以下工具方法中进行 LVITEM 的构造,这个方法在添加和修改项时都会被调用。
struct MyListView {
// ...
std::vector<std::wstring> labels; // [0] is main, [1+] is additional columns
// ...
void addItems(..);
void modifyItems(..);
private:
LVITEM prepareMainLVItem();
}
当该工具方法执行以下操作时:
LVITEM MyListView::prepareMainLVItem() {
LVITEM lvi;
ZeroMemory(&lvi, sizeof(lvi));
lvi.pszText = const_cast <wchar_t*>(labels[0].c_str()); // Need to remove const
//...
return lvi;
结果却是一片空白(尽管调试器在调用此函数后确认 pszText 是正确的):

我也尝试手动把字符串复制到一个 wchar_t buf[256],并把 pszText 设置为它。这样做会得到乱码,尽管调试器再次在末尾显示正确构造的 LVITEM:
LVITEM lvi;
wchar_t buf[256];
ZeroMemory(&lvi, sizeof(lvi));
wcscpy_s(buf, 256, labels[0].c_str());
lvi.pszText = buf;
//...
return lvi;

我知道局部变量会在作用域结束,但在这种情况下它是一个调用该方法的类的数据成员。我应该怎么做,才能使用那个数据成员?
我不想为这些标签使用动态内存,对我来说听起来有点儿大材小用。
解决方案
第二种方法显然是错误的,因为局部缓冲区会在作用域结束后失效,导致 LVITEM::pszText 字段成为悬空指针。
不过,只要向量/字符串在将 LVITEM 插入到ListView之前不被修改,第一种方法就应该完全可行。请确保同时填充好 LVITEM::mask 字段,不要让它为零。
LVITEM MyListView::prepareMainLVItem() {
LVITEM lvi;
ZeroMemory(&lvi, sizeof(lvi));
lvi.mask = LVIF_TEXT;
lvi.pszText = labels[0].data();
//...
return lvi;
}
也就是说,既然你已经把字符串存储在内存中,应该考虑在ListView中使用回调模式(callback mode)或虚拟模式(virtual mode),这样就不需要在ListView的内存中重复你的字符串,而是ListView会在需要从你的字符串获取文本时动态通知你。比如:
LVITEM MyListView::prepareMainLVItem() {
LVITEM lvi;
ZeroMemory(&lvi, sizeof(lvi));
lvi.mask = LVIF_TEXT;
lvi.pszText = LPSTR_TEXTCALLBACK;
//...
return lvi;
}
...
case LVN_GETDISPINFO: {
NMLVDISPINFO* pdi = reinterpret_cast<NMLVDISPINFO*>(lParam);
if (pdi->item.mask & LVIF_TEXT) {
wcscpy_s(pdi->item.pszText, pdi->item.cchTextMax, labels[0].c_str());
}
break;
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。