Chapter 1.Visual C++ 전체 보기

본장은 Visual C++ 프로그래밍에 대한 전체적인 내용을 담고 있습니다. 윈도우 프로그래밍을 한다고 하였을경우 누구나 한번쯤은 들어본 컴파일러이며,한번쯤은 간단한 프로그램을 해본분들도 있을것입니다. 또한 본책을 통해서 처음 알게되는 윈도우 프로그램 개발 툴일수도 있습니다. 위의 3가지 모두에 해당하는 분들이라도 Visual C++이라는 것이 무엇인가 생각한다면 전체적인 답변을 하기에는 너무 방대한것이 Visual C++의 정체 일것입니다. 본책의 내용을 공부해 가기 이전에 우리는 Visual C++로 프로그래밍을 할때 이용하는 전체적인 구조에 대해서 한번쯤은 생각해 보아야 할것입니다. 본장은 Visual C++로 프로그래밍을 할때 어떻게 하는것인가를 전체적인 구도에서 설명을 합니다. 본장을 읽게 되면 프로그래밍이전에 Visual C++이라는 하나의 숲을 볼수 있을것입니다.

대상자: 초급,중급,고급

Chapter 등급: 필수

공부방법: 전체적인 내용을 소설책 읽듯이 한번은 읽어 봅니다.

DemoSdi와 DemoMdi프로그램은 작성합니다.

1 - 8 항목: 초급,중급자용,필수적으로 읽습니다.

9,10 항목 : 고급자용,선택입니다.

1.Micorsoft Visual Studio 와 Visual C++

컴퓨터가 동작하기 위해서 기본적으로 구동되는 프로그램을 OS(Operating System)라고 합니다. 우리가 보통 사용하고 있는 OS는 Windows 98이나 또는 WindowsNT일것입니다. 이외에 UNIX,LINUX등의 여러 OS가 있습니다. 요즘 나온 OS들은 많은 프로그램을 함께 제공하고 있으나, OS 만 의존한다면 컴퓨터의 많은 기능을 이용할수가 없을것입니다. 한가지 예를 든다면 컴퓨터를 이용하여 천리안,하이텔,유니텔 등에 접속하여 채팅을 한다고 하였을경우 Windows 98 과 같은 OS위에 각 통신 회사에서 제공하는 프로그램이나 □새롬 데이터맨 프로□ 같은 통신 애물레이터를 설치해야 합니다. 이런 통신 프로그램들은 OS위에서 특정 목적에 적용하도록 만들어진 어플리케이션 프로그램의 하나입니다. 결국 어플리케이션 프로그램이란 Windows98 과 같은 OS위에서 특정 목적을 위해서 구동하는 또하나의 프로그램을 의미합니다. 어플리케이션 프로그램을 많이 가지고 있는  OS일수록 그 OS를 사용하는 사용자가 많아지고 이렇게 사용자가 많아질수록 OS가 발전하게 됩니다. 때문에 OS프로그램을 내놓은 많은 회사들이 자신의 OS위에서 구동하는 많은 어플리케이션을 만들고자 노력합니다. OS를 제작한 회사가 모든 어플리케이션 프로그램을 다 제작한다는것은 사실 불가능합니다. 그것은 사회가 다양해지면서 컴퓨터를 사용하는 측면이 매우 복잡해지면서 다양하게 변화되었기 때문입니다. 기본적인 어플리케이션 프로그램은 OS를 만든 회사에서 제공할수 있으나 전분야에 전업종에서 이용하는 모든 프로그램을 OS 를 만든 회사에서 흡수할수가 없기 때문입니다. 결국 어플리케이션을 제작하는 것은 여러 다른 업체나 연구소 또는 개인 프로그래머들에게 넘겨주어야 합니다. 마이크로 소프트사는 Window98 ,Windows NT 상에서 어플리케이션 프로그램을 좀더 쉽고 빠르게 만들수 있도록 하나의 개발 통합 프로그램을 만들게 되었습니다. 이것이 Visual Studio 입니다. 이 Visual Studio 라는 프로그램은 Windows 상에서 어플리케이션을 개발하는 통합 환경이라고 볼수 있습니다. 이통합환경에는 그림 1-1-1 같은 6개의 개발 툴들이 내장되어 있습니다.

(그림은 종이 참조)

(그림 1-1-1)Microsoft Visual Studio 와 연계되는 개발 툴들

그림 1-1-1 에서 보듯이 Visual C++ 은 Visual Studio 안에 내장된 어플리케이션 개발툴중에 하나입니다. Visual C++를 Visual Studio 에있는 개발툴중 가장 강력하고 가장 다양한 프로그램을 개발할수 있는 개발툴인것입니다.

Visual Studio 라는 한개의 개발환경에 왜 5개의 다양한 개발툴이 내장되어 있는것인가? 여러 이유가 있겠지만 필자가 보기에는 다음과 같을것입니다.

(1)한개의 개발환경에 여러개의 프로그래밍을 구현함으로써 시간을 절약한다.

프로그래밍 언어를 배워서 프로그래밍 하는 것만큼 프로그래머에게 고통을 주는것은 개발툴 사용법일것입니다. 과거 Visual Basic로 프로그래밍을 하다가 Visual C++로 프로그램을 하고자 할때 언어를 배우는 시간과 더불어 Visual C++ 툴 사용법이 부과되어 프로그래머들에게 배워야 할것을 증가 시켰습니다. 허나 Visual Studio가 만들어짐으로써 Visual Studio사용법만 익히게 되면 Visual Basic를 하다가 Visual C++로 넘어간다고 하였을경우 새로운 프로그램을 익혀야 하는 부담감이 덜해 질것입니다.

(2)상호 호환적인 기능을 이용하여 보다 향상된 프로그램을 만들자

Visual C++,Visual Basic,Visual J++,Visual InterDev,Visual FoxPro 등은 각각마다 고유의 특성이 있습니다. 예를 들어서 Visual C++로 작성하는것보다 Visual Basic로 작성할때가 오히려 쉽고 어떨경우에는 Visual Basic기반에 복잡한 부분이나 속도를 요하는 부분은 Visual C++ 로작성할때가 편리할경우도 있습니다. 이럴경우 각 개발툴의 특징을 살려서 따로 작성하여 한개의 프로그램으로 합치고자 할때 상호 호환성을 가지게 하고자 하는것입니다.

(3)Windows 개발환경의 하나로 통일하자

이부분은 마이크로 소프트의 정책일것 같습니다. 마이크로 소프트사의 Windows 환경에서 어플리케이션을 개발하고자 할경우 마이크로 소프트사의 개발환경 Visual Studio가 독점을 하고자 하는것입니다. 이렇게 할경우 두가지 잇점이 있습니다. 첫번째 잇점으로는 자사의 OS에 맞게 좀더 향상된 어플리케이션을 만들수 있으며 OS의 변화에 따라서 어플리케이션을 함께 변화시킬수 있다는 것입니다. 쉽게 말해서 OS의 기본구조가 바뀔경우에 어플리케이션도 쉽게 따라서 바뀌게 되어서 PC급 OS에서의 선두자리를 그대로 지키자는 의미입니다. 이것은 개발자에게도 매우 편리합니다. OS가 변화될때마다 계속적으로 새로운 정보를 얻고자 하는 노력없이 단순하게 Visual Studio환경에서 계속적으로 개발을 할수 있기 때문입니다. 두번째로는 마이크로 소프트사로 프로그래머들이 종속되는것입니다. 많은 프로그래머들이 마이크로 소프트사의 개발 툴을 이용한다면 Windows가 없어질수도 있다는 불안감이 없기 때문입니다. 세계의 프로그래머들중 많은 프로그래머들이 Visual Studio를 사용한다면 그것은 없어지지 않는 OS가 될것이라는 더불어 계속적으로 소프트웨어 업계를 주도 할수 있다는 의미가 있기 때문입니다.

<1>Visual C++ 은 무엇인가?

Visual C++ 은 원초적으로 본다면 C++ 컴파일러 입니다. 즉 C++라는 객체지향 프로그래밍 언어를 이용하여 프로그램을 작성할때 이것을 컴파일하여 실행파일로 만들어 주는 컴파일러라고 볼수 있습니다. 이것은 기본 개념이며 Visual C++은 그이상의 탁월한 기능을 가지고 있습니다. 이특징을 다음과 같습니다.

(1)다양한 SDK형태의 라이브러리를 제공한다.

SDK란 표준 개발자 킷트 (Standard Develop Kit)의 약자입니다. 이것은 Windows에서 어플리케이션 프로그램을 개발할때 필요한 C언어용 표준 라이브러리입니다. Windows에서 프로그래밍을 하고자 할경우에는 표준 C언어 함수이외에 Windows환경에 맞는 또다른 함수들이 필요합니다. 이함수들을 묶은 라이브러리가 SDK입니다. 이런 함수들이 필요한 이유는 Windows가 GUI(Graphics User Interface)이기 때문입니다. GUI환경이란 화면 이 텍스트 형태가 아닌 그래픽 형태라는 것입니다. 표준 C에는 그래픽을 위주로 하는 함수들이 포함되어 있지 않습니다. 예를 들어 라인을 긋거나 또는 박스나 원을 그리거나 폰트를 로드하는 함수들이 없다는 것입니다. 과거에는 이런것들을 하드웨어 인터럽트를 이용하여 프로그래머들이 직접 설계하였으나 Windows환경에서는 이것이 불가능합니다. 그이유는 Windows환경 자체가 그래픽 환경이기 때문에 Windows 환경에서 라인을 그리거나 원을 그리거나 할경우 Windows에서 제공하는 함수를 사용해야 합니다. 그렇게 하지 않는다면 충돌이 발생하기 때문입니다. Windows환경에서 어플리케이션을 원할하게 만들기 위해서 제공한 라이브러리가 바로 SDK입니다. 초기 SDK에는 윈도우를 만드는 기능과 메시지처리 등 GUI를 위주로 SDK가 제공되엇습니다. 점차적으로 OS의 기능이 다양해 지면서 데이터베이스 처리를 위한 ODBC SDK,DAO SDK 등의 라이브러리와 통신을 위한 라이브러리, OLE용 SDK,ActiveX 용 SDK등 매우 다양한 SDK를 제공합니다.

C언어를 기반으로 프로그래밍을 하고자 할경우 어렵고 복잡한 기능들을 Visual C++에서 제공하는 SDK 들만 이용한다면 쉽게 작성할수 있습니다. 더불어 Microsoft 사의 프로그램의 특정 기능을 이용하고자 할경우에도 아주 쉽게 프로그래밍을 할수가 있습니다. 예를 든다면 Microsoft 사의 엑셀이나 엑세스 같은 프로그램에서 작성된 파일을 이용하거나 Word에서 작성한 문서들을 자신의 프로그램에서 읽고자 할경우 Visual C++에서 제공하는 SDK를 이용하면 손쉽게 사용할수 있기 때문입니다.

(2)강력한 MFC(Microsofr Fundation Class) 가 있다.

C++로 프로그래밍을 할경우 SDK를 이용하여 클래스를 만들어 사용하고자 한다면 매우 복잡해지고 난해해 집니다. C언어를 이용하여 Windows프로그래밍을 하였다가 C++로 만들고자 할때 아마 이런 어려움을 느끼신 분들도 있을것입니다. Visual C++에는 이런 문제를 해결해주는 클래스 라이브러리를 제공합니다. 이것이 바로 MFC입니다. 이클래스는 매우 구조적으로 만들어져 있어 개발자들에게 프로그램을 개발하는데 매우 편리하게 해줍니다. 마이크로 소프트 사의 프로그램 기술력에 대해 다소 의심을 가지고 있는 필자가 MFC클래스를 보고서 실로 감탄을 금치 못햇습니다. □기형아 성격(?)을 띄고 있는 OS에서 어플리케이션을 작성하는데 이렇게 구조적이고 정교한 클래스를 만들수 있단말인가?□ 지금도 MFC를 만든 프로그래머들에게 저는 감탄과 찬사를 아끼지 않습니다. 그만큼 대단한 클래스 라이브러리 이기 때문입니다. 본책을 공부해 가면서 여러분 또한 MFC의 강력함을 느끼실것입니다.

(3)강력한 마법사(Wizard) 들이 있다!

필자가 프로그램을 할때 좋아하는 부분과 싫어하는 부분이 있습니다. 좋아하는 부분은 알고리듬이 조금 어렵고 좀 새로운 부분이며 남들이 하지 않은 부분들을 프로그래밍하는것입니다. 이런 프로그램을 만들때는 매우 재미있고 밤새도록 하여도 지치지가 않습니다. 모든 프로그램이 이렇게 되면 좋겠지만... 위와 같이 좋은 부분이 있는가하면 반대로 단순작업과 비슷하게 다알고 있는 내용을 반복적으로 코딩하는 경우도 있습니다.(필자는 이부분을 노가다 프로그래밍이라고 주로 이야기 합니다) 사실 이런 부분을 프로그래밍 할때는 정말 짜증이 날때가 많습니다. 프로그램을 제작할때 어려운 부분을 만드는것은 밤을새우면서 몇일내 끝내고 어렵지도 않은 부분들을 만들때는 차일 피일 뒤로 미루는 적이 많은 경우가 있는데... 아마 이책을 보시는 다른 프로그래머도 저와 같은 경우를 겪으시면서 고개를 끄덕끄덕 하시는 분들도 있을것입니다.

Visual C++로 프로그래밍한다면 이런 문제의 많은 부분이 정말 쉽게 해결됩니다. ClassWizard와 AppWizard 그리고 Resource Editors 등이 이런 지루한 프로그래밍을 아주 쉽고 간단하게 해주기 때문입니다.

이렇게 함으로써 좀더 어려운 부분이나 또는 실제 목표에 해당하는 주요 알고리듬 개발에 좀더 많은 시간을 할애 할수가 있게 해줍니다.

<2>Windows에서 프로그래밍을 할때 왜 Visual C++인가?

Windows환경에서 프로그래밍을 할때 개발 툴들이 매우 다양합니다. 먼저 C++를 위주로 프로그래밍 하는 측면에서 보겠습니다. Visual C++ 외에도 볼랜드사의 C++ Builder 이나 또는 다른 컴파일러 회사에서 제공하는 강력하고 다양한 C++컴파일러들이 있습니다. 그런데 왜 Visual C++인가? 이부분에서 필자가 이야기 하고 싶은 답은 OS를 만든 회사에서 제공하는 컴파일러 이기 때문이라는 것입니다. 추가적으로 말씀드리면 Windows에서 내세우고 있는 기능들과 완벽한 호환성입니다. 간단한 예로 COM이나 ActiveX ,네트워크 라이브러리등이 Windows 와 형님 아우하면서 서로 정답게 박자를 맞추어 준다는 것입니다. 이렇게 되기 때문에 프로그램을 만들고 나서 황당한 경우가 발생되지 않습니다. 또한 Visual C++로 작성된 프로그램은 Windows 98및 Windows NT에서 모두 완벽하게 호환되면서 동작합니다. 그렇기 때문에 열심히 제작한 프로그램이 Windows98에서만 동작하고 WindowsNT에서는 동작하지 않는 허무한 경우를 만나기가 힘들다는것입니다.(물론 몇개의 특수함수는 Windows 98과 Windows NT와 호환되지 않기도 합니다만 전반적으로 보았을때 의 견해입니다)

두번째는 다른 개발툴과의 비교입니다. 필자가 프로그램을 언어로 그것도 C나 C++로 고집을 하고 있을때 이것보다 쉽고 빠른 저작툴 (델파이,파워빌더) 이나 또는 Visual Basic같은 것을 이용하는것이 생산성이 좋지 않겠는가 하고 건의를 해오신 분들이 많았습니다. 생산성 측면서에서는 저작툴이나 Visual Basic 같이 제작하기 쉽고 빠르다는 좋은 잇점이 있습니다. 그러나 필자는 프로그래머로 살아 남기 위해서 C나 C++를 택했습니다. 필자는 지금까지 10년의 기간을 프로그래머로 살아왔으며 그동안 DOS,UNIX,Linux,Windows 등의 다양한 OS를 접했습니다. 그리고 굉장히 다양하게 빠르게 변화되는 컴퓨터 환경에서 진짜 헉헉거리며 지금까지 달려왔습니다. 이렇게 달려오면서 이제 프로그래머로써 어느정도 인정을 받고 대학강당에서 전산,전자 부분의 학문을 학생들에게 가르치게 되었습니다. 이렇게 10년을 지내오면서 많은 어려움도 있었고 실로 눈물젖은 커피를 마시면서 고생을 한적도 한두번이 아니었습니다. 그래도 10년을 프로그래머로 살아남을수 있었던것은 언어 프로그래밍을 고집했기 때문입니다. DOS가 없어지고 Windows가 나타나고 UNIX쪽에서 갑자기 프로그램할때가 생기고 Linux 에서 웹서버를 구축해야할때가 생겼을때 필자가 어려움 없이 이 모든 OS에서 프로그램을 만들수 있었던 이유는 바로 C++이라는 객체 지향 언어를 이용하여 프로그램을 했기 때문입니다. 위에서 말한 OS안에는 C++컴파일러가 기본적으로 설치되어 있어서 C++만 알고 해당 시스템에서 제공하는 라이브러리의 사용법만 알게 되면 바로 프로그래밍에 들어갈수 있기 때문입니다.

MS 사의 Windows 가 앞으로 많은기간동안 주도권을 잡을것이라는것 예측에 대해서는 저또한 인정합니다만.. 만약 MS사의 Windows가 없어질경우 또는 여러분이 Windows프로그래밍에서 손을 떼고 새로운 OS에서 프로그래밍을 할경우 이럴경우 Windows에 국한된 개발툴을 사용한다면 여러분의 미래는 어떻게 될까요?

□설마 그럴리는 없겠지□ 라고 생각할수도 있지만 이것은 그누구도 약속할수가 없는 것입니다. 반면에 컴퓨터 언어는 쉽게 없어지지 않습니다. 그이유는 언어이기 때문입니다. 한국어,영어,일본어 등이 쉽게 없어지지 않는것과 같은 의미입니다. 또한 새로운 언어가 탄생된다고 하여도 그언어는 기존의 언어를 기반으로 변화되기 때문에 새로운 언어를 익히는데 많은 시간이 소요되지가 않습니다.

