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
....
Related
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.
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 Was working on a win32 project which uses the WM_MOVING message from WndProc to detect the new position where window is moved. but when i try to snap the window to left side the LPARAM gives wrong values for just a millisecond and gives the real value after that.
Please have a look into the output:
Left: 0, Right:683
Left: -205, Right:295
Left: 0, Right:683
Left: -205, Right:295
Left: 0, Right:683
Left: -205, Right:295
The real output is -205 but in between I get zero which cause the content of my window to flicker. In my case the position of the content is depend on the window position, so it causes flicker if I receive a wrong value.
Here is what i am trying to achieve : Manually creating acrylic effect
When ever I move the window in this mode (just before the aero snap mode) the value will get changed to zero.
This is only affecting The Top and Left coordinates of the window and also flickers when aero snapping to any sides.
Here is a simple reproduceable example:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
RECT* hostRect;
char buffer[200];
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0,CLASS_NAME,L"Learn to Program Windows",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOVING:
{
hostRect = reinterpret_cast<RECT*>(lParam);
sprintf_s(buffer, "Left: %d, Top:%d, Right:%d, Bottom:%d\n", hostRect->left, hostRect->top, hostRect->right, hostRect->bottom);
OutputDebugStringA(buffer);
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Run this program and check the value in output while you move the window to any side for aero snap.
Note: In my case i will not be able to use GetWindowRect() in WM_MOVING, because calling GetWindowRect() slow down the render() function (just a directx painting) in my window.
You can try to make temporary changes to the style of the window.
Here is an example:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <stdio.h>
#define WM_RESTOREORIGINALSTYLE WM_USER + 1
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
RECT* hostRect;
char buffer[200];
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, L"Learn to Program Windows", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOVING:
{
hostRect = reinterpret_cast<RECT*>(lParam);
sprintf_s(buffer, "Left: %d, Top:%d, Right:%d, Bottom:%d\n", hostRect->left, hostRect->top, hostRect->right, hostRect->bottom);
OutputDebugStringA(buffer);
break;
}
case WM_SYSCOMMAND:
{
if (wParam == (SC_MOVE | 2)) wParam = SC_SIZE | 9;
if ((wParam & 0xFFE0) == SC_SIZE && (wParam & 0x000F)) // handles MOVE and SIZE in one "if"
{
long int oldStyle = GetWindowLongW(hwnd, GWL_STYLE);
PostMessageW(hwnd, WM_RESTOREORIGINALSTYLE, GWL_STYLE, oldStyle);
SetWindowLongW(hwnd, GWL_STYLE, oldStyle & 0xFEFEFFFF); // disable WS_MAXIMIZE and WS_MAXIMIZEBOX
DefWindowProcW(hwnd, WM_SYSCOMMAND, wParam, lParam);
return 0;
}
return DefWindowProcW(hwnd, WM_SYSCOMMAND, wParam, lParam);
}
case WM_RESTOREORIGINALSTYLE:
{
if ((long int)wParam == GWL_STYLE)
SetWindowLongW(hwnd, GWL_STYLE, lParam);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
More reference : Win32 prevent window “snap”
I want to make WndProc a class member function and I found this article, so I tried to apply it to the simplest Win32 program, which does nothing but creating a blank window, the very first step of Win32.
int Myclass::Start(HINSTANCE hInstance, int nCmdShow)
{
if (FAILED(InitWindow(hInstance, nCmdShow)))
return 0;
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;;
}
LRESULT Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
Myclass* pThis = nullptr;
if (message == WM_NCCREATE) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<Myclass*>(lpcs->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
}
else {
pThis = reinterpret_cast<Myclass*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if(pThis)
return pThis->RealWndProc(hWnd, message, wParam, lParam);
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT Myclass::RealWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
It runs well, but when I close the window the program stays in the message loop and doesn't quit.
I found out that WM_DESTROY is not delivered to RealWndProc(), so PostQuitMessage() is not called.
If I insert if(WM_DESTROY == message) { PostQuitMessage(0); return 0; } right before the last line of StaticWndProc, then the program quits. But I'm not sure if this is a good way to do it.
How can I make WM_DESTROY consumed by RealWndProc()?
Like this is how I did it.. Works just fine.. The only problem I can think of in your code is one of two:
You missed the calling convention for the WindowProcedure.
You forgot to pass "this" to CreateWindowEx.
And the code:
#include <windows.h>
class Window
{
public:
Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW,
POINT Location = {CW_USEDEFAULT, CW_USEDEFAULT}, int Width = CW_USEDEFAULT,
int Height = CW_USEDEFAULT, HWND Parent = HWND_DESKTOP, HMENU Menu = nullptr);
int Loop();
private:
HWND WindowHandle;
static LRESULT __stdcall WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
LRESULT RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
};
Window::Window(LPCTSTR Title, LPCTSTR Class, DWORD dwStyleEx, DWORD dwStyle, POINT Location, int Width, int Height, HWND Parent, HMENU Menu)
{
WNDCLASSEX WndClass =
{
sizeof(WNDCLASSEX), CS_DBLCLKS, Window::WindowProcedure,
0, 0, GetModuleHandle(nullptr), LoadIcon(nullptr, IDI_APPLICATION),
LoadCursor(nullptr, IDC_ARROW), HBRUSH(COLOR_BACKGROUND),
nullptr, Class, LoadIcon(nullptr, IDI_APPLICATION)
};
if(RegisterClassEx(&WndClass))
{
WindowHandle = CreateWindowEx(dwStyleEx, Class, Title, dwStyle, Location.x, Location.y, Width, Height, Parent, Menu, GetModuleHandle(nullptr), this);
}
}
LRESULT __stdcall Window::WindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Window* Data = nullptr;
switch(Msg)
{
case WM_NCCREATE:
{
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
Data = static_cast<Window*>(pCreate->lpCreateParams);
SetWindowLongPtr(Hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(Data));
break;
}
default:
{
Data = reinterpret_cast<Window*>(GetWindowLongPtr(Hwnd, GWLP_USERDATA));
break;
}
}
return Data ? Data->RealWindowProcedure(Hwnd, Msg, wParam, lParam) : DefWindowProc(Hwnd, Msg, wParam, lParam);
}
LRESULT Window::RealWindowProcedure(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(Hwnd, Msg, wParam, lParam);
}
return 0;
}
int Window::Loop()
{
MSG Messages = {nullptr};
ShowWindow(WindowHandle, SW_SHOW);
while(GetMessage(&Messages, nullptr, 0, 0))
{
TranslateMessage(&Messages);
DispatchMessage(&Messages);
}
return Messages.wParam;
}
int main()
{
Window w("TItle", "Class");
return w.Loop();
}
You should catch WM_CLOSE and have it call DestroyWindow() to trigger WM_DESTROY:
case WM_CLOSE:
DestroyWindow(hWnd);
break;
Also, your StaticWndProc() method is using the wrong calling convention. It must use the __stdcall calling convention, which is wrapped by WINAPI and CALLBACK macros, eg:
LRESULT CALLBACK Myclass::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
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;
}