Windows에서 GUI 프로그램이 어떻게 동작하는지 제대로 이해하려면 Win32를 한 번쯤 보는 것이 좋다.
요즘은 WPF, WinUI, Qt, Electron 같은 고수준 프레임워크를 사용하는 경우가 많지만, Windows의 네이티브 기능과 상호작용하는 기본 구조에는 여전히 Win32 API가 깊게 연결되어 있다.
Win32를 공부하면 Window, Message, Handle, Window Procedure, Message Loop 같은 개념이 왜 존재하는지 이해할 수 있다.
Win32의 핵심은 API를 많이 외우는 것이 아니다.
Windows가 애플리케이션을 어떻게 관리하고, 입력과 이벤트를 어떤 방식으로 전달하며, 프로그램이 OS의 리소스를 어떻게 참조하는지를 이해하는 것이 핵심이다.
Win32란
Win32는 Windows가 제공하는 네이티브 API 집합이다.
C/C++ 프로그램에서 Windows의 기능을 직접 사용할 수 있도록 다양한 API를 제공한다.
GUI뿐만 아니라 파일, 프로세스, 스레드, 동기화, 메모리, 입력 등 Windows의 다양한 기능을 다룰 수 있다.
Application -> Win32 API -> Windows -> System Resources
이름 때문에 32비트 전용 API처럼 보이지만 그렇지 않다.
64비트 Windows에서도 Win32 API는 여전히 Windows 애플리케이션 개발의 중요한 인터페이스다.
Win32 GUI의 기본 구조
Win32 GUI 프로그램을 이해할 때 가장 먼저 잡아야 하는 구조는 다음이다.
Application -> Window -> Message -> Message Loop -> Window Procedure -> Application Logic
프로그램은 Windows에 Window를 생성하고, Windows가 전달하는 Message를 Message Loop에서 받아 Window Procedure를 통해 처리한다.
그래서 Win32 GUI 프로그램은 일반적인 순차 실행 프로그램과 달리 메시지를 기다리고 처리하는 이벤트 기반 구조를 가진다.
프로그램 시작
전통적인 Win32 GUI 프로그램은 WinMain() 또는 wWinMain()에서 시작한다.
wWinMain -> Window Class Registration -> Window Creation -> ShowWindow -> Message Loop
일반적인 C++ 프로그램의 main()과 비슷한 역할을 하지만 Windows GUI 애플리케이션의 진입점이라는 차이가 있다.
현대적인 Windows 프로그램에서는 Unicode 처리를 위해 wWinMain()과 Wide Character API를 사용하는 경우가 많다.
Window Class
Win32에서는 Window를 만들기 전에 Window Class를 등록하는 과정이 필요하다.
Window Class에는 해당 Window가 어떤 방식으로 동작할지에 대한 기본 정보를 등록한다.
대표적으로 Window Procedure를 지정한다.
Window Class -> Window Procedure
이후 등록한 Window Class를 기반으로 실제 Window를 생성한다.
Window Class -> CreateWindowEx -> HWND
이 구조 때문에 Win32에서 Window는 단순히 화면에 나타나는 사각형 하나가 아니라 특정 Window Procedure와 연결된 OS 관리 객체라고 이해하는 편이 좋다.
HWND
HWND는 Window를 식별하기 위한 Handle이다.
CreateWindowEx -> HWND -> Windows Window
애플리케이션이 Window 자체의 내부 구조를 직접 관리하는 것이 아니라 Handle을 Windows API에 전달해서 해당 Window를 조작한다.
버튼이나 Edit 같은 기본 Control도 Win32에서는 Window로 취급되는 경우가 많다.
Window -> Control -> HWND
그래서 Win32 코드를 보면 HWND가 매우 자주 등장한다.
Handle
Win32를 이해할 때 중요한 개념이 Handle이다.
Handle은 Windows가 관리하는 시스템 리소스를 애플리케이션이 참조하기 위한 식별자다.
Application -> Handle -> OS Resource
대표적인 예는 다음과 같다.
HWND -> Window
HDC -> Device Context
HINSTANCE -> Application Instance
HANDLE -> 다양한 OS Resource
파일이나 프로세스, 스레드, 동기화 객체 등도 Handle을 통해 접근하는 경우가 많다.
중요한 것은 Handle을 일반적인 C++ 객체 포인터처럼 직접 조작하는 것이 아니라 Windows API에 전달해서 OS가 관리하는 리소스를 제어한다는 것이다.
Message
Win32 GUI의 핵심은 Message다.
사용자가 키보드를 누르거나 마우스를 클릭하거나 창의 크기를 변경하면 Windows가 관련 Message를 전달한다.
대표적인 Message는 다음과 같다.
WM_PAINT
WM_SIZE
WM_CLOSE
WM_DESTROY
WM_KEYDOWN
WM_KEYUP
WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_COMMAND
전체적인 흐름은 다음과 같다.
User Input -> Windows -> Message Queue -> Message Loop -> Window Procedure
애플리케이션은 Message를 직접 만들어내는 것이 아니라 Windows가 전달하는 Message를 받아 처리하는 방식으로 동작한다.
Message Queue
Windows는 애플리케이션에 전달해야 할 Message를 Queue에 넣는다.
Windows -> Message Queue -> Application
예를 들어 사용자가 버튼을 클릭하면 관련 입력과 Window 상태에 따라 Message가 생성되고 애플리케이션이 처리할 수 있도록 전달된다.
이 구조 덕분에 여러 입력과 시스템 이벤트를 순차적으로 처리할 수 있다.
Message Loop
애플리케이션은 Message Loop를 통해 Message를 계속 가져온다.
일반적인 구조는 다음과 같다.
GetMessage -> TranslateMessage -> DispatchMessage -> Window Procedure
GetMessage()는 Message Queue에서 Message를 가져오고, TranslateMessage()는 키보드 Message 변환을 수행하며, DispatchMessage()는 해당 Message를 Window Procedure로 전달한다.
결국 GUI 프로그램은 다음 구조로 계속 동작한다.
Message Queue -> GetMessage -> DispatchMessage -> Window Procedure
프로그램이 아무 작업도 하지 않는 것처럼 보일 때도 실제로는 이 구조를 통해 Windows의 이벤트를 기다리고 있다.
Window Procedure
Window에 전달된 Message를 실제로 처리하는 함수가 Window Procedure다.
보통 WndProc라고 부른다.
Message -> WndProc -> Message Handling
대표적인 처리는 다음과 같다.
WM_PAINT -> 화면 그리기
WM_COMMAND -> Control / Command 처리
WM_KEYDOWN -> 키보드 입력 처리
WM_MOUSEMOVE -> 마우스 이동 처리
WM_CLOSE -> Window 종료 요청
WM_DESTROY -> Window 제거 처리
필요한 Message를 직접 처리하고 나머지는 DefWindowProc()에 전달할 수 있다.
Window 종료 흐름
Win32에서 Window가 종료되는 과정도 Message를 통해 이루어진다.
대표적인 흐름은 다음과 같다.
WM_CLOSE -> DestroyWindow -> WM_DESTROY -> PostQuitMessage -> Message Loop 종료 -> Application 종료
이 구조를 직접 따라가 보면 Win32의 메시지 기반 구조를 이해하기 쉽다.
Event와 Message
고수준 UI 프레임워크에서는 다음처럼 이벤트를 직접 연결하는 경우가 많다.
Button.Click -> Event Handler
Win32에서는 상대적으로 낮은 수준에서 Message를 통해 처리한다.
Button Click -> Windows Message -> WM_COMMAND -> WndProc
그래서 WPF나 WinUI 같은 프레임워크보다 Win32 코드가 더 직접적으로 느껴진다.
고수준 프레임워크가 제공하는 Event 시스템의 아래쪽에서 Windows Message 구조가 동작하는 경우가 많기 때문이다.
GDI
Win32에서 기본적인 2D 화면 출력을 담당하는 대표적인 시스템이 GDI(Graphics Device Interface)다.
Window가 다시 그려져야 할 때 WM_PAINT를 통해 그리기 작업을 수행할 수 있다.
WM_PAINT -> BeginPaint -> HDC -> Drawing -> EndPaint
HDC는 Device Context를 나타내는 Handle이다.
GDI를 이용하면 텍스트, 선, 사각형, 비트맵 등의 기본적인 2D 그래픽을 그릴 수 있다.
HDC -> TextOut
HDC -> Rectangle
HDC -> LineTo
HDC -> BitBlt
다만 실시간 게임 렌더링처럼 높은 성능의 그래픽 처리가 필요한 경우에는 GDI보다 DirectX 같은 그래픽 API를 사용하는 것이 일반적이다.
Win32의 시스템 리소스
Win32는 GUI만 제공하는 API가 아니다.
Windows가 관리하는 다양한 시스템 리소스를 직접 다룰 수 있다.
Win32 API -> File
Win32 API -> Process
Win32 API -> Thread
Win32 API -> Memory
Win32 API -> Synchronization
Win32 API -> Window
그래서 Win32를 공부하면 자연스럽게 운영체제의 구조와도 연결된다.
File API
파일 역시 Win32 API를 통해 직접 다룰 수 있다.
대표적인 API가 CreateFile()이다.
기본적인 흐름은 다음과 같다.
CreateFile -> HANDLE -> ReadFile / WriteFile -> CloseHandle
C++의 std::fstream보다 Windows 시스템에 가까운 수준에서 파일을 제어할 수 있다.
다만 실무에서는 직접 Handle을 관리하기보다 C++ RAII Wrapper나 상위 수준의 라이브러리를 사용하는 경우도 많다.
Process와 Thread
Win32에서는 Process와 Thread도 직접 생성하고 관리할 수 있다.
프로세스 생성에는 CreateProcess()가 사용된다.
CreateProcess -> Process -> Thread -> Handle
스레드 역시 Windows API를 통해 직접 생성하고 동기화할 수 있다.
Process -> Thread -> Synchronization
여기서 Event, Mutex, Semaphore 등의 동기화 객체도 등장한다.
이 영역부터는 GUI보다는 Windows 시스템 프로그래밍에 가까워진다.
Unicode
Win32에서는 문자열 API를 볼 때 A와 W 접미사를 자주 만나게 된다.
예를 들어 다음과 같다.
CreateFileA
CreateFileW
A는 ANSI 계열이고 W는 Wide Character 기반의 Unicode 계열이다.
현대 Windows 애플리케이션에서는 Unicode 기반의 W API를 사용하는 것이 일반적이다.
프로젝트 설정에 따라 매크로를 통해 다음처럼 작성할 수도 있다.
CreateFile -> CreateFileW
Windows 프로그래밍을 하다 보면 문자열 타입과 Unicode 처리 방식도 자연스럽게 만나게 된다.
Win32와 C++
Win32 API는 C 스타일 API의 특성이 강하다.
따라서 C++에서도 다음과 같은 요소를 직접 다루게 된다.
Structure
Callback
Pointer
Handle
Function Pointer
예를 들어 Window Class를 등록할 때 Window Procedure를 Callback으로 전달한다.
Window Class -> Window Procedure Callback
고수준 C++ 프레임워크처럼 객체가 모든 것을 추상화해 주는 구조와 비교하면 상당히 직접적이다.
대신 Windows가 제공하는 기능을 세밀하게 제어할 수 있다는 장점이 있다.
C++에서는 이러한 Win32 Handle을 RAII Wrapper로 감싸 리소스 수명을 자동으로 관리하는 방식도 많이 사용한다.
WPF와의 차이
WPF와 Win32는 추상화 수준이 다르다.
Win32는 Windows의 네이티브 기능에 직접 가까운 구조다.
Application -> Win32 API -> Windows
WPF는 .NET과 UI Framework를 통해 훨씬 높은 수준에서 Windows UI를 구성한다.
XAML -> WPF -> .NET -> Windows
Win32에서는 직접 처리해야 하는 Window, Message, Handle 등의 세부사항을 WPF에서는 Framework가 상당 부분 대신 처리한다.
그래서 같은 버튼을 만들어도 코드의 구조와 복잡도가 크게 다르다.
Win32와 게임 개발
게임 개발에서는 Win32가 생각보다 자주 등장한다.
게임 엔진이나 그래픽 애플리케이션에서도 Windows Window를 생성하고 입력과 OS 이벤트를 처리해야 하기 때문이다.
대표적으로 다음과 같은 영역에서 Win32가 사용될 수 있다.
Window -> Input -> File -> Thread -> Process -> Graphics API
DirectX와 연결할 때도 Window의 HWND가 필요할 수 있다.
예를 들어 그래픽 API가 렌더링할 대상 Window를 지정하기 위해 HWND를 전달하는 구조가 등장한다.
그래서 Windows 기반 게임 개발을 한다면 Win32의 기본적인 Window와 Message 구조를 알아두는 것이 도움이 된다.
Win32의 전체 흐름
Win32 GUI 프로그램의 실행 구조를 하나로 정리하면 다음과 같다.
wWinMain -> RegisterClass -> CreateWindowEx -> ShowWindow -> Message Loop -> WndProc -> Application Logic
사용자 입력까지 포함하면 다음과 같다.
User Input -> Windows -> Message Queue -> GetMessage -> DispatchMessage -> WndProc -> Application Logic
화면 출력까지 포함하면 다음과 같다.
Application State -> WM_PAINT -> HDC -> GDI -> Screen
프로그램 종료는 다음과 같다.
WM_CLOSE -> DestroyWindow -> WM_DESTROY -> PostQuitMessage -> Message Loop 종료 -> Application 종료
Win32를 하나의 구조로 보기
Win32에서 중요한 개념을 연결하면 다음과 같다.
Application -> Win32 API -> Windows
GUI는 다음 구조로 동작한다.
Window -> HWND -> Message -> Message Queue -> Message Loop -> WndProc
화면 출력은 다음과 같다.
WM_PAINT -> HDC -> GDI -> Screen
시스템 리소스는 다음과 같이 접근한다.
Application -> Handle -> OS Resource
파일과 프로세스, 스레드까지 확장하면 다음과 같다.
Win32 API -> Window / File / Process / Thread / Synchronization
결국 Win32의 중심에는 Windows가 관리하는 Resource와 이를 참조하는 Handle, 그리고 OS가 애플리케이션에 전달하는 Message가 있다.
정리
Win32는 Windows의 네이티브 기능에 접근하기 위한 API 집합이다.
GUI 개발에서 가장 중요한 흐름은 다음과 같다.
wWinMain -> Window Class -> Window -> Message Queue -> Message Loop -> WndProc -> Application Logic
사용자 입력은 다음과 같이 들어온다.
User Input -> Windows -> Message -> Message Queue -> WndProc
화면 출력은 다음과 같이 이어진다.
WM_PAINT -> HDC -> GDI -> Screen
Windows가 관리하는 리소스는 Handle을 통해 접근한다.
Application -> Handle -> Windows Resource
그리고 Win32는 GUI에만 한정되지 않는다.
Win32 API -> Window
Win32 API -> File
Win32 API -> Process
Win32 API -> Thread
Win32 API -> Synchronization
Win32를 공부할 때 모든 API를 외우는 것은 의미가 크지 않다.
먼저 Window, Handle, Message, Message Queue, Message Loop, Window Procedure의 관계를 이해하는 것이 중요하다.
이 구조를 잡아두면 WPF나 WinUI 같은 고수준 Windows 프레임워크를 볼 때도 무엇을 추상화하고 있는지 훨씬 명확해진다.
결국 Win32의 핵심은 API 목록이 아니다.
Windows가 애플리케이션을 실행하고, 입력을 전달하고, Window와 시스템 리소스를 관리하는 기본 구조를 이해하는 것이다.