F.R.I.D.A.Y.

Win32 프로그램 생성하기 4 본문

DEV/C C++

Win32 프로그램 생성하기 4

F.R.I.D.A.Y. 2021. 4. 2. 11:22
반응형

이어서, 윈도우를 노출시키고 메시지를 받는 코드를 작성하겠습니다.


코드 확인

 여기까지 완료하면 코드는 아래와 같습니다.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR, int nCmdShow) {
	WNDCLASS wc{};

	wc.lpfnWndProc = nullptr;
	wc.lpszClassName = L"Hello world";
	wc.hInstance = hInstance;

	RegisterClass(&wc);

	HWND hWnd = CreateWindow(L"Hello world", L"My First Win32 Program", WS_OVERLAPPEDWINDOW, 
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);

}

 여기까지 작성했다면 절반 정도 완성한 것입니다! 윈도우 GUI 프로그램은 그래픽 절반, 그래픽에 맞춰 메시지를 처리하는 것이 절반이니까요.[# 어쩌면 절반도 못했을지도.. 사실 메시지 처리하는게 프로그램의 다수를 관장하니까요.. ㅎㅎ]

 

윈도우 화면에 띄우기

 윈도우를 생성했지만 보이지 않습니다. 아직 노출을 하지 않았기 때문이죠. 그럴 때는 ShowWindow를 이용해 윈도우를 노출시킬 수 있습니다.

ShowWindow(hWnd, nCmdShow);

 이 함수는 첫 번째 인자로 노출시키고자 하는 윈도우의 핸들을 넘깁니다. 두 번째 인자는 이 윈도우를 어떤 형태로 띄울 것인지를 나타내는 값을 보냅니다. 대신 처음 호출하는 경우에는 무시된다고 하니, 어떤 값을 넣어도 상관은 없겠죠. 하지만 WinMain에서 받아온 nCmdShow를 넣어줍니다.

 

 그 다음, 윈도우가 정상적으로 출력될 수 있도록 UpdateWindow를 이용해 OS에서 윈도우를 그리도록 명령합니다.

UpdateWindow(hWnd);

 이 함수 역시 인자로 대상 윈도우 핸들을 넘겨줍니다.

 


메시지 루프

 위에서 설명했던 것과 같이, Win32 프로그래밍에서 메시지 처리는 프로그램 개발에서 많은 부분을 차지합니다. 그러나 WinMain 함수에서는 할 작업이 많지는 않습니다.

 OS로부터 메시지를 받기 위해서는 메시지 구조체가 있어야합니다.

MSG msg;

 우리는 메시지 구조체 MSG를 그렇게 알 필요는 없습니다. 왜냐하면 내부 데이터는 우리가 관여하지 않고 함수로만 작업을 하니까요.

 

 메시지를 받아오는 함수는 GetMessage 입니다.

GetMessage

 GetMessage는 메시지를 OS로부터 받아옵니다.

BOOL GetMessage(
  LPMSG lpMsg,
  HWND  hWnd,
  UINT  wMsgFilterMin,
  UINT  wMsgFilterMax
);

 첫 번째 인자는 받아올 메시지 정보를 저장할 공간인데, 우리가 선언한 MSG 구조체 변수 msg에 넣겠습니다. 그리고 나머지 세 인자는 기본값을 넣어주겠습니다.

GetMessage(&msg, nullptr, 0, 0);

 3번 4번 인자의 0은 수신할 메시지 범위입니다. 메시지도 고유한 값을 가지고 있는데, 만일 두 값 모두 0인 경우에는 모든 메시지를 수신하겠다는 말이 됩니다.

 

 중요한 것은 GetMessage 함수의 반환값입니다. GetMessage는 항상 true를 반환하지만, 다음의 경우에선 유일하게 false를 반환합니다.

더보기

# WM_QUIT 메시지

 WM_QUIT 메시지는 PostQuitMessage 함수로부터 생성됩니다. 이 함수는 프로그램 종료하는 명령을 내립니다. 즉, WM_QUIT 메시지는 프로그램이 종료될 때 나오는 것입니다.

 

 따라서 우리는 메시지 루프의 탈출 조건을 이 함수로 설정할 수 있습니다.

while(GetMessage(&msg, nullptr, 0, 0)){

}

 이제 메시지 루프 내부를 구성하겠습니다.

 

TranslateMessage

 키보드 입력과 관련된 함수입니다. 키보드의 각 자판은 고유한 신호를 가지고 있습니다. OS가 이를 드라이버를 통해 가상 키 코드로 변환합니다. 이 작업을 TranslateMessage 함수가 합니다.

 

 예를 들어 설명해보죠. 키보드의 a 키를 눌러봅시다. 그럼 어떤 문자가 나오나요? 'a'가 나올 수도 있고, 'A'가 나올 수도 있습니다. 키보드 상태에 따라 달라지는 것이죠. 만일 TranslateMessage 함수를 이용하지 않으면 각 경우에 따른 문자 구분을 우리가 직접 해야합니다. 그 번거로움을 대신해주는 함수라고 생각하면 됩니다.

TranslateMessage(&msg);

 

DispatchMessage

 메시지는 <GetMessage> 함수에서 받았지만, 이를 처리하지는 않았습니다. 첫 장에서 메시지는 어디에서 처리한다고 했죠? 맞습니다. 윈도우 프로시저입니다. DispatchMessage 함수는 메시지 구조체를 넘겨서 윈도우 프로시저를 호출하는 함수라고 생각하면 됩니다.

DispatchMessage(&msg);

 

중간 코드 점검

 여기까지 잘 따라왔다면 WinMain 엔트리 포인트는 모두 작성했습니다. 아직 수정해야할 부분이 조금 남았지만, 윈도우 프로시저를 작성할 때 함께 수정하겠습니다.

#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR, int nCmdShow) {
	WNDCLASS wc{};

	wc.lpfnWndProc = nullptr;
	wc.lpszClassName = L"Hello world";
	wc.hInstance = hInstance;

	RegisterClass(&wc);

	HWND hWnd = CreateWindow(L"Hello world", L"My First Win32 Program", WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);

	if (!hWnd) return -1;

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	MSG msg;
	while (GetMessage(&msg, nullptr, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

 

 중간에 설명하지 않은 코드 한 줄이 있네요.

if(!hWnd) return -1;

 간혹 CreateWindow가 실패할 수 있습니다. 잘 따라했다면 문제가 없겠지만요.  CreateWindow는 실패시 NULL을 반환합니다. 따라서 if 구문으로 hWnd가 NULL일 때 프로그램을 종료하도록 하는 것이지요.

 


이제 다음 파트에서 윈도우 프로시저를 만들겠습니다.

 

# index

728x90
반응형
Comments