Skip to content

Instantly share code, notes, and snippets.

@cos-public
Last active May 4, 2023 11:50
Show Gist options
  • Save cos-public/dfbf03cd1f60c1f07bdc31b1fdb33ae8 to your computer and use it in GitHub Desktop.
Save cos-public/dfbf03cd1f60c1f07bdc31b1fdb33ae8 to your computer and use it in GitHub Desktop.
classic win32 app with raii style window
#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