c++ - 如何更改 ListView 控件的 WndProc

标签 c++ listview winapi

我正在尝试更改 ListView 控件的 Wndproc,以便 wndproc 中的第一个参数返回接收消息的控制句柄,问题是当我更改它时其他功能停止工作(我不能再插入列或项目),有什么我需要更改或返回以便继续对所有控件使用相同的 wndproc

所有控件的 WNDPROC 相同:

LRESULT CALLBACK staticWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
    std::cout << (int)hwnd << " control received msg:" << uMsg << std::endl; //This must work
    //event = { msg:uMsg, target:(int)hwnd, x:0, y:0, button:0, key:0 };
    switch (uMsg){
        case WM_DESTROY:
            std::cout << "window says bye " << std::endl;
            PostQuitMessage(WM_QUIT);
            break;
        default:
            //msghandlercall(event); //this is handled not in c++
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

在调用 SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc); 后,默认情况下插入消息不起作用

int createColumn(HWND listhandle, int indexCol, char *Text, int width){
    LVCOLUMN lvc={0};
    lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    lvc.fmt = LVCFMT_LEFT;
    lvc.cx = width;
    lvc.pszText = Text;
    lvc.iSubItem = indexCol;
    //return ListView_InsertColumn(listhandle, indexCol, &lvc);
    SendMessage(listhandle,LVM_INSERTCOLUMN,indexCol,(LPARAM)&lvc);
    return 1;
}
void createColumns(HWND listhandle, std::vector<LPSTR> columns){
    for(int i=0; i<columns.size(); i++) createColumn(listhandle, i, columns[i], 50);
}
int createItem(HWND listhandle, const std::vector<LPSTR>& row){ 
    LVITEM lvi = {0};
    //lvi.mask = LVIF_TEXT;
    // lvi.pszText = row[0];
    int ret = ListView_InsertItem(listhandle, &lvi);
    if(ret>-1) for(unsigned i=0; i<row.size(); i++)
        ListView_SetItemText(listhandle, ret, i, row[i]);
    return ret;
} 
HWND createList(int parenthandle=0){
    if(parenthandle==0) parenthandle=(int)GetDesktopWindow();
    HWND handle = CreateWindow(WC_LISTVIEW, "",WS_VISIBLE|WS_BORDER|WS_CHILD | LVS_REPORT | LVS_EDITLABELS, 
                            10, 10, 300, 100,  (HWND)parenthandle, /*(HMENU)ID_LIST*/NULL, GetModuleHandle(NULL), 0);
    if(!handle){ std::cerr << "Failed to create list\n"; return 0; }
    SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc);
    ListView_SetExtendedListViewStyle(handle, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
    createColumns(handle,{"col1","col2","col3"});
    createItem(handle,{"item1.1","item1.2","item1.3","item1.4"});
    createItem(handle,{"item2.1","item2.2","item2.3","item2.4"});
    return handle;
}

注意 我尝试使用 SetWindowSubclass() 添加 wndproc 但出现错误: 错误 C2664:“BOOL IsolationAwareSetWindowSubclass(HWND,SUBCLASSPROC,UINT_PTR,DWORD_PTR)”:无法将参数 2 从“LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)”转换为“SUBCLASSPROC”[build\binding. vcxproj]

最佳答案

当您使用 SetWindowLongPtr(GWLP_WNDPROC) 对窗口过程进行子类化时,它会返回被替换的前一个窗口过程。您的子类过程必须使用 CallWindowProc() 而不是 DefWindowProc() 将未处理的消息传递给之前的过程。这在 documentation 中也有说明。 :

Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process. The SetWindowLongPtr function creates the window subclass by changing the window procedure associated with a particular window class, causing the system to call the new window procedure instead of the previous one. An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.

您没有这样做,这就是您的代码失败的原因。

试试这个:

WNDPROC prevWndProc;

LRESULT CALLBACK staticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    //...
    return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}

HWND createList(HWND parenthandle = NULL)
{
    if (!parenthandle) parenthandle = GetDesktopWindow();

    HWND handle = CreateWindow(..., parenthandle, ...);
    if (!handle) {
        std::cerr << "Failed to create list\n";
        return NULL;
    }

    prevWndProc = (WNDPROC) SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc);
    if (!prevWndProc) {
        std::cerr << "Failed to subclass list\n";
        DestroyWindow(handle);
        return NULL;
    }

    ...

    return handle;
}

也就是说,您真的根本不应该使用 SetWindowLongPtr(GWLP_WNDPROC)。正是出于这个原因(以及其他原因),这是不安全的。你应该使用 SetWindowSubclass()相反:

LRESULT CALLBACK staticSubClass(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    //...

    if (uMsg == WM_NCDESTROY) {
        // NOTE: this requirement is NOT stated in the documentation,
        // but it is stated in Raymond Chen's blog article...
        RemoveWindowSubclass(hWnd, staticSubClass, uIdSubclass);
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

HWND createList(HWND parenthandle = NULL)
{
    if (!parenthandle) parenthandle = GetDesktopWindow();

    HWND handle = CreateWindow(..., parenthandle, ...);
    if (!handle) {
        std::cerr << "Failed to create list\n";
        return NULL;
    }

    if (!SetWindowSubclass(handle, staticSubClass, 1, 0)){
        std::cerr << "Failed to subclass list\n";
        DestroyWindow(handle);
        return NULL; 
    }

    ...

    return handle;
}

有关详细信息,请参阅 MSDN:

Subclassing Controls

Safer subclassing

关于c++ - 如何更改 ListView 控件的 WndProc,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39237806/

相关文章:

android - 在 Android 中为 ListView 设置 onItemClick 监听器

winapi - 如何在纯gdi(不是gdi+)中填充roundrect的渐变

c++ - 包含算法会导致构建失败

c++ - Qt - 打开窗口的单个实例

ios - 包含 ListView 的 View 上的 Touchstart、-end 和 -move 事件

c - 如何改变ATL控件的默认宽高?

C 基本数据类型问题 - const char * to LPCTSTR

c++ - std::function 的 typename 关键字

c++ - 混合调用约定会导致编译错误

c# - 使用 asp.net listview 删除 SweetAlert 确认对话框?