'mfc'에 해당되는 글 3건

MFC

 

  • MFC의 개념과 특징
    - MFC 는 Microsoft Foundation Class의 약자이다.
    - MFC 라이브러리는 윈도우 프로그래밍을 위한 응용 프로그램 프레임 워크이다.
    - 개발 시간을 단축 시켜 만들 수 있다.
    - 이식성이 좋고, DAO, ODBC와 같은 데이터베이스와 윈도우 소켓과 같은 네트워크 프로그래밍을 
      단순화 시킨다.
  • MFC 계층 구조
  • MFC 세부 계층 구조
    1) CObject 클래스
       - MFC의 최상위 클래스이다.

    2) 응용 프로그램 아키텍쳐 클래스
       - CObject의 첫번째 파생 클래스 군이다.

    3) 윈도우 클래스
       - CObject의 두번째 파생 클래스 군이다.

    4) 일반 클래스
       - CObject의 세번째 파생 클래스 군이다.

    5) CObject 클래스로부터 파생되지 않는 클래스
       - CObject 클래스로부터 파생되지 않는 클래스 군이다.


▣ 마법사를 이용하여 응용 프로그램 만들기

  • MFC 응용 프로그램을 이용하여 프로젝트 생성

    * 프로젝트 생성 할 땐 다양한 속성을 개발에 맞게 선택 하여 생성 할 수 있다.

    1) 왼쪽 패널의 트리에서 MFC 프로젝트의 형식을 선택하면 오른쪽 패널에서는 템플릿을 결정하여 생성 
        할 수 있다.


    2) 응용 프로그램 종류 설정

- 단일 문서 기반
- 다중 문서 기반
- 대화 상자 기반
- 다중 최상위 문서 기반
- 도큐머트/뷰 아키텍처 지원 옵션
- 리소스 언어 선택








               
3) 복합 지원 문서




- OLE 지원 유무
- OLE 컨테이너 응용 프로그램 생성
- 미니 서버
- 풀 서버
- 컨테이너 / 풀 서버












4) 문서 템플릿 문자열




- 파일 확장명
- 파일 형식 ID
- 주 프레임 캡션
- 문서 형식 이름
- 필터 이름
- 파일의 새 약식 이름
- 파일 형식의 긴 이름








5) 데이터 베이스 지원 설정



- 데이터베이스 지원 유무
- 헤더 파일만
- 파일을 지원하지 않는 데이터베이스 뷰
- 파일을 지원하는 데이터베이스 뷰











6) 사용자 인터페이스 기능 설정



- 주 프레임 스타일

- 자식 프레임 스타일
- 명령 모음












7) 고급 기능 설정





- 고급 기능
- 고급 프레임 창
- 최근 파일 목록의 파일 수










8) 생성된 클래스


















9) 프로젝트 구성 확인





- 솔루션 탐색기
   (단축키 : Ctrl + Alt + L)
- 클래스 뷰

   (단축키 : Ctrl + Shift + C)
- 리소스 뷰
   (단축키 : Ctrl + Shift + E)













▣ MFC 프로젝트의 기본 흐름

  • MFC 응용 프로그램의 실행 흐름

    * 완성된 MFC 프로젝트에서 F11 키를 눌러서 실행 흐름을 분석 해 볼 수 있다.

      - 여기가 WinMain() 함수부이다.

  • MFC 프로젝트의 구성
    - 전체적인 파일 구성

     파일

    설명 

     MFC(filename).sin

     솔루션 파일이다.

     MFC(filename).h

     MFC(filename).cpp

     응용 프로그램 클래스가 정의 되어 있다.
     - 응용 프로그램 클래스란 윈도우 프로그램 그 자체를 의미한다.
     - 응용 프로그램 클래스는 보통 프로젝트 이름과 같은 이름으로 저장되며, 클래스 이름 뒤에는 App가 추가된다.

     MFC(filename)Frm.h
     MFC(filename)Frm.cpp

     메인 프레임 클래스가 정의 되어 있다.
     - 메인 프레임 클래스는 MainFrm.h 파일에 정의되어 있으며, 구현 파일은 MainFrm.cpp이다. 이 파일들은 프로젝트 이름과 상관 없이 고정되어 있다.

     MFC(filename)Doc.h
     MFC(filename)Doc.cpp

     도큐먼트 클래스가 정의 되어 있다.
     - 데이터 관리를 위한 클래스
     - Serialize 루틴 지원

     MFC(filename)View.h
     MFC(filename)View.cpp

     뷰 클래스가 정의 되어 있다.
     - 도큐먼트에 저장된 정보를 사용자에게 보여주는 역할을 한다.
     - 입력과 출력이 명백하게 보여질 수 있도록 처리해 주는 클래스이다.

     MFC(filename).rc

     리소스정의 파일이다.

     MFC(filename).vcproj

     프로젝트 파일이다.







