[MFC] MFC #1

jckim22·2024년 7월 9일

[C++] STUDY

목록 보기
1/3

윈도우 프로그래밍 모델

  • Win32 API (C언어)

Message driven

  • 메시지를 처리하는 것으로 프로그래밍을 한다.

  • 메시지 주도형

  • 윈도우 메시지

    • WM_xxx 형태로 정의된 부호가 없는 정수
    • 모든 윈도우 메시지는 처리 함수로 전달될 때 메시지 자체와 관련된 매개변수(최대 2개 w, l)와 함께 전달 됨
  • 사용자 입력 이벤트를 OS가 감지한 후 이를 메시지로 변환해 해당 응용 프로그램 메시지 큐에게 전달

  • 메시지를 수신한 응용 프로그램은 메시지 큐에서 하나씩 꺼낸 후 처리하는 이벤트 루프 구조

  • 응용 프로그램은 최소 1개 이상의 스레드로 구성되며 GUI를 갖는 응용 프로그램의 메인 스레드는 메시지 큐를 가짐

  • 메시지 핸들러

    • 윈도우 프로시저 함수는 메시지 큐에서 메시지를 꺼내 1차 처리하는 함수
      • 수백종의 메시지 중 필요한 것만 식별(Switch - case) 후 처리하며 등록하지 않은 경우 시스템 기본처리규칙 적용 -> 보완하기 위해 MFC 등장
  • 메시지 마다 개별적인 처리 함수를 구현하는 것이 보통이며 이를 메시지 핸들러(처리기)라 지칭

  • 사용자 메뉴나 버튼 클릭 시 발생하는 메시지는 구체적인 메뉴나 버튼 식별을 위해 추가적으로 ID 값(w, l)을 전달

Win32

  • Handle
    • 값(int), Pointer
  • Instance
    • void*

리소스

    // 전역 문자열을 초기화합니다.
    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를 쉽게 활용 할 수 있는 지원체계
    • Win32와 혼용도 가능
  • 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;
  • Lookup 으로 포인터 변수에 넣어준다.

문자열

  • 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 기본 구조 및 객체 생성 순서

  1. AfxWinMain() - 내가 건들건 없다
  2. CWinApp (Process, Thread)
  • Init/ExitInstance(), Run()->이벤트 루프가 돌기 시작, 보통 코드를 여기에 넣는다.
  • theApp (전역변수)
  1. CDocument (Data)
  • 데이터(혹은 파일)
  1. CFrameWnd (GUI - Frame)
  2. CView (GUI - View)

각 클래스 접근 방법

  • CWinApp -> 옵션 관련
    • theApp, AfxGetApp()
  • CDocument -> 데이터 다루는 일
    • CFrameWnd::GetActiveDocument()
  • CFrameWnd -> 응용 프로그램 실행 내내 사용되어야 하는 여러 윈도우
    • AfxGetMainWnd()
  • CView -> 어떤 메뉴를 선택했을 때 -> 어떤 화면과 관련이있다.
    • CFrameWnd::GetActiveView();


  • 실제로 코드를 뷰 클래스에 많이 넣는다.
  • 메인 클래스에 코드를 많이 넣지 말자

프레임워크 흐름 분석

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

메시지 맵과 핸들러

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

0개의 댓글