여러분이 프로그래머로 나아가고 싶다면 언어를 기반으로 하는 툴을 선택하시기 바랍니다. 그길이 다른 쪽보다 배우기가 어렵고 많은 시간이 소요된다 하여도 변화되는 컴퓨터 환경에서 프로그래머로써 존재할수 있는 무기가 될것입니다. 필자의 후배가 한말을 써주고 싶습니다.

□공룡은 빙하기가 왔을때 사라졌지만...포유류는 아직도 살아있다!□

<3>프로그래머와 Visual C++과 그리고 본책의 관계는?

필자를 칭찬하는 말중에 이런말이 있습니다. □Visual C++의 대가!□ □Visual C++의 천재!□ 이말은  필자가 가장 싫어하는 칭찬중의 하나입니다. Visual C++의 대가나 또는 Visual C++의 천재는 바로 Visual C++를 만든 프로그래머들이지 제가 아닙니다. 전 단순하게 Visual C++ 를 Windows에서 제공하는 컴파일러로 이용하는 프로그래머 일뿐입니다. 이책을 보시는 분들이 만일 저를 칭찬해 주시고 싶다면 □바람따라 구름따라 떠돌아 다니는 컴삿갓!□ □또는 프로그래머로 남고 싶은 이상엽□으로 이야기 해주었으면 합니다. 그리고 한마디 말씀드리고 싶은것이 있다면

□MFC 를 이용하여 프로그래밍을 하는 부분에는 어떤 알고리듬도 없다!□ 입니다.

필자는 가끔 MFC의 신봉자들을 만납니다. Visual C++로 프로그래밍을 한다고 했을경우 프로그래밍 기술력의 가치를 둘때 MFC함수를 얼마나 멋있고 그리고 깔끔하게 사용하였는가를 완전한 실력자로 평가하고 그렇지 않을 경우에는 프로그램을 잘 짜지 못하는 사람으로 평가하는 경우를 본적이 많습니다. 본책을 읽는 처음 부분부터 이런말을 씀으로써 여러분의 기분을 상하게 할지는 몰라도 MFC함수를 얼마나 깔끔하게 사용하는가는 사실 함수사용법을 얼마나 많이 알고 있고 얼마나 모르는가의 차이입니다. 그것으로 프로그램 실력이 좋고 나쁨을 평가하는 것은 옳지 못한것입니다.

어떤 노우하우가 없이 단순하게 MFC라이브러리를 사용하는것이 남들보다 조금 더 많다는 것을 가지고 자랑하거나 또는 부러워할 필요가 없는것입니다.

그런것을 부러워 하기 보다 복잡한 알고리듬을 언어로 구현하는 사람들을 부러워 해야 할것입니다. 예를 든다면 sort 프로그램을 할경우 한사람은 이것을 그대로 C++ 로 코딩을 하여 함수를 만들었고 또한사람은 qsort 라는 함수를 이용하였을때 실력이 있는 사람은 바로 직접 코딩을 한것입니다. 비유가 심할지 몰라도 위의 두경우에서 qsort 함수를 이용해서 단한줄로 sorting프로그램을 만든사람이 이렇게 말할수도  있을것입니다. □어휴 답답하기는 qsort라는 함수도 몰라? 참 멍청하게 프로그래밍 하는구먼..□ 그리고 단 한줄로 프로그램을 말끔하게 작성한 자신이 더 실력있는 프로그래머라고 속으로 기뻐할수도 있을것입니다. 허구헌날 남이 작성한 라이브러를 이용한다면 여러분은 종속적인 프로그래머가 될것입니다. 진짜 프로그래머가 되기 위해서는 자신만의 알고리듬은 적어도 하나 이상을 가지고 있어야만 합니다. 그렇기 위해서는 알고리듬 개발에 좀더 많은 시간을 투자해야 합니다. 어디 쓸만한 함수 없나 하고 많은 시간을 허송하지 않았으면 한다는 것입니다. 본책이 두꺼운 이유는 바로 이런 부분에 있습니다. 본책은 아마 윈도우에서 프로그래밍을 할때 필요한 거히 모든 부분에 거히 모든 필수 함수들을 알려줄것입니다.

즉 Visual C++로 프로그래밍을 할때 함수 배우는데 많은 시간을 소요 하지 말고 본책을 이용하여 그함수를 가지고 응용해서 사용하시라는 의미입니다. 그리고 많은 시간을 여러분의 알고리듬을 개발하는데 주력하는것이 좋을것이라는 생각입니다.

2.어플리케이션(Application) 프로그램 구조

<1>SDK형태의 프로그램 구조

SDK형태의 프로그램 구조는그림 1-1-2와 같은 형태입니다.

(그림은 종이 참조)

(그림 1-1-2)SDK 형태의 프로그램 구조

그림 1-1-2는 윈도우에서 PaintShop프로그램을 실행한것입니다. 만일 SDK 로 PaintShop 같은 프로그램을 작성한다면 제일먼저 해야할것인 윈도우 시스템으로 부터 인스턴스(Instance) 를 받아야 합니다. 인스턴스(Instance)란 윈도우라는 공간에 상주할수 있는 영역입니다. 프로그램이 아니라 일반 인간 사회에서 본다면 집과 같은 것입니다. 즉 어느 나라에 상주할때 거주할 집이 필요하듯 윈도우에 특정프로그램이 구동할경우에는 이프로그램이 상주할 공간이 필요한것입니다. 이공간을 윈도우 시스템이 만들어주고 이 집의 주소를 프로그램에 넘겨줍니다. 이것을 인스턴스 핸들(Instance handle)이라고 합니다. 인간사회에서는 집을 만든다음 집주소를 등록되는것과 같습니다. 한개의 프로그램은 한개의 인스턴스를 가지고 있습니다. 그러나 그안에는 여러개의 윈도우 클래스가 있습니다. 그림 1-1-2에서 보면 두개의 윈도우 클래스가 있습니다. ② 와  ③ 이 바로 두개의 윈도우 클래스 인것입니다. 그림 1-1-2에서 집안에 남자와 여자 그리고 강아지가 있을것입니다. 이것은 서로 다른 형태의 객체입니다. 이와 같은 의미로  남자라는 하나의 객체와 여자라는 하나의 객체가 존재할수 있듯이 하나의 인스턴스 안에는 다양한 스타일의 윈도우 클래스가 존재할수 있습니다. 그림 1-1-2 에서 ② 라는  윈도우는 메뉴와 툴바를 가지고 있는 메인 윈도우 형태를 가지고 있는 클래스 이며 ③ 은 선택된 그림을 출력하는 기능을 가진 윈도우 클래스 입니다. ② 는 단한개의 윈도우로 존재하나 ③은 여러개의 윈도우가 나타날수 있습니다. 포함하고 있는 그림 내용은 달라도 윈도우가 수행하는 기능이 같기 때문에 같은 윈도우 클래스로써 여러개의 윈도우가 출력될수 있는것입니다. 인간사회에서 본다면 한집에 남자 2명 여자 3명 이렇게 살수 있지만 성별로 본다면 단순하게 남자와 여자가 사는것과 같은 것입니다. 이런 식으로 기능이 다른 여러개의 윈도우를 한개의 프로그램에 출력할수 있습니다. 그림 1-1-3은 여러개의 스타일이 다른 윈도우를 출력한 형태입니다.

(종이 참조)

(그림 1-1-3)Visual C++출력 화면

그림 1-1-3은 Visual C++ 를 출력한 화면입니다. Visual C++에는 다양한 여러개으 윈도우 클래스가 존재합니다. 그림 1-1-3에서 ①은  대화상자를 만드는 기능을 하는 윈도우 이며 ② 는  프로그램 소스를 기록하는 에디터 기능을 가진 윈도우 입니다. 그리고 ③은 메뉴를 수정하는 기능을 가지고 있습니다. 마지막으로 ④는 Visual C++의 메인 프레임 윈도우 입니다.

SDK형태의 어플리케이션 프로그램은 1개의 윈도우 인스턴스와 여러개의 윈도우 클래스가 존재하는 것입니다. SDK형태로 프로그램을 작성하여고자 할경우 기본적 구조를 만들기 위해서는 그림 1-1-4 와 같은 형태가 됩니다.

(종이 참조)

(그림 1-1-4)

프로그램이 구동되면서 윈도우 시스템으로 부터 인스턴스를 얻은다음  그인스턴스 에 필요한 윈도우 클래스들을 등록합니다. 이때 이 윈도우 클래스의 이름및 각종 스타일을 함께 등록해야 합니다. 이렇게 등록한후에 각 윈도우 클래스에서 발행되는 메시지에 해당하는 여러 사항들을 메시지 처리 루틴에서 적절하게 코딩을 하는 형태입니다.

SDK로 윈도우 프로그래밍을 해보신 분이라면 이것이 어떤 의미 인지를 아실것입니다.

<2>MFC 형태의 프로그램 구조

MFC프로그램은 두가지 형태로 나타납니다. SDI(Single Document Interface) 와 MDI(Multiple Document Interface) 가 이것입니다. SDI는 한개의 도큐먼트를 이용한 프로그램이며 MDI는 여러개의 도큐먼트를 이용한 프로그램이라는 의미입니다.

(1)SDI(Single Document Interface)

그림 1-1-4는 SDI 형태의 프로그램 구조를 그림화 한것입니다.

(종이 참조)

(그림 1-1-4)SDI 형 MFC 프로그램

SDI형태는 3개의 윈도우가 연결됩니다. 그림 1-1-4의 ①과 같은 메인프레임(MainFrame) 와 ②와 같은 View 윈도우 ③과 같은 도큐먼트(Document)가 합쳐져서 한개의 ④와 같은 한개의 템플레이트가 형성됩니다. 즉 한개의 템플레이트가 윈도우로 출력되는 형태를 의미합니다. 그리고 이템플레이트 안에는 메인프레임과 View 윈도우 그리고 도큐먼트가 있는 것입니다. 메인프레임은 윈도우 외곽 경계를 담당하면서 메뉴와  툴바 상태바를 가지고 있습니다. 메인프레임에서 하는 주된 기능은 View를 감싸면서 메뉴를 포함하여 메뉴의 이벤트를 얻고 또한 툴바와 상태바를 다른 자식윈도우로 가지고 있는 프레임 형태의 윈도우 입니다. 그안에 View라는 윈도우가 있습니다. 이 윈도우는 실제 화면처리를 담당하는 윈도우 입니다. 문자를 출력,그래픽 출력 등의 여러 형태를 바로 이 윈도우에서 하는 것입니다. SDK형태의 프로그램 구조와 다르게 또다른 구조가 하나 더 있습니다. 이것이 도큐먼트(Document) 입니다. 도큐먼트란 데이터를 저장하는 기능을 가진 클래스 입니다. 윈도우 형태가 아닌 데이터를 저장하는 형태입니다. 주로 데이터를 디스크에 저장을 했다가 필요시에 프로그램이 디스크에 있는 데이터를 로드합니다. 이럴때 이부분을 담당하는 것이 도큐먼트 입니다. 도큐먼트는 View와 쌍으로 움직입니다. 많은 프로그램이 데이터를 보관한다음 보관된 데이터를 적당히 처리하여 화면에 출력하기 때문에 쌍으로 이루어지는 것입니다. 그림 1-1-4에서 ②와 ③ 이 서로 상호 관계가 있는것처럼 설정된것이 바로 이이유입니다.

예를 든다면, test.bmp 파일을 출력하는 프로그램이 있다고 가정을 할때 도큐먼트는 디스크 에서 test.bmp 라는 파일의 정보를 읽어 주고 이것을 View에 전송하여 화면에 출력하게 합니다. 이와 같이 디스크에서 데이터를 읽고 저장하는 기능의 도큐먼트, 화면에 여러 결과를 출력하는 View 윈도우, 메뉴와 툴바,상태바를 가지고 윈도우 경계를 형성하는 메인 프레임이 한개의 템플레이트로 형성되어 프로그램이 만들어지는 것이 바로 SDI형태입니다.

(2)MDI(Multiple Document Interface)

MDI형태는 여러개의 도큐먼트를 가지고 있는 형태로서 간단하게 보면 SDI 형태의 프로그램을 여러개 모아 둔것으로 볼수 있습니다. 그림 1-1-5는 MDI형태의 MFC프로그램 구조를 도시한 그림입니다.

(종이 참조)

(그림 1-1-5)MDI형 MFC프로그램 구조

MDI는 ① 과 같은 메인프레임(MainFrame)이 독립적으로 설정되어 있습니다. 이렇게 독립적으로 설정된 윈도우 안에 템플레이트가 있는것입니다. 이템플레이트는 ②와 같은 차일드 프레임(ChildFrame), ③과 같은 화면을 출력하는 View윈도우 ,④와 같은 데이터를 저장및 디스크에서 로드하는 도큐먼트가 한개의 쌍으로 이루고 있는 것입니다. 이렇게 3개의 객체들이 합쳐져서 ⑤와 같은 한개의 템플레이트를 만듭니다. MDI에서는 같은 기능을 가진 여러개의 템플레이트를 로드할수 있습니다. bmp를 출력하는 프로그램을 작성한다고 가정을 했을때 SDI에서는 한개의 bmp파일만 출력을 합니다. 그러나 MDI에서는 여러개의 bmp파일을 출력할수 있습니다. 그렇다고 여러개의 템플레이트를 만들 필요는 없습니다. bmp를 출력하는 기능을 가진 한개의 템플레이트를 만든다음 이것이 복제되어서 나타나기 때문입니다. 즉 같은 스타일의 템플레이트가 여러개가 화면에 출력된다는 의미입니다.

때로는 여러개의 템플레이트를 만들 필요도 있습니다. Visual C++에서는 우리가 선택한 파일이 어떤 것인가에 따라서 여러 스타일의 템플레이트가 나타납니다. 이것을 다중 템플레이트라고 합니다. 그림 1-1-6은 다중 템플레이트형태를 도시한 그림입니다.

(종이 참조)

(그림 1-1-6) 다중템플레이트 형태의 MDI 구조

그림 1-1-6은 Visual C++에서 대화상자와 메뉴 그리고 소스 파일을 오픈하였을때 스타일이 서로다른 3개의 윈도우가 출력된 형태입니다. 대화상자를 제작하는 템플레이트의 기능과 메뉴를 제작하는 템플레이트 그리고 소스를 입력하는 템플레이트의 그 기능이 서로 다릅니다. 그렇기 때문에 한개의 템플레이트로 이것을 모두 제작할수가 없습니다. 이럴경우에는 MDI형태에서 여러개의 템플레이트를 설정한다음 개개의 템플레이트에 따라서 다르게 프로그램을 해주면 되는 것입니다.

(3)어플리케이션 클래스

SDI,MDI 두가지 형태 모두다 분명 한개의 인스턴스를 가지고 있습니다. 즉 프로그램이 시작될때 윈도우 시스템으로 부터 한개의 인스턴스를 갖는다는 의미입니다. MFC프로그램에서는 템플레이트를 포함하고 한개의 인스턴스를 포함하는 어플리케이션 클래스가 있습니다. 그림 1-1-7은 SDI와 MDI형태에 새롭게 어플리케이션을 추가한 도표입니다.

(종이 참조)

(그림 1-1-7) 어플리케이션 클래스가 포함된  MFC프로그램 구조

그림 1-1-7에서 보듯이 SDI 형태는 어플리케이션 클래스가 한개의 템플레이트를 구성하고 있습니다. 반면 MDI 는 여러개의 템플레이트 들이 있으며 이템플레이트를 메인프레임이 포함을 하고 그위를 어플리케이션 클래스가 포함을 하고 있는 형태입니다.

그림 1-1-7 형태를 주의 깊게 보아 주셔야 합니다. 그리고 어떤 형태로 구성되어 있는가를 이해 하시기를 바랍니다. 뒤에 이 구조에 의해서 계속적으로 설명이 연장되기 때문입니다.

3.어플리케이션 위저드(AppWizard)

본 Chapter 1항목에서 Visual C++의 특징을 설명할때 강력한 위저드가 있다고 설명하였습니다. 이 위저드중 한개인 AppWizard 에 대해서 설명하겠습니다. AppWizard 는 프로젝트를 만들때 실행되는 프로그램으로 단순히 마우스 버튼으로 몇개의 항목을 설정하게 되면 자동적으로 프로젝트를 만들어 주고 그리고 노가다급의 내용들을 코딩을 해주는 기능을 합니다. 실제 프로그램을 제작해 보면 AppWizard의 강력함을 느끼실수가 잇을것입니다.

<1>새로운 프로젝트 만들기

MFC를 이용한 새로운 프로젝트를 하나 만들어 보겠습니다. 그림 1-1-8은 File 메뉴에서 New 항목을 선택하였을때 실행되는 대화상자 입니다.

(종이 참조)

(그림 1-1-8)File에서 New 항목을 선택하였을때 실행하는 대화상자

File메뉴에서 New항목을 선택하게 되면 그림 1-1-8 과 같은 화면이 출력됩니다. 현재 프로젝트가 설정되어 있지 않다면 그림 1-1-8 의 ⓔ 항목이 설정되어 있는 상태에서 대화상자가 실행되며 만일 현재 프로젝트가 이미 설정되어 있다면 ⓓ 항목이 설정되어 있는 상태에서 대화상자가 실행됩니다. 새로운 프로젝트를 만들기 위해서는 그림 1-1-8 처럼 ⓔ 항목이 선택되도록 해야 합니다. 이상태에서 우측 리스트 윈도우에서 ⑤ 항목인 MFC AppWizard[exe]항목을 선택하도록 합니다. Projects항목을 선택하게 되면 거히 일반적으로 ⑤ 항목이 선택되어 있을것입니다. 이상태에서 ① 항목에 프로젝트 명을 기입합니다. 프로젝트명은 여러분이 원하는 형태로 기입하시면 됩니다. 본책에서는 DemoSdi라는 프로젝트 명을 기입하였습니다. ② 항목은 프로젝트가 만들어지는 디렉토리를 선택하는 항목입니다. ② 항목에 설정된 디렉토리 아래 DemoSdi라는 디렉토리가 새로 만들어지고 그안에 프로젝트에 필요한 여러 파일들이 만들어 지게 됩니다. 예를 들어서 ② 항목에 C:/Cppwork 라는 디렉토리가 설정되어 있고 프로젝트 명이 (① 항목에 입력한 이름) DemoSdi일경우 C:/Cppwork/DemoSdi 라는 디렉토리가 만들어지고 그안에 프로젝트에 필요한 여러 정보가 만들어지게 됩니다. 본책에서 제작하는 많은 예제들은 ⑤ 항목이 MFC AppWizard 를 이용할것입니다. 그외에 ⑥ 항목과 ⓑ 항목 ⓒ 항목을 이용할것입니다. 나머지 항목들은 그림에 나온 설명을 참조하시기 바랍니다. 그림 1-1-8 과 같은 화면에서 OK 버튼을 클릭하게 되면 AppWizard가 실행되게 됩니다.

