如何从WndProc的参数中检索消息发送对象?

时间:2016-06-24 04:40:42

标签: c++ winapi

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

我有一个Button类,它继承自一个抽象的Control类,它几乎是Button的HWND句柄的包装器。 还有一个Window类,它包含(你知道什么)窗口的句柄。该类还有一个Control的容器,负责创建自己的(静态)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功能的方法。

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

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

如果我想要获得的内容不是很清楚,我很抱歉,这对我来说是一个复杂的概念。

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:5)

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

只有某些消息(例如WM_COMMANDWM_NOTIFY)会识别发送它们的控件。这些消息被发送到控件的父窗口。这些消息包含子控件HWND。父母可以检索孩子的Control*并将信息转发给她。

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

尝试类似这样的事情(这非常粗糙,在构建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);

答案 1 :(得分:5)

通常的方法是反映消息,例如WM_COMMANDWM_NOTIFY,这些消息会发送到控件的父窗口,返回给发送它们的控件。这是可能的,因为这些消息标识发件人(每个都以独特的方式,因此请检查文档)。

有许多方法可以将C ++对象指针与窗口关联起来:

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

  • 窗口属性。
    SetPropGetProp函数。

  • 窗口对象词 SetWindowLongPtr。需要确保已经分配了空间。

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

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