C++ Winapi - wrapping up a callback of subclass in a class - c++

I wanted to create a class that I will be used to create a static control. The problem is when subclassing the control, the callback should be static which means I won't be able to access non-static variables and function inside the callback.
I've been pulling my hair to make my code works but still no success.
MyClass.cpp
#include "MyClass.h"
MyClass::MyClass(){
non_static_variable = 0; //default is zero.
}
LRESULT CALLBACK MyClass::SubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
MyClass* pThis = reinterpret_cast<MyClass*>(dwRefData);
char buffer[5];
sprintf_s(buffer, "The value of non_static_variable is: %d \n", pThis->non_static_variable);
OutputDebugStringA(buffer);
switch (uMsg) {
case WM_PAINT: {
// do nothing for now
return 0;
}
case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, SubClassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
void MyClass::CreateStaticControl(HWND WindowHandle) {
StaticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, x, y, width, height, WindowHandle, NULL, NULL, this);
SetWindowSubclass(StaticControl, &SubClassProc, ID, reinterpret_cast<DWORD_PTR>(this));
}
void MyClass::SetValue(int value){
non_static_variable = value; //test if I can access this in callback
}
MyClass.h
#include "header.h"
public:
MyClass();
void CreateStaticControl(HWND window);
void SetValue(int value);
static LRESULT CALLBACK SubClassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
private:
int non_static_variable;
HWND StaticControl;
};
Then call the class inside my WM_CREATE from my main window procedure, I used it like this.
...
case WM_CREATE:{
MyClass myClass;
myClass.CreateStaticControl(hwnd);
myClass.SetValue(888);
break;
}
You might think that this is a duplicate of others, I found a lot of them but since this is about subclassing and I don't have access to WM_NCCREATE I can't properly set the pointer of my class. Can anyone help me?
EDIT: I put the exact code I have.

You can use the dwRefData parameter of SetWindowSubclass() to pass the this pointer to the dwRefData parameter of the callback. You can then type-cast that parameter to access non-static members. No need to use GWLP_USERDATA at all (especially since you are not using the callback's lParam correctly when setting GWLP_USERDATA).
Try this instead:
MyClass.h
class MyClass {
public:
MyClass();
void CreateStaticControl(HWND window);
static LRESULT CALLBACK SubClassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
private:
int non_static_variable;
HWND StaticControl;
};
MyClass.cpp:
void MyClass::CreateStaticControl(HWND WindowHandle) {
StaticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, x, y, width, height, WindowHandle, NULL, NULL, this);
SetWindowSubclass(StaticControl, &SubClassProc, ID, reinterpret_cast<DWORD_PTR>(this));
}
LRESULT CALLBACK MyClass::SubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
MyClass* pThis = reinterpret_cast<MyClass*>(dwRefData);
switch (uMsg) {
case WM_PAINT: {
// use pThis->non_static_variable as needed...
return 0;
}
case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, SubClassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}

Related

Subclass a button within a custom class using SetWindowSubclass [duplicate]

This question already has answers here:
Why callback functions needs to be static when declared in class
(6 answers)
Closed 6 years ago.
I am attempting to write a class to handle the creation of a button and it's messages internally. I wish to keep all of the code contained within the class itself. This may be simple or impossible as I am relatively new to Winapi and C++, having used vb.net much more extensively. I was unable to find an example that accomplished this seeming simple conversion. I did find examples suggesting i use an alternate API, or use SetWindowLongPtr, I am told that SetWindowSubclass is the better option. My attempted code is below:
#pragma once
#include <iostream>
#include <Windows.h>
#include <Commctrl.h>
using namespace std;
class button {
public:
bool initialize();
buttton(int x, int y, int length, int width, LPCWSTR text, HWND parent, HINSTANCE hInstance); // This is the constructor
private:
LPCWSTR text = L"Button";
int height = 25;
int width = 100;
int x = 0;
int y = 0;
HWND parent;
HWND thisHandle;
HINSTANCE thisInstance;
bool initialized = false;
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};
button::button(int x, int y, int height, int width, LPCWSTR text, HWND parent, HINSTANCE hInstance) {
this->x = x;
this->y = y;
this->height = height;
this->width = width;
this->text = text;
this->parent = parent;
thisInstance = hInstance;
}
bool button::initialize()
{
thisHandle = CreateWindowW(
L"BUTTON", // Predefined class; Unicode assumed
text, // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_CLIPSIBLINGS | BS_NOTIFY, // Styles
x, // x position
y, // y position
width, // Button width
height, // Button height
parent, // Parent window
NULL, // No ID.
thisInstance,
NULL); // Pointer not needed.
if (!thisHandle)
{
return false;
}
initialized = true;
//Problem Code****
SetWindowSubclass(thisHandle, mySubClassProc, 1, 0);
//****
return true;
}
LRESULT CALLBACK button::mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
//Code handling messages here.
}
}
This throws the error :
argument of type "LRESULT (__stdcall button::*)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)" is incompatible with parameter of type "SUBCLASSPROC"
I am hoping that someone can explain a simple solution, or suggest an alternative in Winapi, as i wish to learn native windows and C++ and not rely on additional libraries.
You are trying to use a non-static class method as the subclass callback. That will not work, because a non-static class method has a hidden this parameter that the API will not be able to pass.
To do what you are attempting, you must remove the this parameter (you can use the dwRefData parameter of SetWindowSubclass() instead), by either:
using a static class method:
class button {
public:
bool initialize();
...
private:
...
static LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};
bool button::initialize()
{
...
SetWindowSubclass(thisHandle, mySubClassProc, 1, (DWORD_PTR) this);
return true;
}
LRESULT CALLBACK button::mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
button *pThis = (button*) dwRefData;
//Code handling messages here.
}
using a non-member function:
class button {
public:
bool initialize();
...
};
LRESULT CALLBACK myButtonSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
button *pBtn = (button*) dwRefData;
//Code handling messages here.
}
bool button::initialize()
{
...
SetWindowSubclass(thisHandle, myButtonSubClassProc, 1, (DWORD_PTR) this);
return true;
}

