This tutorial is part of a Collection: DirectX12 Tutorial
2198
views

S01E02 - First Window
How to open a window :)
01 - FirstWindow.zip...zip 3.23 kb
590 downloads
Hello there :)
This time lets create a window that will display all our stuff. but let's look how we can do that: 1) A simple window code written C-style 2) A window putted into a class with WindowProcedure outside of a class 3) A window putted into a class with WindowProcedure as a lambda 4) A window putted into a class with WindowProcedure Why 4 of them and what are the differences between them. Let's start writing:1) A simple window code written C-style
This one will be created in one file.Quite easy and you should know that if you start programming in Windows :) This can be enough for creation of window but we want more, we want C++ not C-Style.#include <Windows.h> LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { switch (Msg) { case WM_CREATE: return 0; case WM_CLOSE: case WM_DESTROY: case WM_QUIT: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, Msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { WNDCLASSEX wndClass{}; wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.hInstance = GetModuleHandle(nullptr); wndClass.lpszClassName = L"Window"; wndClass.lpfnWndProc = (WNDPROC)WindowProcedure; if (!RegisterClassEx(&wndClass)) { return false; } HWND MainWnd = CreateWindowEx(0, L"Window", L"Window", WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); if (!MainWnd) { return false; } ShowWindow(MainWnd, SW_NORMAL); UpdateWindow(MainWnd); MSG uMsg{}; while (1) { if (PeekMessage(&uMsg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&uMsg); DispatchMessage(&uMsg); if (uMsg.message == WM_QUIT) break; } } return static_cast<int>(uMsg.wParam); }
2) A window putted into a class with WindowProcedure outside of a class
The code is almost identical but putted into a class. The problem is that in this case the WindowProcedure is outside a class. What if we put it into a class. When we put WindowProcedure into a class and make needed changes:#include <Windows.h> #include <memory> class Window { private: HWND MainWnd{}; WNDCLASSEX wndClass{}; public: bool Create(); int MessageLoop(); }; LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { switch (Msg) { case WM_CREATE: return 0; case WM_CLOSE: case WM_DESTROY: case WM_QUIT: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, Msg, wParam, lParam); } bool Window::Create() { wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.hInstance = GetModuleHandle(nullptr); wndClass.lpszClassName = L"Window"; wndClass.lpfnWndProc = (WNDPROC)WindowProcedure; if (!RegisterClassEx(&wndClass)) { return false; } MainWnd = CreateWindowEx(0, L"Window", L"Window", WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); if (!MainWnd) { return false; } ShowWindow(MainWnd, SW_NORMAL); UpdateWindow(MainWnd); return true; } int Window::MessageLoop() { MSG uMsg{}; while (1) { if (PeekMessage(&uMsg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&uMsg); DispatchMessage(&uMsg); if (uMsg.message == WM_QUIT) break; } } return static_cast<int>(uMsg.wParam); } int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { auto Win = std::make_unique<Window>(); Win->Create(); return Win->MessageLoop(); }
the program won't even compile: error C2440: 'type cast': cannot convert from 'overloaded-function' to 'WNDPROC' OK, lets fix that.class Window { private: HWND MainWnd{}; WNDCLASSEX wndClass{}; public: bool Create(); int MessageLoop(); LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); }; LRESULT CALLBACK Window::WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
and it is fixed. The program compile, it runs and everything is perfect. But it isn't. The problem is with static functions. Try to access other data from our class and see it by yourself :)static LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
3) A window putted into a class with WindowProcedure as a lambda
The code is almost identical with one change, the program compile and runs:wndClass.lpfnWndProc = [](HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)->LRESULT { switch (Msg) { case WM_CREATE: return 0; case WM_CLOSE: case WM_DESTROY: case WM_QUIT: PostQuitMessage(0); return 0; }
the only problem here is as you can see we don't have any access to external data. So having a lambda is useless in this case. But there is a solution for this problem and for aspect that I've showed above.return DefWindowProc(hWnd, Msg, wParam, lParam); };
4) A window putted into a class with WindowProcedure
In this case lets write this in our class in provate section:Looks familiar? Why two? one is for handling our messages and the second is for gaining information about our window. It is necessary to obtain those data if we want to have a WindowProcedure function in our class. Here we are gonna to manage all our messages:static LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); LRESULT WinProc(UINT Msg, WPARAM wParam, LPARAM lParam);
Here we are gonna obtain our data of the window. As you can see all data are created before WM_CREATE is even called so that we can get them when the window is created. Since we cannot use data from outside our static method we check if our pointer is valid and call another function which has full access:LRESULT Window::WinProc(UINT Msg, WPARAM wParam, LPARAM lParam) { switch (Msg) { case WM_CREATE: return 0; case WM_CLOSE: case WM_DESTROY: case WM_QUIT: PostQuitMessage(0); return 0; } return 0; }
and the last we have to do is one change in our CreateWindowEx function. The last parameter must beLRESULT CALLBACK Window::WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { Window *AppWin{}; if (Msg == WM_NCCREATE) { CREATESTRUCT * cs = (CREATESTRUCT*)lParam; AppWin = (Window*)cs->lpCreateParams; SetLastError(0); if (SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)AppWin) == 0) { if (GetLastError() != 0) return false; } } else { AppWin = (Window*)GetWindowLongPtr(hWnd, GWLP_USERDATA); } if (AppWin) AppWin->WinProc(Msg, wParam, lParam); return DefWindowProc(hWnd, Msg, wParam, lParam); }
this
instead ofnullptr
. If you don't change that the program won't see your messages and, in this case, don't close itself properly (window will disappear but the program will not close). I asume that convertion of this solution to lambda will be no problem :) So far so good. I will use the last version in the next lesson as a code base and add error handling, timming and some configuration managing. Till next time :)
Sign in to comment