'Programming > [MFC]' 카테고리의 다른 글

[MFC/기초] entry point  (0) 2018.02.07
[MFC/기초] AfxGetapp() 이란?  (0) 2018.02.06
블로그 이미지

덕배님

5년차 S/W 개발자입니다. Android, Unity, JAVA, C, C++, C# 정보를 공유합니다

,


[MFC/기초] entry point

 

 

글 순서

1.  MFC AppWizard에 대해서
2.  실습 환경

3.  가장 간단한 MFC 프로그램

4.  Line By Line Walkthrough

5.  WinMain()은 어디에 있을까 ?

 

1.  MFC AppWizard 에 대해서

 

대부분 MFC를 처음 배울 때 MFC AppWizard를 사용해서 생성된 코드를 가지고 시작합니다. 또한 처음부터 Document/View 구조를 가지고 시작하곤 하지요. 하지만 이런 방식은 몇 가지 문제가 있습니다.

 

(A)   Wizard가 생성해준 많은 양은 코드는 초보자에게 MFC를 더욱 어렵게 느끼게 합니다.

(B)   특정 형태의 프로그램에서는 Document/View 구조가 적합할 수 있지만, 반대로 Document/View 구조가 필요 없는 경우도 많이 있습니다. MFC를 사용함에 있어 Document/View 구조는 필수가 아닌 선택입니다. 하지만 대부분의 서적이 Document/View 구조로만 설명하고 있습니다.

 

그래서 본 Tutorial 에서는 Wizard를 사용하지 않고 MFC의 기본 요소들을 직접 코딩 해서 구현해 보도록 하겠습니다.

물론 어느정고 MFC의 감을 잡았으면 Wizard를 사용해서 학습하는 것이 좋습니다.

Wizard를 사용하지 않기 때문에 코드의 양도 많고 불편하겠지만 오히려 MFC의 구조를 이해하고 활용 하는데 에는 많은 도움이 될 것입니다.

 

 

2. 실습 환경

 

1장에 나오는 예제를 실습해 보려면 다음 단계에 따라 프로젝트를 만들어야 합니다.

 

VC2005/2008의 경우

  • 1. 프로젝트 타입은 Win32 Application 을 선택한다.

  • 2. 응용프로그램 설정에서 “빈 프로젝트”항목을 체크한다.

  • 3. “프로젝트”메뉴에서 제일 밑에 있는 ”속성”을 선택한다.

    • A. 왼쪽 트리의 항목중 구성속성에서 일반을 선택한 후 오른쪽 설정에서 아래 2가지를 변경한다.

      • i. MFC 사용 항목을 “공유 DLL에서 MFC 사용”으로 변경한다.

      • ii. 문자 집합 항목을 “멀티바이트 문자집합사용”으로 변경한다.

  • 4. 이제 .cpp 소스를 추가한 후 교재에 있는 소스를 가지고 실습하면 된다

 

Visual studio 6.0 의 경우

  • 1.     프로젝트 타입은 Win32 Application 을 선택한다.

  • 2.     응용프로그램 설정에서 “빈 프로젝트” 항목을 체크한다.

  • 3.     “프로젝트” 메뉴에서 “속성”을 선택한다.

    • A.      “General Tab” 에서 가운데 콤보 박스에서 “Using MFC DLL Shared”를 선택한다.

  • 4.     이제 .cpp 소스 파일을 추가한 후 교재에 있는 소스를 가지고 실습하면 된다.

 

3. 가장 간단한 MFC 프로그램

 

MFC를 사용해서 만들수 있는 가장 간단한 프로그램으로 시작해 봅시다.

항목 1-01의 내용을 참고 해서 프로젝트를 만들고 아래 코드를 입력한 후 실행해 보도록 하세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 // HelloMFC.cpp
#include<afxwin.h>
 
class CHelloApp : public CWinApp
{
public:
  virtual BOOL InitInstance();
};
BOOL CHelloApp::InitInstance()
{
  CWinApp::InitInstance();
 
  AfxMessageBox("Hello, MFC");
  return TRUE;
}
CHelloApp theApp;
cs

 

 

 

