Share the View/Doc Architecture in a DLL


This article was contributed by Alexandre Komyak.

Tested on: VC++ 4/6 NT2000

My current project made me think on the following point:

It would be useful to separate into a dll the code responsible for running MFC's View/Doc architecture, notably frame, its child area with controls, dialogs etc. This would help different applications to share a grand part of the code.

This article describes a proposed solution. Both SDI and MDI interfaces have been tested. MDI-based applications are the concerned here.

CExeApp is derived from CWinApp. CExeApp::InitInstance contains the code to create document templates and main frame.

  CMultiDocTemplate* pDocTemplate;
  pDocTemplate = new CMultiDocTemplate(
    IDR_EXETYPE,
    RUNTIME_CLASS(CExeDoc),
    RUNTIME_CLASS(CChildFrame), // custom MDI child frame
    RUNTIME_CLASS(CExeView));
  AddDocTemplate(pDocTemplate);

  // create main MDI Frame window
  CMainFrame* pMainFrame = new CMainFrame;
  if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
    return FALSE;
  m_pMainWnd = pMainFrame;

What if we move the above code into a dll? This would be done along with the code other classes (CExeDoc, CChildFrame, CExeView), and resources (IDR_MAINFRAME).Having done this, we'll separate all the Frame/Doc/View stuff into the library that may be reusable by CWinApp based applications.

The attached file comprises two sub-folders, exe and dll. dll is an implementation of the View/Doc architecture in Dll. exe is executable that is linked with dll.

dll:

It must be an MFC extension Dll and provide ann interface class to be used by the exe application:

// interface class identifier
#define CLSID_APP    0x10

// interface IDs
#define IID_MyIUnknown    0x1000 
#define IID_IDocument    0x1001

// document constants
#define C_DOC_1      0x100
#define C_DOC_2      0x101

// instantiate an interface class object
BOOL __declspec(dllexport) App_GetClassObject(int nClsid,
                                              int nId,
                                              void** ppvObj);

// defined instead of MFC's IUnknown
struct MyIUnknown
{
    MyIUnknown() { TRACE("Entering IUnknown ctor %p\n", this); }
    virtual BOOL QueryInterface(int nIid, void** ppvObj) = 0;
    virtual DWORD Release() = 0;
    virtual DWORD AddRef() = 0;
};

struct AFX_EXT_CLASS IDocument : public MyIUnknown
{
    IDocument() { TRACE("Entering IDocument ctor %p\n", this); }
    virtual CDocTemplate* CreateDocTempl( CWinApp*, int ) = 0;
    virtual void CreateFrame() = 0;
};

Thus, IDocument is an interface class. App_GetClassObject() is used to create an object to be controled via its interface. In the code supplied, IDocument is defined in dll_inteface.h. Implementation is hidden in App.[h,cpp] files.

How to create dll. First, we have to create the CWinApp exe application with AppWizzard. Then create dll MFC extension project (again AppWizzard). All of the exe's Frame/Doc/View files move to dll. Create class a wizard database for dll. It must include all the classes for frames, views and documents. Move all the necessary resources from exe to dll. Finally, Build.

exe:

After moving the files, there is exe.[h,cpp] and stdafx.[h,cpp]. We have to add the lines to initialize IDocument interface:

void CExeApp::InitDll()
{
  VERIFY( App_GetClassObject(CLSID_APP, 
                             IID_IDocument,
                             (void**)&pIDoc) );
}

And to use it:

BOOL CExeApp::InitInstance()
{
  InitDll();
  ...
  // Register the application's document templates.
  // Document templates serve as the connection between
  // documents, frame windows and views.
/*
  CMultiDocTemplate* pDocTemplate;
  pDocTemplate = new CMultiDocTemplate(
    IDR_EXETYPE,
    RUNTIME_CLASS(CExeDoc),
    RUNTIME_CLASS(CChildFrame), // custom MDI child frame
    RUNTIME_CLASS(CExeView));
  AddDocTemplate(pDocTemplate);
*/
  pIDoc->CreateDocTempl(this, C_DOC_1);

  // create main MDI Frame window
/*  CMainFrame* pMainFrame = new CMainFrame;
  if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
    return FALSE;
  m_pMainWnd = pMainFrame;
*/
  pIDoc->CreateFrame();
  ...
}

To release interface:

int CExeApp::ExitInstance() 
{
  // TODO: Add your specialized code here and/or call 
  // the base class
  ReleaseDll();  

  return CWinApp::ExitInstance();
}

void CExeApp::ReleaseDll()
{
  pIDoc->Release();
}

conclusion:

The presented inteface class is the minimum needed. It should be advanced for your particular case. Applications may change frame, menu, icon, some aspects of its view, etc. using the same Dll.

Downloads

Download demo project - 66 Kb