如何更改列表视图控件的WndProc

时间:2016-08-30 22:05:40

标签: c++ listview winapi

我正在尝试更改列表视图控件的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,但发生错误: error C2664: 'BOOL IsolationAwareSetWindowSubclass(HWND,SUBCLASSPROC,UINT_PTR,DWORD_PTR)': cannot convert argument 2 from 'LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)' to 'SUBCLASSPROC' [build\binding.vcxproj]

1 个答案:

答案 0 :(得分:3)

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

中有详细说明
  

使用SetWindowLongPtr索引调用GWLP_WNDPROC会创建用于创建窗口的窗口类的子类。应用程序可以子类化系统类,但不应该为另一个进程创建的窗口类创建子类。 SetWindowLongPtr函数通过更改与特定窗口类关联的窗口过程来创建窗口子类,从而使系统调用新窗口过程而不是前一过程。 应用程序必须通过调用CallWindowProc将未通过新窗口过程处理的任何消息传递给上一个窗口过程。这允许应用程序创建一系列窗口过程

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

请改为尝试:

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

相关问题