Friday, January 5, 2007

Implementation of the MFC Document View Architecture.

We know that every MFC application has a application class CWinApp which is derived from CWinThread class. Here First I am trying to find how the wizard creates the Frame, document, view classes, and how they are organized?

 For an MFC application the best place to start is the InitInstance() function. This function is called from AfxWinMain. If IniInstance return false it indicates that application has failed to start. So this causes to immediately terminate the application. So in this case the wizard writes the InitInstance function like this

BOOL CTestApp::InitInstance()



   CSingleDocTemplate* pDocTemplate;
   pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,                        RUNTIME_CLASS(CtestviewDoc),RUNTIME_CLASS(CMainFrame),      

   if (!pDocTemplate)      return FALSE;

   CCommandLineInfo cmdInfo;

   if (!ProcessShellCommand(cmdInfo))return FALSE;

           return TRUE;

First it creates a CSingleDocumentTemplate class’s object. CSingleDocumentTemplate is derived from CDocumentTemplate.

CDocumentTemplate class contains the following members of CRuntime class.
CRuntimeClass* m_pDocClass;         // class for creating new documents
CRuntimeClass* m_pFrameClass;       // class for creating new frames
CRuntimeClass* m_pViewClass;        // class for creating new views
CRuntimeClass* m_pOleFrameClass;    // class for creating in-place
CRuntimeClass* m_pOleViewClass;

In the IniInstance of CWinapp class wizard is creating a CSingleDocumentTemplate class object and initializes it with Run time class objects of our View. Frame, Doc classes.

 After that the app class stores this newly created CSingleDocumentTemplate class object by calling a function AddDocTemplate(CDocTemplate* pTemplate). This is a member function of class CWinApp.

void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)
      if (m_pDocManager == NULL)
            m_pDocManager = new CDocManager;

m_pDocManager is a member variable of class CWinApp. Declared as  CDocManager* m_pDocManager; CDocManager contains a pointer array.  CWinApp::AddDocTemplate simply calls the function of m_pDocManager->AddDocTemplate(). Then Next line of IniInstance is like this.

CCommandLineInfo cmdInfo;The default command line is FileNew , that means create a new new document.

ParseCommandLine(cmdInfo); The parse command line looks the argv of main function and parses it ,

If finds anything it adds it to the cmdInfo object. For example if the command line is app sample.txt it sets the m_nShellCommand memmber of CCommandLineInfo class to FileOpen ,  And m_strFileName to the sample.txt. So in this case there is no command line arguments. Since the default command line is FileNew the ParseCommandLine lines just returns. The Next step wizrd does is calling the function CWinApp::ProcessShellCommand(cmdInfo). The ProcessShellCommand is written like this(removed all other codes.). So in our case it FileNew

switch (rCmdInfo.m_nShellCommand)

      case CCommandLineInfo::FileNew:
      case CCommandLineInfo::FileOpen:
   case CCommandLineInfo::FilePrintTo:
      case CCommandLineInfo::FilePrint:
      case CCommandLineInfo::FileDDE:
      case CCommandLineInfo::AppRegister:
      case CCommandLineInfo::AppUnregister:

 In this case it finaly calls CWinApp::OnFileNew() function. Where all creation takes place. The CWinApp::OnFileNew simlpy calls the doc manager object’s OnFileNew. Like this

void CWinApp::OnFileNew()
      if (m_pDocManager != NULL)

The CDocManager ::OnFileNew() checks for multiple templates . In this case here only 1 temmplate is present(becs this is SDI APP ). Earlier I told that CDocManager stores a list of CDocuemntTemplate class object. Here it gets the First object from list and calls the OpenDocumentFile(0) function. (psuedo code shown below )

void CDocManager::OnFileNew()
  CDocTemplate* pTemplate = CDocTemplate*)m_templateList.GetHead();

The next part is the important part , where all creation takes place. So i am going to dig the CSingleDocument::OpenDoumentFile() function.This function creates the the object of CDocument ( our applications CDocument derived object) , Application's Frame class(CFrameWnd) and application's View class(CView). Well , lets see how these things happpens. Below i am presenting the skelton of CSingleDocTemplate::OpenDocumentFile() function.

CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)
   CDocument *pDocument = NULL;
   CFrameWnd *pFrameWnd = NULL;
   pDocument = CreateNewDocument(); 
   pFrameWnd = CreateNewFrame();

Looking through the above function , we can understand that the Wizrd created document object by calling a function CreateNewObject function. So what is in this CreateNewDocument function ?? . i did dig again and find that , here wizard uses the CRuntime class's object .
Here i am rembering you about CDocTemplate class contains 3 CRuntime class members representing View,Document,Frame. Since CSingleDocTemaplte is derived from CDocTemplate it also gets these members. What i am trying to convey is that the CreateNewDocument function creates a new document using these runtime class. If you remember the Documnet class properly you can see the macros  DECLARE_DYNCREATE and  IMPLEMENT_DYNCREATE .

CDocument * CDocTemplate::CreateNewDocument()
  CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
   return pDocument;


So what these macros doing ? we can see a CreateObject function in CreateNewDocument function. (m_pDocClass->CreateObject()). Actually this function is implemented by the IMPLEMENT_DYNCREATE macro. it just returning a pointer to our class.

Below is the implemenation of IMPLEMENT_DYNCREATE .

IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
return new class_name; } \

 IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject, NULL)

The CreateObject function is highlighted in red. So i think that part is clear. We learned how wizard is creating a new application Document class object. the next is with FrameWnd . Lets look the CreateNewFrame function. The CreateNewFrame function also creates the object of CFrameWnd class by using the Runtime class associated with the CDocTemplate class.The CreateNewFrame function is shown below.

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
 CCreateContext context;
 context.m_pCurrentFrame = pOther;
 context.m_pCurrentDoc = pDoc;
 context.m_pNewViewClass = m_pViewClass;
 context.m_pNewDocTemplate = this;
 if (pDoc != NULL) ASSERT_VALID(pDoc);

 ASSERT(m_nIDResource != 0); // must have a resource ID to load from
 CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
 ASSERT_KINDOF(CFrameWnd, pFrame);
 pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,&context);
 return pFrame;

The m_pFrameClass is a member of CDocTemplate Initialized in IniInistance function. (if you are not remembering refer top of this article ).m_pFrameClass is a CRuntimeClass's object. So the by calling the m_pFrameClass ->CreateObject() returns a pointer to fhe application CMainFrame (derived from CFrameWnd ) class object. The next step is creating the  window by calling LoadFrame().

The CFrameWnd::LoadFrame creates the window by calling the Create method , and  loads the accelerator information.  So what happend to View ???  . Lets look the code.. If you look at the CDocTemplate::CreateNewFrame() function above shown , you can see that the function creates an pbject of CCreateContext. ( CCreateContext context;). The wizrd assiging the context.pNewViewClass with runtime class of our view (m_pViewClass , this gets value on CSingleDocumentTemplate creation. refer IniInstance on the top. ).


msnkd said...

Hey, this is really nice, You should continue.

Shoban said...

Very Good Artical.
By Shoban.