Win32: более «объектно-ориентированная» оконная система обработки сообщений - PullRequest
1 голос
/ 26 ноября 2011

В Win32 API окно имеет указатель на пользовательскую версию функции WndProc, которая обрабатывает его сообщения.

Есть несколько способов покрыть этот механизм низкого уровня решением, таким как карта сообщений MFC и т. Д.

В моем очень маленьком приложении я ищу способ инкапсулировать эту низкоуровневую вещь в объектно-ориентированное решение.

Я попытался создать карту C ++ с ключом HWND и элементом «MyWindowClass», и когда я создал объект MyClass, я добавил пару на карту, а затем искал объект MyWindowClass с помощью HWN. Но проблема в том, что CreateWindowEx с именем Win32 внутренне отправляет сообщение WM_CREATE в только что созданное окно, поэтому я не могу добавить пару в карту перед этим сообщением и не могу управлять WM_CREATE, передавая его экземпляру объекта WndProc.

Код:

#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 Ответ

4 голосов
/ 26 ноября 2011

Предложение: сделайте WndProc виртуальным в вашем базовом классе окна.Попросите Windows выделить дополнительную память для каждого экземпляра окна, достаточно большого для хранения указателя (используйте cbWndExtra).При создании окна поместите указатель на ваш объект класса Windows в эту дополнительную память, связанную с каждым экземпляром окна, используя SetWindowLongPtr.В вашем static sWndProc получите этот указатель, используя GetWindowLongPtr, и вызовите функцию базового класса окна virtual WndProc.На мой взгляд, это более изящный способ, чем целый дополнительный объект карты, предназначенный для отправки WndProc вызовов.

РЕДАКТИРОВАТЬ: Плюс, вы понимаете, в своем коде вы пытаетесь каждый раз регистрировать класс окна WindowsВы создаете объект своего класса окна?Думаю, если вы создадите только одно окно, это технически нормально, но даже в этом случае он подвержен ошибкам.Класс окна Windows должен быть зарегистрирован только один раз, а не каждый раз, когда вы создаете окно с этим классом окна Windows.

РЕДАКТИРОВАТЬ: Кроме того, в вашем коде, если вы не обрабатываете сообщение Windows в вашем WndProc, выкод вызовет DefWindowProc дважды для этого сообщения: первый раз в функции-члене в предложении switch, второй раз в static sWndProc.DefWindowProc не следует вызывать дважды для одного и того же сообщения.

РЕДАКТИРОВАТЬ: Извините, я как-то пропустил ваш фактический вопрос, я думал, что ваш пост был о дизайне, а не о WM_CREATE.Чтобы обрабатывать WM_NCCREATE, WM_NCCALCSIZE или WM_CREATE единообразным способом, вы можете установить lpParam в вызове на CreateWindowEx, чтобы снова указывать на объект вашего класса окна.Этот параметр будет передан вашему static sWndProc как член CREATESTRUCT с WM_NCCREATE и WM_CREATE.Вы можете обработать, скажем, WM_NCCREATE (первое отправляемое сообщение) внутри static sWndProc, получить этот указатель на ваш объект, использовать SetWindowLongPtr, чтобы поместить его в дополнительную память экземпляра окна, а затем использовать его для вызова функции-члена WndProc (просто будьте осторожны с вызовом не полностью созданного объекта вашего класса Windows, если вы вызываете CreateWindowEx из его конструктора).Таким образом, вам не нужно беспокоиться о «низкоуровневой» отправке сообщений Windows где-либо еще в вашей программе.Вам, конечно, все равно понадобится ваша функция-член WndProc для отправки сообщений на реальные вызовы функций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...