<2>AppWizard Step1

그림 1-1-8에서 OK버튼을 클릭하게 되면 AppWizard Step1이 실행됩니다. AppWizard Step1에서는 어떠한 형태의 프로그램을 만들것인가를 물어 보는 항목입니다. AppWizard Step1이 실행된 화면이 그림 1-1-9 입니다.

(종이 참조)

(그림 1-1-9) MFC AppWizard Step1

본항목에서 제작하고자 하는 예제는 SDI형태입니다. SDI형태의 프로그램을 제작하고자 할경우 그림 1-1-9 에서 ① 항목을 선택하시면 됩니다. 만일 MDI 형태의 프로그램을 제작하고자 할경우에는 ② 항목을  선택하시고 단순하게 대화상자만 만들고자 한다면 ③ 항목을 선택하시면 됩니다. ① ~ ③ 까지의 항목을 선택할때 마다 ⓐ 의 화면이 해당 스타일의 윈도우로 바뀌게 됩니다. Visual 6.0에 새롭게 삽입된 항목이 ④ 항목입니다. 이항목은 도큐먼트와 View윈도우의 쌍의 형태로 프로젝트를 만들것인가를 선택하는 항목입니다. 만일 이항목을 해제하게 되면 그림 1-1-7에서 Document항목이 빠지게 됩니다. 일반적으로 프로그래밍을 할경우에는 Document와 View를 쌍으로 이용하는 것이 좋을듯 합니다. 본예제에서는 ④ 항목을 선택하여야 합니다.

⑤ 번항은 자원에서 사용될 언어 체계를 선택하는 콤보 박스 입니다. Visual 6.0 이전에는 이항목에 □한국어□ 가 없기 때문에 자원에서 다시 한글을 고쳐야 하는 불편함이 있었으나 Visual 6.0에서는 □한국어□ 가 지원되기 때문에 매우 편리하게 되어 있습니다. 이부분은 프로그램이 만들어 진후에 보시면 알수 있을것입니다.

그림 1-1-9 에서 ①번항목을 선택한후에 ⑦ 번 버튼을 클릭하여 다음으로 넘어가면 MFC AppWizard-Step2가 실행됩니다.

<3>AppWizard Step2

그림 1-1-9에서 Next버튼을 클릭하였을때 실행되는 AppWizard Step2는 그림 1-1-10 과 같습니다. 이항목에서는 제공되는 데이터베이스 라이브러리 사용에 대한 옵션을 설정합니다.

(종이 참조)

(그림 1-1-10)AppWizard Step2

Visual C++를 이용하여면 오라클,엑세스 같은 데이터베이스 엔진을 ODBC 를 이용하여 사용할수 있으며 또는 DAO 같은 데이터베이스 라이브러리를 사용할수 있습니다. 데이터베이스를 사용하고자 할경우 여러 방법이 있습니다. ②항목을 선택하게 되면 데이터베이스 헤더 파일이 포함되고 ③항목이나 ④항목을 선택하게 되면 데이터베이스 라이브러리를 사용할수 있게 됩니다. ③항목과 ④ 항목의 차이는 File 메뉴항목이 설정되는가 안되는가의 차이입니다. ③ 번항목을 선택하게 되면 File 메뉴에서 Open,Close,Save,Save As 항목이 설정되지 않습니다. ③항목이나 ④항목을 둘중에 한개를 선택하게 되면 ⑤항목 버튼이 비활성화 되어 있는 상태에서 활성화가 됩니다. 이렇게 활성화가 되었을때 ⑤ 항목 버튼을 클릭하여 사용하고자 하는 데이터베이스 엔진과 파일을 선택할수 있습니다. 본 Chapter에서는 데이터베이스를 사용하지 않기 때문에 ① 번 항목을 선택합니다. ①번항목을 선택한후에 Next 버튼을 클릭하게 되면 AppWizard Step3 가 실행됩니다.

<4>AppWizard Step3

그림 1-1-11은 AppWizard Step3항목의 출력 화면입니다. AppWizard Step3는 OLE와 Active X의 기능에 대한 옵션 설정을 하는 항목입니다. OLE에 대해서는 뒷부분에서 자세히 설명을 할것입니다.

(종이 참조)

(그림 1-1-11)MFC AppWziard Step3

① 항목을 선택하게 되면 OLE를 사용하지 않는다는 의미입니다. ② 항목을 선택하게 되면 OLE의 □객체 삽입□ 항목을 이용하겠다는 의미입니다. ② 항목을 선택하게 되면 메뉴에 □객체 삽입□라는 메뉴가 만들어지고 이메뉴를 클릭하면 그림 1-1-12와 같은 화면이 출력됩니다.

(그림 1-1-12) 객체 삽입 대화상자 실행

그림 1-1-12 항목에서 원하는 프로그램을 선택하였을때 선택된 프로그램이 현재 만든 프로그램에 삽입되어 구동되게 됩니다. OLE Container이라는 의미가 바로 OLE를 지원하는 다른 프로그램을 자신의 프로그램에 삽입하여 구동할수 있도록 하는 기능을 가진게 한다는 것입니다. ③과 ④ 항목은 제작되는 프로그램이 다른 프로그램에서 호출되어 실행될수 있도록 한다는 의미입니다. 즉 다른 프로그램에서 □객체 삽입□ 이라는 항목을 선택하였을때 그림 1-1-12와 같은 대화상자가 출력되고 이출력된 대화장사의 리스트 박스에 제작된 프로그램이 설정된다는 의미입니다. ③을 선택하게 되면 혼자서 독립적인 프로그램으로 구동되지는 않고 다른 프로그램에서 호출할경우에만 구동되는 형태이고 ④를 선택하엿을경우에는 자신혼자서 구동이 되면서 다른 프로그램에서 호출될수 있게 됩니다. ⑤번 항목은 자신의 프로그램에 □객체 삽입□ 이라는 항목이 설정되어 다른 OLE 지원 프로그램을 사용할수 있으며 자신 또한 다른 프로그램에 호출될수 있는 즉 ②와 ④의 기능을 함께 가진 프로그램을 의미합니다. ⑥ 항목은 ②,③,④ 항목을 선택하였을때 활성화 되는것인데 Active X형태의 서버와 컨테이너도 포함할것인가를 선택하는 것입니다. Actvie X에 대해서는 본책 뒤에서 자세히 설명하겠습니다. 간단하게 설명한다면 OLE기능을 가지고 있는 프로그램으로써 그 프로그램의 크기가 작고 간단하여 네트워크를 통해서도 구동할수 있는 진보적인 OLE라고 보시면 될것입니다.⑦번 항목은 OLE automation을 이용할것인가를 물어보는 것입니다. 이항목을 선택하게 되면 OLE automation을 이용할수가 있습니다. 이부분 또한 뒤에서 자세히 설명할것입니다. ⑧항목은 Active X 프로그램을 자신의 프로그램에서 이용할수 있도록 하는가를 설정하는 항목입니다. 이항목이 선택되어 있으면 ActiveX 프로그램을 사용할수가 있습니다.

본 Chapter에서는 OLE를 이용하지 않습니다. 그림 1-1-11처럼 ①항목을 선택한후에 Next 버튼을 클릭하시면 AppWizard Step4가 실행됩니다.

<5>AppWizard Step4

그림 1-1-13 은 AppWizard Step4 화면입니다. 이 step에서는 윈도우 출력형태에 대한 여러 옵션을 설정합니다.

(종이 참조)

(그림 1-1-13)AppWizard Step4

①항목이 설정되면 프로그램에 메인프레임이 툴바를 포함하고 설정하지 않으면 툴바가 포함되지 않습니다. ② 항목을 선택유무에 따라서 메인프레임이 상태바를 포함하거나 또는 포함하지 를 않습니다. ③번 항목은 인쇄 항목을 설정할것인가를 선택하는 옵션입니다. 이항목을 선택하였을경우 □Print","Print Preview","Print Setup"항목이 File메뉴 항목에 설정됩니다. ④번은 도움말 항목을 설정할것인가를 선택하는 옵션입니다. 이항목을 선택하였을대는 Help 항목에 "찾아보기□ 항목이 추가 됩니다. ⑤ 항목은 자원을 이용할때 입체적인 형태 즉 3D 형태를 지원할것인가를 선택하는 옵션입니다. 본항목을 선택하거나 해제하면 해당 형태의 스타일이 ⓓ 위치에 바뀌어 나타납니다. ⑧ 항목은 일반적인 형태의 툴바를 만들고자 할경우 선택합니다. ⑨항목은 인터넷 익스플로어 웹브라우저에서 사용하는 리바(ReBar)형태로 툴바를 사용할때 선택하는 항목입니다. ⓐ 항목은 최근까지 읽어졌던 파일 리스트를 메뉴에 최대 몇개 까지 보관할것인가를 선택하는 항목입니다. 이곳에 4라고 하였을경우 File메뉴 하단부에 그림 1-1-14 처럼 최근에 사용하였던 파일이 최대 4개까지 출력됩니다.

(그림 1-1-14) 파일 메뉴에 최근까지 사용한 파일 리스트 출력화면

ⓑ항목은 진보적인 스타일을 설정하는 화면입니다. Advanced 버튼을 클릭하게 되면 그림 1-1-15와 같은 대화상자가 출력됩니다.

(종이 참조)

(그림 1-1-15) Advanced Options 대화상자

Advanced Options 대화상자는 두개의 프로퍼티 페이지로 구성되어 있습니다.(필자는 보통 카드철 이라고 합니다) Document Template Strings,와 Windows Styles가 바로 이것입니다. 그림 1-1-15의 좌측의 화면이 Document Template Strings 를 선택하였을때 화면이며 우측의 화면이 Windows Styles를 선택하였을때의 화면입니다.

① 항목은 □파일□ 메뉴에서 □열기□ 나 □저장□ 항목을 선택하였을때 실행되는 파일 대화상자에서 초기에 읽는 파일 확장자를 어떻게 할것인가를 기입하는 란입니다. 그림 1-1-15 처럼 txt라고 기입하면 □파일□ 메뉴에서 □열기□ 나 □저장□ 하기 항목을 실행하였을대 출력되는 파일 대화상자에 초기 선태파일이 txt로 설정됩니다. 그림 1-1-16 은 txt로 설정하고 □열기□ 항목을 실행시켰을때의 출력되는 파일 대화상자입니다.

(종이 참조)

(그림 1-1-16) 파일 대화상자

⑧항목은 분할윈도우를 사용의 선택 옵션입니다. ⑨와⑩ 항목은 프레임 윈도우의 스타일을 설정하는 항목입니다. 각 항목에 대한 설명은 표1과 같습니다.

frame style	내용
Thick frame	프레임 경계선을 2중선 처리
System menu	시스템 메뉴를 부착한다.
Minimize box	아이콘 형태로 변환하는 버튼 설정
Maxmize box	최대 크기로 설정하는 버튼 설정
Minimized	실행시 아이콘 형태로 나타난다
Maximized	실행시 전체 화면 형태로 나타난다.

(표1) Frame style 설명

<6>AppWizard Step5

그림 1-1-13에서 Next 버튼을 클릭하면 그림 1-1-17과 같은 화면이 출력됩니다. 그림 1-1-17은 AppWizard Step5의 화면입니다.

(종이 참조)

(그림 1-1-17)AppWizard Step5

AppWizard Step5는 3가지의 옵션을 설정하는 항목입니다. 첫번째 항목으로는 어떤스타일의 프로그램을 만들것인가입니다. ① 를 설정하면 일반적인 MFC프로그램이 구동되며 ②를 선택하면 웹브라우저인 익스플로어를 포함한 프로그램이 구동됩니다. 이항목은 Visual C++ 6.0에 새롭게 삽입된 항목으로써 인터넷 익스플로어를 자신의 프로그램에 삽입할수 있는 강력한 기능중에 하나입니다 두번째 항목으로는 프로그램 내에 주석을 달것인가를 물어보는 항목입니다. AppWizard 실행이 종료되면 자동적으로 몇개의 소스파일이 만들어지고 □노가다급□ 프로그램이 자동적으로 코딩됩니다. 이코딩된 소스에 주석을 달고자 할경우 □Yes, please" 항목을 설정하고 그렇지 않을 경우 "No,thank you" 를 설정하시면 됩니다. 일반적으로 소스에 주석을 달아서 코딩되도록 하는것이 좋습니다. 세번째 항목으로 MFC라이브러리를 어떻게 이용할것인가를 물어봅니다. MFC라이브러리를 이용할때는 두가지 방법이 있습니다. 첫번째 방법은 MFC dll 들을 이용하는 것입니다. 이때에는 컴파일된 실행파일이 매우 작습니다. 또한 여러 프로그램이 동시적으로 MFC라이브러리를 이용할경우에 메모리 소비가 적습니다. 단점은 MFC dll를 사용할경우 프로그램을 설치할때 실행파일 외에 Visual C++에서 제공하는 MFC dll를 함께 설치해주어야 합니다. ④ 항목을 선택하면 DLL를 이용하여 프로그램이 구동되는 방식입니다. ⑤ 항목을 이용하였을경우에는 MFC lib를 사용합니다. 이렇게 되면 프로그램이 실행파일로 만들어 질경우 프로그램 크기가 매우 커지는 단점이 있으나 한개의 실행파일만 설치하여도 프로그램이 구동될수 있습니다. 그러나 Step 3에서 ActiveX Controls항목을 해제하지 않으면 URLMOM.DLL를 포함해야만 합니다. 본 Chapter에서는 그림 1-1-17과 같이 선택하고 Next버튼을 클릭합니다.

<7>AppWizard Step6

그림 1-1-18은 AppWizard Step6의 항목이 출력 화면입니다.

(종이 참조)

(그림 1-1-18)

MFC로 SDI형태의 프로그램을 제작하면 어플리케이션 클래스,메인프레임,View윈도우,도큐먼트가 만들어진다고 설명하였습니다. 이것들을 AppWizard에서는 자동적으로 클래스로 만들어 집니다. 그림 1-1-18에서 ① 은 4개의 클래스들의 이름들입니다.

표2는 이클래스들의 설명 입니다.

(표2) AppWizard에 의해서 만들어진 SDI형 클래스 설명

클래스	내용
CDemoSdiApp	어플리케이션 클래스
CMainFrame	메인프레임
CDemoSdiView	View윈도우
CDemoSdiDoc	도큐먼트

그림 1-1-7과 비교해보면서 확인을 해보시기 바랍니다. 메인프레임을 만들고 컨트롤 하는 클래스가 CMainFrame이고 View윈도우를 컨트롤하는 클래스가 CDemoSdiView이며 도큐먼트를 컨트롤하는 클래스가 CDemoSdiDoc라는 의미입니다. 또한 이모든것을 포함하는 어플리케이션 클래스가 CDemoSdiApp라는 의미가 됩니다. 본 Chapter의 2번 항목에서 설명한 내용과 함께 생각해 보시기 바랍니다.

③과 ④는 ① 항목에 선택된 클래스의 헤더파일과 소스파일 명입니다. 이파일명은 여러분이 원하는 대로 변경할수 있습니다. ⑤는 현재 선택된 클래스가 상속받은 클래스 명이 출력된것입니다. 이 클래스들은 MFC에서 제공하는 클래스들입니다. 이클래스들에대한 설명은 본Part 다음 항목에서 할것입니다.

이제 Finish버튼을 클릭하면 그림 1-1-19와 같은 화면이 출력됩니다.

(그림 1-1-19) AppWizard를 완료 하였을때 출력되는 화면

그림 1-1-19의 화면은 AppWizard를 이용하여 어떻게 프로젝트를 만들었는가에 대한 모든 정보가 나타납니다. OK 버튼을 클릭함으로써 새로운 프로젝트가 만들어 집니다.

위와 같이 만든 후에 F7키를 누르거나 메뉴의 Build 항목의 Build DemoSdi.exe를 선택하면 프로그램이 컴파일 되고 컴파일후에 F5키나 Ctrl+F5키를 누르거나 또는 메뉴항목에서 Execute DemoSdi.exe항목을 클릭하면 프로그램이 실행됩니다. 그림 1-1-20 은 DemoSdi.exe가 실행된 화면입니다.

(그림 1-1-20)DemoSdi 실행결과

프로그램에 아무런 코딩 없이 간단하게 프로그램이 만들어진것을 보실수 있을것입니다. 이것을 보시고 감탄하거나 또는 □야 쉽다 정말 편리하게 프로그램 짜는구나□ 라고 생각하신다면 너무 성급하다고 말씀드리고 싶습니다. 이것은 시작이요 이후에 작업들은 그렇게 만만하지 않기 때문입니다.

<8>MDI형 프로젝트 만들기

이제 MDI형 프로젝트를 하나 더만들어 보겠습니다. SDI형을 만들때와 같은 방식으로 File 메뉴에서 New항목을 선택하고 그림 1-1-8이 출력된 상태에서 Project name를 DemoMdi로 입력하여 새로운 프로젝트를 만듭니다. 이렇게 만든후에  그림 1-1-9와 같은 화면이 나올경우 Multiple documents를 선택하고 나머지는 SDI형을 만들때와 같은 방법으로 Step6까지 진행합니다. 이렇게 진행한 Step6은 그림 1-1-21과 같습니다.

