This question already has answers here:
Read access violation using m_pRenderTarget with winapi due to non-NULL pointer
(2 answers)
Closed 2 years ago.
I am trying to learn the win32 api using the Microsoft documentation. I have gotten to chapter 4, and I seem to have run into an issue that I am struggling to debug. The dialogue box triggered by the about button throws an exception:
Exception thrown at 0x773BDCFF (ntdll.dll) in practice.exe: 0xC0000005: Access violation reading location 0x00905A4C.
Here is the declaration of the WndProc and About callbacks:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
Here is the code for the implementation of the WndProc and About callbacks:
LRESULT CALLBACK App::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
App* pApp;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
pApp = (App*)pcs->lpCreateParams;
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, PtrToUlong(pApp));
return TRUE;
}
else
{
pApp = reinterpret_cast<App*>(static_cast<LONG_PTR>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)));
if (!pApp)
return DefWindowProc(hWnd, message, wParam, lParam);
}
int wmld, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(pApp->getInstance(), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, pApp->About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK App::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
I hope the error is within these snippets. If not, I would be happy to provide more code. I have done research, and I truly have very little idea what the problem is. I know that the callback for the about function must be static, which I believe it is. Apart from that, I don't know what would cause it to throw an exception. Thank you for any help you can give me.
Fail to reproduce this issue using your presented code. The following is an example based on your code piece and it works for me. You can refer to.
App.h
class App
{
public:
App(HINSTANCE hInstance, CONST WCHAR* clsName);
private:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
ATOM MyRegisterClass(HINSTANCE hInstance, CONST WCHAR* clsName);
};
App.cpp
#include "App.h"
App::App(HINSTANCE hInstance, CONST WCHAR* clsName)
{
App::MyRegisterClass(hInstance, clsName);
}
ATOM App::MyRegisterClass(HINSTANCE hInstance, CONST WCHAR* clsName)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = App::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DIALOGBOXEXCEPTION));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_DIALOGBOXEXCEPTION);
wcex.lpszClassName = clsName;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
LRESULT CALLBACK App::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
App* pApp;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
pApp = (App*)pcs->lpCreateParams;
::SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)pApp);
return TRUE;
}
else
{
pApp = reinterpret_cast<App*>(static_cast<LONG_PTR>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)));
if (!pApp)
return DefWindowProc(hWnd, message, wParam, lParam);
}
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(NULL, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, pApp->About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK App::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Create the main window:
App pApp = App(hInst, szWindowClass);
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, &pApp);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
Related
I apologize if this is a dumb question, im just starting to learn winapi. here is the code in concern:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WCHAR greeting[] = _T("line1");
WCHAR greeting1[] = _T("another line");
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
repaint = false;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
switch (pageID) {
case 1: {//display first string
TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
break;
}
case 2: {//display the other string
TextOut(hdc, 5, 100, greeting1, _tcslen(greeting1));
break;
}
}
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
/*****************************************************************************/
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
pageID = 1;
repaint = true;
}
if (LOWORD(wParam) == IDC_BUTTON1) {
pageID = 2;
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
I apologize for the seemingly random variables that don't do anything, I was just trying to make this work any way possible, but no dice.
What I am trying to do, is to have one of the textouts run after a button is pressed in a dialog box. Unfortunately, the text isn't displayed until wndproc runs next time. UpdateWindow is useless, because I need to pass hWnd to it, but I can't do that when Im not in wndproc. Need a quick tip on how this should be done. Im sure everyone does it every day, just not obvious for me... Thanks!
Basically use InvalidateRect, but this comment
UpdateWindow is useless, because I need to pass hWnd to it, but I
can't do that when Im not in wndproc.
exhibits kind of a fundamental misunderstanding of how Win32 programming works. You always need a window handle to a window you are working with. You get window handles when you create windows; you need to store those somewhere.
Anyway below is a minimal version of using buttons on one window to drive repainting of another window.
#include <windows.h>
HINSTANCE g_hInstance = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK SomeOtherWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
const int kBtn1ID = 101;
const int kBtn2ID = 102;
int pageID = 1;
HWND g_other_hwnd = NULL;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"foobar";
if (!RegisterClass(&wc))
return 1;
wc.lpszClassName = L"some_other_window";
wc.lpfnWndProc = SomeOtherWndProc;
if (!RegisterClass(&wc))
return 1;
g_hInstance = hInstance;
if (!CreateWindow(L"foobar",
L"foobar",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 640, 480, 0, 0, hInstance, NULL))
return 2;
if (!(g_other_hwnd = CreateWindow(L"some_other_window",
L"quux",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 640, 480, 0, 0, hInstance, NULL)))
return 2;
while (GetMessage(&msg, NULL, 0, 0) > 0)
DispatchMessage(&msg);
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
CreateWindow(L"button", L"some button", BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, 10, 10, 150, 35, hWnd, (HMENU)kBtn1ID, g_hInstance, 0);
CreateWindow(L"button", L"some other button", BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, 10, 55, 150, 35, hWnd, (HMENU)kBtn2ID, g_hInstance, 0);
break;
case WM_COMMAND: {
int btn_id = LOWORD(wParam);
switch (btn_id) {
case kBtn1ID:
pageID = 1;
InvalidateRect(g_other_hwnd, NULL, TRUE);
break;
case kBtn2ID:
pageID = 2;
InvalidateRect(g_other_hwnd, NULL, TRUE);
break;
}
}
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK SomeOtherWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
static auto greeting = L"hello there.";
static auto greeting1 = L"goodbye.";
switch (pageID) {
case 1: {//display first string
TextOut(hdc, 5, 5, greeting, lstrlenW(greeting));
break;
}
case 2: {//display the other string
TextOut(hdc, 5, 100, greeting1, lstrlenW(greeting1));
break;
}
}
EndPaint(hWnd, &ps);
}
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
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)
If I create a child window (In this case window "About") to the main window, the dialog box for some reason is not called. If you do not the child window is a dialog box called normal and works fine. GetLastError returns the error number 1812 (The specified image file did not contain a resource section.). But from a resource file everything is fine. And as I said, if you do not create a child window then everything works fine. What's the problem?
#include <windows.h>
#include "resource.h"
LONG WINAPI WndProc(HWND, UINT, WPARAM,LPARAM);
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PointsProc(HWND hPoints, UINT message, WPARAM wParam,LPARAM lParam);
HINSTANCE hInst;
HWND hPoints;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
hInst = hInstance;
HWND hwnd;
MSG msg;
WNDCLASS w;
memset(&w,0,sizeof(WNDCLASS));
w.style = CS_HREDRAW | CS_VREDRAW;
w.lpfnWndProc = WndProc;
w.hInstance = hInstance;
w.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
w.lpszClassName = L"My Class";
RegisterClass(&w);
hwnd = CreateWindow(L"My Class", L"My title", WS_OVERLAPPEDWINDOW,
300, 200, 200, 180, NULL, NULL, hInstance, NULL);
HMENU main_menu = CreateMenu();
AppendMenu(main_menu, MF_STRING, 1111, L"Box");
WNDCLASS w2;
memset(&w2, 0, sizeof(WNDCLASS));
w2.lpfnWndProc = (WNDPROC)PointsProc;
w2.hInstance = hInst;
w2.lpszClassName = L"About";
w2.hCursor = LoadCursor(NULL, IDC_ARROW);
w2.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(111, 111, 111));
RegisterClass(&w2);
hPoints = CreateWindowEx(0, L"About", (LPCTSTR) NULL,
WS_CHILD | WS_BORDER | WS_VISIBLE | WS_DISABLED, 10, 10,
100, 100, hwnd, (HMENU)1112, hInst, NULL);
ShowWindow(hPoints,SW_NORMAL);
UpdateWindow(hPoints);
SetMenu(hwnd, main_menu);
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LONG WINAPI WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
switch (Message)
{
case WM_COMMAND:
switch(LOWORD(wparam))
{
case 1111:
DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, About);
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wparam, lparam);
}
return 0;
}
LRESULT CALLBACK PointsProc(HWND hPoints, UINT message, WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
break;
default:
return DefWindowProc(hPoints, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
The dialog box is called, only it takes a long time because PointsProc() is looping...
The problem was the break; while handling WM_PAINT in PointsProc() -- it then skips calling DefWindowProc(), so the window keeps getting WM_PAINT messages because the window remains invalid.
// case WM_PAINT:
// break;
My Question is: In the below C++ code, why does clicking on the button do nothing while it is supposed to call MessageBox from WndProc1?
P.S: After compiling, I got some errors like the following:
"C:\Windows\SysWOW64\ntdll.dll", Can't find or open PDB file.
Code:
#include <Windows.h>
LRESULT CALLBACK WndProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
LONG WINAPI WndProc1(
_In_ HWND hwnd_button,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
//Точка входа в программу
int WINAPI WinMain
(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
//Создаем класс окна
WNDCLASS WindowClass;
//Заполняем структуру
WindowClass.style = 0;
WindowClass.lpfnWndProc = (WNDPROC)WndProc;
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hInstance = hInstance;
WindowClass.hIcon = LoadIcon(hInstance,
(LPCTSTR)IDI_APPLICATION);
WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WindowClass.lpszMenuName = 0;
WindowClass.lpszClassName = TEXT("Class");
//Зарегистируем класс окна
RegisterClass(&WindowClass);
//Создаем переменную, в которой поместим идентификатор окна
HWND hWnd;
hWnd = CreateWindow(TEXT("Class"), TEXT("ClickTest"),
WS_OVERLAPPEDWINDOW, 0, 0, 500, 300, NULL, NULL, hInstance, NULL);
//Создаем кнопку
HWND hWnd_button;
hWnd_button = CreateWindow(TEXT("button"), TEXT("Click me"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
10, 10, 80, 30, hWnd, (HMENU)10000, hInstance, NULL);
//показать окно
ShowWindow(hWnd, nCmdShow);
//обновить содержимое окна
UpdateWindow(hWnd);
//Создадим переменную для храненния сообщений
MSG msg;
//Создадим цикл обработки сообщений
while(GetMessage(&msg, NULL,0 ,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LONG WINAPI WndProc1(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
switch (Message){
case WM_COMMAND:
if(LOWORD(wparam)==10000)
{
MessageBox(hwnd, TEXT("Button Pressed"), TEXT(""), 0);
}
return 0;}}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
BeginPaint(hWnd, &PS);
EndPaint(hWnd, &PS);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Now working, just added button function as one of the cases to WndProc (WndProc1 deleted)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lParam)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_CREATE:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
BeginPaint(hWnd, &PS);
EndPaint(hWnd, &PS);
break;
case WM_COMMAND:
if(LOWORD(wparam)==10000)
{
MessageBox(hWnd, TEXT("Button Pressed"), TEXT(""), 0);
}
default:
return DefWindowProc(hWnd, message, wparam, lParam);
}
return 0;
}
One final newbie question: what's the difference between LRESULT CALLBACK and LONG WINAPI then?
Do this modification on WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT PS;
switch(message)
{
case WM_COMMAND:
if(LOWORD(wParam)==10000)
{
MessageBox(hWnd, TEXT("Button Pressed"), TEXT(""), 0);
}
break;
case WM_CREATE:
break;
// ...
I added WM_COMMAND in the switch/case of WndProc.
How do you expect WndProc1 to be called? It isn't associated to any window class... You have to handle the WM_COMMAND inside WndProc (buttons, as well as other common controls, notify their parent of their events via WM_COMMAND).
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?