#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="First";

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
		  ,LPSTR lpszCmdParam,int nCmdShow)
{
	HWND hWnd;
	MSG Message;
	WNDCLASS WndClass;
	g_hInst=hInstance;
	
	WndClass.cbClsExtra=0;
	WndClass.cbWndExtra=0;
	WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
	WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
	WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	WndClass.hInstance=hInstance;
	WndClass.lpfnWndProc=(WNDPROC)WndProc;
	WndClass.lpszClassName=lpszClass;
	WndClass.lpszMenuName=NULL;
	WndClass.style=CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&WndClass);

	hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
		  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
		  NULL,(HMENU)NULL,hInstance,NULL);
	ShowWindow(hWnd,nCmdShow);
	
	while(GetMessage(&Message,0,0,0)) {
		TranslateMessage(&Message);
		DispatchMessage(&Message);
	}
	return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	switch(iMessage) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

실행 결과, 간단한 윈도우 창이 보인다.

 

1. 헤더 파일

첫 행을 보면 windows.h 하나만 include 되어있다.

여기에 모든 API 함수들의 원형과 사용하는 상수들이 죄다 정의되어 있다.

 

2. 시작점

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow)

프로그램의 시작점인 엔트리 포인트(Entry Point)가 WinMain이다.

DOS의 main에서는 인수 사용여부에 따라 여러가지 원형이 있지만 WinMain의 원형은 고정되어 있다.

4개의 인수를 취하는데, 각 인수의 의미는 다음과 같다.

 

인수 의미
hInstance 프로그램의 인스턴스 핸들. 프로그램 자체를 일컫는 정수값
hPrevInstance 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들
없을 경우에는 NULL이며 Win32에서는 항상 NULL
호환성을 위해 존재하는 인수이므로 신경쓰지 않아도 됨
IpCmdLine 명령행으로 입력된 프로그램 인수. DOS의 argv인수에 해당
nCmdShow 프로그램이 실행될 형태이며 최소화, 보통모양 등이 전달됨

 

인스턴스(instance)

:: 클래스가 메모리에 실제로 구현된 실체

HINSTANCE g_hInst;

윈도우즈 프로그램은 여러 개의 프로그램이 동시에 실행되는 멀티태스킹 시스템이며 하나의 프로그램이 여러 번 실행될 수 도 있다. 이때 실행되고 있는 각각의 프로그램을 프로그램 인스턴스라고 하며 간단히 줄여서 인스턴스 라고 한다.

 

예를 들어, 메모장 프로그램 창이 2개 띄어진 상황이라고 생각해보자.

두 프로그램 모두 메모장(Notepad.exe)이지만 운영체제는 각각 다른 메모리를 사용하는 다른 프로그램으로 인식한다.

이때 각 메모장은 서로 다른 인스턴스 핸들을 가지며, 운영체제는 이 인스턴스 핸들값으로 두 개의 메모장을 서로 구별한다.

 

 

3. 메시지 처리 함수

코드를 자세히 보면 두 개의 함수만 있다.

하나는 프로그램의 시작점인 WinMain이고 나머지 하나는 WndProc이다.

DOS에서는 main 함수만으로도 프로그램을 작성할 수 있지만 윈도우즈에서는 아주 특별한 경우를 제외하고는 이 두개의 함수가 모두 있어야 한다.

 

: WinMain에서는 윈도우를 만들고 화면에 출력하는 일만 한다. 그저 프로그램을 시작시키기만

: WndProc이 실질적인 처리를 한다.  이는 프로그램에 따라 천차만별로 달라지며 보통 WinMain 바로 윗부분에 WndProc 함수의 원형이 선언되어 있다.

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

 

 

4. 윈도우 클래스

WinMain 함수에서 하는 가장 중요한 일은 윈도우를 만드는 일이다.

이를 위해선 윈도우 클래스를 먼저 등록한 후 CreateWindow 함수를 호출해야 한다.

