####################################################
2D테트리스만들기
1. 실전 윈도우 기본구조
1) InitApp
2) InitInstance
3) WinMain
4) WndProc
5) OnCreate
6) OnPaint;
7) OnKey
8) OnDestroy;
####################################################
앞으로 강의 할 윈도우의 기본 구조를 분석해보자. 지금까진 워밍업이었다. 이제부터
실전에 들어가보자. 구조가 약간은 달라졌을 것이다. 그러나 중요 기능별로 함수를
만든것에 불과하다. 당부하고 싶은 말은 절대로 첨 보는 함수가 있더라도 어려워 하지
말고 모든 기능을 다 외운다는 생각은 버려야 할 것이다. 모르는 것이 있다면 그냥 이런
기능이 있구나 하구 넘어 가길 바랍니다. 나무에 너무 집착하면 넓은 숲을 못보기 때문입니다.
한번 차근히 검토해 보자.
//------------실전 예 시작------------------
#include <windows.h>
#include <windowsx.h>
// HANDLE_MSG, GlobalFreePtr을 만들려면 해더에 windowsx.h를 선언해 주어야 한다.
/************** 전역변수 선언 ***************/
HWND hMainWnd ;
HINSTANCE hMainInst;
char szAppName[] = "윈도우이름" ;
/*****************************************/
/************** 함수선언 ***************/
BOOL InitApp(HINSTANCE hInstance);
// 윈도우의 기본적인 스타일과 아이콘, 커서모양등을 설정
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);
// 윈도우켑션이름과 위치나 크기등을 정한다.
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
// 윈도우에 실제 명령을 수행하게됨
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
// 윈도우를 생성한다.
void OnPaint(HWND hwnd);
// 화면을 그린다.
void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags);
// 키명령을 수행한다.
void OnDestroy(HWND hwnd);
// 윈도우를 닫는다.
/*****************************************/
BOOL InitApp(HINSTANCE hInstance)
{
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ;//구조체의크기
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //윈도우의 수평,수직 크기가
//변경될 때 다시 그려준다.
wndclass.lpfnWndProc = WndProc ; // 윈프록 함수 이름선언
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ; //현재 winMain()에서 사용되는 Handle값은
// 무엇이냐 OS가 Handle값을 전달 해 준다.
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
//IDI_APPLICATION는 ICON을 default로 쓰겠다는 의미.
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
//IDC_ARROW은 화살표 모양 커서를 나타냄.
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
//WHITE_BRUSH은 바탕색을 흰색으로 쓰겠다
wndclass.lpszMenuName = ""; // NULL;로해도된다.
// 만약 메뉴 이름이 ""로 안되어있고 그냥 이름으로 되어있다면
// MAKEINTRESOURCE(IDR_MENU1); 함수를 사용한다.
wndclass.lpszClassName = szAppName ;
//클래스 이름. SzAppName는 클래스 이름을 담는 문자열 변수이름이다.
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
return(RegisterClassEx (&wndclass)) ;
}
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow)
{
hMainWnd = CreateWindow (szAppName, //윈도우 클래스 이름
"윈도우켑션", //윈도우 켑션
WS_OVERLAPPEDWINDOW,//윈도우 유형
CW_USEDEFAULT, //초기 x위치
CW_USEDEFAULT, //초기 y위치
CW_USEDEFAULT, //초기 x크기
CW_USEDEFAULT, //초기 y크기
NULL, //부모 윈도우 핸들
NULL, //윈도우 메뉴 핸들
hInstance, //프로그램 인스턴스 핸들
NULL) ; //생성 매개 변수
if (!hMainWnd) return FALSE;
hMainInst = hInstance;
ShowWindow (hMainWnd, iCmdShow) ;
//크기나 모양.. 을 눈에 보이게 하는 함수.
// iCmdShow(속성표현-맨 하단의 아이콘 표현 유무)
UpdateWindow (hMainWnd) ;
//크기나 모양.. 을 눈에 보이게 하는 함수.
return TRUE;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG msg ;
if ((!hPrevInstance) & (!InitApp(hInstance)))
return FALSE;
if (!InitInstance(hInstance, iCmdShow)) return FALSE;
while (GetMessage (&msg, NULL, 0, 0))//들어 온 메시지를 받아냄.
//MFC의 LButton과 Rbutton과 같음.
{
TranslateMessage (&msg) ; //알 수 있는 메시지 형태로 변환.
// (키보드에서 메세지를 받아온다.)
DispatchMessage (&msg) ; //Msg값을 4부분으로 쪼개서 winProc에 인
//로 보내고 나서 winProc함수에 WN_DESTRO
//가 올 때 까지 무한루프를 돌린다.
// (메세지를 윈도우 프로시저에 전달)
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam)
{
switch (iMsg)
{
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
// HANDLE_MSG는 메세지를 받아 사용자 함수에 전달하는 역활을 한다.
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_KEYDOWN, OnKey);
HANDLE_MSG(hwnd, WM_KEYUP, OnKey);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
return 1; // True;를 사용해도 된다.
}
void OnPaint(HWND hwnd)
{
HDC hdc; // 디바이스컨텍스트라는 핸들을 생성한다.(페인트에필수)
PAINTSTRUCT ps; //(페인트에필수)
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
}
void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags)
{
switch (vk)
{
case VK_ESCAPE :
PostQuitMessage(0);
// 윈도우핸들에 WM_CLOSE메시지를 보낸다.
//SendMessage (hMainWnd, WM_DESTROY, 0, 0L);
break;
}
}
void OnDestroy(HWND hwnd)
{
PostQuitMessage(0);
}
//------------실전 예 끝------------------
1) BOOL InitApp(HINSTANCE hInstance);
첨보는 함수일 것이다. 내가 만든 함수니깐...ㅎㅎ
이 함수는 윈도우 클래스 등록하는 부분을 따로 분리한 것이다. 모든 함수는 최대한 가볍게 가져가야
한다는 기본 원리를 따른 것입니다. 이해를 돕기 위해 이렇게 한다구 하는군여.
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
여기서 (HBRUSH)는 클레스 형이 hbr이기때문에 형을 변환하기 위해 써준것이다. GetStockObject는
pens, brushes, fonts, or palettes의 핸들을 받아오는 기능을 한다. 많이 사용되니 알아두기 바랍
니다.
wndclass.lpszMenuName = ""; // NULL;로해도된다.
// 만약 메뉴 이름이 ""로 안되어있고 그냥 이름으로 되어있다면
// MAKEINTRESOURCE(IDR_MENU1); 함수를 사용한다.
이것은 윈도우에 들어가 메뉴 이름을 넣는 것인데. 아직은 사용하지 않으니 자세한 내용은 다음으로
미루도록 하겠다.
return(RegisterClassEx (&wndclass)) ;
생성한 클레스를 레지스터에 등록한후 리턴값을 돌려준다.
2) BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);
윈도우를 생성하고 보여주는 함수이다.
hMainWnd = CreateWindow (...);
hMainWnd는 윈도우 핸들을 전역 변수로 만들어 필요한 함수에서 가져다 쓰게 하려구 선언했다.
if (!hMainWnd) return FALSE;
윈도 생성이 실패하면 false를 리턴한다. 윈도우 생성이 안되는 것이다.
hMainInst = hInstance;
인스턴스 핸들을 전역 변수로 만든다.
3) int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
if ((!hPrevInstance) & (!InitApp(hInstance)))
return FALSE;
hPrevInstance 몰라도 된다. InitApp함수 리턴값을 알아본다.
if (!InitInstance(hInstance, iCmdShow)) return FALSE;
InitInstance함수 리턴값을 알아본다.
4)LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HANDLE_MSG함수로 모든 윈도우 메세지를 특정 함수에 전달한다. HANDLE_MSG를 사용하지
않구 직접 메세지를 처리해도 상관없으나 WndProc함수 보기 좋게 정리 하기 위해 사용한다.
참고] 함수 원형을 보면
BOOL OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void OnChar(HWND hWnd, TCHAR ch, int cRepeat);
void OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
void OnPaint(HWND hWnd);
void OnSetFocus(HWND hWnd, HWND hwndOldFocus);
void OnKillFocus(HWND hWnd, HWND hwndNewFocus);
void OnDestroy(HWND hWnd);
LRESULT OnImeComposition(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT OnImeChar(HWND hWnd, WPARAM wParam, LPARAM lParam);
void OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
void OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags);
void OnLButtonUp(HWND hWnd, int x, int y, UINT keyFlags);
void OnTimer(HWND hWnd, UINT id);
void OnSize(HWND hWnd, UINT state, int cx, int cy);
void OnHScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);
void OnVScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);
void OnContextMenu(HWND hWnd, HWND hwndContext, UINT xPos, UINT yPos);
void OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify);
BOOL OnSetCursor(HWND hWnd, HWND hwndCursor, UINT codeHitTest, UINT msg);
UINT OnGetDlgCode(HWND hWnd, LPMSG lpmsg);
5) BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
윈도우를 생성한다.
6) void OnPaint(HWND hwnd);
화면을 그린다.
7) void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags);
vk 키값이 넘어온다.
8) void OnDestroy(HWND hwnd);
PostQuitMessage 응용 프로그램에 종료 WM_QUIT에 메세지를 전달한다.
2D테트리스만들기
1. 실전 윈도우 기본구조
1) InitApp
2) InitInstance
3) WinMain
4) WndProc
5) OnCreate
6) OnPaint;
7) OnKey
8) OnDestroy;
####################################################
앞으로 강의 할 윈도우의 기본 구조를 분석해보자. 지금까진 워밍업이었다. 이제부터
실전에 들어가보자. 구조가 약간은 달라졌을 것이다. 그러나 중요 기능별로 함수를
만든것에 불과하다. 당부하고 싶은 말은 절대로 첨 보는 함수가 있더라도 어려워 하지
말고 모든 기능을 다 외운다는 생각은 버려야 할 것이다. 모르는 것이 있다면 그냥 이런
기능이 있구나 하구 넘어 가길 바랍니다. 나무에 너무 집착하면 넓은 숲을 못보기 때문입니다.
한번 차근히 검토해 보자.
//------------실전 예 시작------------------
#include <windows.h>
#include <windowsx.h>
// HANDLE_MSG, GlobalFreePtr을 만들려면 해더에 windowsx.h를 선언해 주어야 한다.
/************** 전역변수 선언 ***************/
HWND hMainWnd ;
HINSTANCE hMainInst;
char szAppName[] = "윈도우이름" ;
/*****************************************/
/************** 함수선언 ***************/
BOOL InitApp(HINSTANCE hInstance);
// 윈도우의 기본적인 스타일과 아이콘, 커서모양등을 설정
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);
// 윈도우켑션이름과 위치나 크기등을 정한다.
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
// 윈도우에 실제 명령을 수행하게됨
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
// 윈도우를 생성한다.
void OnPaint(HWND hwnd);
// 화면을 그린다.
void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags);
// 키명령을 수행한다.
void OnDestroy(HWND hwnd);
// 윈도우를 닫는다.
/*****************************************/
BOOL InitApp(HINSTANCE hInstance)
{
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ;//구조체의크기
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //윈도우의 수평,수직 크기가
//변경될 때 다시 그려준다.
wndclass.lpfnWndProc = WndProc ; // 윈프록 함수 이름선언
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ; //현재 winMain()에서 사용되는 Handle값은
// 무엇이냐 OS가 Handle값을 전달 해 준다.
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
//IDI_APPLICATION는 ICON을 default로 쓰겠다는 의미.
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
//IDC_ARROW은 화살표 모양 커서를 나타냄.
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
//WHITE_BRUSH은 바탕색을 흰색으로 쓰겠다
wndclass.lpszMenuName = ""; // NULL;로해도된다.
// 만약 메뉴 이름이 ""로 안되어있고 그냥 이름으로 되어있다면
// MAKEINTRESOURCE(IDR_MENU1); 함수를 사용한다.
wndclass.lpszClassName = szAppName ;
//클래스 이름. SzAppName는 클래스 이름을 담는 문자열 변수이름이다.
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
return(RegisterClassEx (&wndclass)) ;
}
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow)
{
hMainWnd = CreateWindow (szAppName, //윈도우 클래스 이름
"윈도우켑션", //윈도우 켑션
WS_OVERLAPPEDWINDOW,//윈도우 유형
CW_USEDEFAULT, //초기 x위치
CW_USEDEFAULT, //초기 y위치
CW_USEDEFAULT, //초기 x크기
CW_USEDEFAULT, //초기 y크기
NULL, //부모 윈도우 핸들
NULL, //윈도우 메뉴 핸들
hInstance, //프로그램 인스턴스 핸들
NULL) ; //생성 매개 변수
if (!hMainWnd) return FALSE;
hMainInst = hInstance;
ShowWindow (hMainWnd, iCmdShow) ;
//크기나 모양.. 을 눈에 보이게 하는 함수.
// iCmdShow(속성표현-맨 하단의 아이콘 표현 유무)
UpdateWindow (hMainWnd) ;
//크기나 모양.. 을 눈에 보이게 하는 함수.
return TRUE;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG msg ;
if ((!hPrevInstance) & (!InitApp(hInstance)))
return FALSE;
if (!InitInstance(hInstance, iCmdShow)) return FALSE;
while (GetMessage (&msg, NULL, 0, 0))//들어 온 메시지를 받아냄.
//MFC의 LButton과 Rbutton과 같음.
{
TranslateMessage (&msg) ; //알 수 있는 메시지 형태로 변환.
// (키보드에서 메세지를 받아온다.)
DispatchMessage (&msg) ; //Msg값을 4부분으로 쪼개서 winProc에 인
//로 보내고 나서 winProc함수에 WN_DESTRO
//가 올 때 까지 무한루프를 돌린다.
// (메세지를 윈도우 프로시저에 전달)
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam)
{
switch (iMsg)
{
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
// HANDLE_MSG는 메세지를 받아 사용자 함수에 전달하는 역활을 한다.
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_KEYDOWN, OnKey);
HANDLE_MSG(hwnd, WM_KEYUP, OnKey);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
return 1; // True;를 사용해도 된다.
}
void OnPaint(HWND hwnd)
{
HDC hdc; // 디바이스컨텍스트라는 핸들을 생성한다.(페인트에필수)
PAINTSTRUCT ps; //(페인트에필수)
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
}
void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags)
{
switch (vk)
{
case VK_ESCAPE :
PostQuitMessage(0);
// 윈도우핸들에 WM_CLOSE메시지를 보낸다.
//SendMessage (hMainWnd, WM_DESTROY, 0, 0L);
break;
}
}
void OnDestroy(HWND hwnd)
{
PostQuitMessage(0);
}
//------------실전 예 끝------------------
1) BOOL InitApp(HINSTANCE hInstance);
첨보는 함수일 것이다. 내가 만든 함수니깐...ㅎㅎ
이 함수는 윈도우 클래스 등록하는 부분을 따로 분리한 것이다. 모든 함수는 최대한 가볍게 가져가야
한다는 기본 원리를 따른 것입니다. 이해를 돕기 위해 이렇게 한다구 하는군여.
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
여기서 (HBRUSH)는 클레스 형이 hbr이기때문에 형을 변환하기 위해 써준것이다. GetStockObject는
pens, brushes, fonts, or palettes의 핸들을 받아오는 기능을 한다. 많이 사용되니 알아두기 바랍
니다.
wndclass.lpszMenuName = ""; // NULL;로해도된다.
// 만약 메뉴 이름이 ""로 안되어있고 그냥 이름으로 되어있다면
// MAKEINTRESOURCE(IDR_MENU1); 함수를 사용한다.
이것은 윈도우에 들어가 메뉴 이름을 넣는 것인데. 아직은 사용하지 않으니 자세한 내용은 다음으로
미루도록 하겠다.
return(RegisterClassEx (&wndclass)) ;
생성한 클레스를 레지스터에 등록한후 리턴값을 돌려준다.
2) BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);
윈도우를 생성하고 보여주는 함수이다.
hMainWnd = CreateWindow (...);
hMainWnd는 윈도우 핸들을 전역 변수로 만들어 필요한 함수에서 가져다 쓰게 하려구 선언했다.
if (!hMainWnd) return FALSE;
윈도 생성이 실패하면 false를 리턴한다. 윈도우 생성이 안되는 것이다.
hMainInst = hInstance;
인스턴스 핸들을 전역 변수로 만든다.
3) int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
if ((!hPrevInstance) & (!InitApp(hInstance)))
return FALSE;
hPrevInstance 몰라도 된다. InitApp함수 리턴값을 알아본다.
if (!InitInstance(hInstance, iCmdShow)) return FALSE;
InitInstance함수 리턴값을 알아본다.
4)LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HANDLE_MSG함수로 모든 윈도우 메세지를 특정 함수에 전달한다. HANDLE_MSG를 사용하지
않구 직접 메세지를 처리해도 상관없으나 WndProc함수 보기 좋게 정리 하기 위해 사용한다.
참고] 함수 원형을 보면
BOOL OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void OnChar(HWND hWnd, TCHAR ch, int cRepeat);
void OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
void OnPaint(HWND hWnd);
void OnSetFocus(HWND hWnd, HWND hwndOldFocus);
void OnKillFocus(HWND hWnd, HWND hwndNewFocus);
void OnDestroy(HWND hWnd);
LRESULT OnImeComposition(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT OnImeChar(HWND hWnd, WPARAM wParam, LPARAM lParam);
void OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
void OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags);
void OnLButtonUp(HWND hWnd, int x, int y, UINT keyFlags);
void OnTimer(HWND hWnd, UINT id);
void OnSize(HWND hWnd, UINT state, int cx, int cy);
void OnHScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);
void OnVScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);
void OnContextMenu(HWND hWnd, HWND hwndContext, UINT xPos, UINT yPos);
void OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify);
BOOL OnSetCursor(HWND hWnd, HWND hwndCursor, UINT codeHitTest, UINT msg);
UINT OnGetDlgCode(HWND hWnd, LPMSG lpmsg);
5) BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
윈도우를 생성한다.
6) void OnPaint(HWND hwnd);
화면을 그린다.
7) void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags);
vk 키값이 넘어온다.
8) void OnDestroy(HWND hwnd);
PostQuitMessage 응용 프로그램에 종료 WM_QUIT에 메세지를 전달한다.