(그림 1-1-21) AppWizard Step6

MDI형은 SDI형과 다르게 클래스가 설정된것을 보실수 있을것입니다. 이것또한 그림 1-1-7를 보시면서 연관적으로 생각해 보시기 바랍니다.

표3는 MDI형으로 만들었을때 클래스와의 관계를 설명한 표입니다.

(표3)MDI형으로 제작하였을때 생성되는 클래스

클래스	내용
CDemoMdiApp	어플리케이션 클래스
CMainFrame	메인프레임
CChildFrame	차일드 프레임
CDemoMdiView	View윈도우
CDemoMdiDoc	도큐먼트

MDI는 메인프레임이 템플레이트를 감싸고 있다고 설명을 하였고 템플레이트의 프레임 윈도우는 차일드 프레임이라고 설명하였습니다. 이차일드 프레임을 컨트롤 하는 클래스가 바로 CChildFrame입니다.

그림 1-1-21에서 Finish 버튼을 클릭하면 그림 1-1-19와 비슷한 화면이 나옵니다. 이때 OK버튼을 클릭하면 AppWizard 를 이용하여 MDI형 프로젝트를 만드는것이 완료 된것입니다.

DemoMdi를 컴파일하고 (F7 키나 또는 Build 항목에서 Build DemoMdi.exe 선택) F5키나 Ctrl+F5키를 클릭하여 실행하면 그림 1-1-22와 같은 화면이 출력됩니다.

(그림 1-1-22) DemoMdi프로그램 실행결과

<9>AppWizard 기능은...

지금까지 설명한 내용으로 AppWizard가 어떤 기능을 하는지 알수 있을것입니다. AppWizard는 우리가 만들고자 하는 프로젝트를 만들어 주고 그안에 필요한 클래스를 생성해주며 이클래스에 기본적인 내용들을 코딩해 주는 기능을 합니다. MFC를 이용하여 프로그램을 하면 하나의 프로그램 외곽을 구성할때 우리가 코딩을 하여 구성시킨다면 사실 매우 많은 시간이 소요됩니다. DemoSdi는 4개의 클래스가 DemoMdi는 5개의 클래스가 필요합니다. 또한 각 클래스마다 헤더파일과 소스파일이 있으므로 기본적으로 총 8개의 소스파일이 필요합니다. 이외에도 메뉴,비트맵 등등 매우 많은 파일들이 내장되어 있습니다. 이것들이 자동적으로 만들어지지 않고 모든것을 수동으로 구성시키자고 한다면 아마 실제 프로그램에 도달하기전에 포기하게 될것입니다. 이런 문제점이 없도록 기본적인 프로그램을 코딩시켜주는것이 바로 AppWizard의 기능입니다. AppWizard은 단순하게 기본 형태만 만들어주는것이지 완벽한 기능을 제공해 주는 것이 아닙니다. 그리고 또하나 꼭 기억해야 할사항은 AppWizard를 이용하여 만들었을때 우리가 실수하여 선택하지 않은 옵션이 있더라도 소스에서 새로 추가할수가 있습니다. 예를 든다면 Step4에서 3D 옵션을 제거하였다 하더라도 소스에서 몇개의 함수를 추가함으로써 3D가 가능하게 할수 있다는 것입니다. 이것들 모두 본책을 통해서 배우시게 될것이며 또한 AppWizard에서 옵션을 바꾸면서 프로그램을 제작하는 방법 또한 배우시게 될것입니다. 본장을 통해서 여러분이 이해하실것은 프로젝트를 만들때 AppWizard를 이용하며 각 스텝 마다 우리가 필요한 옵션들이 있다는 것입니다.

4. MFC 어플리케이션 아키텍처(Application Architecture)

지금까지의 설명을 총정리한다면 SDI 형 MFC 프로그램을 만들고자 할경우에는 메인프레임,View윈도우,도큐먼트 가 하나의 템플레이트로 구성되고 이것을 어플리케이션 클래스가 포함을 하고 있으며 MDI형 MFC프로그램을 만들고자 할경우에는 차일드프레임,View윈도우,도큐먼트가 하나의 템플레이트로 구성되고 이것을 메인 프레임이 포함하고 있으며 이것을 어플리케이션이 포함한다고 설명하였습니다. 또한 이런 각각체 들을 클래스로 만들어 주고 프로젝트에 삽입시켜주는것이 AppWizard라고 하였습니다. 본 Chapter 3항목에서 AppWizard를 사용하여 프로젝트를 만들면 아무런 코딩없이 간단하게 SDI형 프로그램과 MDI형 프로그램을 제작할수 있다는 것을 보실수 있을것입니다. 여기에서 한번 생각해 볼것이 있습니다. AppWizard 가 자동적으로 소스트를 만들어 주는 프로그램인가? 아니면 기존에 있는 소스를 단순하게 복사하여 코딩을 시켜주는것인가? 정답은 위의 두항목 모두 아닙니다. AppWizard 는 기존에 있는클래스를 상속받은 또하나의 클래스를 만들어 주는 일을 하는것 뿐입니다. 그리고 어플리케이션 클래스에서 간단한 초기화를 몇개 하는것입니다. 예를 들어서 메인프레임을 담당하는 윈도우를 만들고자 할때 미리 메인프레임의 기능을 가진 클래스를 라이브러리로 가지고 있고 이클래스를 상속받은 또하나의 클래스를 만들어 프로젝트에 삽입하는 것입니다.

class CMainFrame: public CFrameWnd

{

:

}

메인프레임의 기능을 하는것이 CFrameWnd 크래스 입니다. 이것을 상속받은 CMainFrame를 만들어 주는것이 AppWizard가 하는 일인것입니다. 이런 방법으로 윈도우에 필요한 여러 클래스를 만들어 놓고 이 클래들을 상속받음으로 프로그램을 쉽게 작성할수 있도록 한것입니다.

그렇다면 MFC는 어떤 형식으로 이런 클래스들을 만들어 놓았는가? 좀더 정확하게 말하면 어떤 구조로 만들어 놓았는가를 알아 볼필요가 있습니다. 이것을 알아보는것이 바로 MFC 의 계층적 구조 형태를 분석하는 것이고 이것을 다시 어플리케이션 아키텍처라고 합니다. 아키텍처(Architecture)란 해석 하면 □건축 양식□ 이라는 의미입니다. 어플리케이션 아키텍처라고 하였으므로 어플리케이션을 만들고자 할때 그 구조를 의미하는 말인것입니다.

MFC의 기본적인 구조는 크게 그림 1-1-23과 같이 분리할수 있습니다.

(종이 참조)

그림 1-1-23 MFC 클래스의 기본적인 구조

몇개의 클래스를 제외하고 MFC의 클래스는 CObject라는 클래스를 상속받습니다.

이 CObject의 기능은 메모리에 클래스를 정확하게 위치하는 기능및 크래스의 기능과 종류를 알수 있는 기능을 가지고 있습니다. CObject의 클래스 헤더는 Visual C++ 6이 설치되어 있는 디렉토리의 /MFC/Include/Afx.h에 설정되어 있습니다. 다음은 CObject의 소스 일부입니다.

class CObject

{

public:

virtual ~CObject();  // 소멸자

//메모리 설정과 삭제 부분

void* PASCAL operator new(size_t nSize);

void* PASCAL operator new(size_t, void* p);

void PASCAL operator delete(void* p);

void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);

void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);

protected:

CObject();

private:

//현재 클래스를 다른 클래스로 할당하는 부분

CObject(const CObject& objectSrc);              // no implementation

void operator=(const CObject& objectSrc);       // no implementation

//속성에 대한 부분

public:

BOOL IsSerializable() const;

BOOL IsKindOf(const CRuntimeClass* pClass) const;

// 데이터의 저장기능

virtual void Serialize(CArchive& ar);

//유효성 확인

virtual void AssertValid() const;

virtual void Dump(CDumpContext& dc) const;

};

실제로 Afx.h를 보면 위의 내용이 다소 복잡하게 #ifdef와 #endif로 연결되어 있습니다. 간단하게 본다면 CObject는 클래스를 메모리에 할당하기 위한 new연산자가 오버로딩되어 있습니다. MFC클래스를 메모리에 할당하기 위해서는 여러 부가적인 작업들이 필요합니다. 그이유는 각 클래스의 기능에 따라 할당될 메모리가 변화되기 때문입니다. 그외 CObject는 현재 클래스의 특징과 기능을 할수 있는 여러 함수가 포함되어 있습니다. IsSerializable라는 함수는 현재 클래스가  데이터를 디스크에 저장할수 있는 기능을 가지고 있는가 없는가를 확인하는 함수이며 AssertValid라는 함수는 현재 클래스가 유효한 클래스인가를 확인하는 클래스입니다. Dump 클래스는 현재 클래스의 상태를 확인할수 있습니다. 보통 디버깅을 할때 Dump기능을 이용하여 데이터의 상태를 확인하고 오류를 정리할수 있습니다. 지금까지의 설명으로 본다면 CObject 는 클래스자체가 메모리상에 정확하게 위치하기 위한 기능을 전제고 합니다. 이 CObject클래스를 상속받아 탄생된 최상위의 2번째 클래스가 그림 1-1-23의  ②인 CCmdTarget 클래스입니다. CCmdTarget클래스는 메시지 전송을 담당하는 클래스입니다. 윈도우 프로그램은 메시지 드리븐(message driven) 방식으로 구동됩니다. 즉 하나의 모듈이 순서적으로 구동되는 것이아니라 메시지가 발생되면 해당하는 메시지에 의해서 그모듈이 실행되는 방식으로 구동된다는 것입니다. 예를 들어서 키보드에서 입력된 키를 화면에 출력한다고 가정을 하였을경우 키보드에서 키가 입력되었다는 것을 WM_KEYDOWN이라는 메시지로 어플리케이션 프로그램에 전달합니다. 어플리케이션 프로그램은 이메시지와 함께 전달된 입력 키코드를 메모리에 저장한다음 다시 자신의 프로그램에 WM_PAINT라는 메시지를 보내고 WM_PAINT메시지가 들어온 상태에서 화면에 입력된 키보드 값을 출력하는 것입니다. 이예를 도시한 그림이 1-1-24 입니다.

(종이 참조)

(그림 1-1-24) 메시지에 의해서 처리되는 프로그램 형태

그림 1-1-24 에서 보듯이 윈도우 프로그램은 메시지가 들어 왔을때 그것을 어떻게 처리해야 하는가가 바로 프로그래밍의 전부라고 할수 있습니다. 그만큼 메시지 전송이 중요하기 때문에 최상위 두번째 클래스가 메시지 처리를 담당하는것이되는것입니다.

<1>CWnd 클래스

CCmdTarget를 상속받은 클래스중 가장 많이 사용되는 클래스이면서 우리가 자주 보지 못하는 클래스가 바로 CWnd클래스입니다. 화면에 보이는 윈도우들은 모두 CWnd를 상속받았습니다. 즉 윈도우의 최상위 클래스라고 보시면 됩니다. 그림 1-1-25을 참조하시기 바랍니다.

(종이 참조)

(그림 1-1-25)DemoMdi 프로그램 출력시 포함된 CWnd 클래스 들

그림 1-1-25에 나타난 프로그램을 전에 제작한 DemoMdi프로그램입니다. 이프로그램에서 툴바를 분리시키고 도움말 메뉴항목에서 □DemoMdi 정보(A)"항목을 선택하여 대화상자를 출력한것입니다. 그림 1-1-25에서 화면에 출력되는 모든 형태는 CWnd클래스를 상속받은 클래스로 만들어 진것입니다. ① 의 툴바윈도우나 ②의 View윈도우등등 모든 윈도우들이 근원적으로 보면 CWnd를 상속받았다는 것입니다. 같은 CWnd를 상속받았다 하더라도 그림 1-1-25에서 보듯이 그형태가 다다릅니다. 이렇게 형태가 다르기 때문에 CWnd를 상속받은 새로운 클래스가 만들어지고 이클래스들이 이용되기 때문에 실제 MFC프로그램에서는 CWnd를 직접 사용하는것을 보기가 힘들게 됩니다. CWnd를 상속받은 새로운 클래스를 사용한다고 하더라도 CWnd기능을 모두 상속받았기 때문에 모든 윈도우들은 CWnd의 기능을 그대로 가지고 있습니다. CWnd의 기능리스트는 표 4와 같습니다.

(표4) CWnd의 기능 리스트

기능	내용
Initialization	윈도우를 만들고 이것을 화면에 출력하고 새로운 윈도우에 부모윈도우 또는 자식 윈도우로써 설정하고 윈도우 스타일을 얻거나 받는 기능
Window State Functions	현재 윈도우가 활성화인가 비활성화 인가? 현재 활성화 되어 있는 윈도우는 어떤것인가 현재 윈도우가 마우스 이벤트를 캡처할것인가? 현재 윈도우 스타일을 변경하는 것등 윈도우 상태를 설정하는 기능
Window Size and Position	윈도우 크기를 변경시키고 윈도우 위치를 바꾸는 기능
Window Access Functions	현재 윈도우 다음에 연결되어 있는 윈도우는 어떤것인가? 부모 윈도우는 어떤것인가? 등등 윈도우에 접근하는 기능
Update/Painting Functions	윈도우에 박스,문자,원,도형,비트맵등 그래픽 요소를 출력하는 기능
Coordinate Mapping Functions	스크린 좌표와 윈도우 좌표의 변환등 맵핑을 컨트롤 하는 기능
Window Text Functions	윈도우에 문자를 출력할경우 문자의 속성을 변경하는 기능
Scrolling Functions	윈도우에 스크롤 바를 두고 스크롤바에 의해서 스크롤 위치를 컨트롤 하는 기능
Drag-Drop Functions	마우스를 드래그 와 드롭을 했을경우 실행하는 기능
Caret Functions	Caret란 키보드문자를 입력할때 사용하는 커서임 이커서를 컨트롤 하는 기능
Dialog-Box Item Functions	현재 윈도우에서 대화상자 아이템이 설정되어있을경우 이 아이템들을 컨트롤하는 기능
Menu Functions	윈도우에 메뉴가 설정되었을때 메뉴를 삽입,수정,삭제 등을 하는 기능
ToolTip Functions	풍선 도움말 (마우스가 특정위치에 있을때 마우스아래에 작은 박스가 나타하는 것) 컨트롤 기능
Timer Functions	타이머를 작동하고 타이머에 의해서 이벤트를 발생시키는 기능
Window Message Functions	윈도우에 설정되는 메시지를 얻거나 보낼때 사용하는 기능
Clipboard Functions	클립보드에서 데이터를 얻거나 또는 전송할때 사용하는 기능
OLE Controls	OLE를 사용할수 있도록 컨트롤 하는 기능
Message Handlers	여러 메시지가 전달되었을때 수행되는 해당 메시지 함수들

표4를 보시면 알수 있듯이 윈도우에서 구동되는 모든 기능을 CWnd가 가지고 있습니다. 따라서 윈도우안에 있는 특정기능을 이용하고자 한다면 CWnd를 먼저 참조하는 것이 좋습니다. 예를 들어 본다면, 툴바를 만들때 사용하는 클래스는 CToolBar입니다. 이 툴바를 이동하고자 할경우 도움말에서 CToolBar 의 Class member를 찾아보았자 윈도우를 이동하는 함수는 나타나 있지 않습니다. 이럴때 CToolBar는 CWnd를 상속받았다는것을 생각하고 CWnd의 맴버함수를 찾으시면 됩니다.

뒤에서 다시 CWnd를 상속받은 많은 클래스들을 설명하겠지만 CWnd를 상속받은 클래스는 자신의 특징적인 기능을 맴버함수로 만들어 두고 많은 부분은 CWnd의 기능을 이용하고 있습니다. 여기에서 기억해 둘 말이 있습니다.

□모든 윈도우의 원초적인 클래스는 CWnd!"

<2>CWinThread 클래스

CWinThread클래스는 윈도우가 스레드로 돌아갈수 있도록 구동되는 클래스입니다. 그럼 스레드란 무엇인가? □독립적인 형태로 구동되는 하나의 모듈□ 이렇게 정의할수 있습니다. 독립적인 형태로 구동되는 하나의 모듈이라는 의미는 다른 프로그램과 상관없이 혼자 구동되는 것을 의미합니다. 예를 들어서 윈도우에서 워드프로세서를 구동하여 문서작업을 하면서 화면상단에는 동영상 출력 프로그램을 이용하여 재미있는 영화를 본다고 가정을 합시다. 워드프로세서에서 키보드 입력과 문자 출력과는 상관없이 상단에 있는 동영상 출력프로그램은 독립적으로 영화가 계속적으로 흐르게 됩니다. 즉 동영상 출력프로그램과 워드프로세서는 하나의 독립적인 형태의 스레드이기 때문에 상호 침해를 주지 않습니다. Windows98이나 WindowsNT는 멀티테스킹이 가능합니다. 멀티테스킹이 가능하다는 것은 한개의 프로그램이 독립적인 스레드 형식으로 구동되어 야 한다는 것입니다. 이렇게 한개의 프로그램이 독립적으로 움직이기 위해서 한개의 프로그램은 한개이상의 CWinThread를 포함해야 합니다.

<3>CWinApp 클래스

CWinApp클래스는 CWinThread를 상속받은 클래스입니다. 이클래스가 바로 어플리케이션 클래스 입니다. MFC로 프로그램을 작성할경우 결국 한개의 프로그램을 포함하고 관장하는 클래스가 어플리케이션 클래스라고 하였습니다. 이클래스 이름이 CWinApp입니다. CWinApp가 CWinThread 를 상속받아야 하는 것은 당연합니다. 즉 한개의 프로그램은 다른 프로그램과 상관없이 독립적으로 구동되는 형태로 구성되어야 하기 때문입니다.

<4>CDocument 클래스

