问题
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