본문 바로가기
컴퓨터/MFC_API

RTF 파일과 CRichEditCtrl - 저장과 부르기

by adnoctum 2012. 5. 9.



   CRichEditCtrl 의 내용을 RTF 파일로 저장하거나, 혹은 RTF 파일의 내용을 CRichEditCtrl 로 불러 들이는 방법을 살펴 보자.



먼저, RTF파일의 내용을 CRichEditCtrl 로 불러 들이는 원리는 다음과 같이 도식화 할 수 있다.




즉, 파일이 있으면 그 파일을 접근할 수 있는 Stream 을 만들어 준다. 그 후 CRichEditCtrl 이 사용하는 콜백함수의 적당한 멤버들의 값 중 dwCookie 의 값에 이 스트림을 연결해 주면, CRichEditCtrl 은 다음과 같은 작업을 하면서 계속 콜백함수를 호출하다가 특정 조건이 되면 멈춘다. 

1. 콜백함수는 dwCookie 로 주어진 스트림을 이용해서 파일 내용을 내부의 버퍼에 읽어 들인다. 

2. 버퍼에 저장된 내용을 주어진 포멧에 따라 CRichEditCtrl 안으로 복사해 집어 넣는다. 


위 작업을 하는 콜백함수가 계속 호출되다가 다음과 같은 조건이면 콜백함수의 호출이 중단된다. 

1. 콜백함수가 0 이 아닌 값을 반환했다. 

2. 콜백함수의 파라미터 중 pcb 에 0 값이 들어가 있다. 

3. rich edit control 이 데이터를 읽거나 쓸 수 없는 에러가 발생했다. 가령 메모리 부족. 

4. stream-in (RTF 파일을 여는) 경우, RTF 코드 중 RTF의 블럭의 끝을 나타내는 코드에 도달했다. 

5. CRichEditCtrl 이 multiline mode 가 아니고, RTF 의 코드가 한 단락의 끝을 나타낸다 (CR, LF, VT, LS, or PS). 

