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;
}
Related
I'm new to Win32 API and want to create a window. I have a class (as follows) but I get an exception at the line return p_this->HandleMessages(msg, wParam, lParam);.
Window.h (I got this in some msdn website and modified to some extent):
#include "BaseWin.h"
template<class D_CLASS>
class Window
:public BaseWin
{
public:
int returnValue;
private:
static constexpr LPCSTR className = "Best Window in the UNIVERSE!";
public:
// declare this fn in the inherited class and use as wnd proc
virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam) = 0;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D_CLASS* p_this = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*)lParam;
p_this = (D_CLASS*)create->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
}
else
p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (p_this)
return p_this->HandleMessages(msg, wParam, lParam); // exception here
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Window(HWND parent, LPCSTR title, int id, const Point& pos, const Size& _size,
int styles = WS_OVERLAPPEDWINDOW, int retVal = 0x45, int stylesEx = 0
)
:BaseWin(parent, pos, _size, id), returnValue(retVal)
{
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = D_CLASS::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = nullptr;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = className;
wc.hIconSm = nullptr;
RegisterClassEx(&wc);
HMENU _id = nullptr;
if (!((styles & WS_CHILD) != WS_CHILD || id == -1))
_id = (HMENU)ID;
hWnd = CreateWindowEx(
stylesEx,
className, title,
styles,
Position.x, Position.y, size.x, size.y,
parent, _id, GetModuleHandle(nullptr), this
);
}
~Window()
{
UnregisterClass(className, GetModuleHandle(nullptr));
Destroy();
}
};
I'm using Visual Studio and whenever the line is reached is reached, a breakpoint is placed there are no details through which I can deduce the resaon (It's: main.exe has encountered a breakpoint).
main.cpp:
#include "Window.h"
#include "Button.h"
class MainWindow
:public Window<MainWindow>
{
private:
HMENU menuBar;
HMENU menuIt;
enum
{
BTN_KILL,
M_QUIT,
M_ADD_LOG
};
public:
MainWindow()
:Window<MainWindow>(nullptr, "Useless Manger", -1, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(640, 480), normWinStyle)
{
Button* btn = new Button(hWnd, BTN_KILL, "Kill Me", Point(), Size(300, 100));
menuBar = CreateMenu();
menuIt = CreateMenu();
AppendMenu(menuIt, MF_ENABLED, M_ADD_LOG, "Add Log");
AppendMenu(menuBar, MF_ENABLED | MF_STRING, M_QUIT, "Quit");
AppendMenu(menuBar, MF_POPUP, (UINT_PTR)menuIt, "Add");
SetMenu(hWnd, menuBar);
}
LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hWnd, &ps);
break;
}
case WM_COMMAND:
if (LOWORD(wParam) == BTN_KILL)
{
PostQuitMessage(this->returnValue);
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
};
int CALLBACK WinMain(
HINSTANCE hInst,
HINSTANCE h_p_Inst,
LPSTR nCmdLine,
int nCmdShow)
{
MainWindow* window = new MainWindow();
window->Show();
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BaswWin.h is a class holding the current HWND, position, size, ID, etc with some functions like Show().
How do I fix this?
MainWindow::HandleMessages is called before superclass Window<T> is fully constructed. Before Window<T> is fully constructed, entry for Window<T>::HandleMessages refers to special function witch reports that pure function was called. When Window<T> is fully constructed, this entry is replaced by MainWindow::HandleMessages.
In C++ calling virtual functions when superclasses are still constructed needs special atention.
You can replace Window<T>::HandleMessage with unpure implementation.
virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, msg, wparam, lparam);
}
Initialize hWnd early, because WndProc can be called before CreateWindowEx returns.
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D_CLASS* p_this = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*)lParam;
p_this = (D_CLASS*)create->lpCreateParams;
// initialize early
p_this->hWnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
}
else
p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (p_this)
// if you don't initialize early, skip when hWnd == nullptr
// if (p_this && p_this->hWnd)
return p_this->HandleMessages(msg, wParam, lParam);
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Probably you would also miss WM_DESTROY message, when MainWindow class is already destroyed, but WM_DESTROY would be generated in Window<T>::~Window<T>.
Calling PostQuitMessage(this->returnValue); in MainWindow::~MainWindow could solve this problem.
I´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
....
Here is my header
#pragma once
#ifndef BASE_H
#define BASE_H
#include <Windows.h>
#include <windowsx.h>
class Base
{
HWND hWnd;
WNDCLASSEX WndCls;
HRESULT Hr;
public:
Base();
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RegisterWnd(HINSTANCE hInstance);
void ShowWnd(int nCmdShow);
~Base();
};
#endif
Here is my base.cpp
#include "Base.h"
Base::Base()
{
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch (message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc(hWnd, message, wParam, lParam);
}
void Base::RegisterWnd(HINSTANCE hInstance)
{
ZeroMemory(&WndCls, sizeof(WNDCLASSEX));
WndCls.cbSize = sizeof(WNDCLASSEX);
WndCls.hbrBackground = (HBRUSH)COLOR_WINDOW;
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hIcon = LoadIcon(hInstance, NULL);
WndCls.hIconSm = LoadIcon(hInstance, NULL);
WndCls.hInstance = hInstance;
WndCls.lpfnWndProc = WndProc;
WndCls.lpszClassName = "ClsName";
WndCls.style = CS_HREDRAW | CS_VREDRAW;
Hr = RegisterClassEx(&WndCls);
if (FAILED(Hr))
MessageBox(NULL, "Window Class failed to register.", "ERROR", MB_OK);
hWnd = CreateWindowEx(
NULL,
"WndClassName",
"WndName",
WS_OVERLAPPEDWINDOW,
100, 100,
480, 640,
NULL,
NULL,
hInstance,
NULL);
if (FAILED(hWnd))
MessageBox(NULL, "Window Class failed to create", "ERROR", MB_OK);
}
void Base::ShowWnd(int nCmdShow)
{
Hr = ShowWindow(hWnd, nCmdShow);
if (FAILED(Hr))
MessageBox(NULL, "Failed to display Window", "ERROR", MB_OK);
}
Base::~Base()
{
}
And here is my main.cpp
#include "Base.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
Base CreateWnd;
CreateWnd.RegisterWnd(hInstance);
CreateWnd.ShowWnd(nCmdShow);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&Msg);
// send the message to the WindowProc function
DispatchMessage(&Msg);
}
// return this part of the WM_QUIT message to Windows
return Msg.wParam;
}
The problem is, I keep getting this error message that I dont understand of. Sorry for the bad explanation..Still a student in programming...
UPDATED :
The error above has been corrected by replacing
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
with
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Thanks to ravi and IInspectable for the quick help.
Now I am having another error D: When i clicked on debug, everything run perfectly but nothing shows up. No window is showing. Visual studio is running perfectly as "Ready". (Sorry i do not want to make another new question because it's still related to creating window in oo
SECOND UPDATE :
My class name in CreateWindowEx is different from the RegisterWnd..My bad. Thanks to IInspectable again for the help.
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
You have to define this with class scope OR how compiler know if its global static OR static member of class. So it should be
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Pre-Text/ Question
I am trying to make a fairly simple tool to help debug variable values. For it to be completely self contained within the class is what I am aiming for. The end product I can use a function in the class like ShowThisValue(whatever).
The problem I am having is that I can't figure out, if possible, to have the procedure within the class. Here is the short version, with the problem.
-Code updated again 11/29/13-
-I have put this in its own project now.
[main.cpp]
viewvars TEST; // global
TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil);
[viewvars.h] The entire updated
class viewvars {
private:
HWND hWindow; // the window, a pointer to
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
viewvars(); // blank constructor
int CreateTestWindow(HINSTANCE hInst);
};
// blank constructor
viewvars::viewvars() {}
// create the window
int viewvars::CreateTestWindow(HINSTANCE hInst) {
// variables
char thisClassName[] = "viewVars";
MSG msg;
WNDCLASS wincl;
// check for class info and modify the info
if (!GetClassInfo(hInst, thisClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return -1;
}
}
// create window
hWindow = CreateWindow(thisClassName, "Test", WS_POPUP | WS_CLIPCHILDREN, 10, 10, 200, 200, NULL, NULL, hInst, this);
if (hWindow == NULL) {
MessageBox(NULL,"Problem creating the window.","Error",0);
return -1;
}
// show window
ShowWindow(hWindow, TRUE);
// message loop
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// then quit window?
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
// window proc
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
MessageBox(NULL,"Has it gone this far?","Bench",0);
// variable
viewvars *view;
// ????
if (message == WM_NCCREATE) {
CREATESTRUCT *cs = (CREATESTRUCT*)lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0) {
if (GetLastError() != 0) {
MessageBox(NULL,"There has been an error near here.","Error",0);
return FALSE;
}
}
}
else {
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view) return view->WindowProc(message, wParam, lParam);
MessageBox(NULL,"If shown, the above statement did not return, and the statement below did.","Error",0);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
// you can access non-static members in here...
MessageBox(NULL,"Made it to window proc.","Error",0);
switch (message)
{
case WM_PAINT:
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
break;
}
}
The message boxes appear in this order:
Has it made it this far?
Made it to window proc
DefWindowProc returned
Has it made it this far? // repeated?
Made it to window proc
DefWindowProc returned
Problem Creating the Window
Thanks for the help so far. Do you know where the problem might be?
To use a non-static class method as a window procedure requires a dynamically-allocated thunk, which is an advanced technique that I will not get into it here.
The alternative is to declare the class method as static, then it will work as a window procedure. Of course, being a static method, it can no longer access non-static class members without an instance pointer. To get that pointer, you can have the class pass its this pointer to the lpParam parameter of CreateWindow/Ex(), then the window procedure can extract that pointer from the WM_NCCREATE message and store it in the window using SetWindowLong/Ptr(GWL_USERDATA). After that, subsequent messages can retrieve that pointer using GetWindowLong/Ptr(GWL_USERDATA) and thus be able to access non-static members of that object. For example:
class viewvars
{
private:
HWND hWindow;
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
int CreateTestWindow(HINSTANCE hInst);
};
int viewvars::CreateTestWindow(HINSTANCE hInst)
{
WNDCLASS wincl;
if (!GetClassInfo(hInst, thisClassName, &wincl))
{
...
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
if (RegisterClass(&wincl) == 0)
return -1;
}
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
...
MSG msg;
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
viewvars *view;
if (message == WM_NCCREATE)
{
CREATESTRUCT *cs = (CREATESTRUCT*) lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0)
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view)
return view->WindowProc(message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// you can access non-static members in here...
switch (message)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWindow, message, wParam, lParam);
}
}
The main message loop must not be in your class, and especially not in a "CreateTestWindow" function, as you will not return from that function until your thread receive the WM_QUIT message that makes GetMessage returns 0.
Here is simple implementation of your viewvars class. Key points:
The Window Proc is a static member.
The link between the Window Proc and the object is made through the
use of GWLP_USERDATA. See SetWindowLongPtr.
The class DTOR destroys the window if it still exists. The WM_DESTROY
message set the HWND member to 0.
Adding OnMsgXXX methods to the class is simple: declare/define then
and just call them from the WindowProc using the 'this' pointer
stored in GWLP_USERDATA.
EDIT:
As per Mr Chen suggestion, earlier binding of the HWND to the Object (in WM_NCCREATE) to allow message handler as methods during the Window Creation.
I changed the creation styles, to show the window and to be able to move it.
// VIEWVARS.H
class viewvars {
public:
static viewvars* CreateTestWindow( HINSTANCE hInstance );
viewvars() : m_hWnd( 0 ) {}
~viewvars();
private:
static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static const char * m_pszClassName;
HWND m_hWnd;
};
// VIEWVARS.CPP
#include "viewvars.h"
const char * viewvars::m_pszClassName = "viewvars";
viewvars * viewvars::CreateTestWindow( HINSTANCE hInst ) {
WNDCLASS wincl;
if (!GetClassInfo(hInst, m_pszClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = m_pszClassName;
wincl.lpfnWndProc = WindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return 0;
}
}
viewvars * pviewvars = new viewvars;
HWND hWnd = CreateWindow( m_pszClassName, "Test", WS_VISIBLE | WS_OVERLAPPED, 50, 50, 200, 200, NULL, NULL, hInst, pviewvars );
if ( hWnd == NULL ) {
delete pviewvars;
MessageBox(NULL,"Problem creating the window.","Error",0);
return 0;
}
return pviewvars;
}
LRESULT CALLBACK viewvars::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
switch ( uMsg ) {
case WM_NCCREATE: {
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
viewvars * pviewvars = (viewvars*)pcs->lpCreateParams;
pviewvars->m_hWnd = hwnd;
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG)pcs->lpCreateParams );
return TRUE;
}
case WM_DESTROY: {
viewvars * pviewvars = (viewvars *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
if ( pviewvars ) pviewvars->m_hWnd = 0;
break;
}
default:
return DefWindowProc( hwnd, uMsg, wParam, lParam );
}
return 0;
}
viewvars::~viewvars() {
if ( m_hWnd ) DestroyWindow( m_hWnd );
}
Finally, a "main" sample, but beware that there is here no way to end the process. That should be taken care by regular code (another windows).
// MAIN.CPP
#include <Windows.h>
#include "viewvars.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
viewvars * pviewvars = viewvars::CreateTestWindow( hInstance );
if ( pviewvars == 0 ) return 0;
BOOL bRet;
MSG msg;
while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
delete pviewvars;
return 0;
}
Unfortunately using an instance method as a C-style callback function for the WndProc won't work. At least not in any straight-forward way.
The reason it doesn't work like that is that an instance method requires the this pointer to be passed in (to point to an instance) and that won't be correctly set by the code calling the WndProc. The Win32 API was originally designed with C in mind so this is one area where you have to use some work-arounds.
One way to work around this would be to create a static method to serve as the window proc and dispatch messages to your class instances. The class instances would have to be registered (read added to a static collection) so the static method would know to dispatch WndProc messages to the instances. Instances would register themselves with the static dispatcher in the constructor and remove themselves in the destructor.
Of course all the registration and unregistration and dispatching overhead is only necessary if your WndProc handler needs to invoke other instance member functions, or access member variables. Otherwise you can just make it static and you're done.
Your window procedure is called during CreateWindow. You pass hWindow to DefWindowProc, but hWindow is only set after CreateWindow returns - so you pass DefWindowProc a garbage window handle.
I don't see a nice way to do it. You could set hWindow inside the window procedure, by changing WindowProc to:
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
(added the hwnd parameter), changing the call to:
return view->WindowProc(hwnd, message, wParam, lParam);
creating the window like this:
hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
(the first assignment is to make sure hWindow is initialized; the second one is in case CreateWindow fails after calling the window procedure), and adding this at the start of WindowProc:
if(!this->hWindow)
this->hWindow = hwnd;
Step through the code in the debugger. When you get to the line
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
You will see something wrong: hWindow is garbage. You are using an uninitialized variable.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
To pass a pointer to a member function
When I move out of the class the WNDPROC DefEditProc; and EditKeyProc all works ok. But now as I pasted the code it fails compilation with error error: invalid use of member function (did you forget the '()' ?). So my question is how to squeeze this code into class so I do not pollute global namespace ?
#include <windows.h>
#include <richedit.h>
class richEdit {
HWND richeditWindow;
WNDPROC DefEditProc;
public:
LRESULT EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
}
richEdit() {
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
DefEditProc = (WNDPROC)SetWindowLong(richeditWindow, GWL_WNDPROC, (long)EditKeyProc);
}
~richEdit() {
MSG msg;
while( GetMessageW( &msg, richeditWindow, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessageW( &msg );
}
}
};
int main() {
richEdit re;
}
You are supposed to use a free function instead of a member function, since member functions have an implicit this parameter. You will have to declare EditKeyProc as static CALLBACK, and find another way to pass around the this pointer if you need it.
static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
:::
}
Additionally, you may be able to use SetWindowSubclass which will take care of proper subclassing and will keep around an extra pointer argument for you.
Try this:
#include <windows.h>
#include <richedit.h>
class richEdit
{
private:
HWND richeditWindow;
WNDPROC DefEditProc;
static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
}
public:
richEdit()
: richeditWindow(NULL), DefEditProc(NULL)
{
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
if (NULL == richeditWindow) abort();
SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&EditKeyProc);
}
~richEdit()
{
if (richeditWindow != NULL)
{
SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
DestroyWindow(richeditWindow);
}
}
};
Or:
#include <windows.h>
#include <richedit.h>
namespace myNS
{
LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class richEdit
{
private:
HWND richeditWindow;
WNDPROC DefEditProc;
public:
richEdit()
: richeditWindow(NULL), DefEditProc(NULL)
{
HMODULE richedit_library = LoadLibrary("Msftedit.dll");
if (NULL == richedit_library) abort();
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
MSFTEDIT_CLASS,
L"window text",
WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
0, 0, 500, 500,
NULL, NULL, hInstance, NULL
);
if (NULL == richeditWindow) abort();
SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&richEditKeyProc);
}
~richEdit()
{
if (richeditWindow != NULL)
{
SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
DestroyWindow(richeditWindow);
}
}
};
LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
}
}
You can't use a pointer to a member function as a window procedure because Windows expects a regular function pointer. Internally, it won't have enough storage for the member pointer in some cases (they can be up to 16 bytes in MSVC, 32-bit), and it won't know what "this" pointer to use in any case.
If your only interest is avoiding namespace pollution, then the easiest solution is to use a namespace instead of a class. Otherwise, you'll have to make EditKeyProc a static member of the class and use SetWindowLong to store a copy of the "this" pointer for EditKeyProc to access.