Win32:更多"object oriented"窗口消息处理系统

Win32: More "object oriented" window message handling system

本文关键字:窗口 消息处理系统 oriented object 更多 Win32      更新时间:2023-10-16

在Win32 API中,一个窗口有一个指针,指向一个用户定义的WndProc函数的版本,用来处理它的消息。

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

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

我试图用HWND键和"MyWindowClass"项目创建一个c++地图,当我创建MyClass的对象时,我向地图添加了一对,然后通过HWN寻找MyWindowClass对象。但是问题是在CreateWindowEx调用Win32内部发送WM_CREATE消息到刚刚创建的窗口之后,所以我不能在此消息之前在映射中添加pair,也不能通过将其传递给实例化的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()
{
}

建议:在你的窗口基类中设置WndProc为虚拟的。请求Windows为每个窗口实例分配足够大的内存来存储一个指针(使用cbWndExtra)。当创建一个窗口时,使用SetWindowLongPtr将一个指向你的窗口类对象的指针放入与每个窗口实例相关的额外内存中。在static sWndProc中,使用GetWindowLongPtr检索该指针并调用窗口基类函数virtual WndProc。在我看来,这比拥有一个专门用于调度WndProc调用的整个额外map对象更简洁。

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

编辑:同样,在你的代码中,如果你没有在WndProc中处理Windows消息,你的代码将为这个消息调用DefWindowProc两次:第一次在switch子句中的成员函数中,第二次在static sWndProc中。DefWindowProc不应该在同一条消息上被调用两次。

编辑:对不起,我之前不知怎么错过了你的实际问题,我以为你的帖子是关于设计的,而不是关于WM_CREATE的。为了以统一的方式处理WM_NCCREATEWM_NCCALCSIZEWM_CREATE,您可以在调用CreateWindowEx时将lpParam设置为指向窗口类对象的指针。该参数将传递给static sWndProc,作为CREATESTRUCTWM_NCCREATEWM_CREATE的成员。你可以在static sWndProc中处理WM_NCCREATE(第一个要发送的消息),获得指向对象的指针,使用SetWindowLongPtr将其放入窗口实例的额外内存中,然后使用它来调用成员函数WndProc(如果从其构造函数调用CreateWindowEx,请注意调用未完全创建的窗口类对象)。这样,您就不需要担心程序中其他地方的"低级"Windows消息调度。当然,您仍然需要成员函数WndProc来将消息分派给实际的函数调用。