调用ListView_SortItemsEx后ListView_FindItem始终返回-1的原因排查
问题根源与修复方案
核心问题分析
你的代码存在三个关键错误,直接导致排序后ListView_FindItem查找失败:
排序回调参数完全误用
ListView_SortItemsEx的回调函数参数lParam1和lParam2是每个项设置的lParam值,不是项的索引。但你插入所有项时都把lParam设为0,回调里错误地将这个值当作索引调用ListView_GetItemText,导致排序逻辑彻底混乱,控件内部数据结构被破坏,后续查找自然无法正常工作。列索引创建错误
ListView的列索引从0开始,但你创建列时直接使用iCol=1,导致列0不存在。主项文本(子项0)没有对应的显示列,控件对文本的存储和管理出现异常,进一步影响查找功能。排序回调返回值逻辑错误
strcmp的返回值规则是:
- 返回
<0:str1小于str2 - 返回
0:两字符串相等 - 返回
>0:str1大于str2
你当前的返回值映射完全错误,会导致排序结果不符合预期,甚至加剧控件内部状态混乱。
修复步骤与代码示例
1. 修正项的lParam存储
插入项时,将项的索引存入lParam,这样排序回调可以通过lParam获取到正确的项索引,进而读取文本。
2. 修正列创建逻辑
从索引0开始创建列,确保主项文本(子项0)有对应的显示列。
3. 修正排序回调函数
正确使用lParam作为项索引,同时修复strcmp返回值的映射逻辑。
4. 确保查找逻辑正确
ListView_FindItem使用LVFI_STRING时默认查找子项0的文本,确保该文本已正确设置。
修复后的完整代码:
#include "windows.h" #include "commctrl.h" #include "vector" #include "string" #pragma comment(lib, "Comctl32.lib") HINSTANCE hinst; HWND hwndMain; HWND hwndList; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int CreateColumn(HWND hwndLV, int iCol, CHAR* Text, int iWidth) { LVCOLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = LVCFMT_LEFT; lvc.cx = iWidth; lvc.pszText = Text; lvc.iSubItem = iCol; return ListView_InsertColumn(hwndLV, iCol, &lvc); } int CreateItem(HWND hwndList, CHAR* text, int index) { LVITEM lvi = { 0 }; int res; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = index; lvi.pszText = text; lvi.lParam = index; // 将项的索引存入lParam,用于排序回调 res = ListView_InsertItem(hwndList, &lvi); return res; } void fillListView() { for (int i = 0; i < 10; ++i) { CreateItem(hwndList, (CHAR*)std::to_string(i).c_str(), i); } } // 修正后的排序回调函数 int CALLBACK CompareListViewFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { // lParam1和lParam2是项的索引 int itemIndex1 = static_cast<int>(lParam1); int itemIndex2 = static_cast<int>(lParam2); std::vector<CHAR> str1(MAX_PATH); std::vector<CHAR> str2(MAX_PATH); ListView_GetItemText(hwndList, itemIndex1, 0, &str1.front(), MAX_PATH); ListView_GetItemText(hwndList, itemIndex2, 0, &str2.front(), MAX_PATH); // 直接返回strcmp的结果,符合排序回调的返回值要求 return strcmp(&str1.front(), &str2.front()); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; BOOL bRet; WNDCLASS wc; UNREFERENCED_PARAMETER(lpszCmdLine); // 注册主窗口类 if (!hPrevInstance) { wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon((HINSTANCE)NULL, IDI_APPLICATION); wc.hCursor = LoadCursor((HINSTANCE)NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "MainWndClass"; if (!RegisterClass(&wc)) return FALSE; } hinst = hInstance; hwndMain = CreateWindow("MainWndClass", "Sample", WS_OVERLAPPEDWINDOW, 500, 30, 700, 700, (HWND)NULL, (HMENU)NULL, hinst, (LPVOID)NULL); hwndList = CreateWindow(WC_LISTVIEW, "", WS_VISIBLE | WS_BORDER | WS_CHILD | LVS_REPORT | LVS_NOCOLUMNHEADER, 5, 5, 470, 630, hwndMain, (HMENU)44444, 0, 0); HWND btn = CreateWindow("Button", "Select element 2", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 500, 5, 150, 30, hwndMain, (HMENU)44445, 0, 0); HWND btn2 = CreateWindow("Button", "Sorting", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 500, 55, 150, 30, hwndMain, (HMENU)44446, 0, 0); ListView_SetExtendedListViewStyleEx(hwndList, 0, LVS_EX_FULLROWSELECT | LVS_EX_FLATSB); // 从索引0开始创建列 CreateColumn(hwndList, 0, "", 450); fillListView(); if (!hwndMain) return FALSE; ShowWindow(hwndMain, nCmdShow); UpdateWindow(hwndMain); while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); switch (wmId) { case 44445: { LVFINDINFO lvfiInfo; lvfiInfo.flags = LVFI_STRING; lvfiInfo.psz = "2"; int iIndex = ListView_FindItem(hwndList, -1, &lvfiInfo); if (iIndex != -1) { ListView_SetItemState(hwndList, iIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); SetFocus(hwndList); } else { MessageBox(hWnd, "Not found", "ERROR", MB_ICONINFORMATION); } break; } case 44446: { ListView_SortItemsEx(hwndList, CompareListViewFunc, 0); break; } } break; } case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
额外修正点说明
- 移除了
LVIF_IMAGE标志,因为没有使用图像列表,避免不必要的参数设置。 - 将窗口类的
lpszMenuName改为NULL,原代码中的"MainMenu"未定义,会导致编译警告。 - 移除了ListView创建时的
LVS_SORTDESCENDING风格,避免初始排序干扰测试。
内容的提问来源于stack exchange,提问作者jrfdsa
相关产品推荐
相关产品推荐

