WndProc as a class member, fails to quit - c++

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)

Related

Win32 Api Access violation

I´m learning Win32 api and trying to make it somewhat object oriented while also learning C++. I have created a BaseWindow class and a MainWindow class that derive from it. I have a menu that creates MessageBoxes and a menu option that creates a DialogBox.
It is when I click on this option when the program crashes.
Exception thrown at 0x00000001 in LearningWin32.exe: 0xC0000005: Access violation executing location 0x00000001.
This exception is thrown in BaseWindow.cpp
if (pBaseWindow)
{
OutputDebugString(L"BASE WINDOW pBASEWINDOW\n");
pBaseWindow->m_hwnd = hwnd;
lResult = pBaseWindow->HandleMessage(uMsg, wParam, lParam); // --> ExceptionThrown
}
I´m not sure if it is because I don´t understand the way message flow works, because of not dominating C++ enough or both. Heres is what I have done so far.
Main.cpp
#ifndef UNICODE
#define UNICODE
#endif
#include "MainWindow.h"
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
MainWindow win(hInstance, L"MainWindow", nCmdShow);
if (!win.Create(L"Notepad --", WS_OVERLAPPEDWINDOW, 0, 1024, 720))
{
return 0;
}
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BaseWindow.h
#pragma once
#include <Windows.h>
class BaseWindow
{
public:
BaseWindow();
~BaseWindow();
HWND Window() const;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
virtual PCWSTR GetClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
HWND m_hwnd;
};
BaseWindow.cpp
#include "BaseWindow.h"
BaseWindow::BaseWindow() : m_hwnd(NULL) { }
BaseWindow::~BaseWindow() { DestroyWindow(m_hwnd); }
HWND BaseWindow::Window() const { return m_hwnd; }
LRESULT CALLBACK BaseWindow::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugString(L"BASE WINDOW WindowProc\n");
if (uMsg == WM_NCCREATE)
{
OutputDebugString(L"BASE WINDOW WindowProc -> WM_NCCREATE\n");
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
BaseWindow* pBaseWindow = (BaseWindow*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pBaseWindow);
}
BaseWindow* pBaseWindow = reinterpret_cast<BaseWindow*>(GetWindowLongW(hwnd, GWL_USERDATA));
LRESULT lResult;
if (pBaseWindow)
{
OutputDebugString(L"BASE WINDOW pBASEWINDOW\n");
pBaseWindow->m_hwnd = hwnd;
lResult = pBaseWindow->HandleMessage(uMsg, wParam, lParam);
}
else
{
OutputDebugString(L"BASE WINDOW DefWindowProc\n");
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return lResult;
}
MainWindow.h
#pragma once
#include "BaseWindow.h"
class MainWindow : public BaseWindow
{
private:
HINSTANCE m_hInstance;
LPCWSTR m_className;
int m_nCmdShow;
public:
MainWindow(HINSTANCE hInstance, LPCWSTR ClassName, int nCmdShow);
//~MainWindow();
BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0);
virtual PCWSTR GetClassName() const;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
};
MainWindow.cpp
#include "MainWindow.h"
#include "resource/resource.h"
#include <string>
MainWindow::MainWindow(HINSTANCE hInstance, LPCWSTR ClassName, int nCmdShow)
: m_hInstance(hInstance), m_className(ClassName), m_nCmdShow(nCmdShow)
{
OutputDebugString(L"MAIN WINDOW CONSTRUCTOR\n");
}
PCWSTR MainWindow::GetClassName() const { return m_className; }
BOOL MainWindow::Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle,
int nWidth,
int nHeight,
int x,
int y,
HWND hWndParent,
HMENU hMenu)
{
OutputDebugString(L"MAIN WINDOW CREATE\n");
WNDCLASS wc = { 0 };
wc.lpfnWndProc = BaseWindow::WindowProc;
wc.hInstance = m_hInstance;
wc.lpszClassName = GetClassName();
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wc.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_ICON1));
if (RegisterClass(&wc))
{
OutputDebugString(L"MAIN WINDOW CREATE CLASS REGISTERED\n");
}
else
{
OutputDebugString(L"MAIN WINDOW CREATE CLASS UNABLE TO REGISTER\n");
}
m_hwnd = CreateWindowEx(
dwExStyle,
GetClassName(),
lpWindowName,
dwStyle,
x, y,
nWidth, nHeight,
hWndParent,
hMenu,
m_hInstance,
this);
if (!m_hwnd)
{
OutputDebugString(L"--------- Window Creation Failed ---------\n");
ExitProcess(0);
}
// Centering the window in the screen
RECT rc;
GetWindowRect(m_hwnd, &rc);
int xPos = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
int yPos = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
SetWindowPos(m_hwnd, HWND_TOP, xPos, yPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
ShowWindow(m_hwnd, m_nCmdShow);
UpdateWindow(m_hwnd);
return (m_hwnd ? TRUE : FALSE);
}
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE\n");
switch (uMsg)
{
case WM_COMMAND: // Menu options
{
switch (LOWORD(wParam))
{
case ID_FILE_NEW:
MessageBox(m_hwnd, L"New file", L"Menu option New", MB_OK | MB_ICONASTERISK);
break;
case ID_FILE_OPEN:
MessageBox(m_hwnd, L"Menu option Open", L"Open", MB_OK | MB_ICONASTERISK);
break;
case ID_FILE_EXIT:
{
int msgBoxID = MessageBox(m_hwnd, L"Are you sure you want to exit?", L"Exit", MB_OKCANCEL | MB_ICONHAND);
if (msgBoxID == IDOK) PostMessage(m_hwnd, WM_CLOSE, 0, 0);
}
break;
case ID_HELP_SHOWDIALOGBOX:
{
OutputDebugString(L"MAIN WINDOW SHOWDIALOGBOX\n");
int ret = DialogBox(
m_hInstance,
MAKEINTRESOURCE(IDD_DIALOG1),
m_hwnd,
(DLGPROC)AboutDlgProc(m_hwnd, uMsg, wParam, lParam));
if (ret == IDOK) {
MessageBox(m_hwnd, L"Dialog exited with IDOK.", L"Notice",
MB_OK | MB_ICONINFORMATION);
}
else if (ret == IDCANCEL) {
MessageBox(m_hwnd, L"Dialog exited with IDCANCEL.", L"Notice",
MB_OK | MB_ICONINFORMATION);
}
else if (ret == -1) {
MessageBox(m_hwnd, L"Dialog failed!", L"Error",
MB_OK | MB_ICONINFORMATION);
}
}
break;
}
break;
}
case WM_CLOSE:
OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE -> WM_CLOSE\n");
DestroyWindow(m_hwnd);
return 0;
case WM_DESTROY:
OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE -> WM_DESTROY\n");
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(m_hwnd, &ps);
}
return 0;
default:
OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE -> DefWindowProc\n");
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
BOOL CALLBACK MainWindow::AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
OutputDebugString(L"MAIN WINDOW AboutDlgProc\n");
switch (Message)
{
case WM_INITDIALOG:
// Do any process before dialog is shown
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
EndDialog(hwnd, IDOK);
break;
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
Edit
Solution:
Credit goes to Errorist.
Changed MainWindow.h from
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
to
static BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
and in MainWindow.cpp changed it from
int ret = DialogBox(
m_hInstance,
MAKEINTRESOURCE(IDD_DIALOG1),
m_hwnd,
(DLGPROC)AboutDlgProc(m_hwnd, uMsg, wParam, lParam));
to simply
int ret = DialogBox(
m_hInstance,
MAKEINTRESOURCE(IDD_DIALOG1),
m_hwnd,
(DLGPROC)AboutDlgProc);
don't call AboutDlgProc
rather pass the address of a valid DLGPROC to be called
.....
case ID_HELP_SHOWDIALOGBOX:
{
OutputDebugString(L"MAIN WINDOW SHOWDIALOGBOX\n");
int ret = DialogBox(
m_hInstance,
MAKEINTRESOURCE(IDD_DIALOG1),
m_hwnd,
(DLGPROC)AboutDlgProc(m_hwnd, uMsg, wParam, lParam)); // <---- BOOL value returned
// where a valid DLGPROC address should be passed
....

How to reopen a closed window with the Windows API

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

Handling windows messages from a subclass

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

WinAPI nothing happens on button click

My Question is: In the below C++ code, why does clicking on the button do nothing while it is supposed to call MessageBox from WndProc1?
P.S: After compiling, I got some errors like the following:
"C:\Windows\SysWOW64\ntdll.dll", Can't find or open PDB file.
Code:
#include <Windows.h>
LRESULT CALLBACK WndProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
LONG WINAPI WndProc1(
_In_ HWND hwnd_button,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
//Точка входа в программу
int WINAPI WinMain
(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
//Создаем класс окна
WNDCLASS WindowClass;
//Заполняем структуру
WindowClass.style = 0;
WindowClass.lpfnWndProc = (WNDPROC)WndProc;
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hInstance = hInstance;
WindowClass.hIcon = LoadIcon(hInstance,
(LPCTSTR)IDI_APPLICATION);
WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WindowClass.lpszMenuName = 0;
WindowClass.lpszClassName = TEXT("Class");
//Зарегистируем класс окна
RegisterClass(&WindowClass);
//Создаем переменную, в которой поместим идентификатор окна
HWND hWnd;
hWnd = CreateWindow(TEXT("Class"), TEXT("ClickTest"),
WS_OVERLAPPEDWINDOW, 0, 0, 500, 300, NULL, NULL, hInstance, NULL);
//Создаем кнопку
HWND hWnd_button;
hWnd_button = CreateWindow(TEXT("button"), TEXT("Click me"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
10, 10, 80, 30, hWnd, (HMENU)10000, hInstance, NULL);
//показать окно
ShowWindow(hWnd, nCmdShow);
//обновить содержимое окна
UpdateWindow(hWnd);
//Создадим переменную для храненния сообщений
MSG msg;
//Создадим цикл обработки сообщений
while(GetMessage(&msg, NULL,0 ,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LONG WINAPI WndProc1(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
switch (Message){
case WM_COMMAND:
if(LOWORD(wparam)==10000)
{
MessageBox(hwnd, TEXT("Button Pressed"), TEXT(""), 0);
}
return 0;}}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
BeginPaint(hWnd, &PS);
EndPaint(hWnd, &PS);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Now working, just added button function as one of the cases to WndProc (WndProc1 deleted)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lParam)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
BeginPaint(hWnd, &PS);
EndPaint(hWnd, &PS);
break;
case WM_COMMAND:
if(LOWORD(wparam)==10000)
{
MessageBox(hWnd, TEXT("Button Pressed"), TEXT(""), 0);
}
default:
return DefWindowProc(hWnd, message, wparam, lParam);
}
return 0;
}
One final newbie question: what's the difference between LRESULT CALLBACK and LONG WINAPI then?
Do this modification on WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_COMMAND:
if(LOWORD(wParam)==10000)
{
MessageBox(hWnd, TEXT("Button Pressed"), TEXT(""), 0);
}
break;
case WM_CREATE:
break;
// ...
I added WM_COMMAND in the switch/case of WndProc.
How do you expect WndProc1 to be called? It isn't associated to any window class... You have to handle the WM_COMMAND inside WndProc (buttons, as well as other common controls, notify their parent of their events via WM_COMMAND).

Encapsulating WndProc Problem

Okay so i wrote a basic class to do encapsulation of a win32 window. I ended up creating a static router callback function to route the messages into another function of the class.
EDIT OKAY I Got it
m_MainWindowHandle = CreateWindowEx(
windowFormat,
L"LUDO ENGINE",
m_WindowName.c_str(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
0,
m_WindowDimensions.first,
m_WindowDimensions.second,
NULL,
NULL,
m_EngineInstance,
(void*)this);
I forgot to pass in the (void*)this pointer. AJKFHDJKDF
LRESULT CALLBACK Test::EngineBase::WndRouter(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
Test::EngineBase *base = NULL;
if(message == WM_NCCREATE)
{
base = reinterpret_cast<Test::EngineBase*>((LONG)((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)base);
}
else
{
base = reinterpret_cast<Test::EngineBase*>(GetWindowLongPtr(hWnd, GWL_USERDATA));
}
if(base == NULL)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
base->SetHWnd(hWnd);
return base->WndProc(hWnd, message, wParam, lParam);
}
Which calls this function:
LRESULT CALLBACK Test::EngineBase::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_KEYDOWN:
m_InputManager->GetKeyboard()->SetKeyPressed(static_cast<int>(wParam));
break;
case WM_KEYUP:
m_InputManager->GetKeyboard()->SetKeyUnpressed(static_cast<int>(wParam));
break;
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
m_InputManager->GetMouse()->SetMouseMouse(LOWORD(lParam),HIWORD(lParam));
break;
case WM_MOUSEWHEEL:
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
//case IDM_ABOUT:
// //DialogBox(m_EngineInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
// //break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Then i inherit from EngineBase. Now WndRouter always gets called, instead always results in base == NULL, so it never actually calls the WndProc function.
What am i doing wrong?
EDIT
Heres how i get the intial messages into the engine:
while (returnValue == 9999)
{
engine->Update();
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
returnValue = static_cast<int>(msg.wParam);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Heres The header:
namespace Test
{
//forward defines
namespace Input
{
class InputManager;
}
// main classs
class EngineBase
{
protected:
virtual HRESULT SetKeyBindings();
virtual HRESULT LoadResources();
static LRESULT CALLBACK WndRouter(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// Use reference counting schematics to prevent deletion.
boost::shared_ptr<Test::Input::InputManager> m_InputManager;
public:
EngineBase(__in const HINSTANCE engineHandleInstance);
~EngineBase();
// Engine Specific things
HRESULT InitializeEngine(__in const int nCmdShow, __in const std::pair<INT, INT>& windowDimensions, __in const Test::String& windowName);
HRESULT Register(__in const WNDCLASSEX& windowDefs);
// Game Specific things
virtual HRESULT Initialize() = 0;
virtual HRESULT Update();
inline void SetHWnd(__in const HWND windowHandle)
{
m_MainWindowHandle = windowHandle;
}
inline const std::pair<INT, INT> GetWindowDimensions()
{
return m_WindowDimensions;
}
private:
HRESULT InitializeSubSystems();
// Win32 stuff
HRESULT CheckForOtherInstance();
WORD RegisterEngineClass();
WNDCLASSEX m_WindowClass;
HWND m_MainWindowHandle;
HINSTANCE m_EngineInstance;
HANDLE m_Mutex;
std::pair<INT, INT> m_WindowDimensions;
Test::String m_WindowName;
};
}
EDIT This is how i set my windProc def
In main.cpp
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = NULL;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(1));
windowClass.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(2));
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = L"ENGINE";
if(FAILED(engine->Register(windowClass)))
{
return FAIL;
}
Register does this:
HRESULT Test::EngineBase::Register(__in const WNDCLASSEX& windowDefs)
{
HRESULT hr = S_OK;
m_WindowClass = windowDefs;
m_WindowClass.lpfnWndProc = WndRouter;
m_WindowClass.hInstance = m_EngineInstance;
if(!RegisterClassEx(&m_WindowClass))
{
hr = E_CANNOT_CREATE_WINDOW_CLASS;
return hr;
}
return hr;
}
What inherits from EngineBase?
How do you get the initial messages flowing into EngineBase's WndProc?
Did you call SetWindowLong with GWL_WNDPROC to point the window at your WndProc? (You probably missed some initial messages if you did). Or does the window's WNDCLASS point to your EngineBase::WndProc?