CreateWindowEx does not create HWND with the instance of a class template - c++

I'm following this tutorial and if I keep all the definitions in a single header file, it's all fine and works (I mean, if I directly copy this code). But if I try to move the definitions into separate files, it won't create HWND.
On CreateWindowEx called, it goes to the BaseWindow<DERIVED_TYPE>::WindowProc and sends the following messages, one after the other in a sequence:
WM_GETMINMAXINFO
WM_NCCREATE
WM_NCDESTROY
After it steps out, it results in HWND not being created. Thus, no window will show up.
The structure of the project:
main.cpp
Windows/
BaseWindow.h
BaseWindow.cpp
MainWindow.h
MainWindow.cpp
This is how my code looks.
main.cpp
#ifndef UNICODE
#define UNICODE
#endif // !UNICODE
#include <windows.h>
#include <new>
#include "Windows/MainWindow.h"
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
MainWindow win;
if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
{
return 0;
}
ShowWindow(win.Window(), nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Windows/BaseWindow.h
#pragma once
#include <windows.h>
template <typename DERIVED_TYPE>
class BaseWindow
{
public:
BaseWindow() : m_hwnd(NULL) { }
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0
);
HWND Window() const { return m_hwnd; }
protected:
virtual PCWSTR ClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
HWND m_hwnd;
};
Windows/BaseWindow.cpp
#include "BaseWindow.h"
#include "MainWindow.h"
template <typename DERIVED_TYPE>
LRESULT CALLBACK BaseWindow<DERIVED_TYPE>::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE* pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
}
else
{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
template <typename DERIVED_TYPE>
BOOL BaseWindow<DERIVED_TYPE>::Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu
)
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = ClassName();
RegisterClass(&wc);
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
);
return (m_hwnd ? TRUE : FALSE);
}
// !The definition of the class template
template class BaseWindow<MainWindow>;
Windows/MainWindow.h
#pragma once
#include "BaseWindow.h"
class MainWindow : public BaseWindow<MainWindow>
{
public:
PCWSTR ClassName() const { return L"Sample Window Class"; }
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
Windows/MainWindow.cpp
#include "MainWindow.h"
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
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:
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
I've tried to do it on mere classes without templating and it works.
I've also tried to remove this at the end of the argument list of CreateWindowEx and it worked too! I mean, if it looks like that, it will work:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), NULL
);
So maybe I'm somehow misusing templates.

Before CreateWindowEx returns several messages will be pumped. All but one (WM_NCCREATE) will route through HandleMessage, which expects m_hwnd to be a valid handle. They're not getting that because you never save it until the final result of CreateWindowEx is reaped by return value. By then it is too late.
WM_NCCREATE is the first window message received by top-level windows (e.g. yours), and should be the one used to (a) save the hwnd parameter to m_hwnd, and (b) store the instance pointer int GWLP_USERDATA space. You're doing the latter, but you didn't do the former.
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
pThis->m_hwnd = hwnd; // <===== ADD THIS =====
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);

HandleMessage() uses m_hwnd, which is not initialized. That is why CreateWindowEx() returns NULL.
1. CreateWindowEx
2. sends WM_CREATE
3. WM_CREATE is processed by WindowProc
4. WindowProc calls HandleMessage, which uses m_hwnd,
which is not initialized, so HandleMessage returns
failure
5. Failure is returned as the result for WM_CREATE,
cancelling window creation
6. CreateWindowEx fails and returns NULL <--- and right now
m_hwnd is assigned
In short: you cannot use m_hwnd until CreateWindowEx() returns.
WM_CREATE reference
If an application processes this message, it should return zero to continue creation of the window. If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle.

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
....

Microsoft Windows tutorial code sets the same variable twice(not my code!).C++

