I am using Visual Studio, C++ language, subsystem Windows.
I asked ChatGPT multiple times without success. I tried what he said but either it didn't work or he just repeated himself.
I realised that when I just pass NULL
instead of this
in the windowCreate()
function in my constructor, the creation works and hWND
is not NULL
, but of course WindowProc
doesn't work because pThis
is NULL
then.
Also this is the output I get plottet:
In Window Constructor
The operation completed successfully.
which is weird because it fails (hWND
is NULL
) but the error code is 0 (The operation completed successfully).
Can anyone help me?
main.cpp:
#include "win32engine.h" //engine
using namespace win32engine;
Window myWindow(L"Hello World Application");
int main()
{
myWindow.SetVisibility(SW_SHOW); // Show the window
OutputDebugString(L"Script running!\n"); // Output debug message
return 0;
}
win32engine.h:
#pragma once
#include <windows.h> // Windows API (creating windows)
#include <windowsx.h> // Windows API (creating windows)
#include <stdint.h> // Integer types (standard types)
#include <wingdi.h> // Windows GDI (graphics device interface)
#include "window.h"
#include "input.h"
#include "renderer.h"
#include "application.h"
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
namespace win32engine {
extern HINSTANCE hInstance;
}
application.h:
#pragma once
#include "win32engine.h"
namespace win32engine {
extern HINSTANCE hInstance; // Global instance handle
extern "C" int main();
}
application.cpp:
#include "application.h"
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(hInst)).c_str());
win32engine::hInstance = hInst; // Set global instance handle
return win32engine::main();
}
HINSTANCE win32engine::hInstance = NULL; // Initialize global instance handle
window.h:
#pragma once
#include <windows.h> // Windows API (creating windows)
#include <windowsx.h> // Windows API (creating windows)
#include <stdint.h> // Integer types (standard types)
#include <wingdi.h> // Windows GDI (graphics device interface)
#include <string>
#include <cwchar>
#include "renderer.h"
#include "input.h"
#include "application.h"
namespace win32engine
{
class Input;
class Renderer;
class Window {
private:
uint16_t width, height;
HWND hWND = NULL;
std::wstring className;
LPCWSTR title;
public:
Window(LPCWSTR title);
void SetVisibility(int state);
void SetWindowSize(uint16_t w, uint16_t h);
void SetRenderSize(uint16_t w, uint16_t h);
//win32engine::Renderer Renderer();
Input input;
Renderer renderer;
private:
static LRESULT WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam);
bool isRegistered();
};
}
window.cpp:
#include "window.h"
win32engine::Window::Window(LPCWSTR title) {
HINSTANCE hInst = win32engine::hInstance;
this->title = title;
//className = L"win32engine_class_" + std::wstring(this->title);
className = L"Hello";
UnregisterClass(className.c_str(), hInst);
/*WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.hInstance = hInst;
wc.lpszClassName = className.c_str();
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszMenuName = NULL;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.cbWndExtra = sizeof(Window);
wc.lpfnWndProc = WindowProc;
DWORD err = 0;
if (!RegisterClassEx(&wc)) {
err = GetLastError();
wchar_t buf[512];
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
err,
0,
buf,
sizeof(buf) / sizeof(wchar_t),
nullptr
);
swprintf_s(buf, L"%s (Error code: %lu)\n", buf, err);
OutputDebugString(L"Failed to register window class: \n");
OutputDebugString(buf);
}
else
OutputDebugString(L"Succesfully registered window class\n");
err = 0;
hWND = CreateWindowEx(0, className.c_str(), title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, wc.hInstance, this);
if (hWND == NULL) {
err = GetLastError();
OutputDebugString((std::to_wstring(err) + L"\n").c_str());
wchar_t buf[512];
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
err,
0,
buf,
sizeof(buf) / sizeof(wchar_t),
nullptr
);
swprintf_s(buf, L"%s (Error code: %lu)\n", buf, err);
OutputDebugString(L"Failed to create window: \n");
OutputDebugString(buf);
}
else {
OutputDebugString(L"Window created successfully\n");
}*/
WNDCLASSEX wc;
memset(&wc, 0, sizeof(WNDCLASSEX)); // Clear the structure
wc.cbSize = sizeof(wc);
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.hInstance = hInst;
//wc.lpszClassName = className.c_str();
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
/*wc.cbWndExtra = sizeof(void*);
OutputDebugString((L"Registering Class with name: \n\t\"" + className + L"\"\n").c_str());
if (!RegisterClassEx(&wc))
OutputDebugString(L"RegisterClassEx() failed\n");
else
OutputDebugString(L"RegisterClassEx() succeeded\n");
hWND = CreateWindowEx(0, wc.lpszClassName, title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, this);
if (!hWND)
OutputDebugString(L"CreateWindowEx() failed\n");
else
OutputDebugString(L"CreateWindowEx() succeeded\n");*/
className = L"MyWindowClass";
wc.lpszClassName = className.c_str();
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.lpszMenuName = NULL;
wc.cbWndExtra = sizeof(void*);
if (!RegisterClassEx(&wc))
OutputDebugString(L"RegisterClassEx() failed\n");
hWND = CreateWindowEx(0, className.c_str(), title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, this);
if (!hWND) {
DWORD err = GetLastError();
wchar_t buf[512];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, err, 0, buf, sizeof(buf) / sizeof(wchar_t), nullptr);
OutputDebugString(L"In Window Constructor\n");
OutputDebugString(buf);
}
ShowWindow(hWND, 1);
UpdateWindow(hWND);
SetRenderSize(500, 500);
MSG msg;
while (GetMessage(&msg, hWND, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void win32engine::Window::SetVisibility(int state)
{
if (!isRegistered())
return;
ShowWindow(hWND, state);
}
void win32engine::Window::SetWindowSize(uint16_t w, uint16_t h)
{
if (!isRegistered())
return;
width = w; // Set width
height = h; // Set height
SetWindowPos(hWND, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
}
void win32engine::Window::SetRenderSize(uint16_t w, uint16_t h)
{
if (!isRegistered())
return;
RECT rect = { 0, 0, w, h };
AdjustWindowRect(&rect, GetWindowLong(hWND, GWL_STYLE), FALSE);
width = (uint16_t)(rect.right - rect.left);
height = (uint16_t)(rect.bottom - rect.top);
SetWindowPos(
hWND,
NULL,
0, 0,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOZORDER | SWP_NOMOVE
);
}
/*win32engine::Renderer& win32engine::Window::Renderer()
{
return renderer;
}*/
LRESULT CALLBACK win32engine::Window::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
Window* pThis = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
pThis = (Window*)cs->lpCreateParams;
SetLastError(0);
LONG_PTR prev = SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
if (prev == 0) {
DWORD err = GetLastError();
if (err != 0) {
wchar_t buf[512];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, err, 0, buf, sizeof(buf) / sizeof(wchar_t), nullptr);
OutputDebugString(L"In WindowProc, message WM_NCCREATE\n");
OutputDebugString(buf);
return FALSE;
}
}
pThis->hWND = hwnd;
}
else {
// Zeiger aus UserData holen
pThis = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis) {
return pThis->HandleMessage(msg, wParam, lParam);
}
else {
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
LRESULT win32engine::Window::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_PAINT:
renderer.redraw();
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SETFOCUS:
break;
case WM_KILLFOCUS:
input.ResetKeyStates();
input.SetMouseState(Input::MouseState::NONE);
break;
case WM_KEYDOWN:
input.SetKeyState((uint8_t)wParam, true);
break;
case WM_KEYUP:
input.SetKeyState((uint8_t)wParam, false);
break;
case WM_MOUSEMOVE:
input.SetMousePosition(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_LBUTTONDOWN:
input.SetMouseState(Input::MouseState::LEFT_BUTTON);
break;
case WM_MBUTTONDOWN:
input.SetMouseState(Input::MouseState::MIDDLE_BUTTON);
break;
case WM_RBUTTONDOWN:
input.SetMouseState(Input::MouseState::RIGHT_BUTTON);
break;
case WM_LBUTTONUP:
input.SetMouseState(Input::MouseState::NONE);
break;
case WM_MBUTTONUP:
input.SetMouseState(Input::MouseState::NONE);
break;
case WM_RBUTTONUP:
input.SetMouseState(Input::MouseState::NONE);
break;
case WM_MOUSEWHEEL:
input.SetMouseScrollWheel(GET_WHEEL_DELTA_WPARAM(wParam)); // Set mouse wheel delta
break;
}
return 0;
}
bool win32engine::Window::isRegistered()
{
return hWND != NULL;
}
renderer.h:
#pragma once
#include <windows.h> // Windows API (creating windows)
#include <windowsx.h> // Windows API (creating windows)
#include <stdint.h> // Integer types (standard types)
#include <wingdi.h> // Windows GDI (graphics device interface)
namespace win32engine
{
class Renderer
{
public:
Renderer();
void redraw();
};
}
renderer.cpp:
#include "renderer.h"
namespace win32engine
{
Renderer::Renderer()
{
}
void Renderer::redraw()
{
}
}
input.h:
#pragma once
#include <windows.h> // Windows API (creating windows)
#include <windowsx.h> // Windows API (creating windows)
#include <stdint.h> // Integer types (standard types)
#include <wingdi.h> // Windows GDI (graphics device interface)
namespace win32engine
{
class Input {
friend class Window;
private:
bool key_states[256];
bool key_states_old[256];
uint16_t mouse_x, mouse_y;
uint8_t mouse_state = NONE;
int16_t mouse_wheel_delta;
public:
enum MouseState { NONE, LEFT_BUTTON, RIGHT_BUTTON, MIDDLE_BUTTON };
Input();
bool GetKeyHeld(uint8_t key);
bool GetKeyStateChange(uint8_t key);
bool GetKeyPressed(uint8_t key);
bool GetKeyReleased(uint8_t key);
uint16_t GetMouseX();
uint16_t GetMouseY();
uint16_t GetMouseWheelDelta();
uint8_t GetMouseState();
private:
void SetMousePosition(int x, int y);
void SetMouseState(MouseState state);
void SetMouseScrollWheel(int delta);
void SetKeyState(uint8_t key, bool state);
void ResetKeyStates();
};
}
input.cpp:
#include "input.h"
win32engine::Input::Input() {
ResetKeyStates();
}
bool win32engine::Input::GetKeyHeld(uint8_t key) {
return key_states[key];
}
bool win32engine::Input::GetKeyStateChange(uint8_t key) {
return key_states[key] != key_states_old[key];
}
bool win32engine::Input::GetKeyPressed(uint8_t key) {
return key_states[key] && !key_states_old[key];
}
bool win32engine::Input::GetKeyReleased(uint8_t key) {
return !key_states[key] && key_states_old[key];
}
uint16_t win32engine::Input::GetMouseX() {
return mouse_x;
}
uint16_t win32engine::Input::GetMouseY() {
return mouse_y;
}
uint16_t win32engine::Input::GetMouseWheelDelta() {
return mouse_wheel_delta;
}
uint8_t win32engine::Input::GetMouseState() {
return mouse_state;
}
void win32engine::Input::SetMousePosition(int x, int y) {
mouse_x = x;
mouse_y = y;
}
void win32engine::Input::SetMouseState(MouseState state) {
mouse_state = state;
}
void win32engine::Input::SetMouseScrollWheel(int delta) {
mouse_wheel_delta = delta;
}
void win32engine::Input::SetKeyState(uint8_t key, bool state) {
key_states_old[key] = key_states[key];
key_states[key] = state;
}
void win32engine::Input::ResetKeyStates() {
memset(key_states, 0, sizeof(key_states));
memset(key_states_old, 0, sizeof(key_states_old));
}
I tried changing from WNDCLASS
to WNDCLASSEX
, to set wc.cbWndExtra
to sizeof(void*)
to allocate storage for the this
pointer, I think??
Also, I tried switching from a class name, which is different for each window, to a single one.
If you pass this
to CreateWindowEx()
, then pThis
is not NULL
in WindowProc()
, and you call pThis->HandleMessage()
.
HandleMessage()
returns 0 (FALSE) when processing WM_NCCREATE
, which cancels the window creation:
https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-nccreate
If an application processes this message, it should return TRUE to continue creation of the window. If the application returns FALSE, the CreateWindow or CreateWindowEx function will return a NULL handle.
HandleMessage()
needs to call DefWindowProc()
for any unhandled message, ie in a default
clause of your switch
block, like this:
LRESULT win32engine::Window::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
...
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
DefWindowProc()
returns 1 (TRUE) for WM_NCCREATE
.