본문 바로가기
프로그래밍/API

API-창만들기

by 리뷰하는 (게임)프로그래머_리프TV 2010. 4. 19.


API,

Application Programming Interface 의 약자로써

쉽게 말해 윈도우 프로그래밍이다.

뭐 저게 중요한건 아니고.

API의 가장 기본적인 창을 띄우는 소스에 대해서 분석해 보자.(간략하게)

#include <windows.h>

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("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( COLOR_WINDOW+1 );
	//(HBRUSH)GetStockObject( LTGRAY_BRUSH );			// 이미 지정되어 있는 매크로 색상
	//CreateHatchBrush( HS_DIAGCROSS, COLOR_WINDOW+1 );	// 바둑판 형식으로 출력이 된다.
	//CreateSolidBrush( COLOR_WINDOW+1 );				// 윈도우 기본색상
	WndClass.hCursor = LoadCursor( NULL, IDC_NO );
	//LoadCursor( NULL, IDC_ARROW );
	WndClass.hIcon = LoadIcon( NULL, IDI_ERROR );
	//LoadIcon( NULL, IDI_APPLICATION );
	WndClass.hInstance = hInstance;
	WndClass.lpfnWndProc = 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, NULL, 0, 0 ) )
	{
		TranslateMessage( &Message );
		DispatchMessage( &Message );
	}
	return (int)Message.wParam;
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
	switch( iMessage )
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONDOWN:
		MessageBeep(0);		// 마우스 버튼 클릭시 "띵띵" 소리가 나온다.
		return 0;
	}
	return( DefWindowProc( hWnd, iMessage, wParam, lParam));
}​


결과 창



뭐 특별한건 없고, 그냥 말 그대로 창을 띄우는 가장 기본적인 소스코드 이다.
천천히, 하나하나 살펴보도록 하자.

일단 해더가 인데,
윈API에 대한 대부분의 함수가 저 해더한에 들어가 있기 때문이다.

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


전역 함수 선언, 뭐하는 함수인지는 모르겠지만 HWND, UINT, WPARAM, LPARAM을 인자로 받는다.
LRESULT를 return하고,
어이에 써야 할지 모르는 CALLBACK 이라는 것을 해주고 있다.

LRESULT : 정의를 따라가 보니,
typedef LONG_PTR LRESULT;
typedef _W64 long LONG_PTR, *PLONG_PTR;
#define _W64 __w64
처럼 단계별로 쫒아 갈 수 있었다.
자세히는 모르지만 대략 64비트의 숫자가 아닌가 예상해 본다.

CALLBACK : 이것도 따라가 보니,
#define CALLBACK __stdcall
처럼 되어 있었다.
이것도 뭐하는 건지는 모르겠지만, 들었던 기억으론 stdcall방식을 사용한다! 라는 것을 말하고 싶었다고 한다.
그럼 그냥 __stdcall 이라고 하면 되지 않을까? 하고 생각 했지만,
상황에 따라서 CALLBACK의 define만을 변경하여 이식성을 높이기 위함이라고 한다.

HWND : 핸들을 의미한다.
DECLARE_HANDLE (HWND);
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
식으로 되어 있지만,
책에 있는 설명으로는 구체적인 어떤 대상에 붙여진 번호라고 한다.
핸들의 특징은,
1. 정수값이다. 사용하는 목적은 오로지 구분을 위한 것. 핸들끼리 중복되지 않아야 한다.
2. 핸들은 운영체제가 발급하며 사용자는 쓰기만한다, 사용자가 직접 핸들을 만드는 경우는 없다.
3. 같은 종류의 핸들끼리는 절대 중복된 값을 가지지 않는다.(다른 종류 핸들끼리는 중복된 값을 가질 수도 있다.)
4. 정수형 이지만 어떤 값인지는 몰라도 된다. 핸들을 대입 받아 쓰고 난 후에는 버리면 된다.

이것들 토대로 보았을 때, HWND라는 것은 윈도우핸들 정도를 말하는 것이 아닐까 싶다.

UINT : UINT는 생각보다 단순 했다.
typedef unsigned int UINT;
단순히 부호 없는 int형을 말하는 것, 왜 직접 쓰지 않고 UINT로 변경했는지에 대해선 CALLBACK과 비슷한 이유 일거라 생각한다.

WPARAM :
typedef UINT_PTR WPARAM;
typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;
당장은 뭐에 쓰는지는 모르겠으나, unsigned int 와 그 포인터 정도로 생각 할 수 있겠다.

LPARAM :
typedef LONG_PTR LPARAM;
typedef _W64 long LONG_PTR, *PLONG_PTR;
WPARAM과 비슷하다, 단지 long이라는 차이.

HINSTANCE g_hInst;​


잠시 후 설명,

LPCTSTR lpszClass = TEXT("first");​


이 부분을 보기 전에 미리 파악해야 할 것이 있다.

  C 타입 유니코드 타입 
 char  TCHAR
 char *  LPSTR
 const char *  LPCTSTR


