윈도우 프로그래밍 모델
Message driven
-
메시지를 처리하는 것으로 프로그래밍을 한다.
-
메시지 주도형
-
윈도우 메시지
- WM_xxx 형태로 정의된 부호가 없는 정수
- 모든 윈도우 메시지는 처리 함수로 전달될 때 메시지 자체와 관련된 매개변수(최대 2개 w, l)와 함께 전달 됨
-
사용자 입력 이벤트를 OS가 감지한 후 이를 메시지로 변환해 해당 응용 프로그램 메시지 큐에게 전달
-
메시지를 수신한 응용 프로그램은 메시지 큐에서 하나씩 꺼낸 후 처리하는 이벤트 루프 구조
-
응용 프로그램은 최소 1개 이상의 스레드로 구성되며 GUI를 갖는 응용 프로그램의 메인 스레드는 메시지 큐를 가짐

-
메시지 핸들러
- 윈도우 프로시저 함수는 메시지 큐에서 메시지를 꺼내 1차 처리하는 함수
- 수백종의 메시지 중 필요한 것만 식별(Switch - case) 후 처리하며 등록하지 않은 경우 시스템 기본처리규칙 적용 -> 보완하기 위해 MFC 등장
-
메시지 마다 개별적인 처리 함수를 구현하는 것이 보통이며 이를 메시지 핸들러(처리기)라 지칭
-
사용자 메뉴나 버튼 클릭 시 발생하는 메시지는 구체적인 메뉴나 버튼 식별을 위해 추가적으로 ID 값(w, l)을 전달
Win32
리소스
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WIN32, szWindowClass, MAX_LOADSTRING);
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_WIN32_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_WIN32 107
#define IDI_SMALL 108
#define IDC_WIN32 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
- 위처럼 리소스에 정의된 상수들을 가져와서 윈도우를 구성한다.
- 리소스 뷰에서 확인하고 수정할 수 있다.
InitInstance
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
- 메시지를 꺼내와서 해석한다.
- DispatchMessage에서 윈도우 처리기 함수(WndProc)가 호출된다.
- 까보면 switch-case 문으로 되어있다. (필요한 명령만 처리)
Win32 정리
- wWinMain 시작
- MyRegisterClass로 hinstance 등록
- 이벤트 루프를 돌음 (DispatchMessage로 핸들러 호출)
- 이벤트루프가 멈추면 응답없음 상태가 된다(콜백이 없음)
- 우리 코드는 CALLBACk 함수인 WndProc에 집중된다.
- Win32에서는 switch-case로 되어있지만, MFC에서는 메시지 맵으로 되어있다.
- 거기에 명령에 따른 핸들러 코드를 자주 건들게 된다.
MFC
- Win32 API를 쉽게 활용 할 수 있는 지원체계
- C/C++ 기반 (GUI) 응용 프로그램 프레임워크
- GUI개발에 최적화
- Windows XP 수준에서도 구동 가능
- 문자열 처리, 컬렉션, 시스템 입/출력 클래스 제공
- AFX(windows Apllication FrameworKS)
- MS 팀중 하나
- AFX로 시작하는 함수들이 있다.
MFC 콜렉션 클래스
- The template-based classes
- CArray
- CObArray
- CUintArray
- CPtrArray
- CStirngArray
- CList
- CObList
- CPtrList
- CStringList
- CMap
- CMapStringToPtr
- CMapPtrToPtr
- The collection classes not based on templates
CPtrList
CPtrList list;
list.AddTail((void*)"test");
list.AddTail((void*)"string");
list.AddTail((void*)"data");
POSITION pos = list.GetHeadPosition();
while (pos != NULL) {
void *pData = list.GetAt(pos);
std::cout << (char*)list.GetAt(pos) << std::endl;
list.GetNext(pos);
- pos에 head포인터를 담고 GetNext로 루프를 돈다.
CMapStringToPtr
CMapStringToPtr map;
map.SetAt(_T("test1"), (void*)"test");
map.SetAt(_T("test2"), (void*)"string");
map.SetAt(_T("test3"), (void*)"data");
void* pResult = nullptr;
map.Lookup(_T("test2"), pResult);
if (pResult != nullptr)
std::cout << (char*)pResult << std::endl;
문자열
- TCHAR를 유니코드로 설정하고 하면 wchar_t처럼 바이트를 많이 차지하게 되는 MBCS로 설정을 바꾸면 1바이트씩 차지한다.
- 그래도 유니코드 설정을 사용해야한다.
- 다른 dll이나 라이브러리를 가져와야하기 때문이다.
- CString
- 문자열에 대한 추상성 제공 (유니코드, MBCS 같은걸 생각안해도된다.)
- std::string보다는 CString 사용 권장
- 함수 매개변수로 사용 시 주의
- func(CString param)
- 이렇게 하면 객체가 새로 생성될 것이다. (new, delete 자동화)
- 빌드 모드 이슈
- CString 핵심 멤버함수
- =, !=, ==, +=, <, >, >=, <= 연산자가 오버라이딩돼서 문자열 비교 가능
- Trim() 공백 제거
- CaptateNocase(), MakeUppser/Lower
- Find
- Format
- LoadString -> Win32에도 있었는데 리소스에 문자열을 가져올 때 사용
CString strTest = _T("Test string");
wprintf(_T("%s\n"), strTest);
CString strLeft = _T("data"), strRight = _T("data1");
wprintf(_T("%d\n"), strLeft == strRight);
wprintf(_T("%d\n"), strLeft != strRight);
CString strFind = _T("Test String Data");
wprintf(_T("%d\n"), strFind.Find(_T("String")));
wprintf(_T("%d\n"), strFind.Find(_T("string")));
CString strTrim = _T(" \t\nTrim sample ");
wprintf(_T("%s\n"), strTrim.Trim());
CString strFormat;
strFormat.Format(_T("%d, %s"), 256, _T("Test"));
wprintf(_T("%s\n"), strFormat);
CFile
- 파일 입/출력 (MFC 제공)
- MFC 직렬화 지원(이 때가 아니면 C언어 표준 입/출력 함수나 Win32 API를 직접 사용해도 무방
프로젝트 구조
- 솔루션, 프로젝트, 리소스
- x64 (빌드한 실행파일, pdb파일)
- APP
- .vs (용량 커서 백업할 때는 제외하면 된다.)
- vcxproj (프로젝트 파일) - 여기가 루트 디렉토리
- res -> 그래픽 리소스
CWnd 클래스 상속 구조
- 핵심
- CWnd -> 윈도우라는 것 자체를 추상화하는 클래스
- CObject -> CCmdTarget -> CWnd
- CCmdTarget에 메시지 맵이 구현되어있다.
- Cwnd 클래스와 윈도우 속성
- m_hwnd -> 윈도우 핸들이다.(여러 윈도우 중 식별할 수 있는 식별자다.
- 좌상단 좌표(CPoint)와 폭과 높이 (CSize)
- 윈도우 스타일(상수정의)
- 윈도우 상태
- 윈도우 프로시저
- 최상위 프레임 윈도우마다 개별 스레드와 메시지큐 존재
윈도우 관계
- 부모, 자식, 형제 관계
- 최상위 부모 윈도우는 바탕화면(Desktop)
- 모든 프레임 윈도우의 부모는 바탕화면
- 바탕 화면의 핸들은 null
- 자식 윈도우는 부모 윈도우의 상태를 공유
- 자식 윈도우의 위치는 부모 윈도우를 기준으로 계산
- 부모 윈도우 이동 시 자식 윈도우를 함께 이동
SDI
SDI는 "Single Document Interface"의 약자로, 한 개의 문서를 처리하는 인터페이스를 의미한다.
-
단일 문서 처리: 하나의 문서를 한 번에 표시하고, 수정하며, 저장하는 방식을 말한다. 사용자는 하나의 문서를 열고, 그 문서와 관련된 작업을 수행한다.
-
단일 윈도우: 주로 SDI는 단일 창에서 문서를 표시한다. 사용자는 하나의 메인 창에서 문서를 관리하며, 창을 분할하여 여러 개의 문서를 동시에 표시하는 MDI(Multiple Document Interface)와는 달리, 한 번에 하나의 문서만 볼 수 있다.
-
일반적인 사용 사례: 텍스트 편집기, 그림 그리기 프로그램, 간단한 데이터베이스 뷰어 등의 애플리케이션에서 주로 사용된다. 이러한 종류의 프로그램은 한 번에 하나의 문서를 표시하고, 사용자가 해당 문서를 편집하거나 관리할 수 있도록 한다.
InitInstance
- 말그대로 인스턴스를 생성하는 구간
- 굳이 따지자면 MFC의 메인함수라고 생각하자
MFC 기본 구조 및 객체 생성 순서
- AfxWinMain() - 내가 건들건 없다
- CWinApp (Process, Thread)
- Init/ExitInstance(), Run()->이벤트 루프가 돌기 시작, 보통 코드를 여기에 넣는다.
- theApp (전역변수)
- CDocument (Data)
- CFrameWnd (GUI - Frame)
- CView (GUI - View)
각 클래스 접근 방법
- CWinApp -> 옵션 관련
- CDocument -> 데이터 다루는 일
- CFrameWnd::GetActiveDocument()
- CFrameWnd -> 응용 프로그램 실행 내내 사용되어야 하는 여러 윈도우
- CView -> 어떤 메뉴를 선택했을 때 -> 어떤 화면과 관련이있다.
- CFrameWnd::GetActiveView();


- 실제로 코드를 뷰 클래스에 많이 넣는다.
- 메인 클래스에 코드를 많이 넣지 말자
프레임워크 흐름 분석

- OnCreate() 시점에서 생성 되기 때문에 여기 코드를 고칠 일이 많다.

- 모든 데이터는 OnNewDocument() 시점에서 다 불러오기 때문에 새로운 창을 띄울 때마다 OnInitialUpdate()가 호출된다고 보면 된다.
- Run()부터 윈도우 화면이 보이게 되면서 이벤트 루프가 돌기 시작한다.
- 그 후 들어오는 명령어를 핸들러들이 메시지 맵으로 처리하게 되고 콜백한다.

메시지 맵과 핸들러
- 메시지 맵 Win32 API 방식(switch-case)방식의 윈도우 프로시 저구조를 개선해 만든 것
- 기존 switch-case 구조를 한 메시지(혹은 명령)에 대해 한 함수(핸드러)를 1:1로 매핑
- Visual Studio에서 자동으로 메시지 맵 코드를 관리(코드 추가 및 제거(주석처리방식))
- DECLARE_MESSAGE_MAP