4. Line by Line Walkthrough

 

이제 한 줄 한 줄씩 살펴 보도록 하겠습니다..

 

1
#include <afxwin.h>
cs



 

MFC를 사용해서 윈도우 프로그램을 작성할 경우 반드시 <afxwin.h> 헤더를 포함해야 합니다.

 

 

 


1
2
3
4
5
class CHelloApp : public CWinApp
{
public:
  virtual BOOL InitInstance();
};
cs
 

MFC는 CWinApp 라는 클래스를 제공하는데, MFC를 사용하는 모든 윈도우 프로그램은 CWinApp 클래스의 파생클래스를 반드시 1개 만들어야 합니다.

 

 

1
2
3
4
5
6
7
BOOL CHelloApp::InitInstance()
{
  CWinApp::InitInstance();
 
  AfxMessageBox("Hello, MFC");
  return TRUE;
}
cs

 

CWinApp 클래스는 InitInstance() 라는 가상함수를 제공하는데 사용자는 반드시 이 기상함수를 재정의해서 응용프로그램의 초기화 코드를 만듭니다. 초기화에 성공한 경우는 TRUE를 초기화에 실패한 경우는 FALSE 를 리턴 합니다. 또한, InitInstance() 함수를 재정의 할 때는 CWinApp::InitInstance()를 먼저 한번 호출해야 하는데 CWinApp::InitInstance() 함수 안에서는 언어 관련 리소스를 초기화 하고 있습니다.

 

 

 


1
CHelloApp theApp;
cs
 

마지막으로 사용자가 만든 파생클래스에 대해서 객체를 전역적으로 한 개만 생성 해야 합니다.

 

 

 

결국은 MFC로 윈도우 프로그램을 만들기 위해서는 다음의 4단계의 과정을 따라야 합니다. 

1.  MFC의 기본헤더인 <afxwin.h> 를 포함한다.

2.  CWinApp 의 파생클래스를 만든다.

3.  CWinApp 클래스의 InitInstance() 가상함수를 재정의 한다.

4.  사용자가 만든 클래스의 객체를 전역적으로 1개 생성한다.

 

 

5. WinMain()은 어디에 있을까 ?

 

C/C++ 로 만든 프로그램은 main( ) 함수부터 실행 됩니다. 특히 윈도우 프로그램은 main( ) 함수 대신 WinMain() 함수를 만들어야 합니다. 그럼 위 예제에서 WinMain () 함수는 도대체 어디 있을까요 ?

 

MFC는 내부적으로 WinMain() 함수를 제공 합니다. MFC가 제공하는 WinMain() 함수의 대략적인 내용이 아래에 있습니다. 지금 아래 코드의 전체를 이해 할 필요는 없습니다. 단지, WinMain()함수는 MFC가 제공한다는 사실과 전체적인 흐름만 파악하고 있으면 됩니다. 시간이 지나면 저절로 모든 내용을 이해 하게 될 것입니다.(단, 열심히 해야 겠지요)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                LPTSTR lpCmdLine, int nCmdShow)
{
  // AfxWinMain을 호출합니다. 결국AfxWinMain()에서모든일을수행합니다.
  return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); 
}
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
            _In_ LPTSTR lpCmdLine, int nCmdShow)
{
  //......
  // (A) 사용자가 전역적 으로 만든 객체의 주소를 얻어 옵니다. 
  CWinThread* pThread = AfxGetThread();
  CWinApp* pApp = AfxGetApp();
  // (B) MFC 라이브러리의 초기화 과정을 수행 합니다.
  if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
    goto InitFailure;
  // App global initializations (rare)
  if (pApp != NULL && !pApp->InitApplication())
    goto InitFailure;
  // (C) 여기서 사용자가 재정의한 가상함수가 호출 됩니다.
  if (!pThread->InitInstance())
  {
    if (pThread->m_pMainWnd != NULL)
    {
      TRACE(traceAppMsg, 0"Warning: Destroying non-NULL m_pMainWnd\n");
      pThread->m_pMainWnd->DestroyWindow();
    }
    // (D) 사용자가 FALSE를 리턴한 경우 종료 되기 전에 아래 함수가 호출 됩니다.
    nReturnCode = pThread->ExitInstance();
    goto InitFailure;
  }
  // (E) 사용자가 TRUE를 리턴한경우 아래 함수가 호출됩니다.
  nReturnCode = pThread->Run();
  // (F) 종료 작업을 수행하고 종료됩니다.
InitFailure:
#ifdef _DEBUG
  // Check for missing AfxLockTempMap calls
  if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
  {
    TRACE(traceAppMsg, 0"Warning: Temp map lock count non-zero (%ld).\n",
      AfxGetModuleThreadState()->m_nTempMapLock);
  }
  AfxLockTempMaps();
  AfxUnlockTempMaps(-1);
#endif
  AfxWinTerm();
  return nReturnCode;
}
cs


 자세한 설명은 생략 하도록 하겠습니다. (A)   ~ (F) 까지의 주석을 한번만 읽어 보세요.

 