CDocument는 도큐먼트를 담당하는 클래스입니다. 데이터를 디스크에서 읽어 들이거나 또는 디스크에 저장을 하는 부분을 담당하는 것입니다. 많은 복잡한 프로그램에서는 데이터를 따로 분리해 두는 것이 필요합니다. 이렇게 분리하여 처리하였을경우 후에 여러모로 편리합니다. 한가지 예를 들어서 음성인식을 하는 프로그램을 작성했다고 가정을 합시다. 가장 중요한것은 음성데이터를 읽어서 그것이 어떤 의미인지 분석하는 알고리듬입니다. 이알고리듬을 따로 도큐먼트 클래스에 저장하는 것이좋습니다. 도큐먼트 클래스는 화면과는 상관이 없으며 많은 단지 두개의 간단한 상위클래스를 상속받았기 때문에 따로 띄어서 다른 프로그램에서 사용하기가 편리하기 때문입니다. 음성인식 프로그램을 처음에는 PC에서 사용할려고 했는데 이알고리듬을 PC가 아니라 핸드폰 같은데 사용하여 □우리집!□ 이렇게 외치면 집으로 전화를 걸게 하고자 할경우 이때 필요한것은 음성데이터를 인식하는 알고리듬 뿐입니다. 도큐먼트 클래스에 따로 만들어 두었다면 분리하여 다른 컴퓨터에 탑재하기가 쉬운데... 만일 알고리듬을 이쪽 저쪽에 분산시켰다면 이때는 알고리듬 소스만 따로 분리하기가 매우 힘들것입니다.

5. CWnd를 상속받은 클래스들

CWnd를 상속받은 윈도우  클래스의 계층적 구조는 그림 1-1-26과 같습니다.

(종이 참조)

(그림 1-1-26)CWnd를 상속받은 윈도우 클래스 계층도

그림 1-1-26을 보면 화면에 보이는 여러 윈도우들이 모두 클래스로 설정되어 있다는 것을 알수 있을것입니다. 이렇게 되어 있기 때문에 화면에 여러 윈도우를 출력하는 것이 쉽습니다. 내가 만일 파일 대화상자를 출력하고자 한다면 CFiledialog라는 클래스를 사용하면 되고 에디터 View를 만들고자 한다면 CView 대신에 CEditView를 사용하면 됩니다. 이런방법으로 자신이 출력하고자 하는 윈도우가 있다면 그 스타일에 맞는 클래스를 사용하면 된다는 것입니다.

<1> 프레임 형태의 윈도우

그림 1-1-26에서 보듯이 프레임 윈도우는 CFrameWnd가 최상위 윈도우이며 하부로 CMDIFrameWnd와 CMDIChildWnd가 있습니다. CFrameWnd는 SDI형 프로그램을 제작하였을때 메인 프레임을 담당하는 윈도우이며 CMDIFrameWnd는 MDI형 프로그램을 제작하였을때 메인 프레임을 담당하는 윈도우 입니다. 같은 메인프레임이면서 두개의 윈도우 클래스가 다른것은 MDI에서의 메인프레임은 SDI에서의 메인 프레임에 새로운 기능이 더추가 되어 있기 때문입니다. MDI 형에서의 메인 프레임은 자식윈도우를 관리하고 정렬하는 기능이 있기 때문입니다.

그림 1-1-27은 각 프레임 윈도우 형태를 보여주고 있습니다.

(종이 참조)

(그림 1-1-27) 프레임 윈도우

<2> 컨트롤 바

컨트롤 바는 보통 프레임 윈도우 자식윈도우로 존재하면서 사용자가 좀더 편리하게 프로그램을 구동할수 있도록 도와주는 도구바들입니다. 그림 1-1-28은 각 컨트롤바의 예 입니다.

(종이 참조)

(그림 1-1-28) 각종 컨트롤 바

그림 1-1-28 ① 과 같은 것을 툴바라고 이야기 합니다. 이툴바는 아이콘 버튼을 가지고 있고 때로는 간단한 에디터박스나 콤보박스를 가지고 있습니다. 이 툴바는 메인프레임에 자식윈도우로 만들어지는것이 일반적이며 메뉴항목과 연계되어서 많이 사용됩니다. 이런 툴바를 만들고자 할때는 CToolBar 클래스를 사용하면 됩니다.

그림 1-1-28의 ② 와 같은 것을 다이얼로그바라고 합니다. 툴바와 같이 메인프레임에 분리 도킹 되는 기능을 가지고 있으면서 안에 대화상자와 같이 여러 자원을 포함하는 도구바를 의미합니다. 이런 도구바를 만들고자 할때는 CDialogBar라 클래스를 사용하면 됩니다. 그림 1-1-28 의 ③과 같은 것을 상태바라고 합니다. 보통 메뉴항목을 선택하였을때 그기능을 간단하게 설명하는 기능및 현재 진행상황과 몇개의 키보드 눌림상태를 표현하는 기능을 가지고 있습니다. 이런 상태바를 만들고자 할때 사용하는 클래스가 CStatusBar입니다. 그림에 나와 있지 않는 컨트롤 바로 CReBar가 있는 데 이것은 그림 1-1-29와 같은 바를 이야기 합니다.

(그림 1-1-29) CReBar를 사용한 컨트롤 바

그림 1-1-29는 인터넷 익스플로어 프로그램의 도구바입니다. 즉 익스플로어 같은 웹브라우저에서 사용하는 도구바 같은것을 만들고자 할경우 이때 CReBar를 사용하시면 됩니다.

<3> 대화상자 윈도우

그림 1-1-26 에서 ④항목이 대화상자를 관리하는 윈도우 클래스들입니다. 보통 대화상자라고 하면 사용사에게 어떤 정보나 명령을 얻기 위해서 따로 독립적으로 출력되는 윈도우를 말합니다. 이런 대화상자중에 일반적으로 사용하는 대화상자들이 있는데 파일대화상자,칼라선택 대화상자,폰트 대화상자 들입니다. 그림 1-1-30은 각종 대화상자를 출력한 형태입니다.

(종이 참조)

(그림 1-1-30) 대화상자

그림 1-1-30의 ③ 과 같은 대화상자를 일반대화상자라고 합니다. 이 일반 대화상자에는 리소스에디터를 이용하여 여러 컨트롤들을 설정할수 있습니다. 즉 그림 1-1-26의 ⑥의 컨트롤 들을 대화상자에 설정할수 있다는 것입니다. 일반 대화상자는 프로그래머가 대화상자가 필요할때 새로 만들때 필요한 것입니다. 이때 사용하는 클래스가 CDialog 입니다. 그외 나머지 윈도우들은 공용 대화상자 입니다.  공용 대화상자란 윈도우 시스템에서 일반적으로 사용하는 대화상자라는 의미입니다. 파일을 열거나 저장할때 그림 1-1-30의 ④와 같은 파일대화상자를 많이 이용하며 색을 설정하고자 할경우 그림 1-1-30의 ② 와 같은 칼라 대화상자를 폰트를 바꾸고자 할경우 그림 ①과 같은 폰트대화상자를 사용하고 프린트 설정시에는 ⑤와 같은 프린트 대화상자를 공통적으로 맣이 사용합니다. 이런것들이 공용대화상자이며 이런 공용 대화상자를 사용할경우에는 그림 1-1-30에 있는것처럼 CFontDialog(폰트 대화상자),CColorDialog(칼라 대화상자),CPrintDialog(프린트 대화상자),CFileDialog(파일 대화상자) 클래스를 사용하시면 됩니다.

<4> View윈도우 클래스

그림 1-1-26의 ⑤ 항목이 View윈도우 클래스입니다. 프레임 윈도우와 함께 템플레이트를 구성하는 윈도우 클래스중의 하나입니다. 그림 1-1-31은 View윈도우들을 보여줍니다.

(종이 참조)

(그림 1-1-31)여러형태의 View 윈도우 클래스

그림 1-1-31에서 ③ 형태의 윈도우는 우리가 이미 만들어 본 윈도우입니다. 즉 흰색 바탕에 아무것도 없는 형태의 윈도우 입니다. 이런 윈도우를 만들고자 할때 CView클래스를 사용합니다. 리치에디터 나 에디터를 가지고 있는 View윈도우는 그림 1-1-31의 ①과 ② 에 해당합니다. 리치에디터는 에디터 보다 많은 기능과 OLE를 지원하는 향상된 에디터 윈도우 입니다. View에 리치 에디터를 포함한 윈도우클래스가 CRichEditView이며 에디터를 포함한 클래스가 CEditView입니다. View안에 리스트를 가진 윈도우도 있습니다. 이것을 리스트 View라고 하면 그림 1-1-31에서 ④에 해당됩니다. 가로축으로 컬럼을 나눌수 있으며 그림과 문자를 포함한 리스트를 만들수 있는 형태의 View입니다. 이런 리스트 View윈도우를 만들수 클래스가 CListView입니다. 그림 1-1-31에서 ⑤가 트리View입니다. 윈도우 탐색기처럼 트리 구조를 가지고 있는 트리 리스트 컨트롤을 포함한 View를 트리 View라고 합니다. 이런 View를 만들기 위해서 필요한 클래스가 CTreeView입니다. 그림 1-1-31dml ⑥은 FormView입니다. FormView는 View윈도우에 대화상자와 같은 기능을 포함한 View라고 볼수 있씁니다. 그림  1-1-26에서 ⑥의 컨트롤들을 포함할수 있는 형태의 윈도우를 이야기 합니다. 이 FormView를 만들고자 할경우 사요하는 클래스가 CFormView입니다. CFormView는 CScrollView를 상속받았습니다. 그렇기 때문에 유효한 윈도우 영역이 감추어지면 스크롤바가 나타납니다. 그림 1-1-31의 ⑥에서 우측과 하단부에 스크롤 바가 설치된것을 보실수 있을것입니다. CScrollView클래스를 사용하면 그림 1-1-31의 ③과 같은 형태에 ⑥의 우측과 하단부의 스크롤바가 합친 형태의 윈도우가 나타납니다. 이외에도 CFormView를 상속받아 컨트롤들을 설치할수 있으며 데이터베이스와 연결할수 있는 CRecordView와 CDaoRecordView가 있습니다. 그림 1-1-26의 CWnd의 전체 구조도와 비교해 보면서 이해하시기를 바랍니다.

<5> 컨트롤 클래스

그림 1-1-26의 ⑥에 해당하는 컨트롤 클래스들은 혼자서 독립적으로 출력되는 윈도우들이 아니라 대화상자나 또는 FormView 에 자원으로 삽입되어 출력되는 클래스들입니다. 각 윈도우 스타일은 그림 1-1-32와 같습니다.

(종이 참조)

(그림 1-1-32) 컨트롤 클래스들

그림 1-1-32에 보이는 각 윈도우들은 보통 대화상자 윈도우나 FormView윈도우들이 포함하는 형태로 나타납니다. 쉽게 생각한다면 이런 윈도우들은 자식윈도우로 존재할수밖에 없는 그런 윈도우들을 컨트롤이라고 생각하시는 것이 편리합니다.

그림 1-1-32에 출력되어 있는 10개의 윈도우들은 형태만 보아도 알수 어떤 윈도우인지 알수 있을것입니다. 이런 윈도우 들을 만들고자 할경우 그림 위에 기록한 클래스 명을 사용하시면 됩니다. 예를 들어 대화상자에 트리 컨트롤을 설정하고 이트리 컨트롤을 클래스와 연계하고자 한다면 CTreeCtrl를 사용하면 됩니다.

6. MFC 로 작성한 프로그램과 클래스의 연결

지금까지 MFC로 SDI형태나 MDI형태로 프로그램을 작성하였을때 구성되는 객체에 대해서 설명하였고 MFC클래스의 계층적 구조에 대해서 설명하였으며 CWnd를 상속받은 여러 윈도우 클래스에 대해서 설명하였습니다. 이제 이것들을 통합하여 SDI형태로 만든 프로그램과 MDI형태로 만든 프로그램이 사용하는 클래스들이 어떻게 연계되어 있는가에 대해서 설명하겠습니다. 이부분은 매우 중요한 부분입니다. 본항목 만큼은 꼭 이해를 하시기 바랍니다.

<1> SDI 형태의 프로그램 구조와 연결 클래스

그림 1-1-33은 SDI형태의 프로그램과 각 객체에 해당하는 클래스를 연결시킨것입니다.

(종이 참조)

(그림 1-1-33)SDI형태의 프로그램을 제작할때 필요한 클래스

그림 1-1-33에서 보면 알수 있듯이 SDI 형태의 프로그램을 작성할때는 4개의 클래스가 필요합니다. CWinApp,CFrameWnd,CView,CDocument 가 바로 이것입니다.

여기에서 이 클래스들을 그대로 사용하는 것이 아니라 상속받아서 사용합니다. 예를 들어서 DemoSdi라는 프로그램으로 제작하였을 경우 표5와 같이 새로운 이름으로 상속받아서 사용하게 됩니다.

(표 5) DemoSdi라는 프로그램을 SDI형으로 제작하였을경우

사용되는 클래스	Base MFC 클래스	만들어 지는 파일명
CDemoSdiApp	CWinApp	DemoSdi.h ,DemoSdi.cpp
CMainFrame	CFrameWnd	MainFrm.h, MainFrm.cpp
CDemoSdiView	CView	DemoSdiView.h DemoSdiView.cpp
CDemoSdiDoc	CDocument	DemoSdiDoc.h

예를 들어서 CFrameWnd를 사용하여 프레임 윈도우를 만든다면 CFrameWnd를 상속받은 또하나의 CMainFrame를 만들고 이것을 사용하는 것입니다. 이렇게 상속받았을대 CMainFrame의 클래스 정의는 MainFrm.h에 소스는 MainFrm.cpp에 설정하게 됩니다. 본 Chapter에서 작성한 DemoSdi 프로젝트에서 MainFrm.h와 MainFrm.cpp를 열어 보면 다음과 같은 내용들이 있을것입니다.

헤더 부분 (MainFrm.h)

class CMainFrame : public CFrameWnd <--CFrameWnd를 상속받음

{

protected: // create from serialization only

CMainFrame();

DECLARE_DYNCREATE(CMainFrame)

:

}

소스 부분 (MainFrm.h)

:

CMainFrame::CMainFrame()

{

// TODO: add member initialization code here

}

CMainFrame::~CMainFrame()

{

}

:

즉 SDI형태의 프로그램을 작성하기 위해서 표 5와 같이 MFC 클래스 4개를 상속받은 새로운 클래스들을 만들어 놓고 이클래스를 수정할수 있겠하도록 기본적인 맴버 함수을 표5에서 보듯이 각각의 파일에 만들어 놓은것입니다.

그림 1-1-33을 AppWizard에 의해서 새로 만들어진 클래스 형태로 본다면 그림 1-1-34와 같게 됩니다.

(종이 참조)

(그림 1-1-34)DemoSdi 프로그램을 만들때 사용되는 클래스

<2> MDI 형태의 프로그램 구조와 연결 클래스

그림 1-1-35는 MDI 프로그램을 만들었을경우 이때 사용되는 MFC클래스와 이것을 상속받아 실제로 만들어진 클래스를 나타낸것입니다.

(종이 참조)

(그림 1-1-35)DemoMdi 프로그램과 클래스와의관계

그림 1-1-35에서 보듯이 AppWizard를 이용하여 MDI프로그램을 제작하였을경우 CWinapp,CMDIFrameWnd,CMDIChildWnd,CView,CDocument 5개의 MFC 클래스를 사용합니다. SDI형태에서 설명하였듯이 이클래스를 그대로 사용하지 않고 모두 상속받아서 CDemoMdiApp,CMainFrame,CChildFrame,CDemoMdiView,CDemoMidDoc클래스로 사용되는 것입니다. 표6은 상속받은 클래스와 해당 파일관계 리스트입니다.

(표 6) DemoMdi라는 프로그램을 MDI형으로 제작하였을경우

사용되는 클래스	Base MFC 클래스	만들어 지는 파일명
CDemoMdiApp	CWinApp	DemoSdi.h ,DemoSdi.cpp
CMainFrame	CMdiFrameWnd	MainFrm.h, MainFrm.cpp
CChildFrame	CMDIChildWnd	ChildFrm.h ChildFrm.cpp
CDemoMdiView	CView	DemoMdiView.h DemoMdiView.cpp
CDemoMdiDoc	CDocument	DemoMdiDoc.h DemoMdiDoc.cpp

지금까지의 설명으로 프로그램을 직접 제작할수는 없으나 어느 클래스를 수정해야 한는가를 알수가 있습니다. 예를 들어 메인 프레임을 수정해야 하거나 또는 툴바 상태바등을 컨트롤 하고자 하다면 어떻게 할지는 몰라도 CMainFrame 클래슬 수정해야 한다는 것을 알수 있습니다. 그리고 수정을 해야 할때는 소스파일중 MainFrm.h와 MainFrm.cpp를 수정해야 한다는것도 알수 있는것입니다. 같은 방법으로 만일 화면에 문자를 출력하고자 한다면 어떻게 하는 방법을 알지 못해도 CDemoMdiView를 수정해야 하고 이때 수정할 파일이 DemoMdiView.h와 DemoMdiView.cpp이라는 것을 알수가 있습니다.

7. 자동으로 만들어진 프로그램 분석

지금까지 설명으로 MFC로 제작하는 SDI프로그램과 MDI프로그램이 사용하는 MFC클래스를 알아보았고 또한 이클래스를 상속받아서 새로만들어지는 클래스 이름들을 알아보았습니다. 이제 AppWizard를 이용하여 자동으로 만들어진 클래스들을 분석해 보겠습니다. 본항목도 SDI형태와 MDI 형태를 따로 분석해 보겠습니다.

<1> SDI 형태의 프로그램 소스 분석

본 Chapter에서 제작한 DemoSdi프로그램의 순서도를 도식화 한다면 그림 1-1-36과 같을 것입니다.

(종이 참조)

(그림 1-1-36)DemoSdi 프로그램이 제작되는 도표

그림 1-1-36을 보시면 알수 있듯이 순서도 형태라고 하여도 순서도라고 표현하기가 조금 부적당한 형태인것을 알수 있을것입니다. 실제로 윈도우즈 프로그램을 순서도로 표현한다는것은 매우 어렵습니다. 그이유는 윈도우 프로그램이 순차적으로 진행되는 것이 아니라 한꺼번에 동시적으로 수행되기 때문입니다.

