MDI object oriented approach, retrieving this pointer from MDICREATESTRUCT

≡放荡痞女 提交于 2019-12-08 03:32:51

问题


I'm trying to implement MDI Child window base class, acording to this reference details on remarks say

When the MDI client window creates an MDI child window by calling CreateWindow, the system sends a WM_CREATE message to the created window. The lParam member of the WM_CREATE message contains a pointer to a CREATESTRUCT structure. The lpCreateParams member of this structure contains a pointer to the MDICREATESTRUCT structure passed with the WM_MDICREATE message that created the MDI child window.

I'm using following simple base template class to create MDI child window, implementing above statements to retirieve this pointer. (added few comments)

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;
}

Here is how I create an actual instance of the MDI child window object, the class inherits above templated base class

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);
    }
}

When the MDI child is created I got exception in basemdi.h saying read access violation for pThis pointer.

I'm folowing msdn instructions on how to retrieve pointer to MDICREATESTRUCT that is passed to CreateWindowEx, which holds pointer to this in lpCreateParams but for some reason the retrieved pointer doesn't work.

Do you see what could be cause for this?


回答1:


I managed to solve the problem.

the issue is that MSDN is talking about posibility to use CreateWindow or CreateWindowEx which won't work for child MDI windows, one need to use CreateMDIWindow instead!

here is the working Initialize function from above example, the rest of the code from above is fine:

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;
}



回答2:


I was recently using MDI with an OOP approach for fun and this works for me:

    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 ) ) );


来源:https://stackoverflow.com/questions/55547616/mdi-object-oriented-approach-retrieving-this-pointer-from-mdicreatestruct

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!