(http://msdn.microsoft.com/en-us/library/bb774368.aspx)


즉, 파일을 읽을 때 파일의 내용을 한 번에 읽어서 CRichEditCtrl 로 나타내는 것이 아니라, 일정 부분씩 계속 읽다가[각주:1] 위 조건이 되면 더이상 읽지 않는 것이다. 위 종료 조건 중 2 번은 pcb가 stream 에서 읽은 데이터의 양을 나타내므로 0 이면 파일을 다 읽었기 때문일 것이다. 보통 2번이나 4 번에 의해 콜백함수가 더이상 호출되지 않을 것이다. 1이나 3처럼 에러가 발생하면 에러 코드가 CRichEditCtrl::StreamIn 이나 StreamOut 의 파라미터로 전달이 된다. 다음과 같다. 




위처럼 파일의 일정 부분을 stream 을 이용해서 계속 읽어들이면서 CRichEditCtrl 로 내용을 집어 넣게 된다. 이제 실제 코드를 살펴 보자. 


  256     CFile iFile(_T("test2.rtf"), CFile::modeRead);

  257     EDITSTREAM ies;

  258 

  259     ies.dwCookie =  (DWORD) &iFile;

  260     ies.pfnCallback = (EDITSTREAMCALLBACK) MyStreamInCallback;

  261 

  262     _recTreatment.StreamIn(SF_RTF,ies); // Perform the streaming

  263 


위 코드가 RTF 파일을 읽어 들이는 부분이다. RTF 파일을 접근할 stream 으로 CFile 객체를 사용했는데, std::ifstream 이나 HANDLE 을 사용해도 상관이 없다. 단지 callback 함수만 그에 맞추어 주면 된다. 위에서는 MyStreamInCallback 함수를 사용하고 있는데, 이 함수의 내용은 다음과 같다. 



  195 DWORD CALLBACK MyStreamInCallback(CFile* dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)

  196 {

  197     // Required for StreamIn

  198     CFile* pFile = (CFile*) dwCookie;

  199 

  200     *pcb = pFile->Read(pbBuff, cb);

  201 

  202     return 0;

  203 }

  204 



262 줄에서 StreamIn 함수를 호출하면, EM_STREAMIN 메세지가 호출되면서, callback 함수가 호출된다. 위의 콜백함수를 보면 198 번째 줄에서 CFile의 포인터로 dwCookie를 변환하고 있다. 왜냐 하면, 257 번 줄의 EDITSTREAM 의 멤버 중 dwCookie 를 CFile 의 포인터(259 번 줄)로 해 놓았기 때문이다. callback 함수가 할 일은 pbBuff에 파일의 내용을 집어 넣는 것이다. 따라서, 이렇게 CFile의 포인터를 넘겨 주면 cb 크기만큼 계속 파일을 읽어서 pbBuff에 읽은 내용을 덮어 쓰게 하면 된다. pcb 에는 실제로 읽힌 바이트의 수가 들어가야 하므로 200 번 줄처럼 코딩하게 된다. CRichEditCtrl 은 StreamIn 에 의해 불리는 callback 함수를 이용해서 pbBuff로 값을 계속 복사하고, 이 값을 계속 컨트롤 쪽으로 이동시키는 것이다. 따라서, callback 함수는 stream 을 이용해서 cb 만큼의 내용을 pbBuff로 읽어 들이는 역할만 하면 된다. 이 때, cb 는 우리가 알 필요 없다. 이 크기는 4096 정도로 설정이 되는데, RTF 파일을 한꺼번에 읽는 것이 아니라 일정 크기만큼씩 계속 읽어 오기 위해 필요한 것이다. 4KB 만큼씩 파일을 읽어들인다면 4MB 인 RTF 파일을 부를 때는 callback 함수가 1,000 번이 호출되는 것이다. 


위처럼 간단히 RTF 파일을 CRichEditCtrl 로 불러 들일 수 있다. 


CRichEditCtrl 의 내용을 RTF로 저장하는 것은 위와 정확히 반대로 작용한다. 즉, CRichEditCtrl의 내용이 일정양씩 계속 buffer로 복사가 되고, 이 내용이 주어진 stream 을 이용해서 파일로 조금씩 계속 씌여지는 것이다. 이 때 사용되는 callback 함수는 다음과 같다. 



  184 DWORD CALLBACK MyStreamOutCallback(CFile* dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)

  185 {

  186     // Required for StreamOut

  187     CFile* pFile = (CFile*) dwCookie;

  188 

  189     pFile->Write(pbBuff, cb);

  190     *pcb = cb;

  191 

  192     return 0;

  193 }



마찬가지로, CFile을 이용했기 때문에, pBuff의 내용을 cb 만큼 파일에 계속 써주면 된다. 이 때, pbBuff에는 CRichEditCtrl의 cb 만큼의 내용이 이미 저장되어 있기 때문에 이 값은 우리가 신경쓰지 않아도 된다. 위의 콜백함수를 이용해 파일을 저장하는 코드는 다음과 같이 간단하다.



  246     CFile cFile(_T("test2.rtf"), CFile::modeCreate|CFile::modeWrite);

  247     EDITSTREAM es;

  248 

  249     es.dwCookie =  (DWORD) &cFile;

  250     es.pfnCallback = (EDITSTREAMCALLBACK) MyStreamOutCallback;

  251 

  252     _recSourceText.StreamOut(SF_RTF,es);

  253     cFile.Close();



마찬가지로 이 콜백함수도 CRichEditCtrl의 내용이 다 쓰일 때까지 호출이 될 것이다. 





  1. 파일을 열면 파일 포인터가 0 에 위치하고, 읽을 때마다 읽힌 위치만큼 파일 포인터가 변한다. 즉, 파일은 열린 후 어디까지 읽혔는지를 나타내는 파일 포인터라는 것을 내부에 갖고 있게 된다, CreateFile 로 열거나 std::ifstream 으로 열거나 간에. 따라서 파일을 일정 부분씩 읽을 때마다 이 파일 포인터가 읽힌 크기만큼 계속 증가할 것이다. 이런 식으로 파일을 조금씩 읽을 수 있는 것이다. 만약 100MB 파일에서 끝의 10MB 만 읽고 싶다면 파일 포인터를 강제로 90MB 위치에 위치시킨 후 10MB 만 읽으면 된다. [본문으로]