최호성님의 VISUAL C++ 2008 MFC 윈도우 프로그래밍을 학습한 뒤 정리한 내용입니다.
_tWinMain() 함수는 크게 세부분으로 나눌 수 있는데 첫 번째는 윈도우 응용 프로그램이 갖춰어야 할 기본요소 정의 및 윈도우 생성부분, 두 번째는 메인 메시지 루프 부분, 세 번째는 전달받은 메시지를 어떻게 처리할지 결정하는 윈도우 프로시저 함수 부분(WndProc())입니다.
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow)
_tWinMain() 함수의 인자
- hInstance 응용 프로그램 자체를 식별, 정확히는 실행 파일의 이미지가 로드된 가상 메모리 상의 주소를 의미
- hPrevInstance 먼저 실행된 인스턴스의 핸들, 실제로는 사용하지 않음
- lpCmdLine 실행 파일의 경로와 명령줄의 내용을 담은 문자열의 포인터, C 언어의 argv 파라미터와 유사
- nCmdShow 윈도우 화면을 보여주는 방법을 명시
/* * Message structure */ typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; #ifdef _MAC DWORD lPrivate; #endif } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
MSG 구조체
- WinUser.h에 정의되어 있음
- hwnd 메시지를 전달받을 윈도우 핸들
- message 윈도우 메시지
- wParam, lParam 윈도우 메시지와 연결된 메시지 파라미터
- time 메시지 발생 시간
- pt 메시지가 발생한 화면 상의 좌표
인스턴스 핸들은 대부분 고유한 int형 값이나 메모리의 포인터로 이루어집니다. 모든 윈도우가 자신을 식별하는 고유 번호를 갖게 되어있고 이 번호가 윈도우 핸들입니다. 윈도우 관련 API 함수는 대부분 윈도우 핸들을 인자로 받습니다.
hAccelTable은 액셀러레이터 키 테이블의 핸들입니다. 응용 프로그램 대부분이 바로가기 키를 지원하고 이를 정의하는 것은 개발자가 해야할 일입니다. 정의된 바로가기 키를 담은 테이블이 프로그램 시작에 앞서 로딩되는데 이 테이블을 다루기 위한 핸들을 저장하는 곳이 hAccelTable입니다. 저장된 바로가기 키는 Visual Studio의 리소스 뷰의 Accelerator에서 확인할 수 있습니다.
LoadString() 함수는 문자열 테이블을 참조하여 IDS_APP_TITLE에 해당하는 문자열을 로딩하며 szTitle 버퍼에 저장합니다. IDS_APP_TITLE는 정의된 상수로 Resource.h에서 확인할 수 있습니다.
MyRegisterClass() 함수는 생성할 윈도우의 기본적인 정보를 담은 구조체를 완성하고 RegisterClassEx() API 함수를 호출하여 윈도우 클래스를 등록합니다. 윈도우 클래스는 C++ 클래스와 다릅니다. 윈도우 프로그래밍에서는 만들고자 하는 윈도우의 특징을 정의하고 이름을 붙여 등록한 후 윈도우를 생성하는데 이때 붙여진 이름이 바로 윈도우 클래스입니다.
윈도우 클래스를 생성하려면 WNDCLASSEX 구조체에 값을 채워 윈도우 클래스를 등록해야합니다.
/* WNDCLASSEXW 구조체 선언부 */ typedef struct tagWNDCLASSEXW { UINT cbSize; /* Win 3.x */ UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; /* Win 4.0 */ HICON hIconSm; } WNDCLASSEXW, *PWNDCLASSEXW, NEAR *NPWNDCLASSEXW, FAR *LPWNDCLASSEXW;
윈도우 스타일, 윈도우 프로시저 함수의 주소, 인스턴스의 핸들, 메인 아이콘과 커서, 배경색, 윈도우 클래스 등이 멤버로 포함되어있는데, 이는 결국 윈도우의 기본 외관과 특징을 정의한 것입니다. WNDPROC은 윈도우 프로시저 함수를 등록하는 부분으로 개발에 주의해야합니다.
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
WNDPROC() 함수가 콜백 함수라는 것이 중요합니다. 콜백 함수는 역으로 호출 받는 함수입니다. 윈도우 프로시저 함수는 메인 메시지 루프에서 DispatchMessage() 함수를 호출하면 호출을 받습니다.
WinDef.h 파일을 열면 CALLBACK 이라는 상수가 정의되어 있는데, 이를 통해 CALLBACK이 __stdcall 콜 컨벤션임을 알 수 있습니다. 윈도우 프로시저 함수는 반드시 콜 컨벤션을 사용해야하는데, 그래야 윈도우 운영체제가 해당 함수를 알아서 호출하더라도 아무런 문제가 발생하지 않습니다. 이러한 구조가 되는 것은 C언어에서는 모든 함수를 직접 호출하며 전체 실행 흐름을 코드에 명시하였지만, 윈도우 프로그램 그와는 다르게 언제든 이벤트가 발생할 수 있습니다. 윈도우 프로그램은 비동기적 이벤트에 대해 대응하기 위해 메시지 드리븐 방식을 채택하였고, 이를 통해 이벤트 발생과 처리의 시간차를 극복할 수 있습니다.
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다. hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
InitInstance() 함수는 윈도우 생성코드를 포함합니다. CreateWindow() API 함수를 호출하여 윈도우를 생성합니다.
CreateWindow() 인자값
- WS_OVERLAPPEDWINDOW 일반적인 프레임 윈도우 스타일
- hInstance MyRegisterClass() 함수에서 정의한 것과 같은 이름 사용
ShowWindow() 함수는 특정 윈도우를 화면에 보이거나 감추는 역할을 하며, UpdateWindow() 함수는 윈도우의 외관을 깨끗이 다시 그려 주는 역할을 합니다.