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.
Related
I want to use the Windows API to get keyboard input from a window. From what I have heard, I can create a callback function that will be called when some events happen. I just don't know how.
ModuleManager::ModuleManager() {
HWND getGameWindow = FindWindow(NULL, TEXT("GameName"));
wndProc = (WNDPROC)SetWindowLongPtrA(getGameWindow, GWLP_WNDPROC, 0);
}
LRESULT CALLBACK ModuleManager::WndProc(const HWND hwnd, unsigned int message, uintptr_t wParam, long lparam) {
if (message == WM_CHAR) {
for (Module* m : modules) {
if (m->getKeybinding() == wParam) {
m->isActive = !m->isActive; // toggle
}
}
}
return CallWindowProcA(wndProc, hwnd, message, wParam, lparam);
}
Here is my current code. I want to set a callback on WndProc function.
It seems i figured it out.
The callback function must be a static function for some reason.
So the correct code is following:
ModuleManager::ModuleManager() {
HWND getGameWindow = FindWindow(NULL, TEXT("GameName"));
wndProc = (WNDPROC)SetWindowLongPtrA(getGameWindow, GWLP_WNDPROC, (LONG_PTR) &ModuleManager::WndProc);
}
LRESULT CALLBACK ModuleManager::WndProc(const HWND hwnd, unsigned int message, uintptr_t wParam, long lparam) {
if (message == WM_CHAR) {
for (Module* m : modules) {
if (m->getKeybinding() == wParam) {
m->isActive = !m->isActive; // toggle
}
}
}
return CallWindowProcA(wndProc, hwnd, message, wParam, lparam);
}
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);
}
So I'm writing my own little GUI framework, wrapping the Windows API in useful classes. I'm currently trying to have the user handle WndProc's messages in an object-oriented way, but I've hit a bit of a snag.
I have a Button class, that inherits from an abstract Control class, that is pretty much a wrapper around the Button's HWND handle.
There's also a Window class, that contains (what do you know) a handle to a window. This class also has a container of Controls, and is responsible for creating its own (static) WndProc method.
What I'm trying to do is have a big switch statement in the containing window's WndProc that, based on the function's wParam and lParam parameters, calls the appropriate sending control's handling function. The way I've seen most people do something like it is this:
#define MY_BUTTON 101 //Define button's ID
Button::Button()
{
hwndButton= CreateWindow(
"BUTTON", text,
WS_VISIBLE | WS_CHILD,
position.x, position.y,
size.x, size.y,
parent_handle,
(HMENU)MY_BUTTON,
GetModuleHandle(NULL),
NULL);
}
Button* button = new Button();
//Later on, in the Window class...
LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch((LOWORD)wParam)
{
case MY_BUTTON:
button->HandleMessage(&msg);
break;
}
}
However, I don't want the user to assign a unique integer for every object instance they create.
Rather, since I have a container of controls, I would rather do something like this:
//In the Window Class...
Button* button = new Button();
AddControl(button);
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(&msg)
{
ObtainControlFromParams(wParam, lParam)->HandleMessage(&msg);
//ObtainControlFromParams should return a pointer to a Control,
//and it should look into the window's Control collection.
}
}
For as great as this sounds, I can't find a way to implement the ObtainControlFromParams function.
The way I thought of to distinguish every control instance is to have a string, that I call the control's "name", that should be unique to every object instantiation. This would leave me with two options (that I can think of)
Convert the string to a hash, store it somewhere else (such as in a containing window) and use that as the Button's HMENU. I could then use a hash set to link each string hash to its owner object, and call the corrisponding method in that way.
Use the lParam method, that (to the best of my knowledge) stores the button's HWND handle, and do something with that instead.
I apologize if what I'm trying to obtain isn't really clear, it's kind of a complicated concept to me.
Any help is greatly appreciated!
The usual approach is to reflect messages like WM_COMMAND and WM_NOTIFY, which are sent to the control's parent window, back to the control that sent them. This is possible because these messages identify the sender (each in a unique way, so check the docs).
There are a number of ways to associate a C++ object pointer with a window:
Dynamically generated trampoline function as window-proc.
Most efficient but also most tricky. Probably needs to use VirtualAlloc.
Window properties.
The SetProp and GetProp functions.
Window object words.
SetWindowLongPtr. Needs to be sure there's allocated space.
Static map.
E.g. a singleton std::unordered_map, mapping handle → object.
Whatever's used by Windows standard subclassing.
I.e. using SetWindowSubclass.
Store the button's Control* object pointer in the button HWND itself where the message procedure can retrieve it. You can use (Set|Get)WindowLongPtr(GWL_USERDATA) or (Get|Set)SetProp() for that purpose.
Only certain messages, like WM_COMMAND and WM_NOTIFY, identify the control that sends them. These messages are sent to the control's parent window. These messages contain the child control's HWND. The parent can retrieve the child's Control* and forward the message to it.
Other messages are sent directly to the control's own window, not its parent window. These messages do not identify the control they are being sent to. As such, each Control needs its own individual WndProc procedure for its own HWND. Use SetWindowLongPtr(GWL_WNDPROC) or SetWindowSubclass() to assign that WndProc to the HWND after CreateWindow() is called.
Try something like this (this is very rough, there is a lot more involved in wtiting a UI framework, but this should give you some ideas):
typedef std::basic_string<TCHAR> tstring;
class Control
{
private:
HWND fWnd;
Control *fParent;
POINT fPosition;
SIZE fSize;
tstring fText;
std::list<Control*> fControls;
...
void addControl(Control *c);
void removeControl(Control *c);
virtual void createWnd() = 0;
void internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle);
...
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
...
public:
Control();
virtual ~Control();
HWND getWnd();
void destroyWnd();
void wndNeeded();
Control* getParent();
void setParent(Control *value);
POINT getPosition();
void setPosition(POINT value);
SIZE getSize();
void setSize(SIZE value);
tstring getText();
void setText(const tstring &value);
...
};
static const LPTSTR szControlPropStr = TEXT("ControlPtr")
Control* ControlFromWnd(HWND hwnd)
{
return (Control*) GetProp(hwnd, szControlPropStr);
}
Control::Control()
: fWnd(0), fParent(0)
{
fPosition.x = fPosition.y = 0;
fSize.cx = fSize.cy = 0;
...
}
Control::~Control()
{
setParent(0);
destroyWnd();
}
void Control::addControl(Control *c)
{
fControls.push_back(c);
c->fParent = this;
if (fWnd)
c->wndNeeded();
}
void Control::removeControl(Control *c)
{
fControls.remove(c);
c->destroyWnd();
c->fParent = 0;
}
HWND Control::getWnd()
{
wndNeeded();
return fWnd;
}
void Control::internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle)
{
style |= WS_VISIBLE;
if (fParent)
style |= WS_CHILD;
fWnd = CreateWindowEx(exstyle,
ClassName, fText.c_cstr(), style,
fPosition.x, fPosition.y,
fSize.cx, fSize.cy,
(fParent) ? fParent->getWnd() : 0,
0,
GetModuleHandle(NULL),
NULL);
SetProp(fWnd, szControlPropStr, (HANDLE)this);
SetWindowLongPtr(fWnd, GWL_WNDPROC, (LONG_PTR)&Control::WndProc);
}
void Control::destroyWnd()
{
DestroyWindow(fWnd);
fWnd = 0;
}
void Control::wndNeeded()
{
if (!fWnd)
{
createWnd();
for (std::list<Control*>::iterator iter = fControls.begin(); iter != fControls.end(); ++iter)
iter->wndNeeded();
}
}
Control* Control::getParent()
{
return fParent;
}
void Control::setParent(Control *value)
{
if (fParent != value)
{
if (fParent)
fParent->removeControl(this);
fParent = value;
if (fParent)
fParent->addControl(this);
}
}
POINT Control::getPosition()
{
return fPosition;
}
void Control::setPosition(POINT value)
{
fPosition = value;
if (fWnd)
SetWindowPos(fWnd, 0, fPosition.x, fPosition.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
SIZE Control::getSize()
{
return fSize;
}
void Control::setSize(SIZE value)
{
fSize = value;
if (fWnd)
SetWindowPos(fWnd, 0, 0, 0, fSize.cx, fSize.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}
tstring Control::getText()
{
return fText;
}
void Control::setText(const tstring &value)
{
fText = value;
if (fWnd)
SetWindowText(fWnd, fText.c_str());
}
LRESULT CALLBACK Control::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Control *pThis = ControlFromWnd(hwnd);
if (pThis)
return pThis->HandleMessage(uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT Control::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
WM_NCDESTROY:
{
RemoveProp(fWnd, szControlPropStr);
fWnd = 0;
break;
}
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if ((hwnd) && (hwnd != fWnd))
{
Control *c = ControlFromWnd(hwnd);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_NOTIFY:
{
NMHDR *hdr = (NMHDR*) lParam;
if ((hdr->hwndFrom) && (hdr->hwndFrom != fWnd))
{
Control *c = ControlFromWnd(hdr->hwndFrom);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_WINDOWPOSCHANGED:
{
WINDOWPOS *p = (WINDOWPOS*) lParam;
if (!(p->flags & SWP_NOMOVE))
{
fPosition.x = p->x;
fPosition.y = p->y;
}
if (!(p->flags & SWP_NOSIZE))
{
fSize.cx = p->cx;
fSize.cy = p->cy;
}
...
return 0;
}
case WM_SETTEXT:
{
LRESULT ret = DefWindowProc(fWnd, uMsg, wParam, lParam);
if (ret == TRUE)
fText = (TCHAR*) lParam;
return ret;
}
...
}
return DefWindowProc(fWnd, uMsg, wParam, lParam);
}
class Button : public Control
{
protected:
virtual void createWnd();
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
Button();
};
Button::Button()
: Control()
{
}
void Button::createWnd()
{
Control::intetnalCreateWnd(TEXT("BUTTON"), BS_PUSHBUTTON | BS_TEXT, 0);
...
}
LRESULT Button::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if (hwnd != fWnd)
break;
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
...
return 0;
}
...
}
break;
}
}
return Control::HandleMessage(uMsg, wParam, lParam);
}
//In the Window Class...
Button* button = new Button();
button->setPosition(...);
button->setSize(...);
button->setText(...);
button->setParent(this);
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.
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.