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);
}
Related
I'm making a buttons in a main window ( hwnd ) that when you click on a button it opens new window ( hwndSec ) that can be controlled by the main window !
when i click on a button, the hwndSec showed up ! and while i'm clicking on the other buttons it can be modified ... but when i close it and attempt to reopen it again it doesn't response ! so the buttons become useless !
This a simple example of a single button just to open the window then trying to open it again.
The setting i set for hwndSec:
wincl.hInstance = hInstance;
wincl.lpszClassName = "HwndSecClass";
wincl.lpfnWndProc = WindowProcedureSec;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wincl))
return 0;
hwnd = CreateWindowEx (
0,
wincl.lpszClassName,
_T("Window 2"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
HWND_DESKTOP,
NULL,
hInstance,
NULL
);
i make two window procedures :
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WindowProcedureSec (HWND, UINT, WPARAM, LPARAM);
when i click a button i call this fonction :
ShowWindow(hwndSec,SW_SHOW);
and then the second window appear normally , when i close the window it doesn't came back to appear again.
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_CREATE:
CreateWindow("BUTTON",
"open",
WS_CHILD | WS_VISIBLE ,
200,100,
100,50,
hwnd,
(HMENU) ID_BUTTON,
NULL,
NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_BUTTON :
//printf("%s",(char*)GetLastError());
ShowWindow(hwndSec,SW_SHOW);
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WindowProcedureSec (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
switch (message)
{
case WM_DESTROY:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
When a window is being closed, it receives a WM_CLOSE message. If that message is passed to DefWindowProc(), the default behavior is to destroy the window:
An application can prompt the user for confirmation, prior to destroying a window, by processing the WM_CLOSE message and calling the DestroyWindow function only if the user confirms the choice.
By default, the DefWindowProc function calls the DestroyWindow function to destroy the window.
This is further discussed on MSDN:
Closing the Window
So, you will have to re-create your secondary window with CreateWindow/Ex() each time you want to show it after it has been closed:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
...
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_BUTTON :
if (!hwndSec)
{
hwndSec = CreateWindowEx(
0,
_T("HwndSecClass"),
_T("Window 2"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
HWND_DESKTOP,
NULL,
hInstance,
NULL
);
}
ShowWindow(hwndSec, SW_SHOW);
break;
}
break;
...
}
return 0;
}
LRESULT CALLBACK WindowProcedureSec (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
hwndSec = NULL;
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
If you don't want to do that, then you need to make the secondary window handle the WM_CLOSE message and not pass it to DefWindowProc(). For instance, call ShowWindow(SW_HIDE) instead, and then you can later call ShowWindow(SH_SHOW) when needed
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
...
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_BUTTON :
ShowWindow(hwndSec, SW_SHOW);
break;
}
break;
...
}
return 0;
}
LRESULT CALLBACK WindowProcedureSec (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
ShowWindow(hwnd, SW_HIDE);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
Anyone know how to call non-static member from WndProc?
Here is my WndProc prototype:
LRESULT CALLBACK System::Windows::Forms::Control::WndProc(HWND hWnd,
UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
this->OnCreate(new EventArgs(hWnd, message, wParam, lParam));
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
And defination:
class LIBMANAGED_API Control
{
protected:
HWND hWnd;
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
...
};
This is wrong on so many levels. What do you really want to achieve? Just from this piece of code, there's not enough info.
First, you declare this method using a mixture of C and managed C++. It either
protected virtual void WndProc(Message m) // Managed C++
as you see, NOT static method, LRESULT, HWND and so on, or
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
as you can see, no System namespace.
Second, where are your clases defined? I suspect you should override your method, using Managed C++, see MSDN.
You were not that far as you are already processing the WM_CREATE message.
The trick is to pass an object pointer at creation time and store it in the Window itself with SetWindowLongPtr in the WM_CREATE or WM_NCCREATE message. The you can extract it with GetWindowLongPtr and access your object from the window procedure.
Window creation (say MyWnd myWnd is the C++ object that will represent the window):
HWND hWnd = CreateWindow( m_pszClassName, "Name", WS_VISIBLE | WS_OVERLAPPED,
x, y, w, h, NULL, NULL, hInst, &myWnd);
Window procedure:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
MyWnd *myWnd;
myWnd = (MyWnd *) GetWindowLongPtr(hWnd, GWLP_USERDATA); /* to use it outside WM_CREATE */
switch (message)
{
case WM_CREATE:
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
MyWnd* myWnd= (MyWnd*) pcs->lpCreateParams;
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR) myWnd);
myWnd->OnCreate(new EventArgs(hWnd, message, wParam, lParam));
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
I want to make WndProc a class member function and I found this article, so I tried to apply it to the simplest Win32 program, which does nothing but creating a blank window, the very first step of Win32.
int Myclass::Start(HINSTANCE hInstance, int nCmdShow)
{
if (FAILED(InitWindow(hInstance, nCmdShow)))
return 0;
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;;
}
LRESULT Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
Myclass* pThis = nullptr;
if (message == WM_NCCREATE) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<Myclass*>(lpcs->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
}
else {
pThis = reinterpret_cast<Myclass*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if(pThis)
return pThis->RealWndProc(hWnd, message, wParam, lParam);
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT Myclass::RealWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
It runs well, but when I close the window the program stays in the message loop and doesn't quit.
I found out that WM_DESTROY is not delivered to RealWndProc(), so PostQuitMessage() is not called.
If I insert if(WM_DESTROY == message) { PostQuitMessage(0); return 0; } right before the last line of StaticWndProc, then the program quits. But I'm not sure if this is a good way to do it.
How can I make WM_DESTROY consumed by RealWndProc()?
Like this is how I did it.. Works just fine.. The only problem I can think of in your code is one of two:
You missed the calling convention for the WindowProcedure.
You forgot to pass "this" to CreateWindowEx.
And the code:
#include <windows.h>
class Window
{
public:
Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW,
POINT Location = {CW_USEDEFAULT, CW_USEDEFAULT}, int Width = CW_USEDEFAULT,
int Height = CW_USEDEFAULT, HWND Parent = HWND_DESKTOP, HMENU Menu = nullptr);
int Loop();
private:
HWND WindowHandle;
static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
LRESULT RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
};
Window::Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU Menu)
{
WNDCLASSEX WndClass =
{
sizeof(WNDCLASSEX), CS_DBLCLKS, Window::WindowProcedure,
0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_BACKGROUND),
nullptr, Class, LoadIcon(nullptr, IDI_APPLICATION)
};
if(RegisterClassEx(&WndClass))
{
WindowHandle = CreateWindowEx(dwStyleEx, Class, Title, dwStyle, Location.x, Location.y, Width, Height, Parent, Menu, GetModuleHandle(nullptr), this);
}
}
LRESULT __stdcall Window::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Window* Data = nullptr;
switch(Msg)
{
case WM_NCCREATE:
{
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
Data = static_cast<Window*>(pCreate->lpCreateParams);
SetWindowLongPtr(Hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(Data));
break;
}
default:
{
Data = reinterpret_cast<Window*>(GetWindowLongPtr(Hwnd, GWLP_USERDATA));
break;
}
}
return Data ? Data->RealWindowProcedure(Hwnd, Msg, wParam, lParam) : DefWindowProc(Hwnd, Msg, wParam, lParam);
}
LRESULT Window::RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(Hwnd, Msg, wParam, lParam);
}
return 0;
}
int Window::Loop()
{
MSG Messages = {nullptr};
ShowWindow(WindowHandle, SW_SHOW);
while(GetMessage(&Messages, nullptr, 0, 0))
{
TranslateMessage(&Messages);
DispatchMessage(&Messages);
}
return Messages.wParam;
}
int main()
{
Window w("TItle", "Class");
return w.Loop();
}
You should catch WM_CLOSE and have it call DestroyWindow() to trigger WM_DESTROY:
case WM_CLOSE:
DestroyWindow(hWnd);
break;
Also, your StaticWndProc() method is using the wrong calling convention. It must use the __stdcall calling convention, which is wrapped by WINAPI and CALLBACK macros, eg:
LRESULT CALLBACK Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 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 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.