윈도우 클래스는 windows.h에 아래와 같이 정의되어 있다.

typedef struct tagWNDCLASS
{
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCSTR      lpszMenuName;
    LPCSTR      lpszClassName;
} WNDCLASS;

10개의 멤버를 가지고 있는데 각각 간단하게 설명하면 아래와 같다.

- style : 윈도우의 스타일

- lpfnWndProc : 윈도우 메시지 처리 함수 지정

- cbClsExtra, cbCWndExtra : 예약 영역, 여분의 공간

- hInstance : 이 윈도우 클래스를 사용하는 프로그램의 번호

- hIcon, hCursor : 이 윈도우가 사용할 마우스 커서와 최소화되었을 경우 출력될 아이콘 지정

- hbrBackground : 윈도우의 배경 색상

- lpszMenuName : 이 프로그램이 사용할 메뉴를 지정

- lpszClassName : 윈도우 클래스의 이름을 정의

 

 

ATOM RegisterClass( CONST WNDCLASS *lpWndClass);

RegisterClass 함수의 인수로 WndClass 구조체의 주소를 넘겨주면 된다.

	WNDCLASS WndClass;
	
	WndClass.cbClsExtra=0;
	WndClass.cbWndExtra=0;
	WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
	WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
	WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	WndClass.hInstance=hInstance;
	WndClass.lpfnWndProc=(WNDPROC)WndProc;
	WndClass.lpszClassName=lpszClass;
	WndClass.lpszMenuName=NULL;
	WndClass.style=CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&WndClass);

이런 특성을 가진 윈도우를 앞으로 사용하겠다는 등록 과정인 셈이다.

 

윈도우 클래스를 등록한 후에는 등록한 윈도우 클래스를 기본으로 윈도우를 실제로 생성해야 한다. 

윈도우를 생성할 땐 CreateWindow 함수를 사용한다.

 

HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)

11개의 인수를 가지고 있는데 각각 간단하게 설명하면 아래와 같다.

- lpszWindowName : 윈도우의 타이틀 바에 나타날 문자열

- dwStyle : 만들고자 하는 윈도우의 형태를 지정(시스템 메뉴, 최대 최소 버튼, 타이틀 바, 경계선 등)

- X, Y, nWidth, nHeight : 윈도우의 크기와 위치 지정. 픽셀 단위

- hWndParent : 부모 윈도우 있을 경우, 부모 윈도우의 핸들을 지정

- hmenu : 윈도우에서 사용할 메뉴의 핸들을 지정

- hinst : 윈도우를 만드는 주체, 즉 프로그램의 핸들을 지정

- lpvParam : CREATESTRUCTURE라는 구조체의 주소이며 특수한 목적에 사용. 보통 NULL 값

 

CreateWindow 함수는 윈도우에 관한 모든 정보를 메모리에 만든 후 윈도우 핸들을 리턴값으로 넘겨준다.

넘겨지는 윈도우 핸들은 hWnd라는 지역 변수에 저장되어 다른 곳에서 참조된다.

 

 

BOOL ShowWindow(hWnd, nCmdShow);

메모리에 저장되어진 윈도우를 화면으로 보이게 할 때 쓰이는 함수이다.

hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을 그대로 넘겨주면 된다.

 

 

각각의 자세한 의미를 파악하려면 아래의 링크를 참고하자

http://soen.kr/lecture/win32api/ApiLec.htm

 

API 강좌

 

soen.kr

 

 

윈도우를 만드는 과정을 간단하게 정리하면 아래와 같다.

 

WndClass 정의  : 윈도우의 기반이 되는 클래스 정의

CreateWindow : 메모리 상에 윈도우 생성

ShowWindow : 윈도우를 화면에 표시

메시지 루프 : 사용자로부터의 메시지를 처리

 

 

5. 메시지 루프

 여기서 메시지란 사용자나 시스템 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보를 말한다.