프로그램이 실행되면 제일먼저 CDemoSdiApp클래스가 가동됩니다. CDemoSdiApp클래스가 가동되면 기본적인 초기화루틴은 상속받은 CWinApp클래스에 의해서 자동으로 설정되고 CDemoSdiApp클래스에서는 InitInstance()함수에서 그림 1-1-36의 ①,②,③,④ 항목이 실행됩니다. 제일먼저 프로그램 초기화가 시작되는데 이때에는 윈도우 시스템에 자신의 프로그램을 등록하며 여러 기타 정보들을 초기화 합니다. 그림 1-1-13 즉 AppWizard Step4에서 3D controls를 초기화 하였다면 이것을 초기화 하는 함수를 실행시키고 다음으로 템플레이트를 만듭니다.  템플레이트가 만들어지는 형태를 참조하시려면 그림 1-1-4를 다시 확인해 보시기 바랍니다. 이렇게 템플레이트를 만들때 AppWizard에 의해서 만들어진 자원이 함께 결합됩니다. 결합되는 자원은 메뉴와 아이콘 문자열 테이블 등입니다. 그림 1-1-36의 내부적기능의 ⓐ,ⓑ,ⓒ등을 로드하여 템플레이트를 만들때 함께 결합하는 것입니다. DemoSdi프로그램을 실행시켜 보시면 아시겠지만 자동적으로 만들어진 메뉴와 아이콘 그리고 캡션바에 출력되는 문자데이터등을 보셨을것입니다. 이런것들을 AppWizard에서 자동으로 만들어 준다는 것입니다. 이렇게 만든 템플레이트를 어플리케이션 클래스에 등록합니다. 이렇게 등록함과 동시에 View윈도우 클래스인 CDemoSdiView와 도큐먼트인  CDemoSdiDoc 그리고 프레임 윈도우인 CMainFrame의 내부적 작업이 동시적으로 실행됩니다. 즉 한개의 템플레이트 형태로 묶어지기 위해서 필요한 여러작업들이 내부적으로 수행된다는 의미입니다. 이렇게 하여 템플레이트를 만든다음 쉘 정보를 초기화 합니다. 쉘정보를 초기화 한다는 의미는 이 어플리케이션 프로그램이 다른 프로그램과 통신을 할수 있도록 창구를 만드는일과 (이것을 DDE 수행을 가능하게 한다고 합니다) 또한 쉘에서 프로그램을 구동하였을대 넘겨받는 인자를 얻는 작업을 수행하는 것입니다.

이의미는 프로그램을 콘솔 창에서 수행할때 이프로그램과 한께 들어오는 명령커맨드 문자열을 받아서 처리한다는 의미입니다. 예를 들어서 콘솔창(보통 도스창이라고 하죠)에서 다음과 같이 하여 DemoSdi프로그램을 실행시켰다고 할경우

C:\>DemoSdi  test.dat

test.dat라는 인자가 명령 문자열로 들어오게 됩니다. 이렇게 들어오는 명령문자열을 얻고 처리하는 부분이 그림 1-1-36의 ④ 의 어플리케이션 초기화 인것입니다.

이렇게 한후 메인프레임을 만듭니다. 어플리케이션 클래스 즉 CDemoSdiApp안에는 맴버 변수로 m_pMainWnd가 있는데 이것이 CFrameWnd 형태의 변수입니다. SDI에서는 m_pMainWnd가 템플레이트에 설정된 메인프레임과 같습니다. 이 메인 프레임을 만든다는 것입니다. 이렇게 만들경우 CMainFrame클래스의 OnCreate() 함수가 실행되면서 툴바를 만들고 프레임과 도킹,분리될수 있도록 하며 상태바를 만들어서 프레임 하단부에 붙이게 합니다. 즉 그림 1-1-36의 ⑤,⑥의 기능이 CMainFrame의 OnCreate 함수에서 실행되는 것입니다. 메인 프레임이 로드될때 이때 AppWizard에 의해서 만들어진 툴바 비트맵이 로드되고 이비트맵을 이용하여 툴바를 만들게 됩니다. ③항목과 ⑤,⑥항목이 수행될때 동시적으로 CDemoSdiView와 CDemoSdiDoc에서 ⑦,⑧,⑨ 항목이 실행이 됩니다. 어떻게 보면 매우 복잡한 형태로 프로그램이 만들어 지는것 같이 느끼면서 어떻게 보면 매우 쉽게 만들어지는 느낌을 가진것입니다. (필자는 이 상태를 AppWizard에 의해서 단순성의 탈을쓴 복잡함 이라고 합니다)

(1)CDemoSdiApp의 InitInstance()함수 분석

그림 1-1-36에서 ①,②,③,④ 가 모두 CDemoSdiApp의 InitInstance함수에서 수행된다고 하였습니다. 이제 이 소스를 보겠습니다.

(소스는 폰트를 약간 작게)

BOOL CDemoSdiApp::InitInstance()

{

AfxEnableControlContainer();<--

#ifdef _AFXDLL <--

Enable3dControls();

#else

Enable3dControlsStatic();	// Call this when linking to MFC statically

#endif

//레지스트리에 프로그램 이름을 등록한다.

SetRegistryKey(_T("Local AppWizard-Generated Applications"));

//자신의 프로파일을 로드한다. 자신의 프로그램의 INI가 있다면 이것이 로드됨

LoadStdProfileSettings();  // Load standard INI file options (including MRU)

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CDemoSdiDoc),

RUNTIME_CLASS(CMainFrame),       // main SDI frame window

RUNTIME_CLASS(CDemoSdiView));

AddDocTemplate(pDocTemplate);

// Enable DDE Execute open

EnableShellOpen();

RegisterShellFileTypes(TRUE);

// Parse command line for standard shell commands, DDE, file open

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line

if (!ProcessShellCommand(cmdInfo))

return FALSE;

// The one and only window has been initialized, so show and update it.

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();

// Enable drag/drop open

m_pMainWnd->DragAcceptFiles();

return TRUE;

}

(소스끝)

그림 1-1-36과 비교하면서 소스를 보시면 대략적으로 어떤 내용인지 이해가 되실것입니다. 중요 부분만 다시 설명을 하겠습니다. 먼저 템플레이트를 만드는 부분입니다. 템플레이트를 관장하는 클래스가 CSingleDocTemplate 클래스입니다. 이클래스의 변수를 pDocTemplate 로 만든것을 소스에서 보셨을것입니다. 이때 생성자에 4개의 인자를 설정합니다. 한개의 자원의 아이디 이며 나머지 3개는 도큐먼트,View, 프레임 클래스 입니다. 먼저 자원 아이디 IDR_MAINFRAME 가 무엇인가를 설명하겟습니다.

IDR_MAINFRAME 그림 1-1-36에서 내부적기능으로 만들어진 ⓐ,ⓑ,ⓒ의 아이디 입니다. Visual C++를 구동하였을때 그림 1-1-37 과 같은 윈도우를 볼수 있을것입니다.이 윈도우를 Project Workspace Window라고 합니다. 이 윈도우에서 항목을 Resource View를 선택하시면 그림 1-1-37과 같은 형태가 나타납니다.

(종이 참조)

(그림 1-1-37)IDR_MAINFRAME 를 가진 메뉴,문자열,아이콘

그림 1-1-37의 ①이 프로젝트 워크스페이스 윈도우 (Project WorkSpace Window)라고 합니다. 이윈도우에서 ② 버튼을 클릭하면 ⑦번과 같은 화면이 나타납니다. 이화면은 현재 프로그램에 설정되어 있는 자원 리스트를 보여주는 화면입니다. 그림 1-1-37에서 보듯이 메뉴,아이콘의 아이디중 IDR_MAINFRAME 라는 같은 이름을 가진 자원이 있을것입니다. 그리고 String Table에서 또한 IDR_MAINFRAME라는 아이디가 있을것입니다. 이 CSingleDocTemplate 생성자 함수에서 처음 넘겨주는 인자의 IDR_MAINFRAME란 바로 이 메뉴,아이콘과 문자열 3개를 포함한 아이디 인것입니다. DemoSdi의 아이콘을 바꾸고자 한다면 IDR_MAINFRAME이름을 가진 아이콘을 변경하면 되고 메뉴를 수정하고자 한다면 IDR_MAINFRAME 이름을 가진 메뉴를 수정하면 됩니다. 문자열 IDR_MAINFRAME는 AppWizard의 Step 4에서 Advanced Opriton항목을 선택하였을때 나타나는 그림 1-1-15와 같은 상태에서 입력한 여러 문자열을 결합한것입니다. AppWizard를 수행할때 이 항목에서 잘못입력하였다면 문자열 IDR_MAINFRAME를 수정하시면 됩니다.

CSingleDocTemplate 의 두번째 인자부터  도큐먼트,프레임,View 클래스 이름을 넘겨줍니다. 그런데 그냥 넘겨주는 것이 아니라 RUNTIME_CLASS 라는 매크로를 사용하였습니다. 이매크로는 현재 클래스를 CObject의 기본 클래스 형태를 동반하면서 각종 정보를 가지고 있는 CRuntimeClass클래스로 전환합니다. 이렇게 전환하여 설정한 객체를 CSingleDocTemplate에 설정한 것입니다.

이와 같이 설정하여 템플레이트를 만든다음 AddDocTemplate 템플레이트 함수를 이용하여 템플레이트를 어클리케이션 클래스에 등록한것입니다.

이렇게 하여 설정된 템플레이트가 완벽하게 구동되고 결합되는 순간은 ProcessShellCommand 함수가 실행되는 순간입니다. 이함수가 실행될때 그림 1-1-36에서 CMainFrame와 CDemoSdiView그리고 CDemoSdiDoc의 각종 정보들이 초기화 되는 것입니다. 이모든 준비가 끝난후 메인 프레임 윈도우를 가지고 있는 m_pMainWnd의 멤버 함수 ShowWindow 함수와 UpdateWindow 윈도우 함수가 실행되면서 실제 윈도우가 출력됩니다. 그리고 DragAcceptFiles 이라는 함수를 설정하여서 마우스를 이용하여 윈도우 탐색기등에서 파일을 선택하여 본프로그램에 설정하였을때 □파일□ 메뉴의 □열기□ 항목이 실행되는 것처럼 한것입니다.

(2)CMainFrame의 OnCreate()함수 분석

그림 1-1-36에서 ⑤,⑥ 항목은 CMainFrame의 OnCreate함수에서 실행됩니다. 이함수의 소스는 다음과 같습니다.

(프로그램 소스)

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

//CFrameWnd의 OnCreate함수를 호출하여 기본적인 내용을 수행하도록 한다.

if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

return -1;

//툴바를 만든다 그림 1-1-36의 ⑤항목

if (!m_wndToolBar.CreateEx(this,

TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP

| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |

CBRS_SIZE_DYNAMIC) ||

!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

TRACE0("Failed to create toolbar\n");

return -1;      // fail to create

}

//상태바를 만든다  그림 1-1-36의 ⑥항목

if (!m_wndStatusBar.Create(this) ||

!m_wndStatusBar.SetIndicators(indicators,

sizeof(indicators)/sizeof(UINT)))

{

TRACE0("Failed to create status bar\n");

return -1;      // fail to create

}

//분리 도킹을 가능하게 한다. 그림 1-1-37의 ⑤항목

m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

EnableDocking(CBRS_ALIGN_ANY);

DockControlBar(&m_wndToolBar);

return 0;

}

(프로그램 소스끝)

위의 소스에 대한 함수 사용의 자세한 내용은 본책을 계속 공부하시면서 공부하시게 될것입니다. 본장에서는 자동으로 만들어진 소스가 어떻게 된것인가를 이해하는 것이 중요합니다.

그림 1-1-36의 ⑦,⑧,⑨등은 내부적으로 설정됩니다. 실제 소스에서는 이부분이 어떻게 되는 것인가를 알수는 없습니다. 중요한것은 그림 1-1-36이 구동되는 전반적인 루틴과 CDemoSdiApp의 InitInstance 함수와 CMainFrame의 OnCrate 함수에 코딩된 내용입니다.

<2> MDI 형태의 프로그램 소스 분석

MDI형태로 제작된 CDemoMdi 프로그램 구동 방법과 소스를 분석해 보겠습니다. 전체적으로 설정되는 형태는 그림 1-1-38과 같습니다.

(종이 참조)

(그림 1-1-38)DemoMdi 프로그램이 제작되는 도표

그림 1-1-38을 보면 SDI형태와 다른 몇가지가 나타납니다. 차일드 프레임(CChildFrame) 와 View윈도우(CDemoMdiView) 도큐먼트(CDemoMdiDoc) 3개의 클래스가 템플레이트로 설정되고 메인프레임은 따로 설정된다는 것입니다. AppWizard로 MDI형태의 프로그램을 만들경우에는 메인프레임용 메뉴,아이콘,문자열을 만들고 (아이디가 IDR_MAINFRAME) 이것은 메인프레임이 로드될때 (그림 1-1-38의 ④) 사용되면 템플레이트에서 사용되는 메뉴,아이콘,문자열은 ( 프로젝트명이 DemoMdi일경우 IDR_DEMOMDTYPE) 템플레이트를 만들때 사용합니다. ② 항목에서 기본적인 정보를 미리 설정되어 있는 상황에서  그림 1-1-38의 ⑤ 항목 즉 어플리케이션 쉘이 초기화 될때 차일드 프레임 View 윈도우 도큐먼트가 결합되어 하나의 템플레이트로 형성합니다. 어플리케이션 클래스 즉 CDemoMdiApp클래스에서 설정되는 기능 (그림 1-1-38의 ①~⑥ 까지의 항목)은 CDemoMdiApp의 InitInstance 함수에서 발생디면 CMainFrame에서 발생되는 ⑦과 ⑧ 항목은 CMainFrame의 OnCreate에서 발생됩니다. CMainFrame의 OnCreate함수의 내용은 SDI형태의 프로그램의 내용과 같습니다. 전에 설명한 DemoSdi의 OnCreate함수를 참조하시기 바랍니다.

(1)CDemoMdiApp의 InitInstance분석

DemoMdi 프로젝트를 오픈하고 그림 1-1-37의 ①과 같은 윈도우 (프로젝트 워크스페이스 윈도우) 에서 CDemoMdiApp 를 선택한후에 앞의 + 표시의 버튼을 클릭하면  InitInstance()함수가 나타납니다. 이함수를 클릭하면 새로운 윈도우가 나타나면서 해당되는 내용의 소스가 보일것입니다. 다음은 CDemoMdiApp의 InitInstance 의 소스와 해설입니다.

(프로그램 소스)

BOOL CDemoMdiApp::InitInstance()

{

//Active X컨트롤을 사용할수 있게 초기화 AppWziard step3 항목 참조

AfxEnableControlContainer();

//3d 옵션을 초기화 AppWizard step4항목 참조

#ifdef _AFXDLL

Enable3dControls();

#else

Enable3dControlsStatic();

#endif

SetRegistryKey(_T("Local AppWizard-Generated Applications"));

LoadStdProfileSettings();  // Load standard INI file options (including MRU)

//템플레이트를 만든다 이때 사용하는 클래스는 CMyltiDocTemplate

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(

IDR_DEMOMDTYPE,

RUNTIME_CLASS(CDemoMdiDoc),

RUNTIME_CLASS(CChildFrame), // custom MDI child frame

RUNTIME_CLASS(CDemoMdiView));

//템플레이트를 등록한다.

AddDocTemplate(pDocTemplate);

// 메인프레임을 만들고 이것을 로드한다.

CMainFrame* pMainFrame = new CMainFrame;

if (!pMainFrame->LoadFrame(IDR_MAINFRAME))

return FALSE;

m_pMainWnd = pMainFrame;

// Parse command line for standard shell commands, DDE, file open

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

// 본항목이 실행되면서 템플레이트가 서로 연결되고 만들어진다.

if (!ProcessShellCommand(cmdInfo))

return FALSE;

// The main window has been initialized, so show and update it.

pMainFrame->ShowWindow(m_nCmdShow);

pMainFrame->UpdateWindow();

return TRUE;

}

(프로그램 소스끝)

MDI에서는 템플레이트를 만들때 CMultiDocTemplate 를 사용합니다. 이것이 SDI와 차이점입니다. CMultiDocTemplate 의 생성자 함수의 첫번째 인자 즉 아이콘과 메뉴 그리고 문자열을 설정하는 부분에서 아이디를 IDR_DEMOMDTYPE 로 사용한것을 보셨을것입니다. 이것은 그림 1-1-38의 ⓔ~□ 를 보시면 알수 있을것입니다. 즉 메인프레임에 해당하는 메뉴,아이콘,문자열은 그림 1-1-38의 ④항목에서 사용됩니다.(아이디가 IDR_MAINFRAME인것) 그리고 차일드 윈도우에서 사용되는 메뉴,아이콘,문자열은 IDR_DEMOMDTYPE 의 아이디를 사용하는 것입니다. 쉽게 생각한다면 MDI는 여러개의 SDI 결합이라고 생각하면 이해가 빠를것입니다. 이렇게 하여 모든 템플레이트를 만들고 ProcessShellCommand 에의해서 각 템플레이트 클래스를 메모리에 할당하고 메인 윈도우가 화면에 출력될때 차일드 윈도우도 함께 나타납니다. 바로 이순간에 동시적으로 그림 1-1-38의 ⑨~ⓓ 까지의 항목이 수행됩니다.

8. 프로젝트 워크 스페이스

지금까지 장황하게 달려온 내용을 요약한다면 AppWizard를 이용하여 프로젝트를 만들면 템플레이트를 구성하고 해당되는 클래스를 만들면 (SDI는 4개 MDI는 5개) 그리고 이 클래스들은 헤더와 소스파일 설정되어 있다고 하였습니다. 그리고 템플레이트가 결합되어 화면에 출력될때 각 템플레이트에 필요한 자원 (메뉴,아이콘,문자열) 들이 함께 결합된다고 설명하였습니다. 프로젝트가 만들어지고 난후 크게 3가지로 나누어져 설정됩니다.

①MFC 클래스를 상속받아 탄생된 새로운 클래스

