Last active
May 4, 2023 11:50
-
-
Save cos-public/dfbf03cd1f60c1f07bdc31b1fdb33ae8 to your computer and use it in GitHub Desktop.
classic win32 app with raii style window
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <cstdint> | |
template <typename WindowT, typename CtorArgs> | |
inline LRESULT CALLBACK window_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { | |
if (message == WM_CREATE) { | |
::OutputDebugStringW(L"WM_CREATE\n"); | |
auto cp = reinterpret_cast<LPCREATESTRUCTW>(lParam); | |
void * create_params = cp->lpCreateParams; | |
auto * args_tuple = reinterpret_cast<CtorArgs *>(cp->lpCreateParams); /// last parameter passed to ::CreateWindow() | |
WindowT * wnd = std::apply([hWnd](auto&&... args) { | |
return new WindowT{hWnd, std::forward<decltype(args)>(args)...}; | |
}, std::move(*args_tuple));; | |
::SetWindowLongPtrW(hWnd, 0, reinterpret_cast<LONG_PTR>(wnd)); | |
} | |
auto wnd = reinterpret_cast<WindowT *>(::GetWindowLongPtrW(hWnd, 0)); | |
if (wnd) { | |
auto ret = wnd->wnd_proc(message, wParam, lParam); | |
if (message == WM_DESTROY) { | |
::OutputDebugStringW(L"WM_DESTROY\n"); | |
delete wnd; | |
::SetWindowLongPtrW(hWnd, 0, 0); | |
} | |
return ret; | |
} | |
/// DefWindowProcW calls DestroyWindow(), which sends WM_DESTROY by default | |
return ::DefWindowProcW(hWnd, message, wParam, lParam); | |
} | |
class main_window { | |
protected: | |
template <class, class> | |
friend LRESULT CALLBACK window_proc<main_window>(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |
main_window(HWND hWnd, std::unique_ptr<int> p1, const float & p2, double & p3) : hWnd{hWnd} { | |
::OutputDebugStringW(L"main_window::main_window()\n"); | |
p3 = 1.1111; | |
} | |
~main_window() { | |
::OutputDebugStringW(L"main_window::~main_window()\n"); | |
::PostQuitMessage(EXIT_SUCCESS); | |
} | |
main_window(main_window &&) = delete; | |
main_window(const main_window &) = delete; | |
LRESULT wnd_proc(UINT message, WPARAM wParam, LPARAM lParam) { | |
if (message == WM_PAINT) { | |
::OutputDebugStringW(L"WM_PAINT\n"); | |
PAINTSTRUCT ps; | |
::BeginPaint(hWnd, &ps); | |
::EndPaint(hWnd, &ps); | |
return 0; | |
} | |
return ::DefWindowProcW(hWnd, message, wParam, lParam); | |
} | |
private: | |
HWND hWnd; | |
}; | |
template <class WindowT> | |
class window { | |
public: | |
template <typename ... CtorArgs> | |
window(DWORD dwExStyle, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, | |
CtorArgs && ... ctor_args) | |
: hWnd{create_window(dwExStyle, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, std::forward<CtorArgs>(ctor_args)...)} | |
{ | |
} | |
~window() { | |
/// Could already be destroyed | |
::DestroyWindow(hWnd); | |
} | |
/// May be destroyed by the OS | |
[[nodiscard]] inline WindowT * operator->() { return get(); } | |
[[nodiscard]] inline WindowT & operator*() { return *reinterpret_cast<WindowT *>(::GetWindowLongPtrW(hWnd, 0)); } | |
[[nodiscard]] inline WindowT * get() { return reinterpret_cast<WindowT *>(::GetWindowLongPtrW(hWnd, 0)); } | |
private: | |
HWND hWnd; | |
template <typename ... Args> | |
static HWND create_window(DWORD dwExStyle, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, | |
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, Args && ... args) { | |
static const WNDCLASSEXW wnd_class { | |
.cbSize = sizeof(WNDCLASSEXW), | |
.style = CS_HREDRAW | CS_VREDRAW, | |
.lpfnWndProc = &window_proc<main_window, std::tuple<Args &&...>>, | |
.cbClsExtra = 0, | |
.cbWndExtra = sizeof(std::uintptr_t), | |
.hInstance = ::GetModuleHandle(NULL), | |
.hIcon = NULL, | |
.hCursor = ::LoadCursorW(NULL, IDC_ARROW), | |
.hbrBackground = NULL, | |
.lpszMenuName = NULL, | |
.lpszClassName = L"main_window", | |
.hIconSm = NULL | |
}; | |
static ATOM cls = ::RegisterClassExW(&wnd_class); | |
auto args_tuple = std::forward_as_tuple(std::forward<Args>(args)...); | |
return ::CreateWindowEx(dwExStyle, MAKEINTRESOURCEW(cls), lpWindowName, dwStyle, | |
X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, &args_tuple); | |
} | |
}; | |
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) try { | |
auto p1 = std::make_unique<int>(42); | |
const float p2 = 3.14f; | |
double p3 = 2.71828; | |
/// construct using owning wrapper | |
window<main_window> mw{0, L"Main Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, | |
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, std::move(p1), p2, p3}; | |
MSG msg; | |
BOOL bRet; | |
while ((bRet = ::GetMessage(&msg, NULL, 0, 0)) != 0) { | |
::TranslateMessage(&msg); | |
::DispatchMessage(&msg); | |
} | |
return static_cast<int>(msg.wParam); | |
} catch (...) { | |
return EXIT_FAILURE; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment