본문 바로가기
프로그래밍/C++

Win32 API ] 윈도우 프로그램의 기본 구조

by eteo 2023. 12. 13.

 

 

 

 

MFC 프레임워크 안에 숨겨진 윈도우 프로그램의 기본 구조를 이해해보자.

 

 

 

Windows에서 실행되는 애플리케이션의 종류

 

 

윈도우에서 실행되는 애플리케이션은 크게 콘솔 애플리케이션과 GUI 애플리케이션으로 나눌 수 있다.

 

 

 

 

콘솔 앱 또는 데스크톱 애플리케이션 만들기

 

 

 

 

 

프로젝트 생성 시 콘솔 또는 데스크톱 애플리케이션을 지정하여 생성할 수 있으며, 이미 생성한 프로젝트를 콘솔 또는 GUI 애플리케이션으로 변경하려면 프로젝트 우클릭 - 속성 - 링커 - 시스템 - 하위 시스템에서 변경 가능하다.

 

 

 

 

 

 

 

Windows C/C++ 애플리케이션의 진입점

 

프로그램이 실행될 때 가장 먼저 호출되는 함수를 프로그램의 진입점(entry point)라고 하고, 이 진입점을 호출하는 함수는 C/C++ 런타임(CRT) 초기화를 담당하는 함수로 다양한 초기화 작업을 수행 한 뒤 main함수를 호출하는 역할을 한다.

프로그램이 GUI냐 콘솔이냐에 따라 다른 CRTStartup 함수가 사용되며 이는 프로젝트 설정에 의해 지정된다.

 

 

https://i.stack.imgur.com/VG9RO.jpg

 

 

 

 

✔ GUI 응용 프로그램에서 콘솔창을 띄우려면 다음의 컴파일러 지시문을 추가하면 된다.

#ifdef _DEBUG
#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")
#endif

 

 

 

Win32 API를 사용해 작성된 간단한 윈도우 프로그램

 

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR LpszCmdLine, int nCmdShow)
{
	HWND		hwnd;				// 윈도우 핸들
	MSG			msg;				// 메시지 구조체
	WNDCLASSEX	WndClass;			// 윈도우 클래스 구조체

	// 윈도우 클래스 구조체 WndClass에 값을 채워 윈도우 클래스를 등록한다.
	WndClass.cbSize				= sizeof(WNDCLASSEX);						// 구조체 크기
	WndClass.style				= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;		// 클래스 스타일
	WndClass.lpfnWndProc		= WndProc;									// 윈도우 프로시저
	WndClass.cbClsExtra			= 0;										// 윈도우 클래스 데이터영역
	WndClass.cbWndExtra			= 0;										// 윈도우의 데이터 영역
	WndClass.hInstance			= hInstance;								// 인스턴스 핸들
	WndClass.hIcon				= LoadIcon(NULL, IDI_APPLICATION);			// 아이콘 핸들
	WndClass.hCursor			= LoadCursor(NULL, IDC_ARROW);				// 커서 핸들
	WndClass.hbrBackground		= (HBRUSH)GetStockObject(WHITE_BRUSH);		// 배경 브러시 핸들
	WndClass.lpszMenuName		= NULL;										// 메뉴 이름
	WndClass.lpszClassName		= "EasyText";								// 윈도우 클래스 이름
	WndClass.hIconSm			= 0;										// 기본적인 작은 아이콘

	// 윈도우 클래스를 등록한다.
	RegisterClassEx(&WndClass);

	// 프레임 윈도우를 생성한다.
	hwnd = CreateWindow(			// 윈도우 생성 API 함수
		"EasyText",					// 등록된 윈도우 클랫 ㅡ이름
		"WindowProgram",			// 타이틀 바에 출력될 문자열
		WS_OVERLAPPEDWINDOW,		// 윈도우 스타일
		CW_USEDEFAULT,				// 윈도우 좌측 상단의 x 좌표
		CW_USEDEFAULT,				// 윈도우 좌측 상단의 y 좌표
		CW_USEDEFAULT,				// 윈도우의 너비
		CW_USEDEFAULT,				// 윈도우의 높이
		NULL,						// 부모 윈도우의 핸들
		NULL,						// 메뉴 또는 자식 윈도우의 핸들
		hInstance,					// 애플리케이션 인스턴스 핸들
		NULL						// 윈도우 생성 데이터의 주소
	);

	// 프레임 윈도우를 화면에 표시한다.
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	// 메시지 큐로부터 메시지를 받아와 해당 윈도우 프로시저로 보낸다.
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;				// 디바이스 컨텍스트
	RECT rect;
	PAINTSTRUCT ps;
	LPCSTR szMsg1 = "간단한 윈도우 프로그램";
	LPCSTR szMsg2 = "키보드가 눌러졌습니다.";
	LPCSTR szMsg3 = "키보드가 떼어졌습니다.";

	// 커널에서 들어온 메시지를 switch 문을 이용하여 처리
	switch (message)
	{
	case WM_CREATE:
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		TextOut(hdc, 10, 10, szMsg1, strlen(szMsg1));
		EndPaint(hwnd, &ps);
		break;
	case WM_KEYDOWN:
		hdc = GetDC(hwnd);
		GetClientRect(hwnd, &rect);
		DrawText(hdc, szMsg2, strlen(szMsg2), &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
		ReleaseDC(hwnd, hdc);
		break;
	case WM_KEYUP:
		hdc = GetDC(hwnd);
		GetClientRect(hwnd, &rect);
		DrawText(hdc, szMsg3, strlen(szMsg3), &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
		ReleaseDC(hwnd, hdc);
		break;
	case WM_LBUTTONDBLCLK:
		MessageBox(hwnd, "마우스 더블 클릭", "마우스 메시지", MB_OK | MB_ICONASTERISK);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}

	return 0;
}

 

 

 

모든 윈도우 프로그램과 마찬가지로 MFC 애플리케이션도 WinMain()과 윈도우 프로시저를 가지고 있지만 프레임워크 안에 숨겨져 있기 때문에 전체적인 구조를 파악하기가 어렵다.

 

 

 

 

 

 

코드출처 :

Visaul C++ 2022 MFC 윈도우 프로그래밍, 정일홍 저