Win32:更多"面向对象"窗口消息处理系统

时间:2011-11-25 20:17:25

标签: c++ oop winapi window

在Win32 API中,窗口具有指向用户定义版本的WndProc函数的指针,该函数处理其消息。

有一些方法可以通过MFC消息映射等解决方案来覆盖这种低级别机制。

在我的小应用程序中,我正在寻找一种方法来使用面向对象的解决方案来封装这种低级别的东西。

我尝试使用HWND键和“MyWindowClass”项创建C ++映射,当我创建MyClass的对象时,我在地图中添加了一对,然后通过HWN查找MyWindowClass对象。但问题是在CreateWindowEx调用Win32内部向刚刚创建的窗口发送WM_CREATE消息后,我无法在此消息之前在地图中添加对,并且无法通过将WM_CREATE传递给对象实例化的WndProc来控制WM_CREATE。

代码是:

#ifndef NOTIFYWINDOW_H
#define NOTIFYWINDOW_H

#include "Bacn.h"

class NotifyWindow
{
private:

    HWND m_hWnd;

    Gdiplus::Graphics* m_graphics;

protected:

    static std::map<HWND, NotifyWindow*> s_NotifyWindows;

    static LRESULT CALLBACK s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    static void s_WndMessageLoop();

    LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);

    void Initialize();

    void OnPaint();

    void OnCreate();

public: 

    NotifyWindow();

    ~NotifyWindow();
};

#endif //NOTIFYWINDOW_H

及其实施:

#include "NotifyWindow.h"

using namespace Gdiplus;
using namespace std;

map<HWND*, NotifyWindow> NotifyWindow::s_NotifyWindows;

LRESULT CALLBACK NotifyWindow::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    map<HWND, NotifyWindow*>::iterator search = s_NotifyWindows.find(hWnd);
    if (search != s_NotifyWindows.end())
    {
        search->second->WndProc(uMsg, wParam, lParam);
    }

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

void NotifyWindow::s_WndMessageLoop()
{

}

LRESULT NotifyWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        OnCreate();
        break;
    case WM_PAINT:
        OnPaint();
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_SIZE:
        break;
    case WM_SETFOCUS:
        break;
    case WM_KILLFOCUS:
        break;
    case WM_MOUSEMOVE:
        break;
    default:
        return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

void NotifyWindow::Initialize()
{
    WNDCLASSEX wc;

    const wchar_t *className = L"BacnNotifyWindowClass";
    const wchar_t *windowName = L"BacnNotifyWindow";

    HINSTANCE hInstance = GetModuleHandle(NULL);

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpszClassName = className;
    wc.lpfnWndProc = NotifyWindow::s_WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = NULL;
    wc.hIconSm = NULL;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;

    RegisterClassEx(&wc);

    DWORD dwExtStyle = WS_EX_TOPMOST;
    DWORD dwStyle = WS_POPUP | WS_SYSMENU;  

    m_hWnd = CreateWindowEx(
        dwExtStyle,
        className,
        windowName,
        dwStyle,
        300,
        300,
        100,
        100,
        NULL,
        NULL,
        hInstance,
        NULL);  

    s_NotifyWindows.insert(pair<HWND, NotifyWindow*>(m_hWnd, this));

    ShowWindow(m_hWnd, SW_SHOW);
}

NotifyWindow::NotifyWindow()
{
    Initialize();   
}

NotifyWindow::~NotifyWindow()
{

}

void NotifyWindow::OnPaint()
{

}

void NotifyWindow::OnCreate()
{

}

1 个答案:

答案 0 :(得分:4)

建议:在窗口基类中使WndProc为虚拟。请求Windows为每个足以存储指针的窗口实例分配额外的内存(使用cbWndExtra)。创建窗口时,使用SetWindowLongPtr将指向windows类对象的指针放入与每个窗口实例关联的额外内存中。在static sWndProc中,使用GetWindowLongPtr检索此指针并调用窗口基类函数virtual WndProc。在我看来,它比一个专门用于调度WndProc调用的额外地图对象更简洁。

编辑:另外,您确实意识到,在您的代码中,每次创建窗口类的对象时,您都会尝试注册Windows窗口类?如果你只创建一个窗口,这在技术上很好,我猜,但即使这样,它也是容易出错的设计。 Windows窗口类只应注册一次,而不是每次使用此Windows窗口类创建窗口时。

编辑:此外,在您的代码中,如果您未在WndProc中处理Windows消息,则代码将为此消息调用DefWindowProc两次:第一次在{{1}内的成员函数中}子句,第二次在switch。不应该在同一条消息上调用static sWndProc两次。

编辑:对不起,我在某种程度上错过了你的实际问题,我认为你的帖子是关于设计的,而不是关于DefWindowProc。为了以统一的方式处理WM_CREATEWM_NCCREATEWM_NCCALCSIZE,您可以在调用WM_CREATE时将lpParam设置为再次指向对象的指针你的窗口类。此参数将作为CreateWindowEx成员static sWndProcCREATESTRUCT传递给您的WM_NCCREATE。您可以在WM_CREATE内处理WM_NCCREATE(要发送的第一条消息),获取指向对象的指针,使用static sWndProc将其放入窗口实例额外内存中,然后使用它调用成员函数SetWindowLongPtr(如果从构造函数中调用WndProc,请注意调用未完全创建的windows类对象)。这样,您无需担心程序中其他任何位置的“低级”Windows消息调度。当然,您仍然需要您的成员函数CreateWindowEx将消息分派给实际的函数调用。