Window 만들기
- 윈도우를 만드는 순서
- 윈도우 클래스 만들기
- 윈도우 클래스 시스템에 등록
- 등록된 윈도우 클래스를 이용해서 윈도우 만들기
- 윈도우 나타내기
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
int main()
{
// 1. 윈도우 클래스 만들기
WNDCLASSEX wcex = {0}; // 항목이 12개인데 반드시 채워야하는 항목만 채워넣음
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = DefWindowProc;
wcex.lpszCalssName = _T("MyWindow");
// 2. 윈도우 클래스 시스템에 등록
ATOM atom = RegisterClassEx(&wcex); // ATOM : 16bit정수값 리턴분석용
if(atom == 0)
{
printf("등록 실패 : %d\n", GetLastError());
// WNDCLASSEX의 항목을 잘못넣거나, 동일한 이름이 있을경우 에러리턴됨
}
// 3. 등록된 윈도우 클래스를 이용해서 윈도우 만들기
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"), // 클래스 이름
_T("Hello"), // 캡션 문자열
WS_OVERLAPPEDWINDOW, // 윈도우 스타일
0, 0, 500, 500, // 윈도우 위치
0, 0, 0, 0);
// 4. 윈도우 나타내기
ShowWindow(hwnd, SW_SHOW);
MessageBox(0, _T(""), _T(""), MB_OK); // 프로그램이 종료되지 않게 막기위해서 넣음
}
문제점 해결
- 사이즈 조절 후 무슨색으로 배경색을 채워야 하는지 안넣음
- 커서가 변경될때 어떻게 변경될지 안넣음
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
int main()
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = DefWindowProc;
wcex.lpszCalssName = _T("MyWindow");
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); // 배경색
wcex.hCursor = LoadCursor(0, IDC_ARROW); // 커서
ATOM atom = RegisterClassEx(&wcex);
if(atom == 0)
{
printf("등록 실패 : %d\n", GetLastError());
}
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW,
0, 0, 500, 500,
0, 0, 0, 0);
ShowWindow(hwnd, SW_SHOW);
MessageBox(0, _T(""), _T(""), MB_OK);
}
최소화 버튼을 비활성화
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW & ~WS_MINIMIZEBOX,
0, 0, 500, 500,
0, 0, 0, 0);
확장스타일 : TOPMOST 주기
HWND hwnd = CreateWindowEx(WS_EX_TOPMOST,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW & ~WS_MINIMIZEBOX,
0, 0, 500, 500,
0, 0, 0, 0);
윈도우 프로시저
CreateWindowEx
가 호출 될 시 윈도우는 메시지 큐를 생성한다.
메시지 큐는 GUI에서 발생한 이벤트를 담고있고, 그 처리를 GetMessage
를 통해서 한다.
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
int main()
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = DefWindowProc;
wcex.lpszCalssName = _T("MyWindow");
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); // 배경색
wcex.hCursor = LoadCursor(0, IDC_ARROW); // 커서
ATOM atom = RegisterClassEx(&wcex);
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW,
0, 0, 500, 500,
0, 0, 0, 0);
ShowWindow(hwnd, SW_SHOW);
// 메시지 처리(메시지 루프)
MSG msg;
while(1)
{
// GetMessage : 메시지큐의 모든 메시지를 꺼내겠다
GetMessage(&msg, hwnd, 0, 0);
if(msg.mesage == WM_LBUTTONDOWN)
{
printf("LBUTTONDOWN\n");
}
}
}
이렇게 할 경우 윈도우가 움직이거나 사이즈 조절이 안된다.
MSG msg;
while(1)
{
GetMessage(&msg, hwnd, 0, 0);
if(msg.mesage == WM_LBUTTONDOWN)
{
printf("LBUTTONDOWN\n");
}
else
{
// 내가 처리하지 않은 메시지를 디폴트 처리기에 넘긴다.
DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
}
메시지를 처리하기 위한 함수 윈도우 프로시저(Window Procedure)
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
int main()
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = DefWindowProc;
wcex.lpszCalssName = _T("MyWindow");
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); // 배경색
wcex.hCursor = LoadCursor(0, IDC_ARROW); // 커서
ATOM atom = RegisterClassEx(&wcex);
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW,
0, 0, 500, 500,
0, 0, 0, 0);
ShowWindow(hwnd, SW_SHOW);
MSG msg;
while(1)
{
GetMessage(&msg, hwnd, 0, 0);
// 여기에서 모든 메시지를 처리하면 코드의 길이가 너무 길어진다.
if(msg.mesage == WM_LBUTTONDOWN)
{
printf("LBUTTONDOWN\n");
}
else
{
DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
}
}
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
LRESULT __stdcall WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_LBUTTONDOWN:
printf("LBUTTONDOWN\n");
break;
case WM_KEYDOWN:
printf("LBUTTONDOWN\n");
break;
}
return DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
int main()
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = WndProc; // 메시지를 누가 처리할지 알림
wcex.lpszCalssName = _T("MyWindow");
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wcex.hCursor = LoadCursor(0, IDC_ARROW);
ATOM atom = RegisterClassEx(&wcex);
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW,
0, 0, 500, 500,
0, 0, 0, 0);
ShowWindow(hwnd, SW_SHOW);
MSG msg;
while(1)
{
GetMessage(&msg, hwnd, 0, 0);
DispatchMessage(&msg);
}
}
문제) 현재는 프로그램이 죽지 않고 무한으로 돈다.
종료하는 과정을 추가
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
LRESULT __stdcall WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_LBUTTONDOWN:
printf("LBUTTONDOWN\n");
break;
case WM_KEYDOWN:
printf("LBUTTONDOWN\n");
break;
case WM_DESTROY:
// exit(0); // 이건 강제 종료
// 아래 while(1) 문 밑에 더 처리할 부분이 있었다면 그 코드는 돌지 않는다.
PostQuitMessage(0); // 루프를 종료해 달라
break;
}
return DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}
int main()
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = WndProc; // 메시지를 누가 처리할지 알림
wcex.lpszCalssName = _T("MyWindow");
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wcex.hCursor = LoadCursor(0, IDC_ARROW);
ATOM atom = RegisterClassEx(&wcex);
HWND hwnd = CreateWindowEx(0,
_T("MyWindow"),
_T("Hello"),
WS_OVERLAPPEDWINDOW,
0, 0, 500, 500,
0, 0, 0, 0);
ShowWindow(hwnd, SW_SHOW);
MSG msg;
while(1)
{
GetMessage(&msg, hwnd, 0, 0);
if(msg.message == WM_QUIT)
break;
DispatchMessage(&msg);
}
}
또 다른 방법?
MSG msg;
// 보통 이렇게 만든다
// GetMessage할게 없다면 종료하게 만든다
while(GetMessage(&msg, hwnd, 0, 0))
{
// WM_QUIT이 GetMessage로 들어오면 false가 리턴된다.
DispatchMessage(&msg);
}
추가) 컨트롤을 추가하고 싶다면?
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CREATE) {
gh_font = CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"굴림체");
gh_chat_edit = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
5, 303, 532, 28, hWnd, (HMENU)25002, NULL, NULL);
SendMessage(gh_chat_edit, WM_SETFONT, (WPARAM)gh_font, 1);
gh_send_btn = CreateWindow(L"Button", L"전송",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
540, 301, 80, 31, hWnd, (HMENU)25003, NULL, NULL);
SendMessage(gh_send_btn, WM_SETFONT, (WPARAM)gh_font, 1);
gh_list_box = CreateWindowEx(WS_EX_CLIENTEDGE, L"ListBox", NULL,
WS_CHILD | WS_VISIBLE | LBS_STANDARD,
5, 5, 615, 300, hWnd, (HMENU)25001, NULL, NULL);
SendMessage(gh_list_box, WM_SETFONT, (WPARAM)gh_font, 1);
FindTalkWindow();
return 0;
}
응용 프로그램의 종류
- 콘솔 어플리케이션
- entry : main
- 콘솔창 : 자동 생성
- 링커 옵션 :
/subsystem:console
- 데스크탑 어플리케이션
- entry : WinMain
- 콘솔창 : 생성 안됨
- 링커 옵션 :
/subsystem:windows
결국 링커 옵션만 변경하면 둘은 동일하다
// 이렇게 코드상에서 링커를 변경할 수도 있다.
#pragma comment(linker, "subsystem:console");