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.
Related
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
....
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.
Ok, so i made a class which creates a HWND.
However, the window created shows some strange properties: it is not like other windows - it's non-transparent, the close-minimize-maximize buttons are located differently from normal windows.
But the style specified is default (WM_OVERLAPPEDWINDOW).
What's more, it can't be closed unless i move it a bit (seems like it is not generating WM_DESTROY or WM_CLOSE messages before moving).
This might be a problem with the implementation of main WinProc calling another message processer using pointers. However, i have no idea why the window is unusually looking.
My code:
//mywind.h
class Window
{
private:
HWND mHwnd;
const char* className="Window";
static LRESULT CALLBACK StartWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); //main WindowProc function
LRESULT ThisWindowProc(UINT msg, WPARAM wParam, LPARAM lParam); //Another, object-specific message processing function
bool isClassRegistered(HINSTANCE hinst);
public:
Window() : mHwnd( 0 ) { }
~Window();
int create(std::string title, int width, int height);
};
//mywind.cpp
Window::~Window()
{
if( mHwnd ) DestroyWindow( mHwnd );
}
int Window::create(std::string title, int width, int height)
{
WNDCLASS wincl;
HINSTANCE hInst = NULL;
hInst = GetModuleHandle(NULL);
if(hInst==NULL)
{
printf("Failed to load hInstance\n");
return -1;
}
if(!GetClassInfo(hInst, className, &wincl))
{
printf("Getting class info.\n");
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = className;
wincl.lpfnWndProc = StartWindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wincl.lpszMenuName = NULL;
if(!isClassRegistered(hInst))
{
if (RegisterClass(&wincl) == 0)
{
printf("The class failed to register.\n");
return 0;
}
}
}
mHwnd = CreateWindow(className, title.c_str(), WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
NULL, NULL, hInst, this);
if(mHwnd==NULL)
{
printf("Failed to create HWND.\n");
return -1;
}
MSG msg;
while(GetMessage(&msg, mHwnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
printf("Destroying window.\n");
if(mHwnd) DestroyWindow(mHwnd);
mHwnd=NULL;
printf("Returning.\n");
return msg.wParam;
}
bool Window::isClassRegistered(HINSTANCE hinst)
{
WNDCLASSEX clinf;
if(!GetClassInfoEx(hinst, className, &clinf)) return false;
return true;
}
LRESULT CALLBACK Window::StartWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window* winp = NULL;
if(msg == WM_CREATE)
{
CREATESTRUCT* cs = (CREATESTRUCT*) lParam;
winp = (Window*) cs->lpCreateParams;
SetLastError(0);
if(SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) winp) == 0)
{
if(GetLastError()!=0) return -1;
}
}
else
{
winp = (Window*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if(winp) return winp->ThisWindowProc(msg, wParam, lParam);
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT Window::ThisWindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(mHwnd, msg, wParam, lParam);
}
return 0;
}
//main.cpp
#include "mywind.h"
int main(int argc, char* argv[])
{
Window mwnd;
mwnd.create("Test", 200, 200);
return 0;
}
Notice that you don't set mHwnd until CreateWindowEx returns. This means that all the messages sent during window creation pass 0 to DefWindowProc instead of the actual window handle. This causes the window manager to think you are bypassing default handling and doing custom captions, which is why everything looks wrong.
TL;DR: Set mHwnd inside your WM_NCCREATE handler.
I am 90% sure that your problem is the missing manifest which enables WinXP/7 look. Now you are in compatibility mode and the window frame is for Windows 98/2000 style. For more details read this link (or many others for the same problem):
http://www.mctainsh.com/Articles/Csharp/XpControlsInCS.aspx
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;
}
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?