Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / MFC
Article

Using a Doc/View exported from a dynamically loaded DLL (SDI)

Rate me:
Please Sign up or sign in to vote.
4.93/5 (13 votes)
6 May 20025 min read 199.3K   4.8K   75   42
An article on on how to load DLLs which export views into a SDI Application

Sample Image - SdiCViewDll.gif

Introduction

In a recent project I needed to be able to dynamically load CFormViews into the MainFrame of my application. I searched the developer sites and found a couple articles that explained how to load document/views into MDI applications but unfortunately, I needed to use the SDI approach. I searched MSDN and found even more articles discussing the technique under MDI's. I eventually decided that it shouldn't be too difficult and here is the result.

I started out by referencing an article on Codeguru.com entitled "Replacing a view in a doc-view application" by Jorge Lodos Vigil. That article gave me the basics for switching view in an SDI application. The next problem to solve was how to export the CFormView (or CView derived class) from a DLL and what kind of DLL? In the MFC AppWizard(dll) you have three types of DLLs that you can create, they are:

  • Regular DLL with MFC statically linked
  • Regular DLL using shared MFC DLL
  • MFC Extension DLL (using shared MFC DLL)

After some research and the usual trial and error, I discovered that the third choice MFC Extension DLL was the proper DLL type for my requirements. An MFC Extension DLL will only work from an MFC application and in this case that's exactly what was needed. There are other distinctions between the types of DLLs and I urge you to discover them on your own.

The DLL implementation

After creating an MFC Extension DLL project go to the ResourceView in the Workspace and create a new Dialog Resource of type IDD_FORMVIEW. Next select 'Insert|New Class... ' from the main menu and create a new CFormView derived class using the new dialog as the Dialog ID. Add the controls and functionality as you normaly would and then add this code under the DllMain(), ensuring that you replace the variables as explained in the code.

extern "C" AFX_EXT_API UINT Init(CRuntimeClass** view)
{
    // Replace YourDLLName with the AFX_EXTENSION_MODULE variable above 
    // your DllMain.
    new CDynLinkLibrary(YourDLLName);	
    // Replace CYourClass with the name of CView derived class you are exposing.
    *view = RUNTIME_CLASS(CYourClass);
    return(0);
}

That's pretty much all you need to do for the DLL side of things. Looks pretty simple, right? Let's move onto the main application or the 'host'.

The 'Host' application

There are endless ways that we can set up the ‘host’ application to load up the DLLs and import the views. In this example I’m not going to do anything fancy, as I want you to understand how to accomplish the task. I will explain the method I have chosen, based on my requirements, at the end of the article so stay tuned.

Create an MFC executable project using the Single Document (SDI) option, you can modify all the options in the Appwizard but on the last page select CView or a CView derived class as the Base class.

Select the ResourceView tab in the Workspace, open the Menu treeitem then double-click on the IDR_MAINFRAME and add a menuitem under the View menu or create a new menu - it makes no difference. Then select your new menuitem and bring up the Class Wizard and add command routing for it, here is the code that will load up the DLL, grab the exported function and retrieve the CRuntimeClass.

void CMainFrame::OnViewLoadviewfromdll() 
{
    typedef UINT ( * LPDLLFUNC)(CRuntimeClass**);
    LPDLLFUNC lpfnDllFunc = NULL;
    HINSTANCE hDLL = NULL;     	  
    hDLL = LoadLibrary("InitialContact.dll");
    
    if(hDLL)
    {
        lpfnDllFunc = (LPDLLFUNC)::GetProcAddress(hDLL,"Init_");
        if (!lpfnDllFunc)
	{
	    AfxMessageBox("Function not found in DLL");
	    FreeLibrary(hDLL);
	    return;
	}
	CRuntimeClass* pNewViewClass;
	lpfnDllFunc(&pNewViewClass);
	ASSERT(pNewViewClass);
	CSdiDllFramesDoc* pDoc = (CSdiDllFramesDoc*) GetActiveDocument();
	ASSERT(pDoc); // Hmm, do we need this?
	pDoc->SwitchToView(pNewViewClass);	
    }
    else
    {
        AfxMessageBox("Dll not found!");
    }
}

For clarity, I present the code from Jorge Lodos Vigil's article referenced above (comments removed):

