[WinAPI] 윈도우 생성과 표시, CreateWindow와 ShowWindow 활용 가이드
💻 WinAPI로 창을 만들고 시각적으로 활성화하는 실전 방법과 핵심 원리
PC 애플리케이션을 개발하다 보면 단순한 콘솔 환경을 넘어, 사용자와 상호작용할 수 있는 창(Window)을 구현해야 할 때가 있습니다. 특히 Windows 환경에서는 WinAPI를 통해 창을 만들고, 표시하며, 사용자 이벤트를 처리하는 전통적인 방식이 널리 사용되고 있습니다. 이 과정에서 필수적으로 알아야 할 함수가 바로 CreateWindow 또는 CreateWindowEx입니다. 여기에 ShowWindow와 UpdateWindow를 함께 사용하면, 단순히 내부적으로만 존재하는 창이 아니라 실제 화면에 보이고 반응하는 창을 완성할 수 있습니다. 이번 글에서는 초보자도 이해하기 쉽게, 창 생성과 표시 과정의 흐름과 필수 코드를 단계별로 설명해 드리겠습니다.
아래에서 다룰 내용은 단순히 함수 호출 방법만이 아니라, 각 함수의 역할과 사용 시 주의사항, 그리고 실전에서 자주 맞닥뜨리는 문제 상황의 해결 방법까지 포함됩니다. 이를 통해 WinAPI 기반의 GUI 애플리케이션 제작에 필요한 기본기를 확실하게 다질 수 있을 것입니다.
📋 목차
🔗 CreateWindow와 CreateWindowEx의 기본 개념
Windows API에서 CreateWindow와 CreateWindowEx는 애플리케이션의 창을 생성하는 핵심 함수입니다.
이 함수들은 운영체제에 새로운 윈도우 객체를 만들고, 해당 객체를 관리할 핸들(HWND)을 반환합니다.
즉, 코드에서 윈도우를 조작하기 위해 반드시 거쳐야 하는 첫 단계가 바로 이 과정입니다.
두 함수의 차이점은 CreateWindowEx가 확장 스타일(Extended Styles)을 추가로 지정할 수 있다는 점입니다.
예를 들어, 창을 항상 위에 표시하거나(WS_EX_TOPMOST), 투명 효과를 적용하거나(WS_EX_LAYERED) 하는 고급 기능은 CreateWindowEx에서만 가능합니다.
반면 CreateWindow는 일반적인 스타일로 기본 창을 만들 때 충분히 사용할 수 있습니다.
💡 함수 호출 시 필수로 이해해야 할 매개변수
두 함수 모두 다음과 같은 주요 매개변수를 필요로 합니다.
- 🏷️lpClassName : 사전에 RegisterClass로 등록된 윈도우 클래스 이름
- 🖋️lpWindowName : 창의 제목 표시줄에 나타날 문자열
- 📐dwStyle : 창의 스타일(WS_OVERLAPPEDWINDOW 등)
- 📏x, y, nWidth, nHeight : 창의 위치와 크기
- 🪟hWndParent : 부모 윈도우 핸들(없으면 NULL)
- 🔧hMenu : 메뉴 핸들 또는 자식 윈도우 ID
- ⚙️hInstance : 인스턴스 핸들
- 📦lpParam : 생성 시 전달할 추가 데이터
📜 기본 사용 예제 코드
HWND hWnd = CreateWindowEx(
0, // 확장 스타일
L"SampleWindowClass", // 윈도우 클래스 이름
L"My First Window", // 창 제목
WS_OVERLAPPEDWINDOW, // 윈도우 스타일
CW_USEDEFAULT, // X 좌표
CW_USEDEFAULT, // Y 좌표
800, // 너비
600, // 높이
NULL, // 부모 윈도우
NULL, // 메뉴
hInstance, // 인스턴스 핸들
NULL // 추가 매개변수
);
💡 TIP: CreateWindow 계열 함수 호출 전에 반드시 RegisterClass 또는 RegisterClassEx로 윈도우 클래스를 등록해야 합니다. 그렇지 않으면 창 생성이 실패합니다.
🛠️ 윈도우 생성 절차와 필수 파라미터
WinAPI에서 창을 만들기 위해서는 단순히 CreateWindow나 CreateWindowEx를 호출하는 것만으로는 충분하지 않습니다.
전체 과정은 윈도우 클래스 등록 → 윈도우 생성 → 윈도우 표시 → 메시지 루프 실행이라는 흐름을 반드시 거쳐야 하며, 각 단계마다 필요한 파라미터와 설정이 존재합니다.
📋 윈도우 생성 단계별 절차
- 1️⃣윈도우 클래스 등록 – WNDCLASS 또는 WNDCLASSEX 구조체를 설정하고 RegisterClass 계열 함수로 등록
- 2️⃣창 생성 – CreateWindow 또는 CreateWindowEx 호출
- 3️⃣윈도우 표시 – ShowWindow, UpdateWindow 호출
- 4️⃣메시지 루프 – GetMessage, TranslateMessage, DispatchMessage를 이용해 이벤트 처리
⚙️ 주요 파라미터 설정 시 주의사항
윈도우 생성 시 지정하는 매개변수 값은 프로그램의 동작과 UI에 큰 영향을 미칩니다.
예를 들어 dwStyle에서 WS_OVERLAPPEDWINDOW를 선택하면 제목 표시줄, 크기 조절, 최소화/최대화 버튼이 모두 포함된 기본 창을 만들 수 있습니다.
반면 WS_POPUP을 사용하면 제목 표시줄이 없는 전체 화면 창을 만들 수 있습니다.
또한 좌표(x, y)와 크기(nWidth, nHeight)는 CW_USEDEFAULT를 사용하면 운영체제가 적절한 값을 자동으로 할당합니다.
다만, 해상도에 따라 초기 위치나 크기를 세밀하게 조정해야 하는 경우도 있으니 상황에 맞게 직접 값을 지정하는 것이 좋습니다.
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MyWindowClass";
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(
0, L"MyWindowClass", L"My App",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL, NULL, hInstance, NULL
);
⚠️ 주의: RegisterClassEx로 등록한 클래스 이름과 CreateWindowEx에서 사용하는 클래스 이름이 일치하지 않으면 창 생성이 실패합니다.
⚙️ ShowWindow와 UpdateWindow로 시각적 활성화
CreateWindow 또는 CreateWindowEx를 호출해 창을 만들었다고 해서 화면에 바로 보이는 것은 아닙니다.
운영체제는 내부적으로 창을 생성하지만, 이를 화면에 표시하고 초기 그리기 작업을 수행하려면 ShowWindow와 UpdateWindow 함수를 함께 호출해야 합니다.
🖼️ ShowWindow의 역할과 사용법
ShowWindow는 창의 표시 상태를 제어하는 함수로, 두 번째 매개변수에 지정하는 nCmdShow 값에 따라 창이 어떻게 나타날지를 결정합니다.
예를 들어 SW_SHOW는 창을 보이게 하고, SW_HIDE는 창을 숨깁니다.
이 값은 보통 WinMain 함수의 인자로 전달되는 nCmdShow를 그대로 사용합니다.
🎨 UpdateWindow의 역할과 차이점
UpdateWindow는 ShowWindow로 표시된 창에 대해 WM_PAINT 메시지를 즉시 보내, 화면에 내용을 그리도록 요청합니다.
즉, ShowWindow가 창의 ‘상태’를 바꾸는 것이라면, UpdateWindow는 창의 ‘내용’을 그리게 하는 함수입니다.
일반적으로 창 생성 직후 두 함수를 연달아 호출하는 것이 권장됩니다.
ShowWindow(hWnd, nCmdShow); // 창 표시
UpdateWindow(hWnd); // 즉시 그리기 요청
💡 TIP: 창이 깜빡이거나 비어 있는 상태로 보이지 않게 하려면, ShowWindow 후 UpdateWindow를 호출해 WM_PAINT 메시지가 즉시 처리되도록 하는 것이 좋습니다.
📊 ShowWindow의 주요 플래그
| 플래그 | 설명 |
|---|---|
| SW_SHOW | 창을 활성화하고 표시 |
| SW_HIDE | 창을 숨김 |
| SW_MAXIMIZE | 창을 최대화하여 표시 |
| SW_MINIMIZE | 창을 최소화하여 표시 |
🔌 창 메시지 루프와 이벤트 처리
WinAPI 애플리케이션에서 창을 생성한 뒤에는 메시지 루프를 통해 사용자 입력과 시스템 이벤트를 처리해야 합니다.
Windows 운영체제는 마우스 클릭, 키보드 입력, 창 크기 변경 등 다양한 이벤트를 메시지 형태로 애플리케이션에 전달합니다.
이 메시지를 적절히 받아서 처리하는 것이 프로그램 동작의 핵심입니다.
📨 기본 메시지 루프 구조
WinMain 함수 내에서 보통 다음과 같은 형태로 메시지 루프를 작성합니다.
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 키보드 입력 해석
DispatchMessage(&msg); // 메시지를 윈도우 프로시저로 전달
}
이 루프는 GetMessage가 FALSE를 반환할 때(보통 WM_QUIT 수신 시) 종료됩니다.
즉, 사용자가 프로그램을 닫을 때까지 이벤트를 계속 처리하게 됩니다.
🛠️ 윈도우 프로시저(WndProc)에서의 메시지 처리
윈도우 프로시저 함수는 시스템이 전달하는 메시지를 해석하고, 필요한 동작을 수행하는 역할을 합니다.
아래는 기본적인 예시입니다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_PAINT:
// 그리기 코드
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
💡 TIP: WndProc에서는 반드시 처리하지 않는 메시지를 DefWindowProc으로 전달해야 정상적인 윈도우 동작이 보장됩니다.
⚠️ 메시지 루프에서 흔히 발생하는 문제
- 🚫메시지를 처리하지 않아 창이 응답 없음 상태가 되는 경우
- 🕒긴 연산을 메시지 루프에서 직접 실행하여 UI가 멈추는 문제
- 🔄잘못된 메시지 처리 순서로 인한 비정상 종료
💡 창 생성 시 자주 발생하는 오류와 해결 방법
WinAPI로 창을 만들 때 초보 개발자들이 가장 많이 겪는 문제는 함수 호출 자체보다도, 사전 준비 절차나 파라미터 설정에서 발생하는 오류입니다.
이 단계에서 잘못 설정되면 CreateWindow 계열 함수가 NULL을 반환하거나, 창이 생성되더라도 표시되지 않는 문제가 생깁니다.
🚫 자주 발생하는 오류 유형
- ❌RegisterClass 또는 RegisterClassEx로 클래스 등록을 하지 않음
- ❌등록된 클래스 이름과 CreateWindow 계열 함수의 lpClassName 불일치
- ❌hInstance, dwStyle 등의 필수 파라미터 값 누락 또는 잘못된 값
- ❌ShowWindow/UpdateWindow 호출 누락으로 화면에 표시되지 않는 문제
🛠️ 해결 방법과 디버깅 팁
문제를 해결하기 위해서는 각 단계에서 반환 값을 반드시 확인하고, GetLastError 함수를 통해 구체적인 오류 코드를 확인하는 습관을 들이는 것이 좋습니다.
또한 디버그 모드에서 breakpoint를 사용해 창 생성 전후의 변수를 점검하면 문제 원인을 쉽게 찾을 수 있습니다.
HWND hWnd = CreateWindowEx(...);
if (hWnd == NULL) {
DWORD error = GetLastError();
// error 코드 확인 후 조치
}
💎 핵심 포인트:
오류가 발생하면 CreateWindow 단계뿐 아니라 RegisterClass, ShowWindow, UpdateWindow 등 전체 흐름을 점검하는 것이 중요합니다.
📌 안정적인 창 생성 체크리스트
- ✅RegisterClass 또는 RegisterClassEx 호출 여부 확인
- ✅클래스 이름, hInstance, dwStyle 등 필수 매개변수 일치 여부 점검
- ✅ShowWindow와 UpdateWindow 호출 시점 확인
- ✅GetLastError로 오류 코드 분석
❓ 자주 묻는 질문 (FAQ)
CreateWindow와 CreateWindowEx 중 어떤 것을 사용해야 하나요?
윈도우가 생성되었는데 화면에 보이지 않는 이유는 무엇인가요?
nCmdShow 값은 어떻게 설정하나요?
필요 시 SW_SHOW, SW_MAXIMIZE 등 원하는 표시 방식으로 변경 가능합니다.
메시지 루프가 없으면 어떤 문제가 발생하나요?
WM_PAINT 메시지는 언제 발생하나요?
CreateWindow 호출 전에 반드시 필요한 준비 작업은 무엇인가요?
GetLastError는 언제 사용하나요?
창이 생성되었는데 입력이 반응하지 않는 경우 해결 방법은?
📌 CreateWindow로 창 만들고 ShowWindow로 표시하는 전체 흐름 정리
WinAPI에서 윈도우를 생성하고 화면에 표시하기까지의 흐름은 윈도우 클래스 등록, CreateWindow 또는 CreateWindowEx 호출, ShowWindow와 UpdateWindow로 시각적 활성화, 메시지 루프로 이벤트 처리의 순서로 진행됩니다.
CreateWindowEx는 확장 스타일을 지정해 항상 위에 표시하거나 반투명 처리 등 고급 효과를 부여할 수 있고, 일반적인 창이라면 CreateWindow만으로도 충분합니다.
창 핸들(HWND)이 유효하게 반환되면 ShowWindow로 표시 상태를 정하고, UpdateWindow로 즉시 WM_PAINT를 유도해 초기 깜빡임이나 빈 화면을 최소화합니다.
이후 GetMessage, TranslateMessage, DispatchMessage로 구성된 메시지 루프가 사용자 입력과 시스템 이벤트를 처리하며, WndProc에서 각 메시지의 동작을 구현합니다.
오류가 발생하면 GetLastError로 원인을 추적하고, 클래스 이름과 hInstance, 스타일, 좌표와 크기 같은 파라미터 일관성을 점검하면 대부분의 문제를 해결할 수 있습니다.
실전에서는 RegisterClassEx와 클래스 이름 일치, nCmdShow의 적절한 사용, DefWindowProc 호출 누락 방지 등 기본기를 정확히 지키는 것이 안정성을 좌우합니다.
본 글의 예제와 체크리스트를 바탕으로 최소 코드로 빠르게 창을 만들고 표시한 뒤, 필요한 메시지를 확장해 기능을 더해 가면 생산성이 크게 올라갑니다.
🏷️ 관련 태그 : WinAPI, CreateWindow, CreateWindowEx, ShowWindow, UpdateWindow, 윈도우프로그래밍, C언어, WindowsAPI, 메시지루프, HWND