기본구조 상세설명
// WindowsProject1.cpp: 응용 프로그램의 진입점을 정의합니다.
//
/*
윈도우프로그램의 기본구조
- 메인부분과 메세지 처리부분으로 나뉨 [WinMain(), WndProc()]
- WinMain()은 윈도우 클래스 생성, 등록, 클래스를 기반으로 윈도우 생성
- 발생한 메세지는 큐에 저장, WndProc()에서 이 메세지를 하나씩 처리
- WinProc()은 처리할 메세지에 대해서만 작성, 나머지 메세지처리는 윈도우 커널에게 전달
- 문자열을 프로그램에서 사용할 때는 매크로_T( )를 이용하여 유니코드나 멀티바이트로 변환
*/
#include "stdafx.h"
#include "WindowsProject1.h"
#include "PrintArea.h"
#include "KeyboardMsg.h"
#define MAX_LOADSTRING 100
// 전역 변수:
HINSTANCE hInst; // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING]; // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING]; // 기본 창 클래스 이름입니다.
// 이 코드 모듈에 들어 있는 함수의 정방향 선언입니다.
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
//커널이 응용프로그램에 부여한 프로세스ID, 메모리에 적재된 응용프로그램 구분(위치), 프로그램 실행 시 부여
_In_opt_ HINSTANCE hPrevInstance, //이전에 부여, 지금은 사용 X
_In_ LPWSTR lpCmdLine, //명령라인에서 프로그램 구동 시 전달할 문자열, char *type이다.
_In_ int nCmdShow) //윈도우에 출력하는 형태
// WINAPI = 윈도우 프로그램을 의미
//원형
//=int WINAPI WinMain (HISTANCE hInstance, Hinstance hPrevIntance, PSTR szCmdLine, int iCmdShow)
// -> 윈도우 프로그램 메인함수
//하는일 = 윈도우 만들기, 윈도우 띄우기, 메세지 전송
//메세지전송이란 = 윈도우 및 app에서 발생하는 모든 메세지를 전송하는 역활(이벤트 발생시 전달)
// [발생하는 메세지를 커널이 WinMain()함수로 전달
// -> 메인함수가 메세지 처리부분 즉 WinMain()함수가 WndProc()함수로 전달]
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 여기에 코드를 입력합니다.
// 전역 문자열을 초기화합니다.
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 응용 프로그램 초기화를 수행합니다.
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));
MSG msg;
//MSG 구조체타입의 msg 인스턴스 생성한다
// 기본 메시지 루프입니다.
while (GetMessage(&msg, nullptr, 0, 0))
//GetMessage()함수가 메세지 큐에서 메세지를 꺼낸다.
//꺼낸 메세지는 msg 변수에 저장(없을 때까지 반복, 하나씩 처리)
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg); //두 개의 메세지를 하나의 메세지로 변형
//예를 들면 [shift + a]는 2개의 메세지가 입력되었지만 [A]로 처리
DispatchMessage(&msg); //메세지를 처리하는 함수에 메세지를 전달
}
}
return (int) msg.wParam;
}
//
// 함수: MyRegisterClass()
//
// 목적: 창 클래스를 등록합니다.
//
//typedef struct _WNDCLASSEX {
//
//}WNDCLASSEX;
ATOM MyRegisterClass(HINSTANCE hInstance) //윈도우 클래스를 이 함수가 커널에 등록
//커널에 등록 방법 = 윈도우 클래스 변수의 주소를 RegisterClassEx()함수의 매개변수로 넘기면 등록
//이렇게 커널에 등록한 윈도우 클래스는 이후 윈도우 생성 시 사용
//여러개의 윈도우 클래스를 등록할 수 있기에 윈도우 생성 시 사용할 윈도우 클래스의 이름을 반드시 기재!!!!
//원형 -> ATOM RegisterClassEx(CONST WNDCLASSEX*lpwcx);
{
WNDCLASSEXW wcex; //Window Class는 생성하려는 Window의 속성 값을 저장해 등록하는 구조체
//WNDCLASSEX가 아닌 WNDCLASSEXW를 사용하는 이유는?
//lpszMenuName, lpszClassName 가 문자열포인터 멤버를 가지고 있어서 W형과 A형으로 나뉘기 때문.
//#define _UNICODE, #define UNICODE로 설정되어 있으면 WNDCLASSEXW로 대체
wcex.cbSize = sizeof(WNDCLASSEX); //UINT cbSize = 양의정수, WNDCLASSEXW 구조체의 크기
wcex.style = CS_HREDRAW | CS_VREDRAW;
//UINT style = 윈도우 출력 형태 , CS_HREDRAW | CS_VREDRAW는 윈도우의 크기를 변경하면 다시 그린다는 뜻
wcex.lpfnWndProc = WndProc;
//WNDPROC lpfnWndProc = 메세지처리에 사용될 함수의 이름기재, Window Class등록 시 함께 등록
wcex.cbClsExtra = 0; //int cbClsExtra, int cbWndExtra = 클래스와 윈도우를 위한 여분의 메모리크기
wcex.cbWndExtra = 0; //일반적으로 사용 X
wcex.hInstance = hInstance;
//HANDLE hInstance = WinMain()함수에서 첫 번째 매개변수로 넘어온 응용프로그램의 인스턴스 값을 넘겨줌
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
//HICON hIcon = 기본 아이콘 설정
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); //HCURSOR hCursor = 기본 커서 설정
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); //HBRUSH hbrBackground = 윈도우의 배경색 지정
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
//LPCTSTR lpszMenuName = 메뉴이름 작성
wcex.lpszClassName = szWindowClass;
//LPCTSTR lpszClassName = 윈도우 클래스의 이름을 문자열 타입으로 작성, 윈도우 생성시 이름을 이용[기억]
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
//HICON hIconSm = 작은 아이콘 등록
return RegisterClassExW(&wcex);
}
//
// 함수: InitInstance(HINSTANCE, int)
//
// 목적: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
// 설명:
//
// 이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
// 주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
HWND hWnd = CreateWindowW(
//HandleWindow의 변수 hWnd를 생성, hWnd는 윈도우의핸들번호[=창의 번호]를 저장,
//CreateWindowW()W는 WindowClass로 윈도우를 생성하는 함수이다.
//CreateWindow()함수에서 W는 유니코드를 사용할 경우. CreateWindowW()로 사용
szWindowClass, //윈도우 클래스의 이름, ex)_T("ClassNameEx") 유니코드로 변환해야하기 때문에 _T()를 붙임
szTitle, //만들어질 TITLE에 나타나는 문자열 , 문자열의 경우 _T("") 형식으로 입력하면 유니코드로 변환해서 저장
WS_OVERLAPPEDWINDOW,
//이미 정의된 윈도우의 스타일 값 가운데 선택,
//WS_OVERLAPPEDWINDOW는 타이틀 바에 최소화 최대화 닫기와 우클릭 시 시스템메뉴가 나오는 윈도우 형태
CW_USEDEFAULT, //생성되는 윈도우가 모니터의 어느위치에 나타낼지 X좌표 값
0, //Y좌표 값
CW_USEDEFAULT, //생성되는 윈도우의 폭 값으로 단위는 픽셀
0, //높이 값
nullptr,
nullptr,
hInstance,
nullptr);
if (!hWnd) //핸들번호[=창의 번호] 값이 없으면
{
return FALSE;
}
//윈도우를 생성한 후 커널이 부여한 윈도우의 출력형태에 따라
//ShowWindow()와 UpdateWindow()를 이용하여 윈도우를 화면에 보여줌
ShowWindow(hWnd, nCmdShow);
//창을 보여주는 함수 (나타낼 윈도우의 핸들 값, 윈도우를 화면에 출력하는 방법 상수 값[커널에서 받은 값])
//원형 = BOOL ShowWindow(HWND hWnd, int nCmdShow);
UpdateWindow(hWnd); //WM_PRINT 메세지를 보내서 출력하도록(나타낼 윈도우의 핸들 값에)
//원형
//= UpdateWindow(HWND hwnd); //윈도우에 WM_PRINT 메세지를 보내므로 윈도우 화면에 기본출력을 하도록
/* 윈도우 메세지 처리과정
이벤트 발생 - 이벤트 감지 - 정수 값인 메세지를 보내는 방식(이벤트 발생 방법을 알리는 방법의 일종) - 정수 값 메세지는 큐에 쌓임 -
WinMain() 함수는 메세지 큐에서 맨 앞에 대기중인 메세지 꺼냄 - 꺼낸 메세지를 해석 메세지 처리함수에 전달 -
GetMessage()함수가 메세지 큐에서 메세지를 꺼냄 - 꺼낸 메세지를 msg변수에 저장 -
TranslateMessage()함수로 변형[TranslateMessage()함수는 두 메세지를 하나로 변형 시 사용(Shift+a는 키보드 2개를 누른 것이 아닌 A를 한번 누른거로 처리)] -
DispatchMessage()함수는 메세지를 처리하는 함수에 메세지 전달 -
메세지 처리함수가 WndProc()이라면 WndProc()함수는 메세지를 받아서 반응 -
반응은 주로 출력으로 나타남*/
/*
원형 = BOOL GetMessage(
LPMSG lpMsg, //메세지가 발생한 MSG구조체에 대한 포인터
HWND hWnd, //메세지가 발생한 윈도우의 핸들
UINT wMsgFilterMin, //얻어올 메세지의 최소 정수 값
UINT wMsgFilterMaxW //얻어올 메세지의 최대 정수 값
);
원형 = BOOL TranslateMessage(const MSG *lpMsg);
원형 = BOOL DispatchMessage(const msg *lpmsg);
*/
return TRUE;
}
//
// 함수: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 목적: 주 창의 메시지를 처리합니다.
//
// WM_COMMAND - 응용 프로그램 메뉴를 처리합니다.
// WM_PAINT - 주 창을 그립니다.
// WM_DESTROY - 종료 메시지를 게시하고 반환합니다.
//
//
/*
윈도우 메세지 처리함수 = WndProc()
원형 = LRESULT WndProc(
HWND hWnd, //생성된 윈도우의 창의 값
UINT iMsg, //양의정수 타입인 메세지 번호, 숫자를 기억하기 어렵기 때문에 매크로를 이용해 기억하기 쉽게 정의(단어만 봐도 어떤이벤트 메세지인지 알도록)
WPARAM wParam, //Word Parameter의 약자로 word(2byte)의 handle 값 및 정수(실제 데이터를 넘겨 받을 때)를 받음
LPARAM lParam //Long Parameter의 약자로 long type의 포인터 값(위치, 주소를 넘겨받을 때)을 받음
//wParam, lParam 32비트 값으로 메세지의 종류에 따라 값을 해석하는 방법이 다름(키보드의 문자의 코드값이 들어있을 수도 있고, 마우스의 위치가 들어 있을 수도)
//wParam, lParam의 해석방법은 imsg의 값에 따라 달라짐
);
WndProc()은 메세지 종류에 따라 다르게 처리해야하기 때문에 switch()문이 필요
메세지에 대해 case문을 만들 필요는 없고 프로그램에서 처리를 원하는 메세지만 case문을 만들어서 사용
나머지 메세지는 커널이 처리할 수도 있기 때문에 넘겨받은 모든 값은
DefWindowProc() 함수를 호출해서 넘겨준다.
DefWindowProc()함수는 윈도우에서 발생하는 메세지 중 윈도우 메세지처리함수에서
처리되지 않은 것을 기본메세지 처리함수에서 담당하도록 하는 함수
기본메세지처리함수 덕분에 윈도우에서 발생하는 모든 메세지를 처리 가능
DefWindowProc()함수는 윈도우 메세지 처리함수[WndProc()]와 동일한 매개변수로 호출
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
//메세지 처리부분, 즉 메세지를 받고 메세지에 약속된 반응을 나타냄
{
static TCHAR str[50]; //문자를 저장하기위한 변수 선언
static int cnt;
//static으로 선언한 이유는 메세지가 발생했을 때 저장한 내용을 다음 메세지가 발생해도 계속 유지하기 위함
static int yPos; //enter가 입력되면 문자열의 출력위치를 바꾸기 위함 y축
//static변수나 전역변수로 선언하지 않으면 메세지하나 처리하고나면 모두 잃음,
//이유는 WndProc()함수가 죵료되기 때문
PAINTSTRUCT ps; //출력영역(디바이스 콘텍스트)에 대한 상세정보를 저장할 ps 변수를 선언
//ps는 화면에 관한 다양한 정보를 얻어올 수 있음
HDC hdc; //디바이스 콘텍스트를 저장할 변수 hdc 생성
switch (message) //메세지 번호에 따라 처리를 원하는 것만 case문 제작
{
case WM_CREATE:
cnt = 0; //윈도우가 처음 만들어질 때 cnt를 0으로 초기화
yPos = 0; //윈도우가 처음 만들어질 때 y축을 0으로 초기화
/*
다양한 메세지가 WinMain()함수로부터 오겠지만 가장 먼저 도착하는 메세지는 아마...
윈도우가 처음 만들어졌을 때 발생하는 WM_CREATE. 현재는 아무작업도 안함
*/
break;
case WM_COMMAND: //응용프로그램 메뉴
{
int wmId = LOWORD(wParam);
// 메뉴 선택을 구문 분석합니다.
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CHAR: //키보드의 문자키 누를 때 발생하는 이벤트
{
hdc = GetDC(hWnd); //그릴 영역
if (wParam == VK_BACK && cnt > 0) { //wParam이 backspace이고 1문자이상 입력된경우
cnt--; //cnt를 하나 감소시키고
}
/* 이 부분은 TextOut()을 사용할 경우 ,주석처리를 하면 DrawText()를 사용
이유는 enter자체도 str의 일부로 저장되고 cnt를 0으로 바꾸지 않아도 되기 때문
else if (wParam == VK_RETURN) { //wParam이 enter일 때
cnt = 0;
yPos += 20;
}
*/
else { //아니면
str[cnt++] = wParam; //입력할 때마다 인덱스 증가하면서 값 저장
}
str[cnt] = NULL; //증감된 인덱스에 문자열 마지막을 뜻하는 null 저장
InvalidateRgn(hWnd, NULL, TRUE);
//내용이 많으면 화면이 깜빡거림,, 3번째 매개변수를 FALSE로 하고 변경된부분만 수정하도록
//요약하면 다 지우고 새로운화면 호출
/*
아래 TextOut(hdc, 200, 200, str, _tcslen(str)) 대신 InvalidateRgn(hWnd, NULL, TRUE) 사용
WM_CHAR 메세지 처리부분에서는 키보드로 입력받은 문자를 str문자배열에 저장만 하고 출력하지는 않음
대신 InvalidateRgn(hWnd, NULL, TRUE) 호출. 출력은 WM_PAINT에서 함
InvalidateRgn(hWnd, NULL, TRUE)는 (창의 번호의, 수정창 전체로 확인하고, 수정창 화면을 삭제) 후
WM_PAINT 메세지를 발생
*/
TextOut(hdc, 200, 200, str, _tcslen(str)); //저장한 문자 출력
PrintText(hdc, 0, 0, _T("출력\n"), _tcslen(_T("출력\n"))); //키보드 누르면 출력이라고 나옴
//위는 TextOut()함수를 쓴거와 같다.
ReleaseDC(hWnd, hdc); //GetDC(hWnd) 사용 후 해제
}
break;
case WM_PAINT: //창 그리기
/*
WM_PAINT 메세지가 보내지는 시점?
모든 메세지 중에 우선순위가 거의 가장 낮으며, WM_PAINT보다 낮은 우선순위는 WM_TIMER 밖에 없음
첫째 메세지 큐에 대기중인 메세지 없을 때(사용자의 입력을 처리하는 것이 화면그리기보다 중요, 그래서 입력처리상태에서는 그리지 않음)
둘째 무효영역이 있어야 함(아무리 한가해도 다시 그려야 할 부분이 없으면 WM_PAINT메세지를 보낼필요가 없음)
응용프로그램은 무효영역을 만들뿐이며 무효영역이 있으면 WM_PAINT가 메세지큐에 자동으로 들어감
*/
{
//BeginPaint()함수는 WM_PAINT 메세지를 처리하는 곳에서만 사용가능
//GetDC()함수는 WM_PAINT 메세지 이외에서 사용, 출력을 마칠 때는 ReleaseDC() 함수사용
//BeginPaint()와 GetDC()의 또 다른 차이는 GetDC()함수로 얻어와 화면에 출력할 경우 최소화했다가 원상복귀하면 화면에 출력하는 case문의 내용은 모두 사라짐
hdc = BeginPaint(hWnd, &ps);
//WM_PAINT 발생 시 출력할 영역인 디바이스 콘텍스트를 BeginPaint()함수를 통해 얻어오기
//즉 (생성한 창의 핸들(값)의, 상세정보를 저장할 곳의 주소)를 BeginPaint()함수의 파라미터로 전달하여 값을 얻어오기
RECT rect; //RECT 구조체의 변수 선언. rect는 DrawText()함수의 출력영역을 나타내는 매개변수
RECT rt = { 0, 0, 1000, 1000 };
//이곳에서 출력이 이루어짐
DrawText(hdc, str, _tcslen(str), &rt, DT_TOP | DT_CENTER);
//PrintText(hdc, 200, 200+yPos, str, _tcslen(str));
//화면의 내용이 모두 사라졌다면 WM_PAINT메세지를 발생시켜 200,200 위치에 다시 출력
PrintText(hdc, 100, 100, _T("안녕"), _tcslen(_T("안녕")));
/*
PrintArea.cpp 소스파일을 하나 생성, PrintArea.h 헤더파일을 하나 생성
PrintArea.cpp에 #include를 여기와 동일하게 작성 후
WindowsProject1.cpp와 PrintArea.cpp에 #include "PrintArea.h" 추가
PrintArea.h에는 PrintText(); 함수의 원형 작성
PrintArea.cpp에 PrintText()함수의 내용을 작성
PrintText()함수를 여기서 사용
*/
rect.left = 50;
rect.top = 40;
rect.right = 200;
rect.bottom = 120;
PrintDraw(hdc, _T("hello"), _tcslen(_T("hello")), &rect);
EndText(hWnd, &ps);
EndText(hWnd, &ps); //아래 코드를 함수를 이용
//EndPaint(hWnd, &ps); //출력을 마치고 (생성한 창의 번호와, 상세정보를 ) 반환
}
break;
case WM_DESTROY: //종료메세지 작성 및 반환
//닫기를 누르거나 종료를 누르면 WM_DESTROY 메세지 발생
PostQuitMessage(0);
// WinMain()함수 내 while의 조건문인 GetMessage(&msg, nullptr, 0, 0)를
// 0 으로 만들어 루프 종료하도록 만들고 프로그램 종료
break;
default: //윈도우메세지처리함수에서 처리되지 않은 값들을 커널이 처리하도록 메세지 전달
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// 정보 대화 상자의 메시지 처리기입니다.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
'Programming Language > Windows API' 카테고리의 다른 글
keyboard[1] (0) | 2018.08.27 |
---|---|
키보드 메세지 처리 (0) | 2018.08.26 |
TextOut(), DrawText() (0) | 2018.08.26 |
출력 (0) | 2018.08.25 |
용어 (0) | 2018.07.14 |
댓글