BOOL CYourDoc::SwitchToView(CRuntimeClass* pNewViewClass)
{
   CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
   CView* pOldActiveView = pMainWnd->GetActiveView();

   if (pOldActiveView->IsKindOf(pNewViewClass))
      return TRUE;
	
   ::SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, 0);

   CCreateContext context;
   context.m_pNewViewClass = pNewViewClass;
   context.m_pCurrentDoc = this;
   CView* pNewView = STATIC_DOWNCAST(CView, pMainWnd->CreateView(&context));
   if (pNewView != NULL)
   {
      pNewView->ShowWindow(SW_SHOW);
      pNewView->OnInitialUpdate();
      pMainWnd->SetActiveView(pNewView);
      pMainWnd->RecalcLayout();

      pOldActiveView->DestroyWindow();
      return TRUE;
   }

   return FALSE;
}

There really isn't anything new here. We have typedef'ed a function pointer and created a variable of that type. We then loaded the library, retrieved the address of our exported function and loaded that into our function pointer. Then, using a CRuntimeClass we passed that into our exported function via the function pointer. We then grab a pointer to our CDocument class and call the member SwitchToView() passing in the CRuntimeClass that we retrieved from the DLL.

All in all it was much easier than I thought and I say that with about three failed attempts, having started with the first DLL type and working my way down the list.

An Advanced Example

The requirements in my implementation were that the menuitems were to be read from a database. The table in the database held the following information:

  • Menu Caption
  • Menu ID
  • DLL Name
  • Function Name

With that I decided upon this class framework to hold the information read in from the database. I also provided the class the ability to LoadPlugin() and UnLoadPlugin() the DLLs. Next, I created a CMap to hold each instance of the HSMenuItem class and set the menu id up to be the key. This seemed to be a logical choice and after you see how I handle the menu selections you'll see the ease of use and extensibility that the 'host' application provides. Here's the HSMenuItem class:

class HSMenuItem
{
public:
    HSMenuItem(CString _MenuCaption, DWORD _MenuID, 
               CString _LibraryName, CString _FuncName) :
               m_MenuCaption(_MenuCaption), m_ButtonID(_ButtonID), 
               m_LibraryName(_LibraryName), m_FuncName(_FuncName), 
               m_bLoaded(FALSE)
    {
        m_lpfnDllFunc = NULL;
        m_hDLL = NULL;
    }
    ~HSMenuItem()
    {
        if(m_bLoaded)
        {
	    UnLoadPlugin();
	}
    }
    BOOL LoadPlugin()
    {
        m_hDLL = LoadLibrary(m_LibraryName);
	m_lpfnDllFunc = (LPDLLFUNC)::GetProcAddress(m_hDLL,m_FuncName);
	if (!m_lpfnDllFunc)
	{
            FreeLibrary(m_hDLL);
	    return(FALSE);
        }
        m_bLoaded = TRUE;
	return(TRUE);
    }
    BOOL UnLoadPlugin()
    {
        if(m_hDLL)
	{
	    m_lpfnDllFunc = NULL;
	    FreeLibrary(m_hDLL);
	}
	return(FALSE);
    }
    typedef UINT ( * LPDLLFUNC)(CRuntimeClass**);
    LPDLLFUNC m_lpfnDllFunc;

    CString m_MenuCaption;   // The caption on the menu
    DWORD   m_MenuID;    // The menu ID - used in OnCommand()
    CString m_LibraryName;    // The DLLs name
    CString m_FuncName;       // The initilizer function

private:
    BOOL m_bLoaded;
    HINSTANCE m_hDLL; 
};

typedef CMap <DWORD,DWORD,HSMenuItem*,HSMenuItem*> mapMenuItems;

Loading the Menus up from the database and dynamically creating them is an exercise left to the reader. I will show you how easy it was to handle the menu options. First I hooked up my menus to go through the message map using ON_COMMAND_RANGE routing that to my HandleMenu(UINT menuid) function that looks like this:

BOOL CMainFrame::HandleMenu(UINT menuid) 
{
    if (menuid != 0) 
    {
        HSMenuItem* mi = NULL;
	m_MenuItems.Lookup(menuid,mi);
        if(mi != NULL)
	{
	    CRuntimeClass* runtimeclass;
            mi->m_lpfnDllFunc(&runtimeclass);
	    CAlarmAssistantDoc* pDoc = (CAlarmAssistantDoc*) GetActiveDocument();
	    pDoc->SwitchToView(runtimeclass);
	}
    }
}

As you can see this is very flexible and will load up any CView derived class that you have exported from a DLL. I've found this approach very useful in projects where you can deliver optional functionality in stages. Even better, bug fixes do not require a full recompile of the application - just the DLL.