쉽게 말해서 사용자가 마우스의 버튼을 눌렀다거나 키보드를 눌렀다거나 윈도우가 최소화되었거나 하는 정보들이 메시지이다.

 

메시지가 발생하면 프로그램에서는 메시지가 어떤 정보를 담고 있는가를 분석하여 어떤 루틴을 호출할 것인가를 결정한다. 

즉, 순서를 따르지 않고 주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 진행된다는 뜻

 

보통 WinMain 함수의 끝에 아래와 같은 형식으로 존재한다.

	while (GetMessage(&Message,NULL,0,0)) {
		TranslateMessage(&Message);
		DispatchMessage(&Message);
	}

메시지 루프는 세 개의 함수 호출로 이루어져 있으며 전체 루프는 while 문으로 싸여져 있다.

 

(1) BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

 이 함수는 시스템이 유지하는 메시지 큐에서 메시지를 읽어들인다.

읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장된다. 이 함수는 읽어들인 메시지가 프로그램을 종료하라는 WM_QUIT일 경우 False를 리턴하며 그 외에는 모두 True를 리턴한다. 

따라서 프로그램이 종료할 때까지 while 루프가 계속 실행된다. 나머지 인수는 일단 무시하도록 하자

 

(2) BOOL TranslateMessage( CONST MSG *lpMsg);

 이 함수는 키보드 입력 메시지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 해준다.

즉, 키보드의 눌림(WM_KEYDOWN)과 떨어짐(WM_KEYUP)이 연속적으로 발생할 때 문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 한다. 

 

(3) LONG DispatchMessage( CONST MSG *lpmsg);

 이 함수는 시스템 메시지 큐에서 꺼낸 메시지를 프로그램의 메시지 처리 함수(WndProc)로 전달한다. 

이 함수에 의해 메시지가 프로그램으로 전달되며 프로그램에서는 전달된 메시지를 점검하여 다음 동작을 결정하게 된다.

 

결국, 메시지 루프가 하는 일이란 메시지 큐에서 메시지를 꺼내 메시지 처리 함수(WndProc)으로 보내주는 것 뿐이다.

 

--> 실제 메시지 처리는 별도의 메시지 처리 함수(WndProc)에서 수행한다. 메시지는 시스템의 변화에 대한 정보이며 MSG라는 구조체에 보관된다. 

typedef struct tagMSG
{
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG;
멤버 의미
hwnd 메시지를 받을 윈도우 핸들
message 어떤 종류의 메시지인지. 제일 중요
wParam 전달된 메시지에 대한 부가적 정보. 32비트 값
IParam 전달된 메시지에 대한 부가적 정보. 32비트 값
time 메시지가 발생한 시간
pt 메시지가 발생했을 때의 마우스 위치

 message 멤버를 읽음으로써 메시지의 종류를 파악하며 message 값에 따라 프로그램의 반응이 달라진다.

GetMessage 함수는 읽은 메시지를 MSG 형 구조체에 대입해주며 이 구조체는 DispatchMessage 함수에 의해 응용 프로그램의 메시지 처리 함수(WndProc)으로 전달된다.

 

※ 가장 자주 사용되는 메시지 종류

메시지 의미
WM_QUIT 프로그램 종료 시 발생하는 메시지
WM_LBUTTONDOWN 마우스의 좌측 버튼을 누를 경우 발생
WM_CHAR 키보드로부터 문자가 입력될 때 발생
WM_PAINT 화면을 다시 그려야 할 필요가 있을 때 발생
WM_DESTROY 윈도우가 메모리에서 파괴될 때 발생
WM_CREATE 윈도우가 처음 만들어질 때 발생

 

'Project > WIN32 API' 카테고리의 다른 글

그래픽 오브젝트  (0) 2021.07.12
그래픽  (0) 2021.07.08
리소스  (0) 2021.07.07
WndProc  (0) 2021.06.26
윈도우즈 프로그래밍  (0) 2021.06.25

+ Recent posts