I have three classes...Base, Derived 1, and Derived 2. The base class holds a static LONG( this * ) that it uses in a static function to handle window messages. The problem that I'm having is that when I declare multiple derived classes, the static LONG within the base class is changed upon the second derived class declaration...here is implementation:
BaseDialog.h:
class CBaseDialog;
typedef void(CBaseDialog::*fpMessageHandler)(HWND hDlg,WPARAM wParam,LPARAM lParam);
struct t_MessageEntry
{
fpMessageHandler MsgHandler;
};
/////////////////////////////// MACROS
#define IMPLEMENT_MESSAGE_HANDLER(base,derived) void derived::AddHandler(UINT MessageId, void(derived::*Handler)(HWND hDlg,WPARAM wParam,LPARAM lParam))\
{\
AddMessageHandler(MessageId, (void(base::*)(HWND hDlg,WPARAM wParam,LPARAM lParam))Handler);\
}\
#define DECLARE_MESSAGE_HANDLER(derived) void AddHandler(UINT MessageId, void(derived::*Handler)(HWND hDlg,WPARAM wParam,LPARAM lParam));\
void HandleManager(void);\
#define BEGIN_MESSAGE_MAP(derived) void derived::HandleManager(void) {
#define ADD_MESSAGE_HANDLER(message,handler) AddHandler(message, handler);
#define END_MESSAGE_MAP() }
#define ENABLE_MESSAGE_MAP() HandleManager();
class CBaseDialog
{
public:
std::map<UINT,t_MessageEntry> m_MessageMap;
std::map<UINT,t_MessageEntry>::iterator m_MessageMapIterator;
CBaseDialog(int nResId, HWND hParent=NULL);
virtual ~CBaseDialog();
int DoModal(void);
static BOOL CALLBACK DialogProcStatic(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
void OnOK(void);
void OnCancel(void);
void AddMessageHandler(UINT MessageId, void(CBaseDialog::*Handler)(HWND hDlg,WPARAM wParam,LPARAM lParam));
protected:
int m_nResId;
HWND m_hParent;
static HWND m_hWindow;
static long m_lSaveThis;
};
BaseDialog.cpp:
HWND CBaseDialog::m_hWindow = NULL;
long CBaseDialog::m_lSaveThis = 0; // Changes on second declaration of derived class
CBaseDialog::CBaseDialog(int nResId, HWND hParent)
{
m_lSaveThis = (long)this; /// store this pointer
m_nResId = nResId;
m_hParent = hParent;
}
CBaseDialog::~CBaseDialog()
{
m_hWindow = NULL;
m_lSaveThis = 0;
}
int CBaseDialog::DoModal(void)
{
HWND hWnd = CreateDialog( GetModuleHandle( NULL ), MAKEINTRESOURCE( m_nResId ), m_hParent, ( DLGPROC )DialogProcStatic );
return 0;
}
void CBaseDialog::AddMessageHandler(UINT MessageId, void(CBaseDialog::*MsgHandler)(HWND hDlg,WPARAM wParam,LPARAM lParam))
{
t_MessageEntry MessageEntry;
MessageEntry.MsgHandler = MsgHandler;
m_MessageMap.insert(std::map<UINT,t_MessageEntry>::value_type(MessageId, MessageEntry)); /// insert key & data to map
}
BOOL CALLBACK CBaseDialog::DialogProcStatic(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(m_hWindow == NULL)
{
m_hWindow = hDlg;
}
CBaseDialog *pThis = (CBaseDialog*)m_lSaveThis; /// typecast stored this-pointer to CBaseDialog pointer
return pThis->DialogProc( hDlg, uMsg, wParam, lParam );
}
BOOL CALLBACK CBaseDialog::DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
m_MessageMapIterator = m_MessageMap.find(message); /// find message entry by key
if(m_MessageMapIterator == m_MessageMap.end()) /// check if message entry available
{
return 0;
}
else
{
t_MessageEntry MessageEntry = (*m_MessageMapIterator).second; /// dereference iterator and get message entry
void (CBaseDialog::*MessageHandler)(HWND hDlg,WPARAM wParam,LPARAM lParam);
MessageHandler = MessageEntry.MsgHandler;
(this->*MessageHandler)(hDlg, wParam, lParam); /// execute function
return 0;
}
}
void CBaseDialog::OnOK(void)
{
EndDialog(m_hWindow, IDOK);
}
void CBaseDialog::OnCancel(void)
{
EndDialog(m_hWindow, IDCANCEL);
}
Outliner.h:
#include "BaseDialog.h"
class COutlinerDlg : public CBaseDialog
{
public:
COutlinerDlg( int nResId, HWND hParent=NULL );
virtual ~COutlinerDlg();
void Initialize( LPCWSTR strRootName )
{
m_strRootName = strRootName;
}
public:
VOID Resize( RECT rc );
HWND GetHWND(){ return m_hWindow; }
HWND GetTREEDLG(){ return m_hTreeDlg; }
BOOL GetVisible(){ return m_bVisible; }
VOID SetVisible( BOOL b ){ m_bVisible = b; }
BOOL GetDragging(){ return m_bDragging; }
VOID SetDragging( BOOL b ){ m_bDragging = b; }
VOID SetParentHWND( HWND hWnd ){ m_hParent = hWnd; }
HWND GetParentHWND(){ return m_hParent; }
BOOL Show( DWORD dwFlags ){ return ShowWindow( m_hWindow, dwFlags ); }
HRESULT BuildOutlinerFromDirectory( LPCWSTR rootName, LPCWSTR directory );
HRESULT BuildChildDirectory( LPCWSTR child );
protected:
void On_WM_INITDIALOG( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_COMMAND( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_LBUTTONDOWN( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_LBUTTONUP( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_MOUSEMOVE( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_PAINT( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_SIZE( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_CLOSE( HWND hDlg, WPARAM wParam, LPARAM lParam );
DECLARE_MESSAGE_HANDLER(COutlinerDlg);
private:
// Tree Root name
LPCWSTR m_strRootName;
// Directory
LPCWSTR m_strDirectory;
// Dialog Dimensions
RECT m_rcDlg;
TV_ITEM m_tvi;
TV_INSERTSTRUCT m_tvinsert; // struct to config out tree control
HTREEITEM m_hTISelected;
HTREEITEM m_hTIParent; // Tree item handle
HTREEITEM m_hTIBefore; // .......
HTREEITEM m_hTIRoot; // .......
HIMAGELIST m_hImageList; // Image list array hadle
bool m_bSelected;
// for drag and drop
HWND m_hTreeDlg;
HTREEITEM m_hTIHitTarget;
TVHITTESTINFO m_tvht;
POINTS m_ptsPos;
bool m_bDragging;
bool m_bVisible;
// for lable editing
HWND m_hEdit;
};
Outliner.cpp:
#include "Outliner.h"
IMPLEMENT_MESSAGE_HANDLER( CBaseDialog, COutlinerDlg )
BEGIN_MESSAGE_MAP( COutlinerDlg )
ADD_MESSAGE_HANDLER( WM_INITDIALOG, &COutlinerDlg::On_WM_INITDIALOG )
ADD_MESSAGE_HANDLER( WM_COMMAND, &COutlinerDlg::On_WM_COMMAND )
ADD_MESSAGE_HANDLER( WM_NOTIFY, &COutlinerDlg::On_WM_NOTIFY )
ADD_MESSAGE_HANDLER( WM_LBUTTONDOWN, &COutlinerDlg::On_WM_LBUTTONDOWN )
ADD_MESSAGE_HANDLER( WM_LBUTTONUP, &COutlinerDlg::On_WM_LBUTTONUP )
ADD_MESSAGE_HANDLER( WM_MOUSEMOVE, &COutlinerDlg::On_WM_MOUSEMOVE )
ADD_MESSAGE_HANDLER( WM_PAINT, &COutlinerDlg::On_WM_PAINT )
ADD_MESSAGE_HANDLER( WM_CLOSE, &COutlinerDlg::On_WM_CLOSE )
END_MESSAGE_MAP( )
COutlinerDlg::COutlinerDlg( int nResId, HWND hParent ) : CBaseDialog( nResId, hParent )
{
ENABLE_MESSAGE_MAP( );
m_hTISelected = m_hTIParent = m_hTIBefore = m_hTIRoot = m_hTIHitTarget = NULL;
m_hImageList = NULL;
m_bSelected = m_bDragging = false;
m_bVisible = true;
m_hTreeDlg = NULL;
ZeroMemory( &m_tvi, sizeof( TV_ITEM ) );
ZeroMemory( &m_tvinsert, sizeof( TV_INSERTSTRUCT ) );
ZeroMemory( &m_tvht, sizeof( TVHITTESTINFO ) );
ZeroMemory( &m_ptsPos, sizeof( POINTS ) );
}
COutlinerDlg::~COutlinerDlg( )
{
m_hWindow = NULL;
m_lSaveThis = 0;
}
void COutlinerDlg::On_WM_INITDIALOG( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
...
}
This code is from a demo I found online at code project I think...
Can I instance the base class so that I do not overwrite the static LONG when declaring a new instance of Outliner?
Even if you instantiate the base class, the static long m_lSaveThis; will still change its value when you instantiate a new Outliner.
The reason:
Since, m_lSaveThis is static, it has only one copy in memory, and this code in your Base class constructor m_lSaveThis = (long)this; will be invoked for every instance of CBaseDialog or COutlinerDlg because COutlinerDlg inherits from CBaseDialog so it also invokes its constructor. With this code, m_lSaveThis will only point to the latest instance that you created whether the base class or the derived class
Here you are using static long m_lSaveThis so static declares are common for all instance of the class (it is class level not instance level). So why you use static? I think your requirement can reach if you declare m_lSaveThis without static.
protected:
long m_lSaveThis;
CBaseDialog::CBaseDialog(int nResId, HWND hParent)
{
m_lSaveThis = (long)this;
}
There is my code, declare a static map object to handle the class instance and window handle. The bellow header file show the sample code:
CDialogBase
{
...
static BOOL CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static std::map<HWND, CDialogBase*> m_mapInstance;
}
And implement the DialogProc like this:
BOOL CALLBACK CDialogBase::DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CDialogBase * pThis = NULL;
std::map<HWND, CDialogBase*>::iterator it = m_mapInstance.find(hwnd);
if (it != m_mapInstance.end())
{
pThis = m_mapInstance[hwnd];
}
switch (msg)
{
case WM_INITDIALOG:
{
if (pThis != NULL)
{
m_mapInstance.erase(hwnd);
pThis = NULL;
}
if (lParam == NULL)
{
return FALSE; //Should start dialog by DialogBoxParam and lParam must not be null.
}
pThis = (CDialogBase*)lParam;
m_mapInstance.insert(std::map<HWND, CDialogBase*>::value_type(hwnd, pThis));
pThis->OnInitDialog();
}
break;
case WM_DESTROY:
{
if (pThis != NULL)
{
pThis->OnDestroy();
m_mapInstance.erase(hwnd);
}
}
default:
break;
}
if(pThis != NULL)
return pThis->OnDefaultDialogProc(msg, wParam, lParam); //Must implement this function and default return FALSE.
else
return FALSE;
}
And, show the dialog:
UINT CDialogBase::DoModal(HINSTANCE hInst, HWND hParent, UINT nDlgID)
{
m_nDialogResourceID = nDlgID;
return ::DialogBoxParam(hInst, MAKEINTRESOURCE(nDlgID),
hParent, DialogProc, (LPARAM) this);
}
I hope this is helpful for you.
Related
I'm trying to make an ImGui DX9 Window. However I get an error when I try to reference the wndproc function on the WNDCLASSEX structure.
When I try to pre-define the wndproc function as static I get an error: cannot overload static and non-static member functions with the same parameter types.
I already tried to call the function directly like so: menu::wnd_proc.
When I try to call the function without static I get this error on the function: a value of type "LRESULT (__stdcall menu::*)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)" cannot be used to initialize an entity of type "WNDPROC".
Here's my function
#include "ui.hpp"
bool menu::render( )
{
WNDCLASSEX wc = {
sizeof( WNDCLASSEX ),
CS_CLASSDC,
menu::wnd_proc,
0L,
0L,
GetModuleHandle( NULL ),
NULL,
NULL,
NULL,
NULL,
_T( "ImGui Example" ),
NULL
};
...
}
Here's the ui.hpp header file
#pragma once
class menu
{
public:
bool render( );
private:
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
D3DPRESENT_PARAMETERS g_d3dpp = {};
static LRESULT WINAPI wnd_proc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
private:
...
LRESULT WINAPI wnd_proc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
if ( ImGui_ImplWin32_WndProcHandler( hWnd, msg, wParam, lParam ) )
return true;
switch ( msg )
{
case WM_SIZE:
if ( g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED )
{
g_d3dpp.BackBufferWidth = LOWORD( lParam );
g_d3dpp.BackBufferHeight = HIWORD( lParam );
menu::reset_device( );
}
return 0;
case WM_SYSCOMMAND:
if ( ( wParam & 0xfff0 ) == SC_KEYMENU )
return 0;
break;
case WM_DESTROY:
::PostQuitMessage( 0 );
return 0;
}
return ::DefWindowProc( hWnd, msg, wParam, lParam );
}
};
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am developing a Win32 dialog box wrapper.
.h file
class dlg {
static INT_PTR CALLBACK DlgProcTmp(HWND hwnd,
UINT wm, WPARAM wp, LPARAM lp);
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
bool ismodal;
protected:
HWND hwndDlg;
int id;
public:
virtual void oncreate(const widget &w) { }
virtual void oncmd(const widget &w, int code, int idCntrl, HWND hwnd) { }
virtual void onclose(const widget &w) { }
dlg() { }
dlg(int id);
INT_PTR domodal(HWND hwndOwner = nullptr);
widget domodeless(HWND hwndOwner = nullptr, int cmdshow = SW_SHOW);
};
.cpp file
INT_PTR dlg::DlgProcTmp(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
dlg *This;
if (wm == WM_INITDIALOG) {
This = (dlg *) lp;
setwinlong(hwnd, DWLP_USER, This);
This->oncreate(widget(hwnd));
return TRUE;
}
if ((This = getwinlong<dlg *>(hwnd, DWLP_USER)) != nullptr)
return This->DlgProc(hwnd, wm, wp, lp);
return FALSE;
}
INT_PTR dlg::DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
this->hwndDlg = hwnd;
switch (wm) {
case WM_COMMAND:
this->oncmd(widget(hwnd), HIWORD(wp), LOWORD(wp), (HWND) lp);
if (this->ismodal)
EndDialog(hwnd, LOWORD(wp));
else
DestroyWindow(hwnd);
return TRUE;
case WM_DESTROY:
this->onclose(widget(hwnd));
return TRUE;
}
return FALSE;
}
The dialog is created in domodal with a DialogBoxParam call. The last argument is the this pointer, which I then retrieve from the lparam of the WM_INITDIALOG message. To allow this to be used between different messages, I save this with the HWND in WM_INITDIALOG. However, whenever a message not WM_INITDIALOG arrives, and I get the this pointer with GetWindowLongPtr, it returns a garbage dlg class, whose vtable is corrupt. I use the vtable to call the correct handler function. As a result, my code crashes on the first line of the WM_COMMAND handler. Here is what the debugger shows as the value of this:
Why is GetWindowLongPtr returning garbage?
BTW, getwinlong is a wrapper for GetWindowLongPtr, and setwinlong is a wrapper for SetWindowLongPtr. Here are their implementations:
template <class TYPE> static TYPE getwinlong(HWND hwnd, int idx)
{
return (TYPE) GetWindowLongPtr(hwnd, idx);
}
template <class TYPE> static void setwinlong(HWND hwnd, int idx, TYPE val)
{
SetWindowLongPtr(hwnd, idx, (LONG_PTR) val);
}
I have seen the numerous posts about how GetWindowLongPtr will fail on Win64, if you cast to LONG instead of LONG_PTR, like https://blogs.msdn.microsoft.com/oldnewthing/20131226-00/?p=2263. I believe that my problem is different.
Edit: #andlabs wanted the code that creates the dialog:
INT_PTR dlg::domodal(HWND hwndOwner)
{
this->ismodal = true;
return DialogBoxParam(gethinst(hwndOwner),
MAKEINTRESOURCE(id), hwndOwner, dlg::DlgProcTmp,
(LPARAM) this);
}
I pieced together the code that you've shown us, and was able to create a working sample:
dlg.h
#pragma once
#include <Windows.h>
class dlg {
static INT_PTR CALLBACK DlgProcTmp(HWND hwnd,
UINT wm, WPARAM wp, LPARAM lp);
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
bool ismodal;
protected:
HWND hwndDlg;
int id;
public:
//virtual void oncreate(const widget &w) { }
//virtual void oncmd(const widget &w, int code, int idCntrl, HWND hwnd) { }
//virtual void onclose(const widget &w) { }
dlg() { }
dlg(int id) : id(id) { }
INT_PTR domodal(HWND hwndOwner = nullptr);
//widget domodeless(HWND hwndOwner = nullptr, int cmdshow = SW_SHOW);
};
dlg.cpp
#include "dlg.h"
template <class TYPE> static TYPE getwinlong(HWND hwnd, int idx)
{
return (TYPE) GetWindowLongPtr(hwnd, idx);
}
template <class TYPE> static void setwinlong(HWND hwnd, int idx, TYPE val)
{
SetWindowLongPtr(hwnd, idx, (LONG_PTR) val);
}
INT_PTR dlg::DlgProcTmp(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
dlg *This;
if (wm == WM_INITDIALOG) {
This = (dlg *) lp;
setwinlong(hwnd, DWLP_USER, This);
//This->oncreate(widget(hwnd));
return TRUE;
}
if ((This = getwinlong<dlg *>(hwnd, DWLP_USER)) != nullptr)
return This->DlgProc(hwnd, wm, wp, lp);
return FALSE;
}
INT_PTR dlg::DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
this->hwndDlg = hwnd;
switch (wm) {
case WM_COMMAND:
//this->oncmd(widget(hwnd), HIWORD(wp), LOWORD(wp), (HWND) lp);
if (this->ismodal)
EndDialog(hwnd, LOWORD(wp));
else
DestroyWindow(hwnd);
return TRUE;
case WM_DESTROY:
return TRUE;
}
return FALSE;
}
INT_PTR dlg::domodal(HWND hwndOwner)
{
this->ismodal = true;
return DialogBoxParam(GetModuleHandle(NULL),
MAKEINTRESOURCE(id), hwndOwner, dlg::DlgProcTmp,
(LPARAM) this);
}
main.cpp
int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
dlg myDialog(IDD_DIALOG);
myDialog.domodal();
return 0;
}
You'll notice that there's a fair bit of the code that has been commented out—in particular, the declarations for and calls to the virtual message-handling functions (onXXX) have been omitted. I did this because I didn't have the definition for the widget type, and I didn't think it was germane to your actual problem.
Turns out it must be, since the code works fine so long as those virtual message-handling functions are not called.
I then added in a stub class for widget as follows:
class widget
{
public:
widget(HWND hWnd)
: m_hWnd(hWnd)
{ }
HWND m_hWnd;
};
and uncommented the oncmd function as well as the call to it in the WM_COMMAND handler. Again, the code compiles and works correctly. So I have no idea what problem you're experiencing. It must be in the widget class or some other code you have not shown to us.
There is at least one tweak I would make to the code. Assign the hwndDlg member variable inside of the DlgProcTemp function, rather than waiting until the DlgProc function. So, do this:
INT_PTR dlg::DlgProcTmp(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
dlg *This;
if (wm == WM_INITDIALOG) {
This = (dlg *) lp;
setwinlong(hwnd, DWLP_USER, This);
This->hwndDlg = hwnd;
This->oncreate(widget(hwnd));
return TRUE;
}
if ((This = getwinlong<dlg *>(hwnd, DWLP_USER)) != nullptr)
return This->DlgProc(hwnd, wm, wp, lp);
return FALSE;
}
INT_PTR dlg::DlgProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
switch (wm) {
case WM_COMMAND:
this->oncmd(widget(hwnd), HIWORD(wp), LOWORD(wp), (HWND) lp);
if (this->ismodal)
EndDialog(hwnd, LOWORD(wp));
else
DestroyWindow(hwnd);
return TRUE;
case WM_DESTROY:
return TRUE;
}
return FALSE;
}
Also strongly consider using C++-style casts in preference to C-style casts.
If you are still having a problem getting the code to work, you will have to edit into your question a Minimal, Complete, and Verifiable example, just like I have done here.
I have custom window encapsulated in a class with a static WndProc function to handle messages generated by it. Now I have a child class which implements some of the features in message handling proc differently then the parent class.
For example in the code below, what happens in WM_LBUTTONDOWN in the child class is different then what happens in the parent class.
I thought about polymorphism, but I think it won't work since the ::SetWindowLongPtr() is called from the parent class and "this" pointer passed to belongs to the parent class, correct me if I am wrong.
And if I am wrong and polymorphism will work in this case then too there are some messages which are not handled by the parent class and should be handled in the child class and putting an empty virtual function in the parent class just for that doesn't seems clean, beside its hard to put an empty virtual function for each and every message that window produce just for the sake of if in future it will be used.
There going to be several such child classes each one of them behaving differently for some messages but not all.
So, how should I go about doing it.
parent.cpp
parent::parent()
{
WNDCLASSEX wincl;
wincl.hInstance = hInstance;
wincl.lpszClassName = "parent";
wincl.lpfnWndProc = WndProc;
wincl.style = CS_BYTEALIGNWINDOW;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = 0;
wincl.hIconSm = 0;
wincl.hCursor = ::LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 4;
wincl.hbrBackground = ::CreateSolidBrush( backgroundColor );
::RegisterClassEx ( &wincl );
hwnd = ::CreateWindowEx ( 0, "parent", txt.c_str(), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILD, x, y, width, height, parent, 0, hInstance, 0 ) ;
::SetWindowLongPtr( hwnd , GWLP_USERDATA , ( LONG ) this ) ;
}
LRESULT CALLBACK parent::WndProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
view::parent* panel = reinterpret_cast < view::parent* > ( ::GetWindowLongPtr ( hwnd , GWLP_USERDATA ) );
switch (message)
{
case WM_CREATE:
::SendMessage ( hwnd, WM_SETFONT, ( WPARAM ) panel->hFont, ( LPARAM ) true );
break ;
case WM_COMMAND:
return panel->command ( message, wParam, lParam );
break ;
case WM_LBUTTONDOWN:
return panel->lButton ( message, wParam, lParam );
break;
case WM_RBUTTONDOWN:
return panel->rButton ( message, wParam, lParam );
break;
case WM_ERASEBKGND:
return 1;
break;
case WM_PAINT:
return panel->paint ( );
break ;
default:
return ::DefWindowProc (hwnd, message, wParam, lParam);
}
return 0 ;
};
Thanks.
Try something like this:
class parent
{
private:
// ...
static LRESULT CALLBACK WndProcCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hwnd;
// ...
virtual LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual LRESULT DefWndProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual LRESULT command(WPARAM wParam, LPARAM lParam);
virtual LRESULT lButtonDown(WPARAM wParam, LPARAM lParam);
virtual LRESULT rButtonDown(WPARAM wParam, LPARAM lParam);
virtual LRESULT paint();
// ...
public:
parent();
virtual ~parent();
// ...
};
parent::parent()
{
WNDCLASSEX wincl = {0};
wincl.hInstance = hInstance;
wincl.lpszClassName = "parent";
wincl.lpfnWndProc = WndProcCallback;
wincl.style = CS_BYTEALIGNWINDOW;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hIcon = 0;
wincl.hIconSm = 0;
wincl.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 4;
wincl.hbrBackground = ::CreateSolidBrush(backgroundColor);
::RegisterClassEx(&wincl);
m_hwnd = NULL;
::CreateWindowEx(0, "parent", txt.c_str(), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILD, x, y, width, height, parent, 0, hInstance, this);
}
parent::~parent()
{
if (m_hwnd)
DestroyWindow(m_hwnd);
}
LRESULT CALLBACK parent::WndProcCallback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
parent* panel;
if (message == WM_CREATE)
{
CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT*>(lParam);
panel = static_cast<parent*>(cs->lpCreateParams);
panel->m_hwnd = hwnd;
::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(panel));
}
else
panel = reinterpret_cast<parent*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (panel)
return panel->WndProc(message, wParam, lParam);
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT parent::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
::SendMessage(m_hwnd, WM_SETFONT, (WPARAM) hFont, TRUE);
break ;
case WM_COMMAND:
return command(wParam, lParam);
break ;
case WM_LBUTTONDOWN:
return lButtonDown(wParam, lParam);
break;
case WM_RBUTTONDOWN:
return rButtonDown(wParam, lParam);
break;
case WM_ERASEBKGND:
return 1;
break;
case WM_PAINT:
return paint();
break;
}
return DefWndProc(message, wParam, lParam);
}
LRESULT parent::DefWndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
return ::DefWindowProc(m_hwnd, message, wParam, lParam);
}
LRESULT parent::command(WPARAM wParam, LPARAM lParam)
{
return DefWndProc(WM_COMMAND, wParam, lParam);
}
LRESULT parent::lButtonDown(WPARAM wParam, LPARAM lParam)
{
return DefWndProc(WM_LBUTTONDOWN, wParam, lParam);
}
LRESULT parent::rButtonDown(WPARAM wParam, LPARAM lParam)
{
return DefWndProc(WM_RBUTTONDOWN, wParam, lParam);
}
LRESULT parent::paint()
{
return 0;
}
Not only does this embrace polymorphism and encapsulation, but making WndProc() itself virtual allows derived classes to override behavior for any received message (well, any message received after WM_CREATE that is), especially messages the parent has no concept of:
class child : public parent
{
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
};
LRESULT child::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_SOMETHING)
{
// ...
return 0;
}
return parent::WndProc(message, wParam, lParam);
}
I've been writing a win32 wrapper class, and I've come across a problem: Because each instance of the class has a window, I've enclosed the this pointer in the user info space using SetWindowLongPtrW(), allowing me to call a message handler from the static WndProc function. This works fine: I can call the function. However, when I try to call another member function from the message handler, I get an access violation at 0x00000088
It does compile.
I posted quite a lot, because to be honest I'm not too sure where the problem originates from...
Please feel free to comment/criticize my code in general. Thanks for the help!
Here is the header:
#pragma once
#include <Windows.h>
#include "GlobalDefines.h"
#include "GraphicsWrapper.h"
#include "Keyboard.h"
namespace Startup
{
class GraphicsWrapper;
class WindowsWrapper
{
public:
WindowsWrapper(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow);
~WindowsWrapper();
void EnterMsgLoop(GraphicsWrapper* Gfx);
static LRESULT _stdcall WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
LRESULT _stdcall MessageHandler(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
WNDCLASSEX WndClass;
MSG Message;
RECT Desktop;
RECT Taskbar;
RECT WindowCoordinates;
LPSTR CommandLineArgs;
INT CmdShow;
HINSTANCE TheInstance;
HWND WindowHandle;
void InitializeWndClassEx();
void InitializeWindowHandleHWND();
void ShowWindowOnScreen();
bool GetScreenRect(RECT & Desktop);
bool GetTaskbarRect(RECT& rectTaskbar);
bool GetWindowCoords(RECT& WindowCoordinates);
int GetTaskbarSide();
enum TaskbarSides
{
Top,
Right,
Bottom,
Left
};
void SetFullScreen(bool Enable);
};
static IO::Keyboard * kbd;
}
And this is the relevant part of the implementation. I'll mark where the crash occurs.
void Startup::WindowsWrapper::InitializeWndClassEx()
{
WndClass.hIcon = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.hIconSm = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = WindowProc;
WndClass.hInstance = TheInstance;
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.lpszClassName = L"WindowClassName";
RegisterClassEx(&WndClass);
SetWindowLongPtrW(WindowHandle, GWLP_USERDATA, (long)this);
}
void Startup::WindowsWrapper::SetFullScreen(bool Enable)
{
long style = Enable ? WS_POPUP : WS_OVERLAPPED | WS_SYSMENU;
static RECT windowRect = {};
static bool needRect = true;
if (needRect)
{
GetWindowRect(WindowHandle, &windowRect);
needRect = false;
}
SetWindowLong(WindowHandle, GWL_STYLE, style);
if (Enable)
{
SetWindowPos(WindowHandle, HWND_TOPMOST,
0,0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SWP_SHOWWINDOW);
}
else
{
SetWindowPos(WindowHandle, 0,
windowRect.left,windowRect.top,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
SWP_SHOWWINDOW);
}
}
and
LRESULT CALLBACK Startup::WindowsWrapper::WindowProc
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
WindowsWrapper* ourObjectPtr = NULL;
long thisObject = GetWindowLongW(hWnd, GWLP_USERDATA);
ourObjectPtr = (WindowsWrapper *)( (void*)thisObject);
long Result = ourObjectPtr->MessageHandler(hWnd, message, wParam, lParam);
RET(Result);
}
LRESULT _stdcall Startup::WindowsWrapper::MessageHandler
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0); //Works fine here, but...
break;
case VK_SPACE:
this->SetFullScreen(false); //Crashes here w/ access violation
break;
case VK_SHIFT:
this->SetFullScreen(true); //Or here, w/ the same error.
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
This is the createWindowEx call. Thanks for your help, again.
void Startup::WindowsWrapper::InitializeWindowHandleHWND()
{
WindowHandle = CreateWindowEx(NULL,
L"WindowClassName",
L"WindowTitle"
WS_OVERLAPPED | WS_SYSMENU,
WindowCoordinates.left, WindowCoordinates.top,
WindowCoordinates.right, WindowCoordinates.bottom,
NULL, NULL, TheInstance,
CommandLineArgs);
}
I have some code from a custom dialog handler I wrote quite a while back, which might be of use to you.
Same principle applies for a window but switch the WM_INITDIALOG for WM_CREATE and also replace DWLP_USER with GWLP_USERDATA. The format of the callback is subtley different too. You should be able to salvage almost all of this function though.
LRESULT CALLBACK CDialog::DlgProc( HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
CDialog* pWindow;
if( msg == WM_INITDIALOG ) {
SetWindowLongPtr( hWndDlg, DWLP_USER, (LONG_PTR)lParam );
pWindow = reinterpret_cast<CDialog*>( lParam );
pWindow->m_hWnd = hWndDlg;
} else {
pWindow = reinterpret_cast<CDialog*>( (LPARAM)GetWindowLongPtr( hWndDlg, DWLP_USER ) );
}
if( pWindow != NULL ) {
LRESULT ret = pWindow->OnMessage( msg, wParam, lParam );
if( msg == WM_NCDESTROY ) pWindow->m_hWnd = NULL;
}
return FALSE;
}
I am following a tutorial on MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ff381400(v=vs.85).aspx
Which shows how to make a class to create windows in WINAPI.
I did the exact same thing as them, except that I put the Creation code in the constructor.
When ran, it gives pure virtual method called error and terminates the program with no explanaton.
template<typename T>
class Form
{
public:
Form(std::string Title, std::string Class, /*All other params*/);
private:
static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
protected:
HWND WindowHandle = nullptr;
virtual LRESULT HandleMessages(UINT Msg, WPARAM wParam, LPARAM lParam) = 0;
};
template<typename T>
Form<T>::Form(std::string Title, std::string Class, /*All Other Params*/)
{
WNDCLASSEX WndClass = {/*Filled in with other params*/};
RegisterClassEx(&WndClass);
WindowHandle = CreateWindowEx(/*Other params*/, this);
}
template<typename T>
LRESULT __stdcall Form<T>::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
T* Data = nullptr;
switch (Msg)
{
case WM_NCCREATE:
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
Data = reinterpret_cast<T*>(pCreate->lpCreateParams);
SetWindowLongPtr(Hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(Data));
break;
}
default:
{
Data = reinterpret_cast<T*>(GetWindowLongPtr(Hwnd, GWLP_USERDATA));
return Data ? Data->HandleMessages(Msg, wParam, lParam) : DefWindowProc(Hwnd, Msg, wParam, lParam);
}
}
return 0;
}
Then my main window:
class MainWindow : public Form<MainWindow>
{
public:
MainWindow(std::string Title, /*Other params*/);
LRESULT HandleMessages(UINT Msg, WPARAM wParam, LPARAM lParam);
HWND WindowHandle() {return Form::WindowHandle;}
};
MainWindow::MainWindow(std::string Title, std::string Class, /*Other params*/) {}
LRESULT MainWindow::HandleMessages(UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_CREATE:
{
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(WindowHandle(), Msg, wParam, lParam);
}
return 0;
}
That is all. Why does it give me that error and how can I fix it?
I did the exact same thing as them, except that I put the Creation code in the constructor.
This is why. Calling a virtual method from the constructor doesn't do what you expect - unless you expect it to call the method from the current class in the hierarchy. When a base class constructor executes, the object isn't of derived type yet. And if you call a virtual method which is pure in the current class from the constructor of that class, you'll get that error.