【问题标题】:How can I retrieve the message-sending object from WndProc's parameters?如何从 WndProc 的参数中检索消息发送对象?
【发布时间】:2016-06-24 22:57:52
【问题描述】:

所以我正在编写自己的小 GUI 框架,将 Windows API 包装在有用的类中。我目前正试图让用户以面向对象的方式处理 WndProc 的消息,但我遇到了一些障碍。

我有一个 Button 类,它继承自一个抽象的 Control 类,它几乎是 Button 的 HWND 句柄的包装器。 还有一个Window 类,它包含(你知道什么)一个窗口句柄。这个类还有一个Controls的容器,负责创建自己的(静态)WndProc方法。

我想做的是在包含窗口的 WndProc 中有一个大的 switch 语句,它根据函数的 wParam 和 lParam 参数调用适当的发送控件的处理函数。我见过大多数人这样做的方式是这样的:

#define MY_BUTTON 101 //Define button's ID

Button::Button()
{
    hwndButton= CreateWindow(
        "BUTTON", text,
        WS_VISIBLE | WS_CHILD,
        position.x, position.y,
        size.x, size.y,
        parent_handle,
        (HMENU)MY_BUTTON,
        GetModuleHandle(NULL),
        NULL);
}
Button* button = new Button();

//Later on, in the Window class...

LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch((LOWORD)wParam)
    {
    case MY_BUTTON:
        button->HandleMessage(&msg);
        break;
    }
}

但是,我不希望用户为他们创建的每个对象实例分配一个唯一的整数。 相反,由于我有一个控件容器,我宁愿做这样的事情:

//In the Window Class...
Button* button = new Button();
AddControl(button);

static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(&msg)
    {
        ObtainControlFromParams(wParam, lParam)->HandleMessage(&msg);
        //ObtainControlFromParams should return a pointer to a Control, 
        //and it should look into the window's Control collection.
    }
}

尽管这听起来很棒,但我找不到实现ObtainControlFromParams 函数的方法。

我想区分每个控件实例的方法是有一个字符串,我称之为控件的“名称”,它应该对每个对象实例都是唯一的。这会给我留下两个选择(我能想到的)

  • 将字符串转换为哈希,将其存储在其他位置(例如包含窗口)并将其用作按钮的 HMENU。然后我可以使用散列集将每个字符串散列链接到其所有者对象,并以这种方式调用对应的方法。
  • 使用 lParam 方法,该方法(据我所知)存储按钮的 HWND 句柄,并用它来代替。

如果我试图获得的内容不是很清楚,我深表歉意,这对我来说是一个复杂的概念。

非常感谢任何帮助!

【问题讨论】:

  • 您不必让用户手动分配唯一的 id,您可以使用全局整数(或管理静态变量的类)生成 id,每次创建时都会递增新控件。
  • 你没有控制参数,所以你找错了地方。与窗口关联一个指向对象的指针。有多种方法可以做到这一点。主题已在此处讨论了数千次。
  • @bennji_of_the_overflow 或仅使用 HWND 作为标识符;它已经是独一无二的(在按钮的整个生命周期内)!

标签: c++ winapi


【解决方案1】:

通常的方法是将发送到控件父窗口的WM_COMMANDWM_NOTIFY 等消息反射 回发送给发送它们的控件。这是可能的,因为这些消息会识别发件人(每条消息都以独特的方式,因此请查看文档)。

有多种方法可以将 C++ 对象指针与窗口相关联:

  • 动态生成的蹦床函数作为 window-proc。
    最有效但也最棘手的。可能需要使用VirtualAlloc

  • 窗口属性。
    SetPropGetProp 函数。

  • 窗口对象字。
    SetWindowLongPtr。需要确保有分配的空间。

  • 静态地图。
    例如。一个单例std::unordered_map,映射句柄→对象。

  • Windows 标准子类化使用的任何内容。
    IE。使用SetWindowSubclass