Wndproc handling events less code

I want to know if there is a way i could write this code shorter, if there is anyway to make a #define that can shorten the way im doing my if statements in the message switch.
I check if i have set up a function if there is i then call it
This is just apart of my wndproc it is alot bigger
LRESULT Base::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch (uMsg)
{
case WM_CREATE:
{
if (this->onCreate != NULL)
{
if (onCreate(hwnd, (LPCREATESTRUCT)lParam))
return 1;
}
}break;
case WM_DESTROY:
{
if (onDestroy != NULL)
{
if (onDestroy(hwnd))
return 1;
}
this->Destroy();
}break;
case WM_SIZE:
{
if (onSize != NULL)
{
if (onSize(hwnd, wParam, lParam))
return 1;
}
}break;
case WM_CLOSE:
{
if (onClose != NULL)
{
if (onClose(hwnd))
return 1;
}
}break;
default:
{
}break;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
pointers defined like this
LRESULT(*onCreate) (HWND, LPCREATESTRUCT);
I then add them like this
LRESULT onCreate(HWND, LPCREATESTRUCT)
{
return true;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR pCmdLine, int nCmdShow)
{
Window mainWindow;
mainWindow.onCreate = onCreate;
return 0;
}
Use the message-cracker macros defined in <WindowsX.h>. This won't actually make the code run any faster, and it doesn't really result in a net loss of lines of code, but it certainly makes the code easier to read, which is the important part. Nobody wants to look at a 1000-line switch statement. Plus, these macros extract the parameters for each message from WPARAM and LPARAM, enhancing readability, reducing mistakes and simplifying what you have to remember.
The macros are pretty simple and anything but high-tech (they originated way back in the 16-bit Windows days):
#define HANDLE_MSG(hwnd, message, fn) \
case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
But they are pre-defined for all of the most common messages (you can add your own for the two or three messages that they omitted if you find that you need them), so you don't have to deal with the ugliness. You just get nice, readable code:
#include <Windows.h>
#include <WindowsX.h> // for the message cracker macros
...
void Base::OnClose(HWND hWnd)
{
// ...
}
BOOL Base::OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
// ...
return TRUE;
}
void Base::OnDestroy(HWND hWnd)
{
// ...
}
void Base::OnSize(HWND hWnd, UINT state, int cx, int cy)
{
// ...
}
LRESULT Base::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
HANDLE_MSG(hWnd, WM_CLOSE, OnClose);
HANDLE_MSG(hWnd, WM_CREATE, OnCreate);
HANDLE_MSG(hWnd, WM_DESTROY, OnDestroy);
HANDLE_MSG(hWnd, WM_SIZE, OnSize);
// TODO: Add more message crackers here to handle additional messages.
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
The header file also contains comments showing the signature of the handler function. But check out Hernán Di Pietro's Message Cracker Wizard to make your life even easier. It lists all of the messages, allows you to filter them down to the ones you're looking for, and will automatically copy the template code to the clipboard!
To avoid the definition of all possible specific (virtual) message-handlers in the base class, you may have a map of handlers:
#include <unordered_map>
// Mokup windows.h
typedef intptr_t HWND;
typedef intptr_t LRESULT;
typedef intptr_t WPARAM;
typedef intptr_t LPARAM;
typedef unsigned UINT;
enum {
WM_CREATE
};
// Base
class WindowBase
{
public:
virtual ~WindowBase() {}
LRESULT WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// The complexity of finding an element in an unordered map
// is amortized O(1).
auto kv = window_procedures.find(uMsg);
if(kv != window_procedures.end()) {
auto procedure = kv->second;
return (this->*procedure)(hwnd, uMsg, wParam, lParam);
}
return 0; //::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
protected:
typedef LRESULT (WindowBase::*window_procedure)(HWND, UINT msg, WPARAM, LPARAM);
template <typename Procedure>
void register_window_procedure(UINT msg, Procedure procedure) {
window_procedures[msg] = static_cast<window_procedure>(procedure);
}
private:
std::unordered_map<UINT, window_procedure> window_procedures;
};
// Test
#include <iostream>
class Window : public WindowBase
{
public:
Window() {
register_window_procedure(WM_CREATE, &Window::onCreate);
}
protected:
LRESULT onCreate(HWND, UINT msg, WPARAM, LPARAM) {
std::cout << "onCreate\n";
return 0;
}
};
int main() {
Window w;
WindowBase* p = &w;
p->WindowProc(0, WM_CREATE, 0, 0);
}
Note: This is not following your example using a freestanding message handler. If you want that, you may typedef LRESULT (*window_procedure)(HWND, UINT msg, WPARAM, LPARAM); and adjust the code accordingly. However, the function signature should include an extra argument (e.g.: WindowBase*) to keep the context (besides HWND), in which the message is invoked.

GetWindowLongPtr returns garbage [closed]

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.

Call non-static member from WndProc

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

WndProc as a class member, fails to quit

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)