-
[5] - Window FrameworkGraphics 2021. 7. 10. 14:05
매크로를 정의한 Header File을 추가합니다. 이유는 간단한데 <Windows.h> Header File을 Include할 때, 잘 쓰지도 않을 기능들까지 한꺼번에 포함되는 것을 가능한 막을 생각이기 때문입니다. 멋지게 포장하면 몇 가지 기능을 Disable하겠다는 말입니다.
이런 식으로 잔뜩! 너무 많아서 긁어오기는 포기하기로 합니다 이제부터는 Encapsluation을 진행할 것입니다. 먼저 Design Pattern 중 Singleton을 이용할 예정입니다. Singleton은 잘 사용하지 않는다고 배웠으나 우리의 경우엔 Window가 유일하며 모든 Message를 처리하기 때문에 적합한 선택이라고 할 수 있습니다.
Window에 대한 Handle이 Window Class에 포함되어 있으며 유일합니다. static const char* GetName()과 static HINSTANCE GetInstance()는 어디에서 호출하든 Singleton design에 의해서 같은 HWND Instance를 반환해줄 것입니다.
참고* WNDCLASSEX instance를 초기화 하는 과정에서 LPCWSTR과 const char*의 변환 문제가 종종 있는데 Project Property의 Character Set을 Unicode가 아닌 Multi Byte로 설정하여 해결할 수 있습니다.
1. WindowClass
1. Window Class 안에 WindowClass라는 Class가 존재합니다. Window를 하나 만들 때, 우리는 Window Class가 필요했습니다. 그리고 해당 Class의 Name을 Window API Side에 등록할 필요가 있었습니다. 당연히 등록을 한다는 것은 프로그램을 제거할 때 해제해야 한다는 말을 함께 포함합니다. 그렇다면 Constructor에 API Side에 등록을 하도록 구현하고 Destructor에 UnresgiterClass()를 호출하도록 합니다. 안 해도 상관은 없지만 Symmetry를 생각한 프로그래밍이라면 해두면 좋을 것 같습니다. 실제로 깨끗하고 기분이 좋습니다.
당연히 Constructor에 Register를 수행할 수 있도록 구현을 하고 우리는 단 하나의 Window만 다룰 예정임으로 모든 과정을 Singleton Pattern을 활용하기로 합니다.
또 WindowClass 자체의 Static Instance도 하나 만들도록 합니다. 이는 프로그램이 시작하면 함께 생성될 것입니다. static한 Instance가 아니라면 Constructor에 Parameter를 만들어 HINSTANCE를 초기화해야 하는데 그러면 코드가 지저분해지고 구태여 그럴 필요도 없으니 그러지 않기로 합니다.
왜 scope밖에 선언을 해야만 했을까요? 준비된 모든 것들을 Register하고 있는데 HandleMsgSetup이라는 새로운 함수가 눈에 보입니다. 전에 다루었던 것처럼 이 함수는 사실 CALLBACK과 상관하는 함수입니다. 아주 흥미로운 녀석입니다.
CALLBACK FUNCTION and Hidden Parameter
Windows api가 호출하는 CALLBACK들은 어떤 class 안에 Member function로 정의될 수 없습니다. 이와 관련한 아주 좋은 글이 있습니다. (How can I make a callback function a member of my C++ class? | The Old New Thing (microsoft.com)) 읽기 전에 Free function에 대한 이해도 필요합니다. What is the meaning of the term "free function" in C++? - Stack Overflow. Win32의 CALLBACK함수들은 정보들을 전해주거나 받을 특별한 Pointer를 Parameter로 갖는다는 원칙이 있습니다. 앞으로 마주할 대부분의 상황에서 우리는 이 포인터에 구조체나 클라스를 넣어줄 것입니다. 두 아티클은 무조건 읽어보는 편이 좋습니다. C++ 3D DirectX Tutorial [Window Framework] 6 - YouTube Win32 API는 C++의 Hidden Parameter에 대해서 잘 모르기 때문에 이 Parameter를 Explicit하게 전달해주자는 것이 골자로 보입니다. Member function은 이 parameter를 hidden pointer로 갖고 있으니 Explicitly 전달할 Free Function을 쓰자는 것입니다.
2. Window
parameter로 Window의 width, height, class name을 받습니다. 그 외의 것들은 전에 했던 작업과 같지만 CreateWindowA의 마지막 Argument를 this를 넘겨줍니다. 또 달라진 점은 Window client area의 Width와 Height를 RECT이라는 구조체를 통해 정의했다는 점입니다.
HandleMsgSetup HandleMsgSetup과 HandleMsgThunk 두 가지의 함수가 있습니다.
Window를 Register할 때 보면 HandleMsgSetup이라는 함수를 찾아볼 수 있습니다. 이 함수가 Window가 Initialising 될 때 대부분의 것들을 준비합니다.
이 함수를 보면 하는 일이 굉장히 단순합니다. Message queue로부터 WM_NCCREATE가 있는지 확인합니다. 코드를 보면 아시겠지만 그냥 WM_NCCREATE면 뭔갈 하고 아니면 Default Window Procedure를 반환(행동)합니다. WM_NCCREATE라는 Message는 MSDN에 구체적으로 설명이 되어 있습니다. 이 Message는 Window가 생성되기 전에 보내지는 메시지라고 축약할 수 있습니다. 또 Window가 이 Message를 WindowProc 함수를 통해 전달 받는 다는 정보를 획득할 수 있습니다!
이해를 위해 MSDN의 원문을 읽어 봅니다.
CREATESTRUCT? 이게 무엇일까요? 형광색 부분이 CreateWindowA로 넘겨준 this의 정체입니다 LPVOID lpCreateParams는 CreateWindowA의 마지막 Parameter로 넘겨준 this입니다. 즉, Window의 Instance의 Pointer와 Windows API Message Mechanism을 잇는 방법 중 하나 입니다. 어렵습니다. 코드를 읽어보면 lparam을 CREATESTRUCT로 reinterpret_cast하는 모습을 볼 수 있습니다. CREATESTRUCT는 위에 사진처럼 lpCreateParams를 Member로 가지고 이 member가 window의 instance의 정보를 갖고 있다고 했으니 window*로 다시 cast해주는 모습까지 확인할 수 있습니다.
그외의 HandleMsgThunk의 첫 번째 SetWindowLingPtr이라는 함수는 MSDN을 참조하면 어떤 역할을 하는지 쉽게 알 수 있습니다. 사실 어렵게 알 수 있습니다. 정리하면 현재 활성화 된 Window Class라는 Class의 Pointer를 Win32 API Side에 저장하는 과정이라고 하면 얼추 맞습니다. 이를 통해서 Window와 그 Window를 제어할 Class 간의 연결고리를 갖출 수 있습니다.
두 번째 SetWindowLongPtr이라는 함수는 "Window의 제어권과 핸들을 등록했으니 Default Message Procedure을 대충 이런 Message Procedure로 바꿔가지고 쓰겠다."를 함수 하나로 정리한 것입니다. 그리고 그 Message Procedure이 HandleMsgThunk입니다. 이렇게 놓고 보면 SetWindowLongPtr의 두 번째 Parameter의 역할이 무엇인지 MSDN을 직접 참조하여 읽어보는 편이 좋겠다는 생각이 듭니다. SetWindowLongPtrA function (winuser.h) - Win32 apps | Microsoft Docs
SetWindowLongPtrA function (winuser.h) - Win32 apps
Changes an attribute of the specified window.
docs.microsoft.com
이렇게 되면 두 개의 Free Function에 대한 Explicit Pointer를 Win32 API side에 넘겨주며 생성된 Window의 Instance의 정보를 저장하고 Message Procedure을 정의한다는 것을 알 수 있습니다.
'Graphics' 카테고리의 다른 글
[7] - ICON (0) 2021.07.10 [6] - Error Handling (0) 2021.07.10 [4] - Mouse and Char (0) 2021.07.03 [3] - Window Messages (0) 2021.07.03 [2] - Message Pump (0) 2021.07.03