【讨论】:

    【解决方案2】:

    将按钮的Control* 对象指针存储在按钮HWND 本身中,消息过程可以在其中检索它。为此,您可以使用(Set|Get)WindowLongPtr(GWL_USERDATA)(Get|Set)SetProp()

    只有特定的消息,如WM_COMMANDWM_NOTIFY,识别发送它们的控件。这些消息被发送到控件的父窗口。这些消息包含子控件的HWND。家长可以检索孩子的Control*并将消息转发给它。

    其他消息直接发送到控件自己的窗口,而不是其父窗口。这些消息不标识它们被发送到的控件。因此,每个Control 都需要其自己的单独 WndProc 过程用于自己的HWND。在调用CreateWindow() 之后,使用SetWindowLongPtr(GWL_WNDPROC)SetWindowSubclass() 将该WndProc 分配给HWND

    尝试这样的事情(这很粗略,编写 UI 框架涉及更多内容,但这应该会给您一些想法):

    typedef std::basic_string<TCHAR> tstring;
    
    class Control
    {
    private:
        HWND fWnd;
        Control *fParent;
        POINT fPosition;
        SIZE fSize;
        tstring fText;
        std::list<Control*> fControls;
        ...
    
        void addControl(Control *c);
        void removeControl(Control *c);
    
        virtual void createWnd() = 0;
        void internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle);
    
        ...
    
        static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    protected:
        virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
        ...
    
    public:
        Control();
        virtual ~Control();
    
        HWND getWnd();
        void destroyWnd();
        void wndNeeded();
    
        Control* getParent();
        void setParent(Control *value);
    
        POINT getPosition();
        void setPosition(POINT value);
    
        SIZE getSize();
        void setSize(SIZE value);
    
        tstring getText();
        void setText(const tstring &value);
        ...
    };
    

    static const LPTSTR szControlPropStr = TEXT("ControlPtr")
    
    Control* ControlFromWnd(HWND hwnd)
    {
        return (Control*) GetProp(hwnd, szControlPropStr);
    }
    
    Control::Control()
        : fWnd(0), fParent(0)
    {
        fPosition.x = fPosition.y = 0;
        fSize.cx = fSize.cy = 0;
        ...
    }
    
    Control::~Control()
    {
        setParent(0);
        destroyWnd();
    }
    
    void Control::addControl(Control *c)
    {
        fControls.push_back(c);
        c->fParent = this;
        if (fWnd)
                c->wndNeeded();
    }
    
    void Control::removeControl(Control *c)
    {
        fControls.remove(c);
        c->destroyWnd();
        c->fParent = 0;
    }
    
    HWND Control::getWnd()
    {
        wndNeeded();
        return fWnd;
    }
    
    void Control::internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle)
    {
        style |= WS_VISIBLE;
        if (fParent)
             style |= WS_CHILD;
    
        fWnd = CreateWindowEx(exstyle,
            ClassName, fText.c_cstr(), style,
            fPosition.x, fPosition.y,
            fSize.cx, fSize.cy,
            (fParent) ? fParent->getWnd() : 0,
            0,
            GetModuleHandle(NULL),
            NULL);
        SetProp(fWnd, szControlPropStr, (HANDLE)this);
        SetWindowLongPtr(fWnd, GWL_WNDPROC, (LONG_PTR)&Control::WndProc);
    }
    
    void Control::destroyWnd()
    {
        DestroyWindow(fWnd);
        fWnd = 0;
    }
    
    void Control::wndNeeded()
    {
        if (!fWnd)
        {
            createWnd();
            for (std::list<Control*>::iterator iter = fControls.begin(); iter != fControls.end(); ++iter)
                iter->wndNeeded();
        }
    }
    
    Control* Control::getParent()
    {
        return fParent;
    }
    
    void Control::setParent(Control *value)
    {
        if (fParent != value)
        {
            if (fParent)
                fParent->removeControl(this);
            fParent = value;
            if (fParent)
                fParent->addControl(this);
        }
    }
    
    POINT Control::getPosition()
    {
        return fPosition;
    }
    
    void Control::setPosition(POINT value)
    {
        fPosition = value;
        if (fWnd)
            SetWindowPos(fWnd, 0, fPosition.x, fPosition.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
    }
    
    SIZE Control::getSize()
    {
        return fSize;
    }
    
    void Control::setSize(SIZE value)
    {
        fSize = value;
        if (fWnd)
            SetWindowPos(fWnd, 0, 0, 0, fSize.cx, fSize.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
    }
    
    tstring Control::getText()
    {
        return fText;
    }
    
    void Control::setText(const tstring &value)
    {
        fText = value;
        if (fWnd)
            SetWindowText(fWnd, fText.c_str());
    }
    
    LRESULT CALLBACK Control::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        Control *pThis = ControlFromWnd(hwnd);
        if (pThis)
            return pThis->HandleMessage(uMsg, wParam, lParam);
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    
    LRESULT Control::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            WM_NCDESTROY:
            {
                RemoveProp(fWnd, szControlPropStr);
                fWnd = 0;
                break;
            }
    
            case WM_COMMAND:
            {
                HWND hwnd  = (HWND) lParam;
                if ((hwnd) && (hwnd != fWnd))
                {
                    Control *c = ControlFromWnd(hwnd);
                    if  (c)
                        return c->HandleMessage(uMsg, wParam, lParam);
                }
                ...
                break;
            }
    
            case WM_NOTIFY:
            {
                NMHDR *hdr = (NMHDR*) lParam;
                if ((hdr->hwndFrom) && (hdr->hwndFrom != fWnd))
                {
                    Control *c = ControlFromWnd(hdr->hwndFrom);
                    if  (c)
                        return c->HandleMessage(uMsg, wParam, lParam);
                }
                ...
                break;
            }
    
            case WM_WINDOWPOSCHANGED:
            {
                WINDOWPOS *p = (WINDOWPOS*) lParam;
    
                if (!(p->flags & SWP_NOMOVE))
                {
                    fPosition.x = p->x;
                    fPosition.y = p->y;
                }
    
                if (!(p->flags & SWP_NOSIZE))
                {
                    fSize.cx = p->cx;
                    fSize.cy = p->cy;
                }
    
                ...
                return 0;
            }
    
            case WM_SETTEXT:
            {
                LRESULT ret = DefWindowProc(fWnd, uMsg, wParam, lParam);
                if (ret == TRUE)
                    fText = (TCHAR*) lParam;
                return ret;
            }
    
            ...
        }
    
        return DefWindowProc(fWnd, uMsg, wParam, lParam);
    }
    

    class Button : public Control
    {
    protected:
        virtual void createWnd();
        virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    public:
        Button();
    };
    

    Button::Button()
        : Control()
    {
    }
    
    void Button::createWnd()
    {
        Control::intetnalCreateWnd(TEXT("BUTTON"), BS_PUSHBUTTON | BS_TEXT, 0);
        ...
    }
    
    LRESULT Button::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_COMMAND:
            {
                HWND hwnd = (HWND) lParam;
                if (hwnd != fWnd)
                    break;
    
                switch (HIWORD(wParam))
                {
                    case BN_CLICKED:
                    {
                        ...
                        return 0;
                    }
    
                    ...
                }
    
                break;
            }
        }
    
        return Control::HandleMessage(uMsg, wParam, lParam);
    }
    

    //In the Window Class...
    Button* button = new Button();
    button->setPosition(...);
    button->setSize(...);
    button->setText(...);
    button->setParent(this);
    

    【讨论】:

      猜你喜欢
      • 2014-03-29
      • 1970-01-01
      • 1970-01-01
      • 2020-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      相关资源
      最近更新 更多