coded in c++, presumably using visual studio.
template <class DERIVED_TYPE>
class BaseWindow
{
public:
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
//m_hwnd GETS SET HERE:
pThis->m_hwnd = hwnd;
}
else
{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
BaseWindow() : m_hwnd(NULL) { }
BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0
)
{
WNDCLASS wc = {0};
wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = ClassName();
RegisterClass(&wc);
//m_hwnd GETS SET HERE:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
);
return (m_hwnd ? TRUE : FALSE);
}
HWND Window() const { return m_hwnd; }
protected:
virtual PCWSTR ClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
HWND m_hwnd;
};
the variable m_hwnd gets set twice for the same instance(atleast that is how I perceive it):
pThis->m_hwnd = hwnd;
and here:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
);
Is there a reason for this? is the m_hwnd actually set only once and i am missing something?
MINIMUM REPRODUCIBLE EXAMPLE:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
MainWindow win;
if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
{
return 0;
}
ShowWindow(win.Window(), nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
SITE: https://learn.microsoft.com/en-us/windows/win32/learnwin32/managing-application-state-
The callback is a static function with no access to any instance of the class, it receives a pointer to the class instance passed as parameter when the window was created:
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL),
this /* here you give the instance, Windows will give you this when calling WindowProc */
);
After that you get the pointer to the derived class:
DERIVED_TYPE *pThis = NULL;
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
And you set the derived m_hwnd, which may be the same base, protected member or may be overridden by the derived.

LNK2001 in "How can I make a WNDPROC or DLGPROC a member of my C++ class?"