②클래스 소스가 설정되어 있는 파일들 (소스파일 cpp, 헤더 파일 h)

③프로그램에 필요한 메뉴,아이콘,문자열,대화상자 같은 자원

지금까지 제작하고 분석한 DemoMdi프로그램을 가지고 설명한다면 DemoMdi에서는 CMainFrame,CChildFrame,CDemoMdiView,CDemoMdiDoc,CDemoMidApp 의 5개의 클래스가 설정되며 이에 해당하는 파일들이  MainFrm.h,MainFrm.cpp,ChildFrm.h,ChildFrm.cpp.... 등등으로 만들어지고 IDR_MAINFRAME로 아이콘,메뉴,문자열이 IDR_DEMOMDTYPE로 아이콘,메뉴,문자열이 만들어 집니다. 이렇게 보았을때 우리가 클래스로 분류하여 프로젝트의 내용을 보고 싶을 경우가 있으며 파일로 분류하여 프로젝트 내용을 보고자 할경우가 있고 또한 프로젝트에 포함되어 있는 자원을 보고자 할경우가 있을것입니다.여기에 부과적으로 도움말 기능까지 포함된다면 더이상 바랄게 없겠죠. 바로 이렇게 각 분류에 따라 프로젝트의 내용을 볼수 있도록 설정한것이 프로젝트 워크 스페이스 윈도우 입니다. 전에 프로젝트 워크스페이스 윈도우에 대해서 언급을 약간 했습니다만 본항목에서 더욱 자세하게 설명하겠습니다. 프로젝트 워크스페이는 그림 1-1-37의 ① 과 같은 것으로 총 4개의 항목으로 구성되어 있습니다. 표7은 4개의 항목에 대한 설명입니다.

(표 7) 프로젝트 워크 스페이스의 항목별 설명

항목	내용
ClassView	프로젝트에 설정되어 있는 클래스 별로 출력하고 해당 항목을 선택하면 수정할수 있다.
ResourceView	프로젝트에 설정되어 있는 메뉴,대화상자,문자열,아이콘 비트맵등 자원의 리스트를 출력하고 해당 항목을 선택하면 수정할수 있다.
FileView	프로젝트에 설정되어 있는 파일 리스트를 출력하고 해당항목을 선택하면 수정할수 있다.
InfoView	Visual C++의 도움말이 출력된다.

본프로젝트 워크스페이스에 대한 설명은 그림을 보시면서 이해가 빠르실것입니다.

<1> ClassView

그림 1-1-39는 프로젝트 워크스페이스에서 ClassView항목을 선택하였을때 화면입니다.

(종이 참조)

(그림 1-1-39) 프로젝트 워크 스페이스에서 ClassView항목을 선택하였을때 화면

그램 1-1-39처럼 프로젝트 워크스페이스에서 ClassView항목을 선택하면 현재 프로젝트에 설정되어 있는 클래스 리스트들이 나옵니다. 이때 특정 클래스를 선택하고 좌측 마우스 버튼을 더블 클릭하면 클래스의 헤더로 이동하게 됩니다. 그림 1-1-39의 ①항목처럼 CChildFrame를 선택하고 있는 상태에서 좌측 마우스 버튼을 더블클릭하면 ChildFrm.h 파일이 열어지면서 해당 클래스를 수정할수 있는 형태가 되는 것입니다. 그림 1-1-39의 ①과 같은 상태에서 우측 마우스 버튼을 클릭하면 ⑥과 같은 팝업 메뉴가 설정됩니다. 이설정된 메뉴에서 Add Member Function 이나 Add Member Function을 선택하면 해당 클래스에 새로운 맴버변수와 맴버 함수를 삽입할수 있습니다. 그외 현재 클래스에서 수정하고자 하는 여러 항목들이 나타나게 됩니다. 본 팝업 메뉴를 사용하는 법은 뒤에 자주 설명하게 될것입니다. ClassView 항목을 선택하면 처음에서는 그림 1-1-39의 ②와 같이 클래스앞에 십자형 버튼이 설정됩니다. 이버튼을 클릭하게 되면 해당 클래스에 설정되어 있는 멤버 함수와 변수 리스트가 출력됩니다. 그림 1-1-39의 ③항목이 십자형 버튼을 클릭했을때의 모습입니다. 이상태에서 원하는 클래스 맴버를 선택하고 더블클릭했을때 해당 항목이 설정되어 있는 소스 파일로 이동하게 됩니다. 멤버 변수를 설정하였을때는 클래스 헤더로 멤버 함수를 선택하였을때는 클래스 소스로 이동하는 것입니다. 그림 1-1-39에서 보면 알수 있듯이 멤버 변수중에 열쇠 키가 설정되어 있는 것이 있을것입니다. (그림 1-1-39의 ④와 를 보세요) 이것은 멤버 변수가 프로텍트 형태로 설정되어 있다는 의미입니다. 이 열쇠키가 없는 멤버 들은 public로 설정되어 있다는 의미입니다. 앞으로 프로그램을 작성하면서 이 ClassView를 매우 많이 사용하게 될것입니다.

<2> ResourceView

그림 1-1-40은 프로젝트 워크스페이스에서 ResoruceView항목을 선택했을때의 화면입니다.

(종이 참조)

(그림 1-1-40) 프로젝트 워크 스페이스에서 ResourceView항목을 선택하였을때 화면

그림 1-1-40에서 보듯이 ResourceView항목을 클릭하게 되면 엑셀레이터,메뉴,대화상자,아이콘,문자열테이블,툴바 그림 등이 설정되어 있습니다. Resource란 윈도우즈 프로그램을 만들때 필요한 여러 자원을 의미합니다. 엑셀레이터란 프로그램 항목을 핫키로 설정하였을때 해당하는 핫키를 정의하는 곳이며 대화상자란 대화상자를 구동할때 어떤 형태의 대화상자를 출력할 폼을 만들어서 저장하는 것이며 메뉴,아이콘,문자열 테이블 등 또한 프로그램을 구동할때 필요한 여러 자원에 해당합니다. 이런 자원을 만들거나 수정할경우 본 항목을 클릭하고 해당항목을 선택하면 해당항목을 수정할수 있는 형태의 윈도우가 나타납니다. 예를 들어 대화상자의 특정 자원을 선택하면 해당 대화상자를 수정할수 있는 형태로 출력되며 메뉴를 선택하면 메뉴를 수정할수 있는 형태로 출력이 되는 것입니다.

<3> FileView

그림 1-1-41은 프로젝트 워크 스페이스에서 FileView항목을 선택하였을때의 그림입니다.

(종이 참조)

(그림 1-1-41)프로젝트 워크 스페이스에서 FileView항목을 선택하였을때 화면

프로젝트 워크 스페이스윈도우에서 File View를 선택하게되면 현재 프로젝트에 설정되어있는 소스파일,헤더파일,자원 파일들이 그대로 출력됩니다. 이 파일들을 선택하면 소스파일들은 에디터 형식으로 자원 파일은 해당 자원을 수정할수 있는 형태로 출력됩니다.

(잔소리)

본항목 이후 부터는 고급 프로그래머들이 읽어 보는 항목입니다. Visual C++의 초보자 수준이면 본항목까지만 읽어보셔도 됩니다. 본항목 이후의 내용은 읽었을때 이해가 되지 않아도 상관은 없습니다. 본항목 이후내용을 이해하지 않아도 프로그램을 제작하는데 큰 어려움이 없기 때문입니다. 필자는 본항목 이후부터의 내용을 기록하고 싶지는 않았습니다. 그이유는 MFC 내부 분석하는 시간에 알고리듬을 더 공부하는게 여러분에게 도움이 될것 같은 생각 때문입니다. 그런데도 기록하는 이유는 두가지 입니다. 첫번째는 필자가 알고 있는것은 몽땅 쓰자는 생각입니다. 이렇게 하여 여러분에게 다소 도움이 되기를 바라는 것이고 두번째는 내부를 알면서 프로그램 구조에 대한 지식을 얻기를 바라기 때문입니다. MFC 구조는 매우 정교하기 때문에 그 구조를 보면 객체지향 프로그래밍에서는 구조를 어떻게 해나간다는 생각을 가질수 있기 때문입니다. 그러나 다음 내용을 통해서 MFC내부에 몰두하여 시간을 허비하고 내부의 특이기능을 조금 알고 있다고 자신이 프로그래머로써 굉장한 수준에 있다고 생각하는 착각에 빠지지 않기를 바랍니다.

(잔소리끝)

9. MFC 프로그램의 내부 분석

SDK를 이용하여 프로그램을 한다면 WinMain함수와 WndProc와 같은 메시지처리 함수 즉 프로시저 함수의 쌍으로 이루어져 있습니다. Win32 SDK를 이용하여 윈도우를 출력하는 프로그램을 작성한다면 다음과 같습니다.

(프로그램 소스)

#include <windows.h>

LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,

PSTR szCmdLine,int iCmdShow)

{

static char szAppName[] = "ExSdk";

HWND    hwnd;

MSG             msg;

WNDCLASSEX      wndclass;

wndclass.cbSize         = sizeof (wndclass);

wndclass.style          = CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc        = WndProc;

wndclass.cbClsExtra             = 0;

wndclass.cbWndExtra             = 0;

wndclass.hInstance              = hInstance;

wndclass.hIcon =  LoadIcon (NULL,IDI_APPLICATION);

wndclass.hCursor                = LoadCursor (NULL,IDC_ARROW);

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

wndclass.lpszMenuName = NULL;

wndclass.lpszClassName = szAppName;

wndclass.hIconSm                = LoadIcon (NULL,IDI_APPLICATION);

RegisterClassEx (&wndclass);

hwnd = CreateWindow (szAppName,

"ExSdk",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

NULL,

hInstance,

NULL);

ShowWindow (hwnd, iCmdShow);

UpdateWindow (hwnd);

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg);

DispatchMessage (&msg);

}

return msg.wParam;

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam,

LPARAM lParam)

{

HDC             hdc;

PAINTSTRUCT  ps;

char temp[80];

switch (iMsg)

{

case WM_CREATE :

return 0;

case WM_PAINT :

hdc = BeginPaint (hwnd,&ps);

wsprintf(temp,"SDK로 제작한 간단한 프로그램");

TextOut(hdc,0,0,temp,strlen(temp));

EndPaint (hwnd, &ps);

return 0;

case WM_DESTROY :

PostQuitMessage (0);

return 0;

}

return DefWindowProc (hwnd, iMsg, wParam, lParam);

}

(프로그램 소스끝)

WinMain 에서는 인스턴스 핸들을 받아서 이 인스턴스를 이용하여 윈도우 클래스를 등록하고 메인 윈도우를 만든다음 메시지 루프를 돌게 합니다. 그리고 메시지 루프에서 메시지를 받은 부분은 WndProc함수에서 처리하게 됩니다. 위와 같이 하면 단한개의 메인 윈도우만 출력시키는 프로그램을 만들수가 있습니다. 그림 1-1-42 는 위의 프로그램을 컴파일하여 실행시킨 화면입니다.

(그림 1-1-42)ExSdk프로그램 출력결과

그렇다면 MFC를 이용하여서는 위와 같은 프로그램을 작성할수 없는가? 물론 가능합니다. 이제 MFC를 이용하여 그림 1-1-42와 같은 윈도우를 출력하는 방법을 알아보겠습니다.

<1> MFC를 이용한 간단한 윈도우 출력 프로그램 만들기

Visual C++에서 File메뉴에서 New항목을 선택하고 프로젝트 항목에서 Win32 Application 항목을 선택하고 프로젝트명은 ExMfc로 설정하여 Win32용 프로젝트를 만듭니다. 그림 1-1-43은 File메뉴에서 New항목을 선택해 Win32용 프로젝트를 만드는 화면입니다.

(그림 1-1-43)Win32 Application 용 ExMdi 프로젝트 만들기

위와 같이 만들고 나면 그림 1-1-44와 같은 화면이 출력됩니다.

(그림 1-1-44) Win32 Application Step 1

Visual C++ 6.0부터는 SDK프로그램을 만들경우에도 자동적으로 코딩을 해주는 마법사가 가동됩니다. 그림 1-1-44에서 A typical "Hello Word" application이라는 항목을 선택하면 윈도우에 간단한 메뉴를 설정하고 화면에 □Hello Word" 라는 문자열이 출력되는 그런 프로그램이 SDK형태로 제작됩니다. 우리는 MFC를 이용하여 하나의 윈도우를 출력해야 하기 때문에 그림 1-1-44에서 An empty project 항목을 선택합니다.

이렇게 프로젝트를 만든다음 Project 메뉴에서 Settings항목을 선택하고 General 항목을 선택한다음 Microsoft Foundation Classes항목에서 □Use MFC in a Shared  DLL" 항목을 선택합니다. 즉 SDK형태의 프로젝트를 만든다음 MFC 라이브러리를 이용한다는 것입니다. 이렇게 프로젝트를 만든다음 새로운 파일을 만든다음 다음과 같이 코딩한후에 ExMfc.cpp로 저장하고 프로젝트에 삽입합니다.(본 항목은 고급 사용자들이 읽는다는 생각으로 프로젝트에 새로운 파일을 삽입하거나 컴파일하는 부분들은 생략합니다.)

(프로그램 소스)

#include <afxwin.h>

class CExMfcApp : public CWinApp {

public:

virtual BOOL InitInstance();

};

class CMainWindow : public CFrameWnd {

public:

CMainWindow() {

Create(NULL,"ExMfc");

}

afx_msg void OnPaint();

DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)

ON_WM_PAINT()

END_MESSAGE_MAP()

void CMainWindow::OnPaint(){

CPaintDC dc(this);

CString temp="MFC로 제작한 간단한 프로그램";

dc.TextOut(0,0,temp);

}

BOOL CExMfcApp::InitInstance()

{

m_pMainWnd= new CMainWindow();

m_pMainWnd->ShowWindow(m_nCmdShow);

m_pMainWnd->UpdateWindow();

return TRUE;

}

CExMfcApp ExMfcApp;

(프로그램 소스끝)

위와 같이 ExMfc.cpp에 코딩을 한후 컴파일하면 그림 1-1-45와 같은 화면이 출력됩니다.

(그림 1-1-45) ExMfc 프로그램 출력결과

먼저 AppWizard를 이용하여 DemoSdi를 만들었을때와의 비교를 해보겠습니다. ExMfc 는 두개의 클래스를 이용하였습니다. 어프리케이션 클래스인 CExMfcApp 클래스와 실제 메인 윈도우를 담당하는 CMainWindow를 입니다. CExMfcApp클래스는 CWinApp 클래스를 상속받았으며 CMainWindow는 CFrameWnd를 상속받은것입니다. 그리고 CExMfcApp의 맴버 함수 InitInstance 에서 메인윈도우를 만들고 이것을 화면에 보여줍니다. 그리고 메인윈도우가 만들어질때 즉 CMainWindow의 생성자에서 Create 함수를 이용하여 메인윈도우를 만듭니다. 이때 OnCreate 함수가 수반이 됩니다. 즉 템플레이트 개념을 제외하고는 DemoSdi가 구동되는것과 비슷한 루틴으로 들어갑니다. 이런의미에서 InitInstance 함수와 OnCreate함수를 본 Chapter에서 중점적으로 설명하였습니다. 이제 SDK 프로그램과 비교를 해보겠습니다. ExMfc프로그램과 ExSdk 프로그램은 그출력이 거히 같습니다. 그렇다면 ExMfc내부에는 ExSdk형태로 프로그램 코딩되어 있을것이라는 것을 예측할수 있습니다. 즉 겉에는 클래스 두개로 구성되어 있으나 실제로 보면 윈도우 한개와 클래스한개 그리고 해당 프로그램의 메시지 처리를 담당하는 프로시저가 있을것이라는 예측을 할수 있는 것입니다. 이제 두개의 클래스가 구동되면서 어떤 방식으로 윈도우가 출력되는지를 알아보겠습니다.

<2> ExMfc 프로그램 모듈 분석

ExMfc프로그램을 실행시키면 제일먼저 수행되는것이 Visual C++의 서브디렉토리의 /mfc/src/appmodul.cpp 파일에 있는 _tWinMain 함수입니다. 이함수는 다음과 같습니다.

(프로그램 소스)

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

// call shared/exported WinMain

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}

(프로그램 소스끝)

C로 프로그래밍을 하든 C++로 프로그램을 하든간에 윈도우즈에서 어플리케이션 프로그램을 할경우에는 WinMain함수가 꼭 있어야 하며 MFC로 프로그래밍할경우에는 컴파일할때 /mfc/src/appmodul.cpp 프로그램을 링크하여 WinMain이 설정되는 것입니다. 이 WinMain 함수가 구동됨과 동시에 인스턴스(hInstnce)와 명령인자 (lpCmdLine)가 함께 들어옵니다. 이렇게 들어온 인자자를 AfxWinMain함수에 넘겨주고 바로 AfxWinMain함수를 호출함으로써 WinMain함수실행은 끝나게 됩니다. 이제 AfxWinMain함수를 분석해 보겠습니다. AfxWinMain함수는 Visual C++이 설치된 디렉토리의 /mfc/src/winmain.cpp에 들어 있습니다. 이소스는 다음과 같습니다.

(프로그램 소스)

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

//hPrevInstance가 NULL일것. Windows 98과 Windows NT에서는 항상 hPrevInstance가

//NULL로 설정됨

ASSERT(hPrevInstance == NULL);

int nReturnCode = -1;

//윈도우어플리케이션은 한개의 스레드임 현재 프로그램의 스레드 포인터를 얻는다.

CWinThread* pThread = AfxGetThread();

// 현재 프로그램의 어플리케이션 포인터를 얻는다.

CWinApp* pApp = AfxGetApp();

// AFX_MODULE_STATE 클래스에 현재 인스턴스를 설정하고 현재 어플리케이션

//의 인스턴스에 현재 인스턴스와 lpCmdLine,nCmdShow값을 설정하고 어플키이션 클래스를

//초기화한후  스레드를 초기화한다. 이때 SetWindowsHookEx 함수를 이용하여 메시지를

// 훅킹할수 있도록 한다.

//SDK부분에서 윈도우 클래스를 초기화 하는 부분에 해당된다.

