MDI объектно-ориентированный подход, извлекающий этот указатель из MDICREATESTRUCT - PullRequest
1 голос
/ 06 апреля 2019

Я пытаюсь реализовать базовый класс дочернего окна MDI, согласно этой ссылке подробности в комментариях говорят

Когда окно клиента MDI создает дочернее окно MDI, вызывая CreateWindow, система отправляет сообщение WM_CREATE созданному окно. Член lParam сообщения WM_CREATE содержит указатель в структуру CREATESTRUCT. Член lpCreateParams этого структура содержит указатель на переданную структуру MDICREATESTRUCT с сообщением WM_MDICREATE, которое создало дочернее окно MDI.

Я использую следующий простой базовый шаблонный класс для создания дочернего окна MDI, реализуя приведенные выше операторы для повторного получения этого указателя. (добавлено несколько комментариев)

basemdi.h

#pragma once
#include <Windows.h>

template <typename DERIVED_TYPE>
class BaseMDI
{
public:
    inline HWND GetHandle() const;
    BOOL Initialize(
        PCTSTR szWindowName,
        HWND hParent,
        DWORD dwExStyle = WS_EX_MDICHILD, // THIS IS MDI WINDOW
        DWORD dwStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int width = CW_USEDEFAULT,
        int height = CW_USEDEFAULT,
        HMENU hMenu = nullptr,
        HINSTANCE hInstance = GetModuleHandle(nullptr),
        LPVOID lpCreate = nullptr
    );

protected:
    virtual PCTSTR ClassName() const = 0;
    static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    HWND m_hwnd = nullptr;
};

template<typename DERIVED_TYPE>
BOOL BaseMDI<DERIVED_TYPE>::Initialize(
    PCTSTR szWindowName,
    HWND hParent,
    DWORD dwExStyle,
    DWORD dwStyle,
    int x,
    int y,
    int width,
    int height,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpCreate)
{
    UNREFERENCED_PARAMETER(lpCreate);

    WNDCLASSEX wc = { };

    wc.cbClsExtra = 0;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbWndExtra = 0;
    wc.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hIcon = nullptr;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = ClassName();
    wc.lpszMenuName = nullptr;
    wc.style = CS_VREDRAW | CS_HREDRAW;

    RegisterClassEx(&wc);

    MDICREATESTRUCT mdicreate;

    // ASSIGN POINTER TO THIS SO THAT WE LATER RETRIEVE IT
    mdicreate.lParam = (LPARAM) this;

    mdicreate.szClass = ClassName();
    mdicreate.szTitle = TEXT("Hello");
    mdicreate.hOwner = hInstance;
    mdicreate.x = CW_USEDEFAULT;
    mdicreate.y = CW_USEDEFAULT;
    mdicreate.cx = CW_USEDEFAULT;
    mdicreate.cy = CW_USEDEFAULT;
    mdicreate.style = dwStyle;

    m_hwnd = CreateWindowEx(
        dwExStyle,
        ClassName(),
        szWindowName,
        dwStyle,
        x, y,
        width,
        height,
        hParent,
        hMenu,
        hInstance,
        &mdicreate // PASS ADDRESS OF MDICREATESTRUCT
    );

    return m_hwnd ? TRUE : FALSE;
}
// following base class WndProc calls derived class procedure, 
// I retrive this pointer here to call correct procedure, but pThis is read acess vioalaiton
template<typename DERIVED_TYPE>
inline LRESULT BaseMDI<DERIVED_TYPE>::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    DERIVED_TYPE* pThis = nullptr;

    if (uMsg == WM_CREATE)
    {
        // RETRIEVE POINTER TO THIS
        CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
        MDICREATESTRUCT* pMdi = reinterpret_cast<MDICREATESTRUCT*>(pCreate->lpCreateParams);
        pThis = reinterpret_cast<DERIVED_TYPE*>(pMdi->lParam);
        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));

        pThis->m_hwnd = hWnd;
    }
    else
    {
        pThis = reinterpret_cast<DERIVED_TYPE*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    }

    if (pThis)
    {
        // EXCEPTION IS THROWN HERE
        return pThis->HandleMessage(uMsg, wParam, lParam);
    }
    else
    {
        return DefMDIChildProc(hWnd, uMsg, wParam, lParam);
    }
}

template <typename DERIVED_TYPE>
HWND BaseMDI<DERIVED_TYPE>::GetHandle() const
{
    return m_hwnd;
}

Вот как я создаю фактический экземпляр объекта дочернего окна MDI, класс наследует выше шаблонного базового класса

mdiwindow.h

#pragma once
#include "mdibase.h"

class MDI : public BaseMDI<MDI>
{
public:
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;

private:
    inline virtual PCTSTR ClassName() const override;
};

PCTSTR MDI::ClassName() const
{
    return TEXT("MDIWindow");
}

mdiwindow.cpp

 #include "mdiwindow.h"


LRESULT MDI::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    default:
        return DefMDIChildProc(m_hwnd, uMsg, wParam, lParam);
    }
}

Когда создается дочерний MDI-файл, в basemdi.h я получил исключение, говорящее нарушение прав чтения для pThis указателя.

Я следую инструкциям msdn о том, как извлечь указатель на MDICREATESTRUCT, который передается в CreateWindowEx, который содержит указатель на this в lpCreateParams, но по какой-то причине извлеченный указатель не работает.

Вы видите, что может быть причиной для этого?

Ответы [ 2 ]

0 голосов
/ 22 мая 2019

Я недавно использовал MDI с подходом ООП для развлечения, и это работает для меня:

    MDICREATESTRUCT mcs;
    mcs.szTitle = WindowText();
    mcs.szClass = ClassName();
    mcs.hOwner = GetModuleHandle( nullptr );
    mcs.x = x;
    mcs.y = y;
    mcs.cx = width;
    mcs.cy = height;
    mcs.style = dwStyle;
    mcs.lParam = reinterpret_cast< LPARAM >( this );

    mSubWnd = reinterpret_cast< HWND >( SendMessage( hParent, WM_MDICREATE, 0, reinterpret_cast< LONG >( &mcs ) ) );
0 голосов
/ 06 апреля 2019

Мне удалось решить проблему.

проблема в том, что MSDN говорит о возможности использования CreateWindow или CreateWindowEx, который не будет работать для дочерних окон MDI, нужно использовать CreateMDIWindow взамен!

вот рабочая функция Initialize из вышеприведенного примера, остальная часть кода из выше подходит:

template<typename DERIVED_TYPE>
BOOL BaseMDI<DERIVED_TYPE>::Initialize(
    PCTSTR szWindowName,
    HWND hParent,
    DWORD dwExStyle,
    DWORD dwStyle,
    int x,
    int y,
    int width,
    int height,
    HMENU hMenu,
    HINSTANCE hInstance,
    LPVOID lpCreate)
{
    UNREFERENCED_PARAMETER(lpCreate);

    WNDCLASSEX wc = { };

    wc.cbClsExtra = 0;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbWndExtra = 0;
    wc.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hIcon = nullptr;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = ClassName();
    wc.lpszMenuName = nullptr;
    wc.style = CS_VREDRAW | CS_HREDRAW;

    if (!RegisterClassEx(&wc)) abort();

    m_hwnd = CreateMDIWindow(
        ClassName(),
        szWindowName,
        dwStyle,
        x, y,
        width,
        height,
        hParent,
        hInstance,
        (LPARAM)this);

    if (!m_hwnd) abort();

    return m_hwnd ? TRUE : FALSE;
}
...