즉, const char* 타입의 변수 정도로 생각하면 될 것이다.

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


윈도우 프로그램의 시작은 WinMain이다. C, C++에서의 시작이 main이듯이 윈도우 프로그래밍의 시작이라고 생각하자.
APIENTRY 지정자는 윈도우즈 표준 호출 규약인 __stdcall을 사용한다는 뜻이란다.
CALLBACK와 같은 맹락인듯,
안에 포함되어 있는 4개의 인자는 다음과 같다.

 인수  의미
 hInstance  프로그램의 인스턴스(실행 중인 프로그램 하나를 칭함) 핸들.
 hPrevInstance  바로 앞에 실행된 현재 프로그램의 인스턴스 핸들이다. 없을 경우는 NULL이 되며 Win32에서는 항상 NULL이다. 16비트와의 호환성을 위해서만 존재하는 인수이므로 신경쓰지 않아도 된다.
 lpszCmdParam  명령행으로 입력된 프로그램 인수. 도스의 argv 인수에 해당하며 보통 실행 직후에 열 파일의 경로가 전달된다.
 nCmdShow  프로그램이 실행될 형태이며 최소화, 보통, 모양 등이 전달.


정도로 생각하자.

위에 존재하는
HINSTANCE g_hInst;
WinMain의 hInstance를 저장하기 위해 같은 타입을 정의했다. 정도로 생각하자.



	HWND hWnd;
	MSG Message;
	WNDCLASS WndClass;
	g_hInst = hInstance;​



윈도우 핸들을 담당하는 hWnd;
메시지를 담당하는 MSG Message;
윈도우 클래스를 담당하는 WndClass;
이다.
g_hInst에 hInstance;를 해주는 이유는,
WinMain의 지역 변수는 hInstance를 외부에서도 쓰기 위해서
전역적인 g_hInst에 저장해 두는 것이다.


	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hbrBackground = (HBRUSH)GetStockObject( COLOR_WINDOW+1 );
	//(HBRUSH)GetStockObject( LTGRAY_BRUSH );			// 이미 지정되어 있는 매크로 색상
	//CreateHatchBrush( HS_DIAGCROSS, COLOR_WINDOW+1 );	// 바둑판 형식으로 출력이 된다.
	//CreateSolidBrush( COLOR_WINDOW+1 );				// 윈도우 기본색상
	WndClass.hCursor = LoadCursor( NULL, IDC_NO );
	//LoadCursor( NULL, IDC_ARROW );
	WndClass.hIcon = LoadIcon( NULL, IDI_ERROR );
	//LoadIcon( NULL, IDI_APPLICATION );
	WndClass.hInstance = hInstance;
	WndClass.lpfnWndProc = WndProc;
	WndClass.lpszClassName = lpszClass;
	WndClass.lpszMenuName = NULL;
	WndClass.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass( &WndClass );​


윈도우 클래스라는 것이 윈도우(창)에 대한 정보를 가지고 있게 되는데,
이 부분에서는 그것에 대해서 초기값을 셋팅해 둔다고 보면 된다.

windows.h 파일에 보면,
다음과 같은 구조제가 정의되어 있다.

typedef struct tagWNDCLASSW {
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCWSTR     lpszMenuName;
    LPCWSTR     lpszClassName;
}​


변수 명명법이 잘 되어 있어서,
의미를 몰라도 뭐하는 녀석들인지 대충 감이 오기도 한다.

최 상위에 있는 Extra는, 예약 영역이라고 한다. 내부적으로 사용한다고 하는데, 사용하지 않을 경우는 0으로 초기화 하면된다.
hrbBackground는 말그대로 윈도우 창의 백그라운드 색상이라고 볼 수 있겠다.
간단하게 주석을 달아 놓았지만, 색상에 변경이나 무늬의 변경을 줄 수 있는 듯 하다.
hCursor, hIcon은 커서와 아이콘의 설정인데, 기본 윈도우 커서와 아이콘을 사용할 때에는 첫번째 인자로 NULL을 사용,
cursor은 IDC_ARROW나, IDC_NO등 다양하게 존재,
Icon도 마찬가지로 IDI_APPLICATION, IDI_ERROR등이 존재.
hInstance는 윈도우클래스에 hInstance를 대입한다고 보면 되고,
lpfnWndProc는 윈도우(창) 실행중에 접근하게 될 함수 포인터를 가지게 된다.
lpszClassName 이는 윈도우 상단에 이름을 삽입, 전역으로 선언했던 lpszClass가 이것이다.
lpszMenuName 윈도우(창)이 사용하게 될 메뉴에 대해 설정한다, 메뉴를 사용하지 않을 경우 NULL
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 );​