이 처럼 MFC는 내부적으로는 프로그램의 전체 적인 흐름을 만들어 놓고 있습다. 사용자는 그 흐름에 끼어 맞추어 가면서 프로그램을 만드는 것입니다. 이런 기법을 흔히 “Application Framework” 라고 부릅니다. 흔히 MFC관련 서적이나 글을 읽다 보면 “MFC의 Framework 에 의하면” 이라는 말을 자주 보게 되는데 결국은 “MFC가 미리 만들어둔 내부 코드에 의하면” 이라고 생각하면 됩니다. 이처럼 MFC를 정확히 이해하려면 내부구조도 어느 정도 잘 이해 하고 있어야 합니다.

 

 

[ 보충 설명 ]

 

MFC의 Entry Point 부분을 간단히 흉내 내어 보도록 하겠습니다. 완전히 동일 하려면 너무 복잡하니까 이해를 위해서 간단하게 만들어 보도록 하겠습니다. 먼저 다음의 2가지 내용을 정확히 알고 있어야 합니다.

 

1. C++에서는  전역 객체의 생성자가 main()함수 보다 먼저 실행됩니다.

2. 파생 클래스의 객체를 생성하면 기본 클래스의 생성자가 먼저 호출되고 자신의 생성자가 호출됩니다.

[참고] 정확히는 mainCRTStartup(), 전역객체 생성자, main()함수의 순서이다.

먼저 console application 형태로 프로젝트를 만든 후에 프로젝트 속성에서 빈 프로젝트를 선택합니다.

 

먼저 MFC와 비슷한 라이브러리를 코드를 만들어 보겠습니다. 좋은 방법은 아니지만 간단하게 실험해 보기 위해서 헤더 파일에 구현부도 제공하도록 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// afxwin.h
typedef int BOOL;
 
#define TRUE  1
#define FALSE 0
 
class CWinApp
{
public:
  CWinApp();
  virtual BOOL InitInstance();
  virtual int  Run();
  virtual int  ExitInstance();
};
 
CWinApp* g_app = 0// 사용자 객체의 주소를 보관할 전역변수.
 
CWinApp::CWinApp()
{
  g_app = this;    // 자신의 주소를 전역변수에 보관한다.
}
BOOL CWinApp::InitInstance()
{
  // 어떤초기화작업을수행한다.
  return TRUE;
}
int CWinApp::Run()
{
  return 0;
}
int CWinApp::ExitInstance()
{
  return 0;
}
 
// 사용자가 만든 객체의 주소를 리턴한다. 
CWinApp* AfxGetApp()
{
  return g_app;
}
 
int main()
{
  CWinApp* pThread = AfxGetApp(); // 사용자 객체의 주소를 리턴한다.
 
  if ( ! pThread->InitInstance() )
  {
    pThread->ExitInstance();
    goto Failure;
  }
  pThread->Run();
 
Failure:
  return 0;
}
cs

 

 

이제 위의 라이브러리는 사용하는 사용자의 코드 입니다. 우리가 만든 코드와 비슷하지요 ?

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// main.cpp
#include "afxwin.h"
#include <iostream>
using namespace std;
 
class CMyApp : public CWinApp
{
public:
  CMyApp();
  virtual BOOL InitInstance();
};
CMyApp::CMyApp()
{
}
BOOL CMyApp::InitInstance()
{
  CWinApp::InitInstance();
  cout << "Hello, MFC" << endl;
  return TRUE;
}
 
CMyApp theApp;
cs

  

이제 간단히 실행되는 순서를 생각해 보도록 하겠습니다.

 

(A)   전역객체 theApp가 있으므로 생성자가 제일 먼저 호출되어야 합니다. 그런데, 기본 클래스가 있으므로 기본 클래스의 생성자인 CWinApp::CWinApp()가 실행됩니다. theApp의 주소를 전역 변수 g_app에 보관하고 있군요.

 

