. 프로젝트의 구성

코드명을 정했으므로 새 이름으로 프로젝트를 다시 만들어 보도록 하자. 당근 프로젝트는 컨트롤 테스트용으로 만든 ApiEditTest와는 근본적으로 다른데 복수 개의 문서를 열어서 편집할 수 있는 MDI(Multiple Document Interface) 형식이라는 점이 가장 큰 차이점이다. MDI 자체는 어렵지 않지만 여러 윈도우가 동시에 열릴 수 있기 때문에 잔손이 많이 간다. 지금까지 만들어 온 ApiEdit는 편집 컨트롤이고 여기서부터 만들 Dangeun은 편집기이다. 프로젝트의 성격이 바뀌었으므로 이전 프로젝트를 복사하지 않고 새로 프로젝트를 다시 만들었다.

ApiEditTest까지는 프로젝트 제작 과정 전부를 다 기록하고 실습을 유도했지만 이 예제는 그렇게 하지 않고 CD-ROM에 있는 예제를 불러와서 분석만 해보기로 한다. 계속 실습을 하면서 직접 프로젝트를 만들어 온 사람에게는 다소 실망스러울지도 모르겠지만 이 예제를 직접 실습할 수 있도록 하지 않은 데는 나름대로 이유가 있다. 주로 리소스 관리상의 문제들이다.

 

리소스 작업은 단순 작업이기 때문에 직접 실습해 봐야 얻을 것이 없다. 리소스는 단순한 데이터이기 때문에 어떤 치밀한 논리를 다루는 것이 아니며 따라서 실습의 의미가 없다. 남이 만들어 놓은 리소스를 그대로 같이 만들려고 하면 시간만 낭비되고 짜증만 날 것이다. 리소스 만들 시간에 코드를 좀 더 보는 것이 훨씬 도움이 된다.

기능이 들어갈 때마다 리소스가 조금씩 바뀌는데 일일이 캡처해서 보여주는 것이 비효율적이고 실습 시간과 지면이 너무 많이 낭비된다. 리소스 제작 과정을 지면으로 보이는 데는 한계가 있다.

리소스는 미리 만들어 놓아도 프로젝트의 논리에 전혀 방해를 주지 않는다. 코드는 미리 만들어 놓을 수가 없지만 비트맵이나 커서 같은 것은 처음부터 그냥 있어도 상관없다. 리소스가 필요해질 때마다 이런 리소스를 사용하겠다는 언급만 있으면 된다.

가장 큰 이유는 비주얼 C++ .7.0이 리소스 관리상의 버그가 많다는 점이다. ID를 제대로 관리하지 못하며 종속성 체크도 엉망이라 툴을 제대로 쓰기가 무척 어렵다. 특히 초보자의 경우 이 버그 투성이의 개발툴로 책을 따라 리소스를 만드는 것은 불가능하다고 판단했다. 그대로 따라했는데 결과가 제대로 나오지 않으면 곤란하다. VC 6.0은 오랫동안 안정화를 해 왔기 때문에 버그가 없다.

 

Dangeun1 프로젝트에 포함된 Dangeun.rc resource.h는 기능이 다 완료된 후의 모양과 완전히 같으며 이후 실습에서 리소스는 전혀 편집할 필요 없이 사용만 하면 된다. 이 두 파일의 스크립트와 헤더 파일은 손으로 직접 만든 것이다. Dangeun1 프로젝트는 앞으로의 실습을 위해 일종의 예비 동작을 취해놓은 시작 프로젝트이다. 리소스와 프로젝트 옵션 등을 실습에 편리하도록 미리 다 작성해두었다. 이후 Dangeun2부터는 다시 실습을 시작하게 될 것이다.

CD-ROM Dangeun1 폴더에 있는 Dangeun 프로젝트를 읽어 놓고 구경만 해보도록 하자. 편집기의 시작 프로젝트이므로 직접 만들어 보지는 않는다 하더라도 구성은 잘 알아둘 필요가 있다. 한 번 훑어만 보면 프로젝트의 개요가 쉽게 파악될 것이다. 먼저 프로젝트의 속성창을 열어 보자.