이제 윈도우 생성을 하면서 그 핸들을 hWnd에 넣어 주는 것을 볼 수 있다.
인자에 대한 설명은,
클래스이름, 윈도우이름, 스타일, x, y, Width, Height, 부모윈도우, 메뉴, 인스턴스 핸들, 파라미터 값이 들어가게 되는데,
클래스 이름과 윈도우 이름은 보통 동일하게 한다고 하며,
스타일은 스크롤바는 있는지, 타이틀바는 있는지 크기 조절이 가능한지에 대해 정의를 내린다고 하며,
x,y의 위치에 Width, Height만큼의 윈도우를 생성하고,
부모 윈도우의 존재 여부,
메뉴의 설정, 윈도우 클래스에서 지정한 메뉴를 그대로 사용히 NULL, 아니라면 메뉴 핸들을 지정하면 된다.
WinMain에서의 인스턴스 핸들을 넘겨주고,
파라미터는 여러 개의 윈도우를 만들 때 각 윈도우에 파라미터를 전달한다고 하나, 잘 사용되지 않는다고 한다.

	ShowWindow( hWnd, nCmdShow );​


드디어! 윈도우를 보여준다. CreateWindow의 핸들인 hWnd를 1번째 인자로,
2번째 인자는 WinMain의 4번째 인자를 보내주는 것이라고 알고 있자.


이렇게 간신히 윈도우를 띄우게 되면 이제부터 메시지 루프로 돌아 가게 되는데,
그 순서는,
WndClass 정의
윈도우 클래스 등록
CreateWindow
ShowWindow
정도로 나눌 수 있겠다.
이 작업이 끝나면 이제부터 메시지 루프에 돌입한다.

	while( GetMessage( &Message, NULL, 0, 0 ) )
	{
		TranslateMessage( &Message );
		DispatchMessage( &Message );
	}
	return (int)Message.wParam;​



while문은 종료 메시지가 도착하기 전까지는 무한 반복을 하게 된다.
GetMessage에서는 종료 메시지가 오지 않으면 계속 true를 반환하기 때문이다. TranslateMessage와, DispatchMessage 의 함수가 존재하는데,
TranslateMessage는 키보드 입력 메시지가 나오게 되면 메시지큐에 삽입한다. 키 입력을 하지 않을 것이라면 없어도 된다고 함.
DispatchMessage같은 경우엔 메시지 큐에서 꺼낸 메시지를 윈도우 메시지 처리 함수(WndProc)로 보내는 역활을 한다.

이쯤에서 메시지가 어떤식으로 전달되는지 알아 보도록하자.
MSG 타입은 구조체로써,

typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
}​


를 가지고 있다.

 멤버  의미
 hWnd  메시지를 받을 윈도우 핸들
 message  어떤 종류의 메시지인가를 나타냄
 wParam  전달된 메시지에 대한 부가적인 정보, 어떤 의미로 가지는가는 메시지별로 다르다고 함(32비트의 값)
 lParam  전달된 메시지에 대한 부가적인 정보를 가짐, 어떤 의미를 가지는가는 메시지별로 다르다고 함(32비트 값)
 time  메시지가 발생한 시간.
 pt  메시지가 발생 했을 때의 마우스 위치.



마지막으로 프로그램이 종료 되면,
 return (int)Message.wParam;를 하면서 프로그램을 종료한다.


마지막으로 WndProc에 대해 살펴보자.

WndProc의 4개 인자는 MSG의 상위 4개의 타입과 동일하다,
구지 설명하지 않아도 이해할 수 있을 것이라 생각한다.


	switch( iMessage )
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONDOWN:
		MessageBeep(0);		// 마우스 버튼 클릭시 "띵띵" 소리가 나온다.
		return 0;
	}
	return( DefWindowProc( hWnd, iMessage, wParam, lParam));​


WndProc에 들어 오게 되면, 프로그래머가 원하는 메시지에 원하는 행동들을 할 수 있게 된다.
기본적으로 WM_DESTORY는 윈도우를 종료할때 발생함으로써,
메시지큐에 프로그램 종료 메시지를 끼워 넣게 되고,
예제로 들어가 있는 WM_LBUTTONDOWN 같은 경우엔,
마우스 버튼을 클릭시 저 안으로 들어가게 된다.(예제에서는 버튼 클릭시 띵띵 소리를 나오게 하였다.
그리고 메시지를 받고 WndProc에 왔을 경우, 프로그래머가 정의하지 않은 메시지(창 이동, 창 크기 변경, 최소화등)는 DefWindowProc에서 해주게 된다.-즉 매우 기본적인 기능들에 대해서 자동으로 해결해준다.-




정말 간단하게,,,,,,,,,,,,,,,,,,,,,,,,,,, 과연?
윈 API에 기본 창 띄우기에 대해 알아 보았다.
하아.. 참 간단하네..........
그래도 한번 집고 나니 마음은 후련하구만 !

'프로그래밍 > API' 카테고리의 다른 글

API-Key입력(1)  (0) 2010.04.21
API-MessageBox  (0) 2010.04.19
API-GraphOut  (0) 2010.04.19
API-DrawText  (0) 2010.04.19
API-TextOut  (0) 2010.04.19