저번 게시물에서 정상적인 Hello! World가 출력되는 것을 확인할 수 있었습니다.
하지만 개인적인 성향으로 어떠한 이유로 이런 저런 형식을 사용하는지를 모르면 답답해하고 나중에라도 꼭 이해하고
알고가야하기 때문에 작성한 코드를 하나하나 해석하면서 해당 부분을 조금 더 살펴 보겠습니다.
#include <Windows.h>
HINSTANCE _hInstance;
HWND hWnd;
#include <Windows.h>
: 윈도우 API를 사용하기 위해 Windows.h 헤더 파일을 인클루드 하는 부분입니다.
이전에 콘솔에서 기본 함수를 사용하기 위해 iostram이나 난수를 생성하기 위해 time.h를 선언하는것과
동일한 부분입니다.
HINSTANCE _hInstance
: 해당 윈도우에 인스턴트 핸들값을 정의하는 부분입니다.
많은 함수들이 이런 핸들값을 요구하기 떄문에 전역변수로 지정합니다.
HWND hWnd
: 윈도우 자체의 핸들값을 정의하는 부분입니다
원래 각 윈도우마다 핸들값을 따로 가져야 하는 부분으로 각 윈도우에 대한 메세지를 처리할 때 참고할 수
있 는 핸들 값 입니다. 메인 함수안에서 선언해도 기본적인 입출력은 되지만 해당 예제에서는 전역변수로
선언하였습니다.
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); |
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM)
: 메세지 처리 함수 입니다.
1. LRESULT의 함수 정의를 찾아보면 long 변수를 다른 이름으로 사용하도록 처리되어있습니다.
typedef LONG_PTR LRESULT; -> typedef _W64 long LONG_PTR, *PLONG_PTR; |
즉 해당 함수는 long 형태의 값을 반환해주는 함수라는 뜻 입니다.
마지막 함수의 리턴 부분에 DefWindowProc함수를 리턴해주는 해당 함수도 LRESULT형 함수입니다.
주로 포인터값을 반환할때에도 자주 사용합니다.(포인터도 4바이트 변수이기 때문입니다.)
해당 부분의 LRESULT를 long으로 바꾸어도 컴파일 에러가 발생하지 않습니다.
[LRESULT를 long으로 바꾸어서 컴파일해도 정상적으로 출력된다. LRESULT가 long이기 때문이다.]
2. CALLBACK 은 메인 함수의 APIENTRY와 마찬가지로 표준 호출규약인 _stdcall을 사용하고 있습니다.
[내용참고 : https://kldp.org/]
[caller & callee]
caller가 해당 스택 포인터 값을 해제해줄 경우 자기 자신이 넘긴 값이므로 확실하게 해제됩니다.
다만, callee의 경우 부르는쪽과 불리는 쪽에 스택값이 명확하지 않으면 잘못되는 경우가 발생합니다.
callee로 처리할 경우 몇 바이트 정도를 절약할 수 있다.
* 기본적으로 함수 호출 규약에는 _stdcall과 cdel이 있는데, 함수 호출 방식을 결정합니다.
기본적으로 _stdcall 과 cdel 함수 호출시 인자를 스택 메모리에 오른쪽에서 왼쪽순으로 저장하는
부분은 같지만 저장된 인자 메모리를 호출당한 쪽 이냐 아니면 호출한 쪽 함수에서 해제해주는지에
차이가 발생합니다. C/C++에서는 cdel 이 기본 값이기 때문에 API에서 함수 호출을 하는 경우
명시적으로 _stdcall을 사용해야 합니다.
만약 해당하는 부분을 명시적으로 써주지 않는다면 cdel 방식으로 간주되어 함수 호출시에 오류가
발생합니다. WndProc WinMain 함수에서는 _stdcall 형식을 사용해야 함으로 각기 _stdcall형식을
사용합니다. 이때 CALLBACK과 APIENTRY의 차이점은 크게 없습니다.
typedef에서 재정의 해줄 뿐입니다.(해당 부분을 같은 stdcall 형식을 써도 오류가 발생하지 않습니다.)
[CALLBACK 이나 APIENTRY 이나 어차피 _stdcall 이라는 형식의 재정의 일 뿐이다.]
추가적으로 이해를 돕자면 printf와 같은 함수에 인자값으로 인자의 갯수를 넘기지 않는 구조에서는 호출한 함수(caller)가 제거해야만 합니다. printf를 호출할때 그 인자의 갯수를 알게되기 때문입니다.
하지만 윈도우 API에서는 인자의 갯수가 정해져 있기 때문에 호출한 쪽이 아닌 호출당한 쪽(callee)가 해제
하는것 입니다.
WNDCLASS WndClass; MSG mag; _hInstance = hInstance; |
WNDCLASS
: 윈도우의 클래스를 선언한 부분입니다.
해당 부분은 기존 WIndows API - #3. 윈도우창에 Hello World 출력하기(2)에 설명되어 있습니다.
MSG mag;
: 지금보니까 오타가 있군요..mag라니..
MSG는 메세지이며, 구조체 입니다. 즉 MSG에 mag라는 구조체를 선언한겁니다.
typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG, *LPMSG; |
매개 변수 | 의미 |
hwnd | 절차에 따른 메세지를 받은 윈도우 핸들값입니다. 메세지가 쓰레드 메세지일 경우 해당 멤버는 NULL 입니다. |
message | 메세지 식별자 입니다. 프로그램은 낮은 언어만을 사용할 수 있으며, 높은 언어의 경우 시스템의 의해 예약되어있습니다.(?) |
wParam | 메세지에 대한 추가정보입니다. 정확한 의미는 메세지 멤버의 값에 따라 다릅니다. |
lParam | 메세지에 대한 추가정보입니다. 정확한 의미는 메세지 멤버의 값에 따라 다릅니다. |
time | 어느 시점에 메세지가 기록됬는지를 의미합니다. |
pt | 화면 좌표의 커서위치로 언제 메세지가 표시되었는지를 의미합니다. |
사실상 위에 구조체 멤버 함수를 일일이 선언하지는 않습니다.
_hInstance = hInstance
: 메인함수의 hInstance에 전역변수로 선언한_hInstance를 대입해주는 부분입니다.
RegisterClass(&WndClass); CreateWindowA ShowWindow |
RegisterClass(&WndClass);
CreateWindowA
ShowWindow
: 해당 부분은 기존 WIndows API - #3. 윈도우창에 Hello World 출력하기(2)에 설명되어 있습니다.
while ( GetMessage( &mag , 0 , NULL , NULL ) ) { DispatchMessage(&mag); } return message.wParam; |
메세지 루프
: while 반목문에 GetMessage가 조건으로 지정된 루프문입니다.
BOOL WINAPI GetMessage( _Out_ LPMSG lpMsg, _In_opt_ HWND hWnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax ); |
매게 변수 | 의미 |
lpMsg | 쓰레드 메세지 큐에서 전달된 메세지 정보인 MSG 구조체에 대한 포인터입니다. |
hWnd | 검색된 윈도우 메세지의 핸들값입니다. hWnd가 NULL 일 경우 윈도우 메세지와 쓰레드 메세지 모두를 처리합니다. hWnd가 -1일 경우 현재 쓰레드의 메세지 큐만 검색합니다. |
wMsgFilterMin | 최저 메세지 값의 정수 값을 검색합니다. 마우스와 키보드의 첫번째 메세지를 사용합니다. 여기서 WM_INPUT 메세지를 지정하기 위해서 wMsgFilterMax 값에 WM_INPUT을 사용합니다. 만약 wMsgFilterMin와 wMsgFilterMax가 0인경우 가능한 모든 메세지를 반환 합니다. |
wMsgFilterMax | 최대 메세지 값의 정수 값을 검색합니다 마우스와 키보드의 마지막 메세지를 사용합니다. 여기서 WM_INPUT 메세지를 지정하기 위해서 wMsgFilterMax 값에 WM_INPUT을 사용합니다. 만약 wMsgFilterMin와 wMsgFilterMax가 0인경우 가능한 모든 메세지를 반환 합니다. |
* 참고 *
윈도우 메세지 처리는 키보드를 눌렀을때와 떨어졌을떄의 연속적인 처리가 발생할 경우 메세지가 발생한다.
즉 위 2개의 매게 변수는 각 입력값을 처리하기 위한 부분이다.
DispatchMessage (키보드 메세지를 MSG 구조체에 전달)
TranslateMessage (윈도우 발생 메세지를 MSG 구조체에 전달)
: 메세지를 담고 있는 컨테이너 구조체를 가르키는 포인터 입니다.
return message.wParam;
: 메세지의 wParam(메세지의 추가정보)값을 리턴해줍니다.(해당 부분이 없어도 실행됩니다.)
나머지 부분은 WIndows API - #4. 윈도우창 출력 함수 자세히보기(2)로 이어집니다.
'프로그래밍 관련 > Win API' 카테고리의 다른 글
WIndows API - #5. 윈도우 문자열 (0) | 2015.08.24 |
---|---|
WIndows API - #4. 윈도우창 출력 함수 자세히보기(2) (0) | 2015.08.23 |
WIndows API - #3. 윈도우창에 Hello World 출력하기(2) (0) | 2015.08.21 |
WIndows API - #3. 윈도우창에 Hello World 출력하기(1) (4) | 2015.08.21 |
WIndows API - #2. Hello World MessageBox 출력하기(2) (0) | 2015.08.21 |