CDocument 클래스와 파일 처리
CDocument 클래스는 파일에 관련된 처리를 하지 않는다면 그리 필요한 클래스는 아니다.
일반적으로 이 클래스에서는 새 파일, 열기, 저장, 다른 이름으로 저장을 처리하는데,
MFC로 SDI(Single Document Interface) 프로그램을 만들면 기본적으로 다음 그림과 같은
ID가 생성된다.
프로그램은 처음 실행될 때 항상새 파일(ID_FILE_NEW)메시지를 내부적으로 부르게 되어 있다.
새 파일은 InitInstance의ProcessShellCommand에 의해 내부적으로 호출된다.
BOOL CCabinetApp::InitInstance()
{
...
// Dispatch commands specified on the command line
if(!ProcessShellCommand(cmdInfo)) // 새로운 문서를 생성(ID_FILE_NEW)
returnFALSE;
...
}
결과적으로ProcessShellCommand는 내부에서 CDocument의 OnNewDocument() 함수를
호출한다. 그래서 OnNewDocument() 함수는 프로그램 실행 시 반드시 한 번 불려진다.
BOOL CTestDoc::OnNewDocument()
{
if(!CDocument::OnNewDocument())
returnFALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
returnTRUE;
}
만약 프로그램 초기화 시 파일을 읽어서 화면에 표현해야 한다면, 다음과 같이 코딩해야 한다.
BOOL CCabinetApp::InitInstance()
{
...
// Dispatch commands specified on the command line
//if (!ProcessShellCommand(cmdInfo)) // 막아 놓고
// return FALSE;
// 우선 새로운 빈 문서를 생성한다.
CTestDoc* pDoc = (CTestDoc*)pDocTemplate->OpenDocumentFile( NULL );
CString strPath = "c:\\project\\test.txt";
// CTestDoc 클래스에서 파일을 열어서 처리할 수 있도록OnOpenDocument를 호출한다.
pDoc->OnOpenDocument( strPath );
...
}
위의OnOpenDocument()함수는 CTestDoc의OnOpenDocument()함수를 호출한다.
BOOL CTestDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if(!CDocument::OnOpenDocument(lpszPathName)) // Serialize를 호출
returnFALSE;
returnTRUE;
}
호출된OnOpenDocument() 함수에서CDocument::OpOpenDocument() 함수를 호출하고,
결국CDocument::OpOpenDocument() 함수는 내부적으로Serialize() 함수를 호출해 준다.
그러면 아래의Serialize() 함수가 호출되는데, 이 곳에 파일과 관련된 처리를 하면 된다.
이미 파일은 개방이 되어 있는 상태이고, 파일을 읽을 수 있는 CArchive 객체가 매개 변수로
넘어오게 된다. 그러므로 이CArchive객체를 사용하여 아래와 같이 파일을 읽을 수 있다.
void CCabinetDoc::Serialize(CArchive& ar)
{
if(ar.IsStoring()) // 저장을 하기 위한 객체이면
{
int count = 5;
ar << count; // 파일에 count를 저장, OnSaveDocument() 함수 호출 시 사용
}
else
{
int count;
ar >> count; // 파일에서 count를 읽음
}
}
Serialize() 함수는 CDocument::OnSaveDocument() 함수의 호출에 의해서도 호출된다.
BOOL CCabinetDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
returnCDocument::OnSaveDocument(lpszPathName); // Serialize() 함수를 내부적으로 호출
}
위에서 파일 읽는 것을 Serialize()를 사용하지 않고 처리하려면, 다음과 같이 함수 호출 부분을
주석으로 처리한 후, CFile 클래스 등을 이용하여 처리할 수 있다.
BOOL CTestDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
//if (!CDocument::OnOpenDocument(lpszPathName)) // Serialize를 호출
// return FALSE;
CFile file;
file.Open( lpszPathName, CFile::modeRead );
...// 파일 읽기
file.Close();
returnTRUE;
}
또한 파일 쓰는 것을 Serialize()를 사용하지 않고 처리하려면, 다음과 같이 함수 호출 부분을
주석으로 처리한 후, CFile 클래스 등을 이용하여 처리할 수 있다.
BOOL CCabinetDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
//return CDocument::OnSaveDocument(lpszPathName); // Serialize() 함수를 내부적으로 호출
CFile file;
file.Open( lpszPathName, CFile::modeWrite | CFile::modeCreate );
... // 파일 쓰기
file.Close();
returnTRUE;
}
참고로,ID_FILE_NEW와ID_FILE_OPEN은 CTestApp 클래스에 다음과 같이 매크로가 존재한다.
BEGIN_MESSAGE_MAP(CTestApp, CWinApp)
//{{AFX_MSG_MAP(CTestApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard file based document commands
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Standard print setup command
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
만약에 ID_FILE_NEW와 ID_FILE_OPEN을 직접 처리하고자 한다면, 위 두 줄을 주석처리해야 한다.
종합적으로 파일 처리에 대해 정리를 해 보면,
ID_FILE_NEW는 처음 프로그램이 실행될 때 발생되며, 이 때 CDocument 클래스의 OnNewDocument()
함수가 호출된다. 이 이벤트는 SDI로 프로그램을 생성하였을 때 CWinApp의 OnFileNew() 함수와 연결이
되어 있으며, 사용자가파일 >> 새 파일을 누를 때도 발생한다. OnFileNew() 함수의 역할은 새로운
도큐먼트를 생성하고, 생성된 도큐먼트의 OnNewDocument() 함수를 호출해 주는 것이다. 만약 도큐먼트를
초기화 해주어야 한다면, OnNewDocument() 함수가 적합하다.
ID_FILE_OPEN메시지는 CWinApp 클래스의OnFileOpen() 함수를 호출한다. 사용자가파일 >> 열기
메뉴를 선택하면, 이 메시지가 발생되며, 파일을 선택할 수 있는 대화 상자를 보여 준다. 대화 상자에서
사용자가 파일을 선택한 후 열기 버튼을 누르면, 도큐먼트의 OnOpenDocument() 함수가 자동적으로
호출된다. 그러면OnOpenDocument() 함수는 연속적으로Serialize() 함수를 호출해 준다. 우리는
Serialize() 함수에서 CArchive 객체를 사용해서 파일을 읽을 수 있다.
ID_FILE_SAVE메시지는 CDocument 클래스의OnFileSave() 함수를 호출한다. 이 함수는 다시 CTestDoc
클래스의 OnSaveDocument() 함수를 호출한다. 그러면OnSaveDocument() 함수는 연속적으로 Serialize()
함수를 호출해서 CArchive 객체를 사용한 파일 저장을 가능하게 한다.
ID_FILE_SAVE_AS메시지는 CDocument 클래스의OnFileSaveAs() 함수를 호출한다. 나머지 기능은
OnFileSave() 함수와 같다.
CArchive 클래스는 CFile 클래스의 객체를 사용하여, 파일 입출력을 가능하게 해주는 클래스이다.
Serialize를 사용한 데이터의 저장
int a = 5, b = 10;
ar<< a << b;
Serialize를 사용한 데이터의 읽기
int a, b;
ar>> a >> b; // a는 5, b는 10이 읽힘