My thanks go out to Mr. Vigil for his SwitchToView() as well as all the other contributors to CodeProject.com.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Dave has been programming for the past 20+ years first on a variety of platforms and operating systems using various languages. As a hobbyist Dave cut his teeth on the Commodore Pet and the 64 coding in basic and then moving to 6502 ASM. Dave moved to the Amiga using 68000 ASM and then C. His knowledge of the C language offered the stepping stone for him to make his hobby his profession taking a position coding C on an AIX Unix platform. Since then he has worked on many flavors of Unix, QNX, Windows (3.11 – present), and has been coding games for his Pocket PC in his spare time.

Dave lives in Indiana with his two teenage daughters and two cats.

Comments and Discussions

 
Question"Using a Doc/View exported from a dynamically loaded DLL (SDI) " do not be downloaded Pin
Member 1041359320-Nov-13 13:53
Member 1041359320-Nov-13 13:53 
GeneralMy vote of 5 Pin
JasMineLeaf14-Oct-13 0:19
JasMineLeaf14-Oct-13 0:19 
GeneralAdd _AFXDLL into the EXE project to solve the RUNTIME_ERROR of this demo Pin
Jaken Tong7-Nov-09 6:25
Jaken Tong7-Nov-09 6:25 
QuestionRuntime Error using VC++ 8 Pin
batang_igat9-May-07 21:50
batang_igat9-May-07 21:50 
QuestionCan't make it work with CHTMLView Pin
lviolette14-Jan-07 14:31
lviolette14-Jan-07 14:31 
GeneralUse Custom Control Pin
redjes11-Jan-07 18:27
redjes11-Jan-07 18:27 
Generaldll 32 and mfc Pin
azucop24-Jan-06 21:50
azucop24-Jan-06 21:50 
Questionat Win32 API ? Pin
desccode6-Mar-05 20:48
desccode6-Mar-05 20:48 
GeneralXP-Style and password edit control Pin
pinyi9-Sep-04 21:30
pinyi9-Sep-04 21:30 
Generalmdi Pin
tuxyboy15-Aug-04 19:14
tuxyboy15-Aug-04 19:14 
GeneralRe: mdi Pin
Dave Loeser16-Aug-04 4:15
Dave Loeser16-Aug-04 4:15 
GeneralCDocument access by the DLL Pin
MartinWN15-Jul-03 8:11
MartinWN15-Jul-03 8:11 
GeneralRe: CDocument access by the DLL Pin
Dave Loeser18-Jul-03 14:13
Dave Loeser18-Jul-03 14:13 
GeneralRe: CDocument access by the DLL Pin
iProgram (WangYue)21-Aug-03 23:51
iProgram (WangYue)21-Aug-03 23:51 
GeneralRegular DLL Displaying CFrameWnd Pin
Alain H.28-Jun-03 8:14
Alain H.28-Jun-03 8:14 
GeneralRe: Regular DLL Displaying CFrameWnd Pin
Dave Loeser18-Jul-03 14:28
Dave Loeser18-Jul-03 14:28 
GeneralRe: Regular DLL Displaying CFrameWnd Pin
Alain HJ.29-Jul-03 4:25
Alain HJ.29-Jul-03 4:25 
QuestionWhy not opening a database in the exported view? Pin
Chao Zuo7-Jun-03 3:14
Chao Zuo7-Jun-03 3:14 
AnswerRe: Why not opening a database in the exported view? Pin
Dave Loeser18-Jul-03 14:21
Dave Loeser18-Jul-03 14:21 
GeneralCan't use Custom controls in exported views Pin
DREVET Olivier6-Mar-03 0:49
DREVET Olivier6-Mar-03 0:49 
GeneralRe: Can't use Custom controls in exported views Pin
Dave Loeser7-Jun-03 8:57
Dave Loeser7-Jun-03 8:57 
GeneralRe: Can't use Custom controls in exported views Pin
Florian DREVET7-Jun-03 9:01
Florian DREVET7-Jun-03 9:01 
GeneralRe: Can't use Custom controls in exported views Pin
Dave Loeser9-Jun-03 2:15
Dave Loeser9-Jun-03 2:15 
GeneralRe: Can't use Custom controls in exported views Pin
galroy30-Dec-04 10:01
professionalgalroy30-Dec-04 10:01 
AnswerRe: Can't use Custom controls in exported views Pin
redjes11-Jan-07 20:53
redjes11-Jan-07 20:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.