WinAPI-기본코드 함수분석 및 정리
- 프로그래밍/WindowsAPI
- 2011. 5. 2. 12:42
윈도우즈 API의 기본코드와 구조에 대해서 알아보자
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("SKFirst");
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;
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;
}
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
*WinMain
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
위의 WinMain은 프로그램의 시작점이다. 도스에서의 main과 같다.
프로그램의 시작점이WinMain이므로 main이라는 함수를 만들어서 사용할 수도 있다.
하지만 일반적으로 main이라는 함수는 사용하지 않는 것이 좋다. WinMain은 원형이 고정되어있다.
APIENTRY 지정자는 윈도우즈 표준 호출 규약인 __stdcall을 사용한다는 뜻
콜백함수이다.
hInstance 프로그램의 인스턴스 핸들
hPrevInstance 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들, 없을 경우 NULL, 이제 거의 사용하지 않는다.
lpszCmdParam 명령 행으로 입력된 프로그램 인수, 도스의 argv인수에 해당
nCmdShow 프로그램이 처음 실행 되었을 때 윈도우 창의 형태, 최소, 보통 등…
위 설명 중 인스턴스(Instance)라는 용어는 실행중인 프로그램 하나를 칭하는 용어이다.
예를 들어 Windows Internet Explorer을 두 개 실행 할 때 운영체제는 이 프로그램의 인스터스를 이용하여 각각의 독립적인 프로그램으로 인식한다. 즉, 윈도우에서 사용하는 프로그램은 고유한 인스턴스 번호를 가지고 있다.
WinMain 함수에서 하는 가장 중요한 일은 메인 윈도우를 만드는 일이다. 윈도우가 존재해야 입출력이 가능하기 때문이다. 윈도우를 만들기 위해서는 먼저 윈도우 클래스를 등록한 후CreateWindow 함수를 호출해야 한다. 윈도우 클래스(WNDCLASS)는 윈도우의 여러 가지 특성을 정의하는 구조체이다. 구조체의 멤버는 10개가 존재하면 역할을 아래와 같다.
*윈도우 클래스(WNDCLASS)와 등록
윈도우 클래스는 만들어질 윈도우의 여러 가지 특성을 정의하는 구조체이며 모든 윈도우는 윈도우 클래스의 정보를 기반으로 만들어진다.
typedef struct tagWNDCLASS
{
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASS;
style
윈도우의 스타일을 정의한다. 윈도우의 형태를 지정하는 곳이다. 만은 멤버를 가지며, OR( | )연산자로 연결하여 사용한다.
lpfnWndProc
위도우의 메시지 처리 함수를 지정한다. 메시지가 발생할 때마다 이 멤버가 지정하는 함수가 호출되며 이 함수가 모든 메시지를 처리한다.
cbClsExtra, cbWndExtra
일종의 예약 영역이다. 윈도우가 내부적으로 사용하며 아주 특수한 목적에 사용되는 여분의 공간이다.
hInstance
이 윈도우 클래스를 등록하는 프로그램의 번호이다. WinMain의 인수로 전달된 hInstance 값을 그대로 대입하면 된다.
hIcon, hCursor
생성된 윈도우가 사용할 마우스 커서와 아이콘의 모양을 지정한다.
hbrBackground
윈도우 배경색을 색칠할 붓을 지정한다. 가장 일반적으로 흰색 붓(WHITE_BRUSH)를 사용한다.
lpszMenuName
생성할 윈도우가 사용할 메뉴를 지정한다. 예를 들어 Explorer에서 도구나 페이지 같은 것이다.
lpszClassName
윈도우 클래스 이름을 정의한다. 단순히 이름이므로 한글이나 공백을 포함할 수 있다. 일반적으로 실행 파일의 이름과 일치시켜 사용한다.
*윈도우 특성 등록
ATOM RegisterClass(CONST WNDCLASS* lpWndClass)
위 함수는 윈도우 클래스(WNDCLASS) 구조체의 번지를 전달받아 앞으로 전달한 특성을 가진 윈도우를 사용하겠다고 운영체제에게 알리는 역할을 한다.
*윈도우 생성(CreateWindow)
RegisterClass를 이용하여 윈도우 클래스를 등록한 후에는 윈도우 클래스 특성을 기본으로 해서 윈도우를 생성해야 한다. 윈도우를 생성할 때에는 CreateWindow함수를 사용한다.
HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)
lpszClassName
생성하고자 하는 윈도우의 클래스를 지정하는 문자열이다. 앞서 설명했듯이 CreateWindow함수는 윈도우를 생성할 때 윈도우 클래스(WNDCLASS)에 정의된 속성대로 윈도우를 생성한다.
lpszWindowName
윈도우의 타이틀 바에 나타날 문자열이다. 지금 이 포스트를 보고 있는 Explorer의 타이틀 바는 “엉뚱하고 기발하게……”일 것이다. 이러한 타이틀 바에 글을 정의할 수 있다.
dwStyle
만들고자 하는 윈도우의 형태를 지정하는 인수이다. 예를 들어, 생성하는 윈도우의 크기를 조절 가능하도록 할 것인가, 타이틀 바를 가질 것인가, 또는 스크롤 바의 유무 등을 설정한다. 앞서 윈도우 클래스의 Style을 설정하듯이 OR( | )연산자를 여러 개의 속성을 지정할 수 있다.
X, Y, nWidth, nHeight
생성할 윈도우의 크기와 위치를 지정한다. 앞의 두 인수는 생성될 윈도우의 위치이고 뒤에 두 인수는 윈도우의 크기이다. 단위는 픽셀이다.
hWndParent
부모 윈도우가 있을 경우 보모 윈도우의 핸들을 지정한다. 예를 들어 팝업창과 같은 현재 윈도우에서 새로운 윈도윈도 생성할 때 이전 윈도우가 부모 윈도우가 된다. 이때 부모 윈도우의 핸들을 지정한다. 부모가 없는 경우에는 NULL을 지정하면 된다.
hmenu
윈도우에서 사용할 메뉴의 핸들을 지정한다. 앞서 윈도우 클래스(WNDCLASS)에도 메뉴를 지정하는 멤버가 존재했다. 윈도우 클래스에서 지정한 메뉴는 그 윈도우 클래스를 기반으로 하는 모든 윈도우에서 공통적으로 사용되는 반면 지금 이 인수로 지정되는 메뉴는 현재 생성되는 윈도우에서만 사용된다.
hinst
윈도우를 만드는 주체, 즉 프로그램의 핸들을 지정한다. WinMain의 인수로 전달된hInstance를 입력하면 된다.
lpvParam
CREATESTRUCT라는 구조체의 번지이며 여러 개의 윈도우를 만들 때 사용된다. 잘 사용하지 않는다.
CreateWindow 함수는 윈도우에 관한 모든 정보를 메모리에 만든 후 윈도우를 대표하는 번호인 윈도우 핸들을 리턴 한다. 넘겨지는 윈도우 핸들은 hWnd라는 변수에 저장되었다가 이 윈도우를 참조하는 모든 함수의 인수로 사용된다. CreateWindow 함수로 만든 윈도우는 현재 메모리상에만 존재한다. 메모리 상에 존재하는 윈도우를 화면에 보이게 하기 위해서는 아래와 같은 함수가 필요하다.
*윈도우 화면에 출력하기
BOOL ShowWindow(hWnd, nCmdShow);
이 함수는 앞서 설명한 것과 같이 CreateWindow를 이용하여 윈도우를 메모리상에 저장한 것을 이용하여 화면에 보여주는 역할을 한다.
*메시지 루프
윈도우는 메시지 구동 시스템(Message Driven System)이다. 무슨 말이냐 하면 도스와 같이 미리 정해진 명령들을 순차적으로 실행 하는 방법이 아닌 프로그램의 실행 순서가 명확하게 정해져 있지 않으며 Event에 따라 실행 순서가 달라진다. 여기서 Event란 사용자의 내부적인 동작을 말하는데 키보드를 누르거나 마우스의 이동 등이 이에 해당된다. Event가 곧 메시지로 볼 수 있는데 정확히 말하면 메시지는 Event에 의해 발생된 일체의 변화에 대한 정보이다. 메시지를 받은 프로그램은 메시지가 어떤 정보를 담고 있는가를 분석하여 무슨 동작을 할 것인가를 결정한다. 아래의 코드는 윈도우 프로그램에서 메시지를 처리하는 부분이다. 일반적으로WinMain 함수의 끝에 아래의 형식으로 존재한다.
while(GetMessage(&Message, NULL, 0, 0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
이 함수는 메시지 큐에서 메시지를 읽어 들인다. 여기서 메시지 큐는 Event가 임시로 저장되는 공간이라고 보면 된다. 읽어 들인 메시지는 첫 번째 인수(LPMSG)가 지정하는 MSG 구조체에 저장된다. 만약 읽어 들인 메시지가 프로그램을 종료하라는 WM_QUIT일 경우 외엔 항상TURE를 리턴한다. 나머지 3개의 인수는 잘 사용하지 않는다.
BOOL TranslateMessage(CONST MSG* lpMsg)
이 함수는 키보드 입력 메시지를 가공하여 프로그램에서 쉽게 사용할 수 있도록 한다. 쉽게 말해 사용자가 키보드를 눌러 키보드의 눌림(WM_KEYDOWN) 메시지가 발생할 때 문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 한다. 즉, A를 누르면 A문자가 입력되었다는 메시지를 만든다.
LONG DispatchMessage(CONST MSG* lpmsg)
이 함수는 메시지 큐에서 꺼낸 메시지를 윈도우의 메시지 처리 함수(WndProc)로 전달하는 역할을 한다.
위 함수에서 공통적으로 MSG라는 구조체를 사용하는데 이 구조체는 메시지에 대한 정보를 정의한다. MSG의 구조는 아래와 같다.
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
hwnd 메시지를 받을 윈도우 핸들
message 어떤 종류의 메시지인가를 나타낸다.
wParam 전달된 메시지에 대한 부가적인 정보
lParam 전달된 메시지에 대한 부가적인 정보
time 메시지가 발생한 시간
pt 메시지가 발생했을 때 마우스의 위치
[출처] Windows 창 만들기 - 6. 메시지 루프|작성자 싸마
*윈도우 프로시저
윈도우 프로시저(Window Procedure)란 메시지를 처리하는 함수로 메시지가 발생할 때 발생한 메시지에 대한 반응을 처리하는 일을 한다. 앞서 배우 포스트의 메시지 루프에서는 단순히 메시지를 메시지 처리함수 즉, 윈도우 프로시저로 보내는 역할을 할 뿐 메시지에 대한 처리는 하지 윈도우 프로시저가 한다. 이렇게 운영체제에 의해 호출되는 응용 프로그램 내의 함수를 콜백(CallBack) 함수라고 한다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
위 함수는 윈도우 프로시저 함수로 총 4개의 인수를 가진다. 이때 이 4개의 인수의 기능은 이전 포스트에서 배운 MSG구조체의 앞의 4개의 인수의 기능과 동일하다. 위 함수에서“WndProc”은 사용자가 임의로 지정할 수 있는 함수의 이름이다. ( 하지만 그냥 기본적으로 사용하는것이 일반적이다. )
윈도우 프로시저의 구조는 대체로 아래와 같다.
switch( iMessage)
{
case Msg1:
처리1;
return 0;
case Msg2:
처리2;
return 0;
case Msg3:
처리3;
return 0;
default:
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
윈도우 프로시저는 위와 같은 분기 구조로 메시지에 해당하는 분기를 찾아가 처리를 하는 방식이다. 이때 무사히 모든 처리를 완료 했다면 0을 리턴 하도록 약속되어 있다.
또, 마지막의DefeWindowProc 함수는 윈도우 프로시저에서 처리하지 않는 나머지 메시지에 관한 처리를 대신 해준다. 한 예로, 윈도우의 이동이나 크기의 변경 등을 처리해 준다.
지금까지 포스트에서 다룬 내용은 Windows API를 이용하여 간단한 윈도우 생성이었다. 앞으로 다룰 포스트는 Windows API함수를 이용하여 윈도우를 조작하거나, 그림을 그리는 것 등에 관해서 다룰 것이다.
참고 : www.winapi.co.kr
추가한 내용
winapi 프로젝트를 만들땐 콘솔이 아닌 윈도우환경으로 만들어야한다.
=> 프로젝트 설정이 다를 경우 main을 못찾는다고 에러난다.
LIBCMT.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
유니코드와 멀티바이트등을 신경써주어야 한다. ( 유니코드 디폴트 설정 )
또한 많은 여러 라이브러리가 존재하므로 프로젝트 설정도 신경 써주어야한다.
[출처] Windows 창 만들기 - 7. 윈도우 프로시저|작성자 싸마
윈도우 프로시저(Window Procedure)란 메시지를 처리하는 함수로 메시지가 발생할 때 발생한 메시지에 대한 반응을 처리하는 일을 한다. 앞서 배우 포스트의 메시지 루프에서는 단순히 메시지를 메시지 처리함수 즉, 윈도우 프로시저로 보내는 역할을 할 뿐 메시지에 대한 처리는 하지 윈도우 프로시저가 한다. 이렇게 운영체제에 의해 호출되는 응용 프로그램 내의 함수를 콜백(CallBack) 함수라고 한다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
위 함수는 윈도우 프로시저 함수로 총 4개의 인수를 가진다. 이때 이 4개의 인수의 기능은 이전 포스트에서 배운 MSG구조체의 앞의 4개의 인수의 기능과 동일하다. 위 함수에서“WndProc”은 사용자가 임의로 지정할 수 있는 함수의 이름이다.
윈도우 프로시저의 구조는 대체로 아래와 같다.
switch( iMessage)
{
case Msg1:
처리1;
return 0;
case Msg2:
처리2;
return 0;
case Msg3:
처리3;
return 0;
default:
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
윈도우 프로시저는 위와 같은 분기 구조로 메시지에 해당하는 분기를 찾아가 처리를 하는 방식이다. 이때 무사히 모든 처리를 완료 했다면 0을 리턴 하도록 약속되어 있다. 또, 마지막의DefeWindowProc 함수는 윈도우 프로시저에서 처리하지 않는 나머지 메시지에 관한 처리를 대신 해준다. 한 예로, 윈도우의 이동이나 크기의 변경 등을 처리해 준다.
지금까지 포스트에서 다룬 내용은 Windows API를 이용하여 간단한 윈도우 생성이었다. 앞으로 다룰 포스트는 Windows API함수를 이용하여 윈도우를 조작하거나, 그림을 그리는 것 등에 관해서 다룰 것이다.
[출처] Windows 창 만들기 - 7. 윈도우 프로시저|작성자 싸마
이 글을 공유하기