(B)   이제 CMyApp::CMyApp() 생성자가 호출 됩니다.. 우리의 경우 특별히 하는 일은 없습니다.

 

(C)   이제 main()함수가 호출됩니다.

 

(D)   main 함수에서는 AfxGetApp() 함수를 호출해서 g_app에 보관된 주소를 얻어서 pThread라는 변수에 보관하고 있습니다.

 

(E)    다음으로 pThread->InitInstance()를 호출 합니다. 드디어 사용자가 만든 코드가 수행되는 군요.

 

(F)    TRUE를 리턴 했으므로 CWinApp::Run()을 수행후 종료됩니다.

 

결국 MFC도 이와 비슷하게 구현되어 있습니다. 물론  완전히 동일 하지는 않다. MFC는 내부적으로 전역객체의 주소를 보관하기 위해서 AFX_MODULE_THREAD_STATE 라는 복잡한 구조체를 사용합니다.  또한, 사용자 객체의주소를 얻기 위해 MFC는 내부적으로 AfxGetApp(), AfxGetThread()라는 두개의 함수를 제공하는데 두 함수의 차이는 뒤에서 시간나면 이야기를 해보도록 하지요. MFC의 내부적인 정확한 동작 방식은 앞으로 계속 이야기 하겠습니다. 지금 예제는 단지 흐름을 살펴 보기 위한 예제일 뿐입니다.

 

다음 글에서는 윈도우를 만들어 보겠습니다.





[출처] MFC 기초 첫번째 이야기 - entry point (C++ Master) 

'Programming > [MFC]' 카테고리의 다른 글

MFC 프로그래밍 기초  (0) 2018.02.19
[MFC/기초] AfxGetapp() 이란?  (0) 2018.02.06
블로그 이미지

덕배님

5년차 S/W 개발자입니다. Android, Unity, JAVA, C, C++, C# 정보를 공유합니다

,

AfxGetapp() 이란?


우선 MFC로 프로그램을 만들면, 다음과 같이 클래스가 생성되며, 프로젝트 명을 Test라고 가정해 봅시다.

  • CTestApp - CWinApp 클래스를 상속, 프로그램 초기화 클래스 (InitInstance)

  • CMainFrame - CFrameWnd 클래스를 상속, 전체 윈도우 관련 클래스

  • CTestDoc - CDocument 클래스를 상속, 문서 관련 클래스(Open, Save, Serialize)

  • CTestView - CView 클래스를 상속, 사용자 화면 클래스(OnPaint, OnDraw)

  • CAboutDlg - CDialog 클래스를 상속, 도움말 대화 상자 클래스(DoModal)


어느 곳에서나 CTestApp의 포인터를 얻고자 한다면 AfxGetApp()를 호출합니다.

AfxGetApp()은 전역 함수이므로 어느 곳에서나 호출이 가능합니다.


CTestApp* pApp = AfxGetApp();

위와 같이 코딩을 하게 되면, 에러가 발생하오니 아래와 같이 코딩합시다.

CTestApp* pApp = (CTestApp*)AfxGetApp();

AfxGetApp() 전역 함수를 호출하게 되면 MFC 응용프로그램의 최초에 생성된 스레드의 app 를 반환해 줍니다.

반환 타입이 CWinApp * 이므로 사용할때는 사용할 타입의 타입 캐스팅을 해주어야 합니다.


CTestApp 클래스의 선언 부분에 포함(include)을 시키지 않으면 에러가 발생할 수 있는데, 이럴 때는 CTestApp 클래스의 선언을 다음처럼 포함시켜야 합니다.


#include "Test.h"


 메인 스레드의 메인 윈도우 객체 포인터를 얻기 위해선

::AfxGetApp()->m_pMainWnd;

와 같이 m_pMainWnd 멤버 변수 값을 참조 하면 됩니다.


또는

::AfxGetApp()->GetMainWnd(); 

멤버 함수를 호출하여 값을 얻을 수도 있습니다.





출처


AfxGetApp() 와 AfxGetMainWnd()

MFC 클래스 포인터 얻어오기


'Programming > [MFC]' 카테고리의 다른 글

MFC 프로그래밍 기초  (0) 2018.02.19
[MFC/기초] entry point  (0) 2018.02.07
블로그 이미지

덕배님

5년차 S/W 개발자입니다. Android, Unity, JAVA, C, C++, C# 정보를 공유합니다

,