[Win32] 3. Win32 프로그래밍 시작

Sireal·2022년 3월 30일
0

C++/Win32/MFC

목록 보기
8/12
post-thumbnail

김성엽님의 블로그를 따라하고 있습니다.

Win32 프로젝트 생성

새 프로젝트를 생성할 때 Win32프로젝트 또는 Windows 데스크톱 어플리케이션 을 선택해서 진행하면 됨(VS2019에서는 Windows 데스크톱 어플리케이션 마법사)

  • 경로는 짧은 경로로 하고, 영어를 사용해서 만든다.
  • 빈프로젝트 체크하지 말고 그냥 바로 다음>다음>마침 으로 진행한다.

기본 프로그램 틀을 제공해준다. 빌드 또한 가능하다.

  • 빌드>솔루션 다시 빌드
  • 디버그>디버그 하지 않고 시작(Ctrl+f5)

    벌써 프로그램 하나 만들었다.

기본 코드들이 굉장히 긴데, 사실 이거 다 안씀
실제로는 이렇게 사용함.

#include "framework.h"
#include "MyWin32.h"

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg == WM_DESTROY) PostQuitMessage(0);

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

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

	wchar_t my_class_name[] = L"tipssoft";
	wc.cbClsExtra = NULL;
	wc.cbWndExtra = NULL;
	wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = my_class_name;
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;

	RegisterClass(&wc);

	HWND hWnd = CreateWindow(my_class_name, L"Sihwan_Window",
		WS_OVERLAPPEDWINDOW, 100, 90, 400, 350, NULL, NULL, hInstance, NULL);
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

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

	return msg.wParam;
}

WinMain 함수와 매개 변수

매개변수설명
hInstance이프로그램의 instance 핸들값이 전달됨
hPrevInstance현재 이 매개변수는 사용하지않음(NULL)
lpCmdLinemain함수의 argc,argv 인자처럼 실행인자가 전달됨. 근데 각 인자가 문자열로 나눠져서 가는게 아니라, 실행파일 문자열 없이 하나의 인자로 전달됨. ex) Sihwan.exe Hello! -> Hello! 만 전달
nCmdShow응용 프로그램 초기시작 형식. 보통 SW_SHOWDEFAULT값이 전달됨. 아이콘이나 특정값을 지정하면 해당값이 여기 매개변수로 전달됨

WinMain 함수의 소스 구성

크게 4가지 기능으로 구분됨

  • 윈도우 클래스 등록
  • 윈도우 생성
  • 메시지 루프(프로그램에 전달된 메시지를 번역하고 실행하는 작업)
  • 메시지 처리기(사용자가 메시지를 처리하는 함수)->WndProc

윈도우 생성

  • CreateWidow 함수를 사용하여 프로그램에서 사용할 윈도우를 생성한다.

메시지 루프

  • 다른 응용프로그램과 메시지를 주고받는 형식으로 Win32 응용프로그램이 진행됨
  • 메세지 큐 를 가지고 송수신하며, 처리하는 작업은 프로그램이 종료될 때 까지 진행되게 반복문으로 구성됨
	/*프로그램에 전달된 메시지를 번역하고 실행하는 작업*/
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) { // 메시지를 큐에서 읽는 함수
		TranslateMessage(&msg); // 가상 키 메시지이면 ASCII 형태의 메시지를 추가로 생성
		DispatchMessage(&msg); // 변환 된 메시지를 처리하는 함수
	}

MSG 구조체

  • 메세지 큐에 전달된 메시지를 읽어올 때 사용됨.
  • WinUser.h 에 이렇게 선언됨
/*
 * Message structure
 */
typedef struct tagMSG {
    HWND        hwnd;			// 메시지가 발생한 윈도우 핸들
    UINT        message;		// 발생한 메시지 아이디
    WPARAM      wParam;			// 메시지와 함께 전달된 32비트 데이터
    LPARAM      lParam;			// 메시지와 함께 전돨된 32비트 데이터
    DWORD       time;			// 메시지가 전달된 시간(초단위)
    POINT       pt;				// 메시지가 전달되었을 때 화면 마우스 기존 커서 좌표값
#ifdef _MAC
    DWORD       lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

GetMessage()

  • 메시지큐에서 메시지를 읽을 때 사용하는 함수
  • 보통 0이 아닌값이 반환됨
    • WIM_QUIT메시지가 수신됐을 때 0을 반환
    • WIM_QUIT : 종료한다는 메시지
  • 직접 WIM_QUIT를 사용해서 프로그램을 끄고싶다면 이렇게 하면됨
    PostQuitMessage(0)
  • wParam에 전달된 값을 보통 Winmain함수의 최종 반환값으로 사용됨. 그래서 PostQuitMessage()함수로 전달한 인자값이 최종 반환값임.

TranslageMessage()

  • 가상키(WM_KEYDOWN과 같은)관련 메시지이면 키보드배열이 아닌 ASCII 값으로 해석된 WM_CHAR와 같은 추가메시지를 발생시켜줌

DispatchMessage()

  • 현재 수신된 메시지를 처리하는 함수
  • 사용자의 메시지 처리기인 WndProc함수를 호출함.

WndProc

  • 윈도우에 발생하는 메시지를 처리하는 함수
  • 함수원형을 이걸로 유지하며 사용하게됨.
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	// 자신이 처리하지않은 메시지들의 기본 작업을 대신 처리해주는 함수
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

WndProc에서 발생하는 문제점

  • 무조건 이 형식 그래도 사용하는 것은 권장하지 않음
    • 종료버튼(x버튼) 을 클릭하면 해당 윈도우가 파괴됨
    • 여기서 부터 아무런 작업도 안하면 문제가 발생
    • 메인 윈도우가 이렇게 파괴되버리면 WinMain 함수 메시지처리 루틴이 응답없음에 빠져 비정상 종료가 됨.(오류가 터진다 이거야)

문제 발생시 해결법

  • 작업관리자로 프로세스 찾아서 직접 꺼주면 됨

이런 문제가 발생하지 않도록 하는 법

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	// 메인 윈도우가 파괴될 때 메시지 큐에  WM_QUIT 메시지를 추가한다.
	if (uMsg == WM_DESTROY) PostQuitMessage(0);
    
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
	

실제 프로그래밍 작업

위의 WndProc을 가지고 마우스 좌클릭 하는 작업을 만들게 된다면 이렇게 된다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if(uMsg == WM__LBUTTONDOWN){
    	// 좌클릭 시 무언가 일어난다.
    }
	else if (uMsg == WM_DESTROY) PostQuitMessage(0);
   	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

참고

profile
🚄계속 앞으로🚄

0개의 댓글