불필요한 64비트 이식성 점검을 하지 않도록 했다. 이 옵션은 선택해 봐야 경고만 많이 발생한다. 비주얼 C++ 6.0에는 이 옵션이 없으므로 신경쓰지 않아도 된다. 코드 생성 탭의 런타임 라이브러리 옵션은 다중 스레드로 선택해놓았는데 파일검색 기능에서 스레드를 사용하기 때문이다.

 

Debug 버전은 다중 스레드 디버그(/MTd)를 선택하고 Release 버전은 다중 스레드(/MT)를 선택하였다. 링크/입력 옵션에는 앞으로 사용할 임포트 라이브러리를 모두 기입해두었다.

 

imm32.lib는 물론이고 공통 컨트롤 라이브러리, 쉘 라이브러리, 인터넷 라이브러리가 미리 프로젝트에 포함되어 있다. 다음은 프로젝트에 어떤 모듈들이 있는지 솔루션 탐색기를 보자.

ApiEdit.* ApiEditTest에서 작성한 것을 그대로 가져온 것인데 이 파일을 프로젝트에 포함시킴으로써 Dangeun ApiEditTest까지 작성된 모든 성과물을 계승하게 된다. Dangeun.cpp가 호스트의 메인 모듈이며 곧 텍스트 편집기 자체라고 할 수 있다.

Util.*는 앞으로 필요한 일반함수를 정의하는 모듈인데 현재는 별다른 함수가 작성되어 있지 않다. Util.h에는 최적화에 사용했던 STARTQ, ENDQ 등의 속도 측정 매크로들만 작성되어 있으며 Util.cpp에는 디버깅을 위한 보조 함수 하나를 작성해두었다.

 

#include "stdafx.h"

 

#define DEBUGLOGFILE "c:\\DgDebug.txt"

void WriteLogFile(TCHAR *strLog,...)

{

     HANDLE hLog;

     static int count=0;

     DWORD dwWritten;

     TCHAR szLog[1024];

     TCHAR strLog2[1024];

     va_list marker;

     SYSTEMTIME st;

 

     va_start( marker, strLog );

     vsprintf(szLog,strLog,marker);

 

     if (count == 0) {

          hLog=CreateFile(DEBUGLOGFILE,GENERIC_WRITE,0,NULL,

          CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

     } else {

          hLog=CreateFile(DEBUGLOGFILE,GENERIC_WRITE,0,NULL,

          OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

     }

 

     GetLocalTime(&st);

     wsprintf(strLog2,"카운터=%06d(%d:%d:%d:%d) %s\r\n",count++,

          st.wHour,st.wMinute,st.wSecond,st.wMilliseconds,szLog);

     SetFilePointer(hLog,0,NULL,FILE_END);

     WriteFile(hLog,strLog2,strlen(strLog2),&dwWritten,NULL);

     CloseHandle(hLog);

}

 

WriteLogFile 함수는 가변 인수를 전달받아 서식화된 문자열을 로그 파일에 기록함으로써 디버깅을 도와주는 함수이다. 디버거로 디버깅이 어려운 상황일 때는 이 함수로 중간중간에 변수 확인문을 작성하도록 하고 로그 파일을 참조함으로써 디버깅할 수 있다. 스레드간의 동기화 문제가 있을 때나 마우스 캡처 상황을 디버깅할 때는 디버거를 쓸 수 없기 때문에 로그 파일을 만들어야 한다. 또한 릴리즈 모드에서만 문제가 있을 때도 이 함수가 유용하게 사용된다. 다음과 같은 형식으로 사용한다. printf 함수와 사용방법이 동일하다.

 

WriteLogFile("현재 오프셋=%d, 이때의 문서 크기=%d",off,doclen);

 

앞으로 이 모듈에도 많은 함수들이 추가될 것이다. 레지스트리 입출력이나 쉘 관련 함수, 일반적으로 많이 사용되는 함수들이 이 모듈에 작성되며 당근 프로젝트에서 사용할 보조 클래스들도 이 파일에 작성된다. 프로젝트가 점점 커짐에 따라 메인 모듈에서 모든 것을 처리하기가 어려워지기 때문에 보조 모듈을 만들었다.