c++ - 不再需要在子类化后恢复 GWLP_USERDATA

标签 c++ winapi

我创建了一个 Subclass 类,并在其中使用 SetWindowLongPtr()GWLP_USERDATA 设置为包含指向我用来分发消息的派生类的指针。

所以如果我使用 SetWindowLongPtr(CurrentWindow, GWLP_USERDATA, (LONG_PTR)Data);,我该如何恢复 当我不再想继承旧数据时? MSDN 说初始值为零,所以我应该使用 SetWindowLongPtr(CurrentWindow, GWLP_USERDATA, (LONG_PTR)NULL); 函数吗?

编辑: 实际上,我使用 SetProp、GetProp、RemoveProp 找到了解决此问题的方法,现在我不必担心会弄乱其他函数。 下面是我的代码,供可能需要的人使用:

#ifndef WIN32_SUBCLASS_CLASS_H
#define WIN32_SUBCLASS_CLASS_H

#include <Windows.h>
#include <tchar.h>

class SubclassWindow
{
public:
    static LRESULT CALLBACK stWinSubclassHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        SubclassWindow* pWnd;
        pWnd = (SubclassWindow*)GetProp(hwnd, TEXT("Subclass"));

        if (pWnd)
            return pWnd->WinSubclassHandler(hwnd, uMsg, wParam, lParam);
        else
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    BOOL Subclass(SubclassWindow* Data, HWND hwnd)
    {
        if (Subclassed == TRUE || CurrentWindow != NULL)
            return 0;

        CurrentWindow = hwnd;
        Subclassed = TRUE;
        OriginalProc = (WNDPROC)GetWindowLongPtr(hwnd, GWL_WNDPROC);
        SetProp(CurrentWindow, TEXT("Subclass"), (HANDLE)Data);
        SetWindowLongPtr(CurrentWindow, GWL_WNDPROC, (LONG_PTR)SubclassWindow::stWinSubclassHandler);

        return 1;
    }
    BOOL RemoveSubclass()
    {
        if (OriginalProc == NULL || CurrentWindow == NULL)
            return 0;

        Subclassed = FALSE;
        RemoveProp(CurrentWindow, TEXT("Subclass"));
        SetWindowLongPtr(CurrentWindow, GWL_WNDPROC, (LONG_PTR)OriginalProc);

        return 1;
    }

    BOOL IsSubclassed() { return Subclassed; }
protected:
    HWND CurrentWindow;
    WNDPROC OriginalProc;
    BOOL Subclassed;

    SubclassWindow()
    {
        CurrentWindow = NULL;
        OriginalProc = NULL;
        Subclassed = FALSE;
    }

    virtual LRESULT CALLBACK WinSubclassHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};

#endif

最佳答案

SetWindowLongPtr() 返回旧值,因此保存它,然后在稍后调用 SetWindowLongPtr() 删除子类时恢复它。

也就是说,为不是您自己创建的窗口替换 GWLP_USERDATA 是危险的。您不知道该窗口是否已将 GWLP_USERDATA 用于其自身目的。使用 SetWindowSubclass()相反,它旨在解决该问题:

class SubclassWindow
{
public:
    static LRESULT CALLBACK stWinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
    {
        SubclassWindow* pWnd = (SubclassWindow*) dwRefData;    
        if ((pWnd) && (uIdSubclass == 1))
            return pWnd->WinSubclassHandler(hwnd, uMsg, wParam, lParam);
        else
            return DefSubclassProc(hWnd, uMsg, wParam, lParam);
    }

    BOOL Subclass(SubclassWindow* Data, HWND hwnd)
    {
        if (CurrentWindow != NULL)
            return FALSE;

        if (!SetWindowSubclass(hwnd, &stWinSubclassHandler, 1, (DWORD_PTR)Data))
            return FALSE;

        CurrentWindow = hwnd;
        return TRUE;
    }

    BOOL RemoveSubclass()
    {
        if (CurrentWindow == NULL)
            return FALSE;

        if (!RemoveWindowSubclass(CurrentWindow, &stWinSubclassHandler, 1))
            return FALSE;

        CurrentWindow = NULL;
        return TRUE;
    }

    BOOL IsSubclassed() { return (CurrentWindow != NULL); }

protected:
    HWND CurrentWindow;

    SubclassWindow()
    {
        CurrentWindow = NULL;
    }

    virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};

如果您的派生类需要默认处理任何给定的消息,您唯一需要做的改变就是让它们的 WinSubclassHandler() 实现调用 DefSubclassProc()而不是 DefWindowProc()(顺便说一句,调用的 API 是错误的 - 你应该调用 CallWindowProc(OriginalProc, ...) 而不是你之前的窗口过程子类替换有机会处理消息)。为此,我建议将该调用包装在您的 SubclassWindow 类中,以对后代隐藏该细节,例如:

class SubclassWindow
{
...
protected:
    ...
    static LRESULT DefaultHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        return DefSubclassProc(hWnd, uMsg, wParam, lParam);
    }
    ...
};

class MySubclass : public SubclassWindow
{
protected:
    virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ...
        return DefaultHandler(hWnd, uMsg, wParam, lParam);
    }
};

或者(这将允许后代更好地派生自后代):

class SubclassWindow
{
...
protected:
    ...
    virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        return DefSubclassProc(hWnd, uMsg, wParam, lParam);
    }
    ...
};

class MySubclass1 : public SubclassWindow
{
protected:
    virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ...
        return SubclassWindow::WinSubclassHandler(hWnd, uMsg, wParam, lParam);
    }
};

class MySubclass2 : public MySubclass1
{
protected:
    virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ...
        return MySubclass1::WinSubclassHandler(hWnd, uMsg, wParam, lParam);
    }
};

class MySubclass3 : public MySubclass2
{
protected:
    virtual LRESULT CALLBACK WinSubclassHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ...
        return MySubclass2::WinSubclassHandler(hWnd, uMsg, wParam, lParam);
    }
};

关于c++ - 不再需要在子类化后恢复 GWLP_USERDATA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18174566/

相关文章:

c++ - xvalue 和 prvalue 之间的一些区别

c++ - 调整大小后保持对 vector 元素的引用有效

c++ - HttpFilterProc 在 C++ 中不起作用

c - SetWindowsHookEx - 取消Winkey + L(电脑锁定)

c++ - LVM_GETNEXTITEM 在 ListView 中找不到项目

c++ - Variadic 模板类的构造函数无法接受可变参数

c++ - 此代码是否违反一个定义规则?

c++ - 可以使用 mingw 为 Windows Vista 或 7 编译代码吗?

c - 用于 win32/C 的线程生产者/消费者的阻塞队列

c++ - clang++ 是否以更宽松的方式对待系统头?