포스트

Win32 프로그램의 기본 흐름과 자료형 정리

Win32 프로그램의 기본 흐름과 자료형 정리

Win32 API를 처음 공부했을 때 가장 크게 다가왔던 부분은 콘솔 프로그램처럼 한 번에 쭉 실행되는 구조가 아니라는 점이었다.
프로그램은 창을 띄우고 난 뒤에도 계속 살아 있어야 하고, 사용자가 어떤 행동을 할지 모르기 때문에 그 흐름을 운영체제가 전달해주는 방식으로 유지한다.

그래서 WinMain, 메시지 루프, WndProc 같은 요소들이 등장한다.
처음에는 형태가 낯설었지만, 전체 흐름을 자연스럽게 따라가면서 이해하려고 하니 그제서야 하나의 구조처럼 보이기 시작했다.


프로그램 전체 흐름

Win32 프로그램은 이벤트 기반으로 움직인다.
창이 만들어지면 프로그램은 멈춰 있는 것이 아니라 운영체제가 보내주는 메시지를 계속 기다리는 상태가 된다.
사용자가 창을 클릭하거나 움직이거나 키보드를 누르면 운영체제가 그 내용을 메시지로 전달하고, 프로그램은 그 메시지를 받아서 필요한 동작을 수행한다.

이 흐름을 한 번에 바라보면 다음과 같은 느낌이다.

프로그램이 시작되면 WinMain이 호출되고, 여기서 창을 만들기 위한 준비를 하고, 메시지 루프가 실행된다.
그 상태로 프로그램은 계속 살아 있으며, 발생하는 모든 메시지는 결국 WndProc으로 전달된다.


WinMain

WinMain은 콘솔 프로그램의 main처럼 시작점이 되지만, 하는 일은 조금 더 많다.
창을 만들기 위한 기본 정보들을 준비하고, 실제 창을 생성한 뒤 메시지 루프를 돌리는 역할까지 포함되어 있다.

함수 앞에 붙은 APIENTRY는 이 함수가 Windows가 지정한 호출 규약(stdcall)을 따른다는 의미다.
호출 규약이라는 말이 어렵게 느껴질 수 있는데, 쉽게 말하면 함수가 호출될 때 인자를 어떤 방식으로 스택에 쌓고 누가 정리하는지를 정한 약속이다.

WinMain의 첫 번째 인자인 HINSTANCE는 실행 중인 프로그램 자체를 식별하기 위한 값이다.
운영체제 입장에서 보면 이 프로그램이 어떤 파일에서 로드되었고 어디에 존재하는지를 나타내는 정보가 필요한데, 그것을 구분하기 위한 번호표 같은 개념이다.

그리고 다음으로 hPrevInstance는 옛날 16비트 Windows 시절에 사용되던 인자이고 현재는 항상 비어 있는 값으로 전달된다.
lpCmdLine은 프로그램 실행 시 전달된 문자열 인자를 그대로 담고 있으며, nCmdShow는 처음 창이 어떤 상태로 표시될지를 나타낸다.

이건 부가적인 이야기 일수도 있지만 ‘in’, ‘in_opt’ 같은 애들은 SAL 이라고 하는 주석이다.


메시지 루프

프로그램은 WinMain에서 창을 만들고 나면, 바로 메시지 루프에 들어간다. 여기서 운영체제가 보내주는 메시지를 계속 받으면서 프로그램이 반응하게 된다.

이 루프는 프로그램이 살아 있는 동안 계속 실행되며, 운영체제가 보내는 모든 이벤트가 이 흐름을 통해 프로그램에 전달된다.


WndProc

WndProc은 Win32 프로그램의 중심에 있는 함수라고 해도 좋다.
창이 이동하거나 크기가 바뀌는 상황, 키보드를 눌렀을 때, 마우스를 움직일 때, 창을 닫으려고 할 때
거의 모든 흐름이 메시지 형태로 전달되고 이 함수에서 처리된다.

운영체제는 발생한 상황을 메시지라는 형태로 만들어 보내고, 프로그램은 그 메시지를 WndProc에서 받아 해석한다.
어떤 메시지가 어떤 상황을 의미하는지 알고 나면 WndProc 내부에서 어떤 처리가 이루어지는지 훨씬 자연스럽게 이해된다.


Win32에서 자주 등장하는 자료형

WinAPI는 C 기반으로 제작된 오래된 구조다 보니 처음 보면 낯선 자료형들이 많다. 하지만 흐름 속에서 의미를 잡아가다 보면 그 존재 이유가 자연스럽게 보인다.

HANDLE

운영체제가 관리하는 객체들은 대부분 HANDLE이라는 공통된 형태를 사용한다.
파일, 프로세스, 스레드처럼 운영체제가 직접 들고 있어야 하는 자원들은 직접 주소를 노출하지 않고 이 핸들 값을 통해 접근하도록 되어 있다. 직접 주소를 알려주지 않고 핸들 값을 통해 간접적으로 접근하는 이유 중 가장 큰 이유는 보안성과 안정성 때문이다.
그리고 사실상 핸들을 주고 관리는 OS가 하기에 좀 더 편하다는 생각이 든다.


HWND

HWND는 Window를 식별하기 위한 핸들이다.
이미 만들어진 창을 조작하거나 그 창으로 메시지를 보낼 때 항상 HWND가 필요하다.


WPARAM과 LPARAM

WndProc에서 메시지를 처리할 때 추가 정보가 필요한 경우 WPARAM과 LPARAM에 데이터가 들어 있다.
윈도우 메시지는 상황에 따라 필요한 정보의 양이 다른데, 이 두 변수를 통해 메시지마다 필요한 정보를 유연하게 전달할 수 있다.

WPARAM은 비교적 간단한 값을 전달하는 데 쓰이고, LPARAM은 좌표나 구조체 포인터처럼 더 많은 데이터가 필요할 때 사용된다.


LRESULT

WndProc이 메시지를 처리한 뒤 운영체제에게 결과를 알려주는 값이 LRESULT다.
특정 메시지를 처리했다는 의미를 담거나 기본 동작을 사용하겠다는 판단을 내려줄 때 사용된다.


CALLBACK과 WINAPI

WinAPI 함수들에는 CALLBACK 또는 WINAPI 같은 키워드가 붙어 있다.
이 키워드는 함수가 어떤 호출 규약을 따라야 하는지를 정해준다.
운영체제가 호출하는 함수와 사용자가 직접 호출하는 함수 사이에 혼동이 생기지 않도록 하는 장치라고 보면 된다.


마무리

Win32 프로그램은 WinMain으로 시작해 메시지 루프를 통해 계속 이벤트를 받아들이고, 처리는 결국 WndProc에서 이루어진다.
처음 보면 복잡해 보이던 흐름도 전체 구조 안에서 보면 각각의 역할이 자연스럽게 이어진다. Win32 API가 사용하는 자료형들도 이런 흐름 안에서 보면 훨씬 이해하기 쉬워지고,
실제로 코드를 작성하거나 디버깅할 때도 훨씬 편하게 다가온다. 한번에 이해하기는 어렵지만 문서화를 시켜 구체화를 하다보면 잊었던 내용도 다시 생각이 나기에 남겨두는 글이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

© Cryptonite7777. 일부 권리 보유

Powered by Jekyll with Chirpy theme