VS10: MCBS: Hi there,
In the light of this discussion encountered a problem attempting a Hello World implementation of Raymond Chen's method in How can I make a WNDPROC or DLGPROC a member of my C++ class? using Pudeyev's code for "Hello World":
error LNK2001: unresolved external symbol "private: long __thiscall
BaseWnd::WndProc(struct HWND__ *,unsigned int,unsigned int,long)"
(?WndProc#BaseWnd##AAEJPAUHWND__##IIJ#Z)
The code is as follows:
// ...Written by Oleg Pudeyev, (c) 2003
#include <windows.h>
HINSTANCE appHinst;
class BaseWnd
{
public:
BaseWnd();
// This is the static callback that we register
static LRESULT CALLBACK s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// The static callback recovers the "this" pointer and then calls this member function.
LRESULT BaseWnd::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
BaseWnd::BaseWnd(void)
{
WNDCLASSW wc = {
// Request redraws as we're centering the string
CS_HREDRAW|CS_VREDRAW,
BaseWnd::s_WndProc,
// No per-class data
0,
// No per-window data
0,
appHinst,
LoadIcon(0, IDI_APPLICATION),
LoadCursor(0, IDC_ARROW),
HBRUSH(COLOR_BACKGROUND),
0,
L"BaseWnd"
};
RegisterClassW(&wc);
HWND hwnd = CreateWindowW(L"BaseWnd", L"Hello, World!", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, appHinst, this);
ShowWindow(hwnd, SW_SHOW);
}
LRESULT BaseWnd::WndProc(HWND hwnd, UINT Umsg, WPARAM wParam, LPARAM lParam)
{
switch (Umsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
RECT r;
GetClientRect(hwnd, &r);
SetBkMode(ps.hdc, TRANSPARENT);
DrawTextW(ps.hdc, L"Hello, World!", -1, &r, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
EndPaint(hwnd, &ps);
break;
default:
// Use default handling for messages we don't process
return DefWindowProc(hwnd, Umsg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK BaseWnd::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BaseWnd *pThis; // our "this" pointer will go here
if (uMsg == WM_NCCREATE) {
// Recover the "this" pointer which was passed as a parameter to CreateWindow(Ex).
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<BaseWnd*>(lpcs->lpCreateParams);
// Put the value in a safe place for future use
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
} else {
// Recover the "this" pointer from where our WM_NCCREATE handler stashed it.
pThis = reinterpret_cast<BaseWnd*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
if (pThis) {
// Now that we have recovered our "this" pointer, let the member function finish the job.
//Removal of this line removes the LNK2001
return pThis->WndProc(hwnd, uMsg, wParam, lParam);
}
// "this" pointer unknown, so just do the default thing. Hopefully, didn't need to customize behavior yet.
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPTSTR cmdline, int showcmd)
{
appHinst = hinst;
BaseWnd p;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Is there something wrong with either how the BaseWnd constructor is invoked or the class setup - or perhaps, viz the date on Raymond's article, a revision of c++11 that cannot cope?
Edit: Replaced WndProc with absolute qualifier BaseWnd::WndProc and made the offending static callback a callback. Great, the other issues with the code we can work with.
Edit2: The final piece of the puzzle was returning Pudeyev's original CS_HREDRAW|CS_VREDRAW to the WNDCLASS structure.
Apparently I think a definition of scope is none.
LRESULT CALLBACK WndProc
{
 ↓
LRESULT CALLBACK BaseWnd::WndProc
{
The error should be self-explanatory. BaseWnd::WndProc is declared, but not defined. See #nariuji
Also BaseWnd needs a public constructor because you are going to call it later:
class BaseWnd
{
//******* make this public:
public:
BaseWnd();
...
};
WNDCLASSW wc has to be initialized to zero:
WNDCLASSW wc = { 0 };
When you create the window it is invisible. You have to make it visible with ShowWindow or change the style to WS_VISIBLE | WS_OVERLAPPEDWINDOW
HWND hwnd = CreateWindowW(L"BaseWnd", L"Hello, World!", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, appHinst, this);
ShowWindow(hwnd, SW_SHOW); //*** add this
Declaring a pointer doesn't do anything. You also need a message loop:
WinMain(...)
{
//BaseWnd *pthis; //***remove this line
BaseWnd wnd;//***add this line
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
...
}

Win32 window fails to create?

I did my research but couldn't find an answer. The closest thing I found is "Cannot create window", but it didn't help me. So, here it is!
Basic info
I have a static library and an application using a static library. I hooked up the application to the static library correctly (Include directories, library directories, actual library dependencies, etc). In the static library i have 1 file: IWindow.h. In the application I have 3 files: Main.cpp, WindowMain.h and WindowMain.cpp. IWindow.h defines an abstract window class:
#ifndef IWINDOW_H
#define IWINDOW_H
#include <Windows.h>
namespace NamespaceName
{
template<class T>
class IWindow
{
public:
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
virtual ~IWindow(){}
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
virtual VOID paint(HDC hDC) = 0;
VOID create(HINSTANCE hI, LPCWSTR title, LPCWSTR className, DWORD dwStyle, DWORD dwExStyle = 0, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT, HWND hWndParent = 0, HMENU hMenu = 0);
HWND getHWND();
BOOL isCreated();
protected:
HWND m_hWnd;
BOOL created;
};
template<class T>
LRESULT CALLBACK IWindow<T>::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
T* pThis = NULL;
if(uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (T*)pCreate->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
}
else
{
pThis = (T*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
if(pThis)
{
return pThis->handleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
template<class T>
VOID IWindow<T>::create(HINSTANCE hI, LPCWSTR title, LPCWSTR className, DWORD dwStyle, DWORD dwExStyle = 0, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT, HWND hWndParent = 0, HMENU hMenu = 0)
{
WNDCLASS windowClass = {0};
windowClass.hInstance = hI;
windowClass.lpszClassName = className;
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.lpfnWndProc = IWindow::windowProc;
RegisterClass(&windowClass);
m_hWnd = CreateWindowEx(dwExStyle, className, title, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this);
created = (m_hWnd ? TRUE : FALSE);
}
template<class T>
HWND IWindow<T>::getHWND()
{
return m_hWnd;
}
template<class T>
BOOL IWindow<T>::isCreated()
{
return created;
}
}
#endif
Then,
WindowMain.h defines a subclass of IWindow.h
Code:
#ifndef WINDOWMAIN_H
#define WINDOWMAIN_H
#include <FolderName\Video\GUI\IWindow.h>
class WindowMain : public NamespaceName::IWindow<WindowMain>
{
public:
~WindowMain(){}
LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID paint(HDC hDC);
};
#endif
And,
it's accompanying .cpp file
completes it:
#include "WindowMain.h"
LRESULT WindowMain::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
hDC = BeginPaint(m_hWnd, &ps);
FillRect(hDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
paint(hDC);
EndPaint(m_hWnd, &ps);
}
return 0;
}
return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
VOID WindowMain::paint(HDC hDC)
{
}
And finally,
the Main.cpp
Code:
#include <Windows.h>
#include <tchar.h>
#include <GdiPlus.h>
#include "WindowMain.h"
#pragma comment(lib, "Gdiplus.lib")
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WindowMain window;
MSG msg;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
window.create(hInstance, L"Test Window", L"Test Window Class", WS_OVERLAPPEDWINDOW);
if(!window.isCreated())
return 1;
ShowWindow(window.getHWND(), nCmdShow);
UpdateWindow(window.getHWND());
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
The window.isCreated() always returns false, thus making the if statement in the Main.cpp invert it to true and always returning 1 and exiting the application. If I omit the if statement from Main.cpp the window does not show up and the application goes on forever, until i force-stop it in the IDE.
Additional questions (answer in comments if you want, these are optional and do not relate to the former question in any way)
I don't like Visual Studio at all. I prefer using NetBeans so much more. I tried to use it for Windows programming but I failed. Do I have to use Visual Studio for Windows programming? Does it have some magical compiler that can compile Win32 programs in a special way? Or am I just doing something wrong?
What are these Gdiplus tokens and startup input? A link to somewhere that explains it would be awesome.
Finally
Thank you for taking your time to read this, and potentially trying to help me. If you need any more information I'll be happy to provide it. If the question was poorly constructed please let me know how to improve it and I will. ;)
Edit #1
Found this: "Win32 API window won't open", just for the record, it didn't help my case either.
Edit #2
In IWindow.h, when I was creating a WNDCLASS, for the window process, I tried to use T::windowProc instead of IWindow::windowProc, but it didn't help either.
Edit #3
Found "WinAPI window doesn't appear", but didn't help either.
Edit #4
"Try setting windowClass.cbWndExtra to sizeof(LONG_PTR) before registering the class." -suggestion from the comments. Tried it too, and also didn't help.
Edit #5
Tried replacing WNDCLASS with WNDCLASSEX and RegisterClass with RegisterClassEx and added windowClassEx.cbSize = sizeof(WNDCLASSEX) (I changed the variable name from windowClass to windowClassEx too), but didn't help either...
I see at least two errors.
First, you register the class with
windowClass.hInstance = hI;
but when you create the window, you pass a different HINSTANCE:
m_hWnd = CreateWindowEx(dwExStyle, className, title, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this);
// ^^^^^^^^^^^^^^^^^^^^^
The HINSTANCE parameters must match in order for the class to be found. You should use hI in both places.
Second, your code uses the m_hWnd member before it is initialized.
return DefWindowProc(m_hWnd, uMsg, wParam, lParam);
When the window receives the WM_NCCREATE message, m_hWnd has not yet been initialized. It doesn't get initialized until the CreateWindowEx returns. You need to get the correct window handle to DefWindowProc. One way is to pass the hWnd parameter from windowproc through to handlemessage. Another is to add
m_hWnd = hWnd;
in your if(uMsg == WM_NCCREATE).
Notice that your code assumes that if GWLP_USERDATA is nonzero, then m_hWnd is valid. However, you did nothing to actually make this assumption valid. Between the receipt of the WM_NCCREATE message and the completion of CreateWindowEx, you have a nonzero GWLP_USREDATA but m_hWnd is not initialized.
The way to debug this is to set a breakpoint on your window procedure and step through it. When stepping through the handling of the WM_NCCREATE message, you should have noticed that m_hWnd is not initialized.
There is a third bug in this code, which you would eventually stumble across once you get the window created: You never set m_hWnd back to NULL when the window is destroyed.

C++, WIN32, WndProc to Member Function Crash (Access violation at 0x88)

I've been writing a win32 wrapper class, and I've come across a problem: Because each instance of the class has a window, I've enclosed the this pointer in the user info space using SetWindowLongPtrW(), allowing me to call a message handler from the static WndProc function. This works fine: I can call the function. However, when I try to call another member function from the message handler, I get an access violation at 0x00000088
It does compile.
I posted quite a lot, because to be honest I'm not too sure where the problem originates from...
Please feel free to comment/criticize my code in general. Thanks for the help!
Here is the header:
#pragma once
#include <Windows.h>
#include "GlobalDefines.h"
#include "GraphicsWrapper.h"
#include "Keyboard.h"
namespace Startup
{
class GraphicsWrapper;
class WindowsWrapper
{
public:
WindowsWrapper(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow);
~WindowsWrapper();
void EnterMsgLoop(GraphicsWrapper* Gfx);
static LRESULT _stdcall WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
LRESULT _stdcall MessageHandler(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
WNDCLASSEX WndClass;
MSG Message;
RECT Desktop;
RECT Taskbar;
RECT WindowCoordinates;
LPSTR CommandLineArgs;
INT CmdShow;
HINSTANCE TheInstance;
HWND WindowHandle;
void InitializeWndClassEx();
void InitializeWindowHandleHWND();
void ShowWindowOnScreen();
bool GetScreenRect(RECT & Desktop);
bool GetTaskbarRect(RECT& rectTaskbar);
bool GetWindowCoords(RECT& WindowCoordinates);
int GetTaskbarSide();
enum TaskbarSides
{
Top,
Right,
Bottom,
Left
};
void SetFullScreen(bool Enable);
};
static IO::Keyboard * kbd;
}
And this is the relevant part of the implementation. I'll mark where the crash occurs.
void Startup::WindowsWrapper::InitializeWndClassEx()
{
WndClass.hIcon = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.hIconSm = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = WindowProc;
WndClass.hInstance = TheInstance;
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.lpszClassName = L"WindowClassName";
RegisterClassEx(&WndClass);
SetWindowLongPtrW(WindowHandle, GWLP_USERDATA, (long)this);
}
void Startup::WindowsWrapper::SetFullScreen(bool Enable)
{
long style = Enable ? WS_POPUP : WS_OVERLAPPED | WS_SYSMENU;
static RECT windowRect = {};
static bool needRect = true;
if (needRect)
{
GetWindowRect(WindowHandle, &windowRect);
needRect = false;
}
SetWindowLong(WindowHandle, GWL_STYLE, style);
if (Enable)
{
SetWindowPos(WindowHandle, HWND_TOPMOST,
0,0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SWP_SHOWWINDOW);
}
else
{
SetWindowPos(WindowHandle, 0,
windowRect.left,windowRect.top,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
SWP_SHOWWINDOW);
}
}
and
LRESULT CALLBACK Startup::WindowsWrapper::WindowProc
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
WindowsWrapper* ourObjectPtr = NULL;
long thisObject = GetWindowLongW(hWnd, GWLP_USERDATA);
ourObjectPtr = (WindowsWrapper *)( (void*)thisObject);
long Result = ourObjectPtr->MessageHandler(hWnd, message, wParam, lParam);
RET(Result);
}
LRESULT _stdcall Startup::WindowsWrapper::MessageHandler
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0); //Works fine here, but...
break;
case VK_SPACE:
this->SetFullScreen(false); //Crashes here w/ access violation
break;
case VK_SHIFT:
this->SetFullScreen(true); //Or here, w/ the same error.
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
This is the createWindowEx call. Thanks for your help, again.
void Startup::WindowsWrapper::InitializeWindowHandleHWND()
{
WindowHandle = CreateWindowEx(NULL,
L"WindowClassName",
L"WindowTitle"
WS_OVERLAPPED | WS_SYSMENU,
WindowCoordinates.left, WindowCoordinates.top,
WindowCoordinates.right, WindowCoordinates.bottom,
NULL, NULL, TheInstance,
CommandLineArgs);
}
I have some code from a custom dialog handler I wrote quite a while back, which might be of use to you.
Same principle applies for a window but switch the WM_INITDIALOG for WM_CREATE and also replace DWLP_USER with GWLP_USERDATA. The format of the callback is subtley different too. You should be able to salvage almost all of this function though.
LRESULT CALLBACK CDialog::DlgProc( HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
CDialog* pWindow;
if( msg == WM_INITDIALOG ) {
SetWindowLongPtr( hWndDlg, DWLP_USER, (LONG_PTR)lParam );
pWindow = reinterpret_cast<CDialog*>( lParam );
pWindow->m_hWnd = hWndDlg;
} else {
pWindow = reinterpret_cast<CDialog*>( (LPARAM)GetWindowLongPtr( hWndDlg, DWLP_USER ) );
}
if( pWindow != NULL ) {
LRESULT ret = pWindow->OnMessage( msg, wParam, lParam );
if( msg == WM_NCDESTROY ) pWindow->m_hWnd = NULL;
}
return FALSE;
}