Related
I'm new to Win32 API and want to create a window. I have a class (as follows) but I get an exception at the line return p_this->HandleMessages(msg, wParam, lParam);.
Window.h (I got this in some msdn website and modified to some extent):
#include "BaseWin.h"
template<class D_CLASS>
class Window
:public BaseWin
{
public:
int returnValue;
private:
static constexpr LPCSTR className = "Best Window in the UNIVERSE!";
public:
// declare this fn in the inherited class and use as wnd proc
virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam) = 0;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D_CLASS* p_this = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*)lParam;
p_this = (D_CLASS*)create->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
}
else
p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (p_this)
return p_this->HandleMessages(msg, wParam, lParam); // exception here
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Window(HWND parent, LPCSTR title, int id, const Point& pos, const Size& _size,
int styles = WS_OVERLAPPEDWINDOW, int retVal = 0x45, int stylesEx = 0
)
:BaseWin(parent, pos, _size, id), returnValue(retVal)
{
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = D_CLASS::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = nullptr;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = className;
wc.hIconSm = nullptr;
RegisterClassEx(&wc);
HMENU _id = nullptr;
if (!((styles & WS_CHILD) != WS_CHILD || id == -1))
_id = (HMENU)ID;
hWnd = CreateWindowEx(
stylesEx,
className, title,
styles,
Position.x, Position.y, size.x, size.y,
parent, _id, GetModuleHandle(nullptr), this
);
}
~Window()
{
UnregisterClass(className, GetModuleHandle(nullptr));
Destroy();
}
};
I'm using Visual Studio and whenever the line is reached is reached, a breakpoint is placed there are no details through which I can deduce the resaon (It's: main.exe has encountered a breakpoint).
main.cpp:
#include "Window.h"
#include "Button.h"
class MainWindow
:public Window<MainWindow>
{
private:
HMENU menuBar;
HMENU menuIt;
enum
{
BTN_KILL,
M_QUIT,
M_ADD_LOG
};
public:
MainWindow()
:Window<MainWindow>(nullptr, "Useless Manger", -1, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(640, 480), normWinStyle)
{
Button* btn = new Button(hWnd, BTN_KILL, "Kill Me", Point(), Size(300, 100));
menuBar = CreateMenu();
menuIt = CreateMenu();
AppendMenu(menuIt, MF_ENABLED, M_ADD_LOG, "Add Log");
AppendMenu(menuBar, MF_ENABLED | MF_STRING, M_QUIT, "Quit");
AppendMenu(menuBar, MF_POPUP, (UINT_PTR)menuIt, "Add");
SetMenu(hWnd, menuBar);
}
LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hWnd, &ps);
break;
}
case WM_COMMAND:
if (LOWORD(wParam) == BTN_KILL)
{
PostQuitMessage(this->returnValue);
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
};
int CALLBACK WinMain(
HINSTANCE hInst,
HINSTANCE h_p_Inst,
LPSTR nCmdLine,
int nCmdShow)
{
MainWindow* window = new MainWindow();
window->Show();
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BaswWin.h is a class holding the current HWND, position, size, ID, etc with some functions like Show().
How do I fix this?
MainWindow::HandleMessages is called before superclass Window<T> is fully constructed. Before Window<T> is fully constructed, entry for Window<T>::HandleMessages refers to special function witch reports that pure function was called. When Window<T> is fully constructed, this entry is replaced by MainWindow::HandleMessages.
In C++ calling virtual functions when superclasses are still constructed needs special atention.
You can replace Window<T>::HandleMessage with unpure implementation.
virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, msg, wparam, lparam);
}
Initialize hWnd early, because WndProc can be called before CreateWindowEx returns.
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D_CLASS* p_this = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*)lParam;
p_this = (D_CLASS*)create->lpCreateParams;
// initialize early
p_this->hWnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
}
else
p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (p_this)
// if you don't initialize early, skip when hWnd == nullptr
// if (p_this && p_this->hWnd)
return p_this->HandleMessages(msg, wParam, lParam);
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Probably you would also miss WM_DESTROY message, when MainWindow class is already destroyed, but WM_DESTROY would be generated in Window<T>::~Window<T>.
Calling PostQuitMessage(this->returnValue); in MainWindow::~MainWindow could solve this problem.
I am following the Microsoft Docs for Windows api. I a currently on chapter 4 and trying to draw an ellipse. The m_pRenderTarget is declared in the class App.h. In the function OnRender(HWND hwnd) I am trying to use it to draw the geometry (ellipse). However, I am getting the following error:
Exception thrown: read access violation. this->m_pRenderTarget was 0x38.
After some debugging, I noticed the in the HRESULT App::CreateDeviceResources(HWND hwnd) function, m_pRenderTarget was for some reason not NULL, even though I initialized it as such and hadn't yet changed it (I don't think at least). My guess is that this is the problem. For reference, here is the relevant code:
#pragma once
#define MAX_LOADSTRING 100
#include "resource.h"
#include "pch.h"
class App
{
public:
App();
~App();
bool Init(HINSTANCE instance, int cmd);
int RunMessageLoop();
HINSTANCE getInstance() { return hInstance; }
private:
HINSTANCE hInstance;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
ID2D1Factory* m_pD2DFactory;
ID2D1EllipseGeometry* m_pEllipseGeometry;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pBlackBrush;
ATOM RegisterClass();
BOOL InitInstance(int);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
HRESULT CreateDeviceIndependentResources();
HRESULT CreateDeviceResources(HWND hwnd);
HRESULT OnRender(HWND hwnd);
};
Here are the implementations:
#include "pch.h"
#include "App.h"
App::App()
: m_pRenderTarget(NULL)
{}
App::~App(){}
bool App::Init(HINSTANCE instance, int cmd)
{
hInstance = instance;
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
RegisterClass();
if (!InitInstance(cmd))
return false;
return true;
}
int App::RunMessageLoop()
{
HACCEL hAccelTable;
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM App::RegisterClass()
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL App::InitInstance(int nCmdShow)
{
HRESULT hr = CreateDeviceIndependentResources();
if FAILED(hr)
{
return FALSE;
}
HWND hWnd;
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, NULL, NULL, hInstance, this);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK App::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
App* pApp;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
pApp = (App*)pcs->lpCreateParams;
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, PtrToUlong(pApp));
return TRUE;
}
else
{
pApp = reinterpret_cast<App*>(static_cast<LONG_PTR>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)));
if (!pApp)
return DefWindowProc(hWnd, message, wParam, lParam);
}
int wmld, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(NULL, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, pApp->About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
pApp->OnRender(hWnd);
EndPaint(hWnd, &ps);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK App::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
HRESULT App::CreateDeviceIndependentResources()
{
HRESULT hr;
// Create a Direct2D factory
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
if (SUCCEEDED(hr))
{
// Create an ellipse geometry
const D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(105.0f, 105.0f), 25.0f, 25.0f);
hr = m_pD2DFactory->CreateEllipseGeometry(ellipse, &m_pEllipseGeometry);
}
return hr;
}
HRESULT App::CreateDeviceResources(HWND hwnd)
{
//Notice that this causes HERE to be printed out, indicating that m_pRenderTarget != NULL
if (m_pRenderTarget != NULL)
OutputDebugStringA("\nHERE\n");
else
OutputDebugStringA("\nTHERE\n");
HRESULT hr = S_OK;
if (!m_pRenderTarget) {
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create a Direct2D render target
hr = m_pD2DFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size), &m_pRenderTarget );
if (SUCCEEDED(hr))
{ // Create a black brush
hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush );
}
}
return hr;
}
HRESULT App::OnRender(HWND hwnd)
{
HRESULT hr;
hr = CreateDeviceResources(hwnd);
if (SUCCEEDED(hr))
{
if (!(m_pRenderTarget->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
{
m_pRenderTarget->BeginDraw();
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pRenderTarget->FillGeometry(m_pEllipseGeometry, m_pBlackBrush);
hr = m_pRenderTarget->EndDraw();
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
m_pRenderTarget->Release();
m_pRenderTarget = NULL;
m_pBlackBrush->Release();
m_pBlackBrush = NULL;
}
}
}
return hr;
}
I apologize for the amount of code I am offering. I am completely confused about the problem as I had thought that I was copying exactly what was written in the docs. I suppose I must have made an error in transcription. Thank you for any help you can offer.
This kind of issue screams one of the following:
your class pointer is invalid through being destroyed, uninitialized, or otherwise;
you have stack or heap corruption e.g. buffer overflow or other undefined behavior.
Now, let's look at the strings appearing in your class, which are candidates for buffer overflow. Nope, it looks like you're initializing those fine.
Okay, more static analysis. Work backwards from where the issue happens.
who called CreateDeviceResources? It was OnRender
who called OnRender? It was the WM_PAINT handler.
that call is very simple: pApp->OnRender(hWnd);
so, is pApp valid? Where is that initialized?
it's stored in the window long pointer -- when is that stored?
And this leads me to this line:
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, PtrToUlong(pApp));
Okay, what's fishy about that? Well, PtrToULong is not a function (or macro) I've ever used, but based on its name, I'm immediately suspicious because I'm used to programming in 64-bits and I know that the type ULONG in Windows API is 32-bit.
So, I go and check the docs, and sure enough I find that's the case. Not only this, but I find several writings about how this is actually a common culprit for breaking programs when porting to 64-bit.
Note that you could have arrived here already if you had attached your debugger, which would break when the access violation occurs. And then you could have looked at the call stack. You could then check the other contents of your pApp class and probably see the entire thing is corrupt.
At this point, you just go "hey I know this is sketchy", and you change the line to something like:
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pApp));
Now you compile your program and try again. I'm willing to bet that it works. So much so, that I've written this entire answer based on that bet.
Another way you could have arrived at this particular path of investigation would have been to print out the value of this in your App constructor, and check it again in OnRender when you saw things go wonky.
The reason is that you wrote an extra parameter when using the CreateWindow function, which caused hInstance to be acquired by lpCreateParams, which caused an access exception.
Just modify the code as follows:
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), this);
And it works for me:
I have adopted the typical solution you find out there in order to use the WNDPROC as an object method, but it looks like the WM_DESTROY message is not sent to the object window's own WNDPROC and the program does not exit after closing the window.
My window class looks like this (irrelevant code removed):
class MyWindow : MyApp
{
public:
MyWindow();
void Create(void);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
private:
HWND _hWnd;
};
void MyWindow::Create()
{
// Here I register my class and call CreateWindowEx
// Everything works fine so far
// Part of the code where I assign the static WNDPROC
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = MyApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = this->Instance;
wcex.hIcon = LoadIcon(this->Instance, MAKEINTRESOURCE(32512));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "MyWindowClass";
wcex.hIconSm = LoadIcon(this->Instance, MAKEINTRESOURCE(32512));
RegisterClassExW(&wcex);
this->_hWnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_TOOLWINDOW,
wcex.lpszClassName,
"Window Title",
WS_POPUP,
10, 10,
600, 400,
nullptr,
nullptr,
this->Instance,
nullptr
);
ShowWindow(this->_hWnd, SW_SHOW);
UpdateWindow(this->_hWnd);
}
LRESULT CALLBACK MyWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
// If I place a MessageBox here, it shows up
}
break;
case WM_DESTROY:
// It never gets to this point
// Decrease windows count
this->WindowsCount--;
PostQuitMessage(0);
break;
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
And now a class which holds the static WNDPROC, which is assigned at creation
class MyApp
{
public:
static HINSTANCE Instance;
static int WindowsCount;
MyApp();
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
};
and implementation
LRESULT CALLBACK MyApp::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Window object
MyWindow* myWindow = NULL;
if (msg == WM_CREATE) {
myWindow = reinterpret_cast<MyWindow *>(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)myWindow);
}
else {
myWindow = reinterpret_cast<MyWindow *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
// If window not identified, identify now
if (myWindow) {
return myWindow->WndProc(hWnd, msg, wParam, lParam);
}
// Call window object's processor
return DefWindowProc(hWnd, msg, wParam, lParam);
}
The WM_CLOSE message is not caught either. I really do not understand why these messages are not passed on
You are setting the lpParam parameter of CreateWindowEx() to nullptr, so myWindow is always nullptr in MyApp::WndProc(), thus MyWindow::WndProc() is never called. You need to pass this instead of nullptr.
You are also not doing any error checking to make sure RegisterClassExW() and CreateWindowEx() succeed before calling ShowWindow()/UpdateWindow().
Also, consider using SetWindowSubclass() instead of (Get|Set)WindowLongPtr(GWLP_USERDATA). See Subclassing Controls on MSDN, and Raymond Chen's blog article on Safer Subclassing.
Pre-Text/ Question
I am trying to make a fairly simple tool to help debug variable values. For it to be completely self contained within the class is what I am aiming for. The end product I can use a function in the class like ShowThisValue(whatever).
The problem I am having is that I can't figure out, if possible, to have the procedure within the class. Here is the short version, with the problem.
-Code updated again 11/29/13-
-I have put this in its own project now.
[main.cpp]
viewvars TEST; // global
TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil);
[viewvars.h] The entire updated
class viewvars {
private:
HWND hWindow; // the window, a pointer to
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
viewvars(); // blank constructor
int CreateTestWindow(HINSTANCE hInst);
};
// blank constructor
viewvars::viewvars() {}
// create the window
int viewvars::CreateTestWindow(HINSTANCE hInst) {
// variables
char thisClassName[] = "viewVars";
MSG msg;
WNDCLASS wincl;
// check for class info and modify the info
if (!GetClassInfo(hInst, thisClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return -1;
}
}
// create window
hWindow = CreateWindow(thisClassName, "Test", WS_POPUP | WS_CLIPCHILDREN, 10, 10, 200, 200, NULL, NULL, hInst, this);
if (hWindow == NULL) {
MessageBox(NULL,"Problem creating the window.","Error",0);
return -1;
}
// show window
ShowWindow(hWindow, TRUE);
// message loop
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// then quit window?
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
// window proc
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
MessageBox(NULL,"Has it gone this far?","Bench",0);
// variable
viewvars *view;
// ????
if (message == WM_NCCREATE) {
CREATESTRUCT *cs = (CREATESTRUCT*)lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0) {
if (GetLastError() != 0) {
MessageBox(NULL,"There has been an error near here.","Error",0);
return FALSE;
}
}
}
else {
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view) return view->WindowProc(message, wParam, lParam);
MessageBox(NULL,"If shown, the above statement did not return, and the statement below did.","Error",0);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
// you can access non-static members in here...
MessageBox(NULL,"Made it to window proc.","Error",0);
switch (message)
{
case WM_PAINT:
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
break;
}
}
The message boxes appear in this order:
Has it made it this far?
Made it to window proc
DefWindowProc returned
Has it made it this far? // repeated?
Made it to window proc
DefWindowProc returned
Problem Creating the Window
Thanks for the help so far. Do you know where the problem might be?
To use a non-static class method as a window procedure requires a dynamically-allocated thunk, which is an advanced technique that I will not get into it here.
The alternative is to declare the class method as static, then it will work as a window procedure. Of course, being a static method, it can no longer access non-static class members without an instance pointer. To get that pointer, you can have the class pass its this pointer to the lpParam parameter of CreateWindow/Ex(), then the window procedure can extract that pointer from the WM_NCCREATE message and store it in the window using SetWindowLong/Ptr(GWL_USERDATA). After that, subsequent messages can retrieve that pointer using GetWindowLong/Ptr(GWL_USERDATA) and thus be able to access non-static members of that object. For example:
class viewvars
{
private:
HWND hWindow;
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
int CreateTestWindow(HINSTANCE hInst);
};
int viewvars::CreateTestWindow(HINSTANCE hInst)
{
WNDCLASS wincl;
if (!GetClassInfo(hInst, thisClassName, &wincl))
{
...
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
if (RegisterClass(&wincl) == 0)
return -1;
}
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
...
MSG msg;
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
viewvars *view;
if (message == WM_NCCREATE)
{
CREATESTRUCT *cs = (CREATESTRUCT*) lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0)
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view)
return view->WindowProc(message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// you can access non-static members in here...
switch (message)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWindow, message, wParam, lParam);
}
}
The main message loop must not be in your class, and especially not in a "CreateTestWindow" function, as you will not return from that function until your thread receive the WM_QUIT message that makes GetMessage returns 0.
Here is simple implementation of your viewvars class. Key points:
The Window Proc is a static member.
The link between the Window Proc and the object is made through the
use of GWLP_USERDATA. See SetWindowLongPtr.
The class DTOR destroys the window if it still exists. The WM_DESTROY
message set the HWND member to 0.
Adding OnMsgXXX methods to the class is simple: declare/define then
and just call them from the WindowProc using the 'this' pointer
stored in GWLP_USERDATA.
EDIT:
As per Mr Chen suggestion, earlier binding of the HWND to the Object (in WM_NCCREATE) to allow message handler as methods during the Window Creation.
I changed the creation styles, to show the window and to be able to move it.
// VIEWVARS.H
class viewvars {
public:
static viewvars* CreateTestWindow( HINSTANCE hInstance );
viewvars() : m_hWnd( 0 ) {}
~viewvars();
private:
static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static const char * m_pszClassName;
HWND m_hWnd;
};
// VIEWVARS.CPP
#include "viewvars.h"
const char * viewvars::m_pszClassName = "viewvars";
viewvars * viewvars::CreateTestWindow( HINSTANCE hInst ) {
WNDCLASS wincl;
if (!GetClassInfo(hInst, m_pszClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = m_pszClassName;
wincl.lpfnWndProc = WindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return 0;
}
}
viewvars * pviewvars = new viewvars;
HWND hWnd = CreateWindow( m_pszClassName, "Test", WS_VISIBLE | WS_OVERLAPPED, 50, 50, 200, 200, NULL, NULL, hInst, pviewvars );
if ( hWnd == NULL ) {
delete pviewvars;
MessageBox(NULL,"Problem creating the window.","Error",0);
return 0;
}
return pviewvars;
}
LRESULT CALLBACK viewvars::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
switch ( uMsg ) {
case WM_NCCREATE: {
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
viewvars * pviewvars = (viewvars*)pcs->lpCreateParams;
pviewvars->m_hWnd = hwnd;
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG)pcs->lpCreateParams );
return TRUE;
}
case WM_DESTROY: {
viewvars * pviewvars = (viewvars *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
if ( pviewvars ) pviewvars->m_hWnd = 0;
break;
}
default:
return DefWindowProc( hwnd, uMsg, wParam, lParam );
}
return 0;
}
viewvars::~viewvars() {
if ( m_hWnd ) DestroyWindow( m_hWnd );
}
Finally, a "main" sample, but beware that there is here no way to end the process. That should be taken care by regular code (another windows).
// MAIN.CPP
#include <Windows.h>
#include "viewvars.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
viewvars * pviewvars = viewvars::CreateTestWindow( hInstance );
if ( pviewvars == 0 ) return 0;
BOOL bRet;
MSG msg;
while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
delete pviewvars;
return 0;
}
Unfortunately using an instance method as a C-style callback function for the WndProc won't work. At least not in any straight-forward way.
The reason it doesn't work like that is that an instance method requires the this pointer to be passed in (to point to an instance) and that won't be correctly set by the code calling the WndProc. The Win32 API was originally designed with C in mind so this is one area where you have to use some work-arounds.
One way to work around this would be to create a static method to serve as the window proc and dispatch messages to your class instances. The class instances would have to be registered (read added to a static collection) so the static method would know to dispatch WndProc messages to the instances. Instances would register themselves with the static dispatcher in the constructor and remove themselves in the destructor.
Of course all the registration and unregistration and dispatching overhead is only necessary if your WndProc handler needs to invoke other instance member functions, or access member variables. Otherwise you can just make it static and you're done.
Your window procedure is called during CreateWindow. You pass hWindow to DefWindowProc, but hWindow is only set after CreateWindow returns - so you pass DefWindowProc a garbage window handle.
I don't see a nice way to do it. You could set hWindow inside the window procedure, by changing WindowProc to:
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
(added the hwnd parameter), changing the call to:
return view->WindowProc(hwnd, message, wParam, lParam);
creating the window like this:
hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
(the first assignment is to make sure hWindow is initialized; the second one is in case CreateWindow fails after calling the window procedure), and adding this at the start of WindowProc:
if(!this->hWindow)
this->hWindow = hwnd;
Step through the code in the debugger. When you get to the line
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
You will see something wrong: hWindow is garbage. You are using an uninitialized variable.
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?