if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

goto InitFailure;

//CDocManager 클래스를 이용하여 템플레이트가 있다면 초기화 한다.

if (pApp != NULL && !pApp->InitApplication())

goto InitFailure;

// 프로그램에서 제작한 CExMfcApp클래스이 InitInstance 함수를 호출한다.

// SDK에서 메인 윈도우를 만드는 부분에 해당된다.

if (!pThread->InitInstance())

{

if (pThread->m_pMainWnd != NULL)

{

TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");

pThread->m_pMainWnd->DestroyWindow();

}

nReturnCode = pThread->ExitInstance();

goto InitFailure;

}

//스레드가 구동된다. 이스레드는 메시지를 루프로 돌면서 가로챈다.

//SDK에서 메시지 루프가 도는 부분에 해당된다.

nReturnCode = pThread->Run();

InitFailure:

#ifdef _DEBUG

// Check for missing AfxLockTempMap calls

if (AfxGetModuleThreadState()->m_nTempMapLock != 0)

{

TRACE1("Warning: Temp map lock count non-zero (%ld).\n",

AfxGetModuleThreadState()->m_nTempMapLock);

}

AfxLockTempMaps();

AfxUnlockTempMaps(-1);

#endif

AfxWinTerm();

return nReturnCode;

}

(프로그램 소스끝)

AfxWinMain함수는 SDK에서 WinMain함수에 설정된 기능을 그대로 설정합니다. 즉 윈도우 클래스를 이용하여 윈도우 시스템에 클래스를 등록하는 부분이 실행되고 어플리케이션의 InitInstance 함수를 호출시켜서 메인 윈도우를 만들도록 합니다. 이렇게 만든다음 본 어플리케이션의 스레드를 구동시켜 메시지를 계속적으로 받도록 되어 있습니다. 윈도우 클래스를 받는 함수가 pApp->InitApplication() 입니다. 이전에 내부적으로 초기화 하는 함수가 AfxWinInit 입니다. 이두개의 함수를 이용하여 클래가 설정되어 지고 InitInstance에 의해서 메인 윈도우가 만들어지는 것입니다.

그림 1-1-46은 MFC를 이용하여 프로그램이 구동될때 내부적으로 구동되는 프로그램 순서 도표입니다.

(종이 참조)

(그림 1-1-46) ExMfc프로그램을 구동시킬때 내부적으로 구동되는 도표

<3> AFX_MODULE_STATE 클래스 와 스레드 상태 얻기

AfxWinMain함수에서 AfxWinInit 함수가실행되면 다음과 같은 소스가 구동됩니다.

AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

pModuleState->m_hCurrentInstanceHandle = hInstance;

pModuleState->m_hCurrentResourceHandle = hInstance;

AFX_MODULE_STATE 클래스는 MFC내부적으로 사용되는 클래스이나 쓸모있는 클래스 이기에 소개를 합니다. 이클래스가 정의되어 있는 곳은 Visual C++  디렉토리의 서브디렉토리인 /mfc/include/afxstat_.h에 정의되어 있습니다. 이클래스 형태는 다음과 같습니다.

(프로그램 소스)

class CTypeLibCacheMap : public CMapPtrToPtr

{

public:

virtual void RemoveAll(void* pExcept);

};

// AFX_MODULE_STATE (global data for a module)

class AFX_MODULE_STATE : public CNoTrackObject

{

public:

#ifdef _AFXDLL

AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion);

AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,

BOOL bSystem);

#else

AFX_MODULE_STATE(BOOL bDLL);

#endif

~AFX_MODULE_STATE();

CWinApp* m_pCurrentWinApp;

HINSTANCE m_hCurrentInstanceHandle;

HINSTANCE m_hCurrentResourceHandle;

LPCTSTR m_lpszCurrentAppName;

BYTE m_bDLL;    // TRUE if module is a DLL, FALSE if it is an EXE

BYTE m_bSystem; // TRUE if module is a "system" module, FALSE if not

BYTE m_bReserved[2]; // padding

DWORD m_fRegisteredClasses; // flags for registered window classes

// runtime class data

#ifdef _AFXDLL

CRuntimeClass* m_pClassInit;

#endif

CTypedSimpleList<CRuntimeClass*> m_classList;

// OLE object factories

#ifndef _AFX_NO_OLE_SUPPORT

#ifdef _AFXDLL

COleObjectFactory* m_pFactoryInit;

#endif

CTypedSimpleList<COleObjectFactory*> m_factoryList;

#endif

// number of locked OLE objects

long m_nObjectCount;

BOOL m_bUserCtrl;

// AfxRegisterClass and AfxRegisterWndClass data

TCHAR m_szUnregisterList[4096];

#ifdef _AFXDLL

WNDPROC m_pfnAfxWndProc;

DWORD m_dwVersion;  // version that module linked against

#endif

// variables related to a given process in a module

//  (used to be AFX_MODULE_PROCESS_STATE)

#ifdef _AFX_OLD_EXCEPTIONS

// exceptions

AFX_TERM_PROC m_pfnTerminate;

#endif

void (PASCAL *m_pfnFilterToolTipMessage)(MSG*, CWnd*);

#ifdef _AFXDLL

// CDynLinkLibrary objects (for resource chain)

CTypedSimpleList<CDynLinkLibrary*> m_libraryList;

// special case for MFCxxLOC.DLL (localized MFC resources)

HINSTANCE m_appLangDLL;

#endif

#ifndef _AFX_NO_OCC_SUPPORT

// OLE control container manager

COccManager* m_pOccManager;

// locked OLE controls

CTypedSimpleList<COleControlLock*> m_lockList;

#endif

#ifndef _AFX_NO_DAO_SUPPORT

_AFX_DAO_STATE* m_pDaoState;

#endif

#ifndef _AFX_NO_OLE_SUPPORT

// Type library caches

CTypeLibCache m_typeLibCache;

CTypeLibCacheMap* m_pTypeLibCacheMap;

#endif

// define thread local portions of module state

THREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread)

};

(프로그램 소스끝)

AFX_MODULE_STATE 클래스의 기능은 무엇인가? 바로 현재 모듈의 상태에 대한 여러 정보를 가지고 있는 클래스 입니다. 이클래스이 맴버변수 리스트는 표8과 같습니다.

표8 AFX_MODULE_STATE 중요 맴버 변수

변수명	내용
m_pCurrentWinApp	현재 모듈의 CWinApp클래스 포인터
m_lpszCurrentAppName	현재 모듈의 어플리케이션 이름
m_hCurrentInstanceHandle	현재 모듈의 인스턴스 핸들
m_hCurrentResourceHandle	현재 모듈의 인스턴스 핸들 ,리소스를 컨트롤 하기 위해서 따로 하나의 변수를 둔것임
m_bDLL	현재 모듈이 DLL라이브러리를 사용하는가 안하는가의 플러그 사용하면 현재 플러그는 1 그렇지 않으면 0
m_pfnAfxWndProc	현재 모듈의 전체를 담당하는 윈도우 프로시저 포인터
m_classList	CRuntimeClass 구조체로 연결된 클래스 리스트중 첫번째 리스트를 포인터로 가지고 있다.
m_factoryList	COleObjectFactory 클래스 리스트중 첫번째 run-time class를 포인터로 가지고 있다.
m_nObjectCount	COM 객체 리스트 수
m_bUserCtrl	OLE 서버일경우 1 그렇지 않을경우 0
m_pDaoState	DAO가 구동될경우 DAO상태 정보를 가지고 있는 _AFX_DAO_STATE 클래스 포인터
m_pOccManager	OLE 컨트롤 컨테이너 관리자

표 8을 보시면 알수 있듯이 편재 프로그램의 기본적인 상태 정보 대부분을 가지고 잇는 클래스입니다. 프로그램을 하다보면 프로시저 포인터를 직접 받고자 할경우도 있으며 현재 모듈의 인스턴스및 그밖에 여러 정보를 얻고자 할때가 있습니다. 이럴경우

AFX_MODULE_STATE 사용할수도 있습니다. 필자는 다만 이런 클래스가 있다는것을 여러분에게 알려드릴뿐입니다. 이클래스를 어떻게 사용하든간에 그것은 여러분에게 달려 있습니다. 필자가 AFX_MODULE_STATE 클래스를 사용할때 가장 자주 이용하는것은 m_pDaoState와 m_pfnAfxWndProc 입니다. m_pfnAfxWndProc는 메시지를 가로챌때 아주 유용하게 사용하기 때문입니다.

afxstat_.h에는 또하나의 클래스가 있는데 이것이 _AFX_THREAD_STATE 입니다. 이클래스의 소스를 보시면서 여러분이 어떤 맴버가 있는지 확인을 해보시기 바랍니다. _AFX_THREAD_STATE 현재 스레드의 상태를 얻는데 사용됩니다. 이것은 바로 윈도우 스레드를 의미합니다. AfxWinMain함수에 다음과 같은 내용이 있을것입니다.

CWinThread* pThread = AfxGetThread();

본 Chapter 앞부분에 CWinApp는 CWinTherad를 상속받았다고 설명하였습니다. 즉 하나의 어플리케이션은 하나의 스레드 형태로 구동된다는 것입니다. 이 스레드의 주목적은 메시지를 루프로 돌면서 받아들이는 기능을 합니다. 이 스레드 상태를 체크하고 싶을경우에는 _AFX_THERAD_STATE 클래스를 이용하면 됩니다. 이클래스를 이용하여 현재 스레드 모듈을 얻고자 할경우에는 다음과 같이 하면 됩니다.

_AFX_THREAD_STATE* TharedState=AfxGetThreadState();

_AFX_THREAD_STATE 의 클래스 내용은 다음과 같습니다.

class _AFX_THREAD_STATE : public CNoTrackObject

{

public:

_AFX_THREAD_STATE();

virtual ~_AFX_THREAD_STATE();

// override for m_pModuleState in _AFX_APP_STATE

AFX_MODULE_STATE* m_pModuleState;

AFX_MODULE_STATE* m_pPrevModuleState;

// memory safety pool for temp maps

void* m_pSafetyPoolBuffer;    // current buffer

// thread local exception context

AFX_EXCEPTION_CONTEXT m_exceptionContext;

// CWnd create, gray dialog hook, and other hook data

CWnd* m_pWndInit;

CWnd* m_pAlternateWndInit;      // special case commdlg hooking

DWORD m_dwPropStyle;

DWORD m_dwPropExStyle;

HWND m_hWndInit;

BOOL m_bDlgCreate;

HHOOK m_hHookOldCbtFilter;

HHOOK m_hHookOldMsgFilter;

// other CWnd modal data

MSG m_lastSentMsg;              // see CWnd::WindowProc

HWND m_hTrackingWindow;         // see CWnd::TrackPopupMenu

HMENU m_hTrackingMenu;

TCHAR m_szTempClassName[96];    // see AfxRegisterWndClass

HWND m_hLockoutNotifyWindow;    // see CWnd::OnCommand

BOOL m_bInMsgFilter;

// other framework modal data

CView* m_pRoutingView;          // see CCmdTarget::GetRoutingView

CFrameWnd* m_pRoutingFrame;     // see CCmdTarget::GetRoutingFrame

// MFC/DB thread-local data

BOOL m_bWaitForDataSource;

// common controls thread state

CToolTipCtrl* m_pToolTip;

CWnd* m_pLastHit;       // last window to own tooltip

int m_nLastHit;         // last hittest code

TOOLINFO m_lastInfo;    // last TOOLINFO structure

int m_nLastStatus;      // last flyby status message

CControlBar* m_pLastStatus; // last flyby status control bar

// OLE control thread-local data

CWnd* m_pWndPark;       // "parking space" window

long m_nCtrlRef;        // reference count on parking window

BOOL m_bNeedTerm;       // TRUE if OleUninitialize needs to be called

};

(프로그램 소스끝)

10. CRuntimeClass 와 매크로

거히 모든 MFC 클래스는 CObject를 상속받았습니다. CObject를 상속받아서 그형태가 여러가지로 변화되어 나타납니다. 이것을 CObject형태의 관점에서 보았을때 어떤형태냐는 것입니다. 좀더 쉽게 설명하자면 숲속에서 무엇인가 움직이고 있을때 동물인것은 확실한데 도데체 어떤 동물인가 하는 것과 같은 의미입니다. 현재 구동되고 있는 클래스가 CObject라는 것은 확실한데 이것이 어떤것인가를 확인할때 또하나의 클래스를 이용합나다. 이것인 CRuntimeClass 입니다. CObject의  run-time class information을 RTCI라고 합니다. 즉 현재 CObject의 정보를 바로 RTCI 라고 하는 것입니다. CObject 는 메모리 관리를 처리해준다고 설명하였습니다. 좀더 세부적으로 들어간다면 CObject를 상속받아 탄생된 다양한 클래스를 생성하거나 삭제할경우 이 클래스가 보유하고 있는 메모리를 다이나믹하게 생성 삭제하는 기능을 가지고 있다는 것입니다. 만약 삭제를 하거나 또는 이클래스 정보들을 새로운 메모리 위치로 이동하고자 할경우에 CObject는 CRuntimeClass 형태로 전환된 객체를 참조하여서 메모리에서 이동,수정 삭제를 합니다. 이렇기 때문에 CRuntimeClass는 객체의 크기와 객체가 어떤 객체로 부터 상속받았는가의 정보를 가지고 있습니다. Visual C++ 이 설치된 디렉토리의 /mfc/include/afx.h에 CRuntimeClass에 대한 정의가 되어 있습니다. 이 구조체는 다음과 같습니다.

(프로그램 소스)

struct CRuntimeClass

{

// 속성

LPCSTR m_lpszClassName; //현재 클래스 이름

int m_nObjectSize;//객체의 크기

UINT m_wSchema; // 로드된 클래스의 수

CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class

#ifdef _AFXDLL

CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();

#else

CRuntimeClass* m_pBaseClass;

#endif

// Operations

CObject* CreateObject();

//이클래스가 pBaseClass로 부터 상속받았는가의 함수

BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

// Implementation

//CArchive클래스를 이용한 저장과 로드

void Store(CArchive& ar) const;

static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

// 본클래스와 연결된 다음 클래스

CRuntimeClass* m_pNextClass;       // linked list of registered classes

};

(프로그램 소스끝)

<1> MFC 매크로

MFC를 보게되면 두가지 형태의 매크로를 볼수 있습니다. DECLARE 와 IMPLEMENT입니다.

DECLARE 매크로는 클래스 에 위치하면서 구동의 선언을 의미하며 IMPLEMENT는 이선언에 해당하는 부분의 구동을 의미합니다. 이런 매크로의 예를 작성해 본다면 다음곽 같습니다.

class CTest : public CObject

{

DECLARE_DYNAMIC(CTest);

public:

:

void test();

}

IMPLEMENT_DYNAMIC(CTest,CObject)

void Test::test()

{

CTest *test = new CTest;

if(test->IsKindOf(RUNTIME_CLASS(Test))

{

AfxMessageBox("같은 종류임");

}

}

Test 라는 클래스에 DECLARE_DYNAMIC 매크로를 사용하여 CTest클래스를 정의하였고 이와 쌍으로 IMPLEMENT_DYNAMIC라는 매크로가 소스에서 사용되었습니다. 즉 CTest 라는 클래스는 다양한 형태로 변화되지만 CObjec클래스를 베이스 클래스로 설정된다는 의미입니다. 이렇게 설정하엿을때 RUNTIME_CLASS 매크로를 사용하여 CRuntimeClass로 변화될수 있으며 이렇게 됨으로써 비교를 할수 있게 됩니다. 위의 예에서 test라는 클래스가 CTest라는 클래스와 같은 종류인가를 확인하는 것입니다. 분명 같은 종류이기 때문에 메시지 박스가 출력될것입니다. 그러나 DECLARE_DYNAMIC 매크로와 IMPLEMENT_DYNAMIC 매크로를 쌍으로 설정하지 않으면 위의 IsKindOf함수는 제대로 동작하지가 않습니다. 이런 선언과 수행 매크로들의 리스트는 표 9와 같습니다.

(표 9) 선언과 수행 매크로 리스트

선언 매크로	수행 매크로	설명
DECLARE_DYNAMIC	IMPLEMENT_CYNAMIC	베이스 클래스로 부터 파생되어 변화된 클래스로 구동된다.
DECLARE_DYNCREATE	IMPLEMENT_DYNCREATE	CObject클래스로 부터 파생되어 다양한 형태의 클래스로 만들어진다.
DECLARE_SERIAL	IMPLEMENT_SERIAL	CObject 클래스로 부터 파생된 다른 클래스와 연결할수 있도록 한다.
DECLARE_MESSAGE_MAP	BEGIN_MESSAGE_MAP END_MESSAGE_MAP	매시지 맵핑을 선언하고 수행매크로 사이에 설정된 메시지처리를 해당 클래스에서 구동한다.

MFC로 프로그램을 작성했을대 표9에 있는 매크로들을 보았을때 과연 이런 매크로가 어떤기능을 하는가에 대해서 궁금하였을것입니다. 이매크로들이 발생되는 원인은 계층적으로 파생되어 클래스자체가 다양하게 변화되기 때문입니다. 이렇게 다양하게 변화되어도 현재 클래스의 기본적인 정보를 알아야 합니다. 이렇게 하기 위해서는 도대체 근원이 어디인가를 알아야 하는것입니다. 일상생활에서도 우리는 근원이 어디인가를 무척 많이 궁금해 합니다. □어디 사세요?□ □고향이 어디세요?□ 등등 말입니다. 그리고 질문에 대한 대답을 듣고 그사람의 기본적인 정보는 얻을수 잇습니다. 마찬가지로 현재 복잡하게 파생된 여러 클래스들의 베이스 클래스가 무엇인가를 알아야만 이클래스를 정확하게 구동할수 있는 것입니다. 이럴때 사용하는것이 매크로 입니다. 또한 이렇게 매크로로 정의를 해놓음으로써 CRuntimeClass클래스를 이용하거나 또는 RUNTIME_CLASS 매크로를 이용하여 클래스를 CObjec형태와 그외 여분의 정보로 분리할수 있게 됩니다.