Click here to Skip to main content
Click here to Skip to main content

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

By , 6 May 2002
 

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

About the Author

Dave Loeser
Web Developer
United States United States
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralAdd _AFXDLL into the EXE project to solve the RUNTIME_ERROR of this demomemberJaken Tong7 Nov '09 - 6:25 
As I download this demo and setup my code by the instruction, I also met the error that my view class cannot be recongenized as a CView divered class, after step by step debugging, I found the different is that my project is built without macro _AFXDLL, so the CRuntimeClass::IsKindOf() goes into a diffent way to check the view class, then I add this macro into my EXE project and modify it's compile option /MT to /MDd, it works.
QuestionRuntime Error using VC++ 8memberbatang_igat9 May '07 - 21:50 
Runtime Error in STATIC_DOWNCAST statement.
How can this be prevented?
QuestionCan't make it work with CHTMLViewmemberlviolette14 Jan '07 - 14:31 
I've compiled this code and added my own CHTMLView derived class in an MFC Extension DLL. Added menu option to load it.
 
Everything seems to work fine until it calls SwitchToView with my new view. In CSdiDLLFramesDoc::SwitchToView, it fails on the
STATIC_DOWNCAST(CView, pMainWnd->CreateView(&context))
statement.
 
I'm using VS.NET 2005 SP1.
It's ASSERTing in CFrameWnd::CreateView on statement:
ASSERT_KINDOF(CWnd, pView);
 
However, the pView is in fact my CHTMLView derived class although its m_hWnd is NULL, but at this point it should be.
 
Can anyone else make this work with CHTMLView?
 
- lviolette

GeneralUse Custom ControlmemberMember #48493911 Jan '07 - 18:27 
for Use of Custom Control
 

--> USE CS_GLOBALCLASS
Ex) in Custom Control -> AfxRegisterClass()
 
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
 
You Can See Custon Control in Dll With [CS_GLOBALCLASS] style -> From MSDN(DynDlg: Dynamic Dialog Box Creation Sample)
 

sorry for my poor English -.-

Generaldll 32 and mfcmemberazucop24 Jan '06 - 21:50 
the code can use w32 and mfc dll. in mfc dll can use like function CString mfc. the sample same like in msdn. but the code not include project only code. azucop
Questionat Win32 API ?memberdesccode6 Mar '05 - 20:48 
I call to this Doc/View, in Win32 API ( Non MFC )
My Win32 App (Non MFC ) call the Doc/View ?
 
Please sample source code ?
 
sukru_uzel@mynet.com
 
Frown | :(
GeneralXP-Style and password edit controlmemberpinyi9 Sep '04 - 21:30 
Hi!
I have a CFormView derived View and when I put a Edit Control with Password property=True, I load this view, and the contents of edit control are strange, apear " |||||| " characters (not circles)... All other controls work correctly...

 
Josep Maria Pinyol Fontseca
Generalmdimembertuxyboy15 Aug '04 - 19:14 
I'm to implement dynamic view loading from dll's in an mdi app, and tried to use your sample suited to mdi app, but it doesn't work. It loads the class from the dll, but when it comes to displaying it, it just doesn't do it. No errors, but instead of the new view the old one appears without coloring. It's true that I implemented the SwitchToView function into CMDIFrameWnd's class, but I don't think this should make any difference. I found other approaches at different dev sites, but I don't want overridden DocManager, or having to register different doc templates at the Init of the App. I just want one doc handling a bunch of views. Any ideas or links you think I should look around at?
GeneralRe: mdimemberDave Loeser16 Aug '04 - 4:15 
Hi tuxyboy,
I think Roger Allen wrote an article that does what you are wishing to do - you can find it here: http://www.codeproject.com/docview/docviewfromdll.asp[^]
 
Dave Dak Lozar Loeser
The WM_NULL message performs no operation. An application sends the WM_NULL message if it wants to post a message that the recipient window will ignore. - MSDN

GeneralCDocument access by the DLLmemberMartinWN15 Jul '03 - 8:11 
The code you've posted here is great! I'm new to DLL development so my question is probably a well-known question. The executable that has loaded the DLL into its process space is an SDI. The DLL is a CFormView. How do I get the CFormView to access the SDI's data such as: CChartDoc* pDoc = GetDocument; which would be used in a CView class in the SDI project?
 
Thanks, Martin
GeneralRe: CDocument access by the DLLmemberDave Loeser18 Jul '03 - 14:13 
Hi MartinWN and thanks for the complement. I've not been coding with MFC much lately, I'm hooked on WTL and have been learning C#, so I can not give you a proper (read correct) answer to your question. However, I can give you a clue or some insight on how you could expose the CMainFrame to the CFormView (again, someone that has been coding MFC will probably have a better method).
 
In my WTL project, that I will post the code and article real soon now, I've been implementing much of the same functionality that this articles example describes. Basically, I'm making use of an Abstract Interface to expose my classes in a dll to the host application and the same can be done from the host. Without going into much detail here you can read the new article on my website - note that it is a work in progress... but it should give you an ideal how to better implement your work.
 
Dave Dak Lozar Loeser
When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. - MSDN:C# Programmer's Reference

GeneralRe: CDocument access by the DLLmemberiProgram (WangYue)21 Aug '03 - 23:51 
Hi Martin,
Do you have the answer now? I want to ask the same question.
GeneralRegular DLL Displaying CFrameWndmemberAlain H.28 Jun '03 - 8:14 
Hi
I Wrote a Plugin (with a Regular DLL) for an SDI Application. The DLL display a CFrameWnd and doc/CFormView in separate Window. When the DLL is called from the SDI Application, the CFrameWnd and the Doc/CFormView are displayed as well. When the Focus is in the CFormView and the user press a Ctrl-Copy or Ctrl-Paste in the CFormView the Message doesn't dispatched to the CFrameWnd but to the SDI application. in fact, the WM_PASTE or WM_COPY and not handled in the CFrameWnd of my Plugin but handled in the calling application and the text is copied/pasted inside !!!
 
The SDI application is a separate software that I can't modify it, so can anybody tell me how to handle WM_PASTE or WM_COPY in this case or STOP dispatching the WM_PASTE and WM_COPY to the main application when the plugin is focused.
 
I tried to define a proper accelerator for CFrameWnd but it doesn't work.
Any idea or comment will be appreciated
 
Thanks a lot
 
Alain.
GeneralRe: Regular DLL Displaying CFrameWndmemberDave Loeser18 Jul '03 - 14:28 
Hi Alain,
Did you write the SDI application? Since I've all but abandoned coding MFC - I've begun to forget things about the message proc... My best guesses would be that you need to code the mainframe to forward messages to the formview.
 
I hope that gives you a start…

 
Dave Dak Lozar Loeser
When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. - MSDN:C# Programmer's Reference

GeneralRe: Regular DLL Displaying CFrameWndmemberAlain HJ.29 Jul '03 - 4:25 
Hi Dave,
 
Thanks a lot for you help, in fact the SDI application is a third party software. If it's mine I think don't need any plugin for it but your suggestion was a way to solve my probleme.
 
I found the best way to solve the problem. In fact when then DLL is called, the CFrameWnd is dispayed and handled in the same thread of the SDI application, so before instanciating the CFrameWnd and the doc/CFormview I created a new thread and i attached the CFrameWnd to it so when then user focus the CFormView, all the messages have been dispatching to the CFrameWnd.
 
Thinks for your help.
 
Alain
QuestionWhy not opening a database in the exported view?memberzuoch7 Jun '03 - 3:14 
Hi,
In fact, the database could be opened , insert and delete records in the exported view.But when I quited the program, it haved some truble,"0xC0000005:Access Violation".Why?
Pls help me.thx
AnswerRe: Why not opening a database in the exported view?memberDave Loeser18 Jul '03 - 14:21 
Hi Zuoch,
You're going to have to give me more info than that Smile | :)
Are you using ADO, ODBC...? I can tell you that I have successfully used ADO to access a SQL Server database and populate data in a View imported from a DLL.
 
Dave Dak Lozar Loeser
When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. - MSDN:C# Programmer's Reference

GeneralCan't use Custom controls in exported viewsmemberpimOOsse6 Mar '03 - 0:49 
Hi there,
 
I have a problem using custom control and advanced controls (such calendar) in exported views : the view cannot be loaded from the host (in SwitchView()).
 
Any help is welcome because I really need to use custom controls in the exported views.
 
Thanks in advance !
 
Florian
GeneralRe: Can't use Custom controls in exported viewsmemberDave Loeser7 Jun '03 - 8:57 
I'm looking into this... I think it may have something to do with InitCommonControls() - when I find out, I'll let you know. Or if anyone else has insight into this issue, please comment...

 
Dave Dak Lozar Loeser
When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. - MSDN:C# Programmer's Reference

GeneralRe: Can't use Custom controls in exported viewssussFlorian DREVET7 Jun '03 - 9:01 
Hi Smile | :)
 
Thank you for your answer !! Don't forget to let me know how to achieve this if you find how to do Smile | :)
 
Cheers,
 
Florian
GeneralRe: Can't use Custom controls in exported viewsmemberDave Loeser9 Jun '03 - 2:15 
OK, I haven't tested this due to current schedule/time constraints but I believe that you need to add this to your CView based class in the construction of this view and before the controls are created -
 


INITCOMMONCONTROLSEX commoncontrols;
ZeroMemory(&commoncontrols, sizeof(INITCOMMONCONTROLSEX));
commoncontrols.dwSize = sizeof(INITCOMMONCONTROLSEX);
commoncontrols.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&commoncontrols);

 
Take a look at InitCommonControlsEx() as well as the INITCOMMONCONTROLSEX structure;
 
I hope that helps you some...
 
Dave Dak Lozar Loeser
When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. - MSDN:C# Programmer's Reference

GeneralRe: Can't use Custom controls in exported viewsmembergalroy30 Dec '04 - 10:01 
Hi Dave Loeser And pimOOsse
I have the same Problem with custom control I try the solution above but nothing happen. if you have any new idea or can you guide my how to use this solution I will be thank full.
 
Thanks in advance
Gal

AnswerRe: Can't use Custom controls in exported viewsmemberredjesred11 Jan '07 - 20:53 
for Use of Custom Control
 

--> USE CS_GLOBALCLASS
Ex) in Custom Control -> AfxRegisterClass()
 
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
 
You Can See Custon Control in Dll With [CS_GLOBALCLASS] style -> From MSDN(DynDlg: Dynamic Dialog Box Creation Sample)
 

sorry for my poor English -.-
 
red
GeneralJust for general informationmemberMazerim11 Feb '03 - 14:40 
Roll eyes | :rolleyes: I spent an hour or three banging my head against a simple issue. If you compile the host program as statically linked to MFC, it will crash during it's usage of externally linked CRuntimeClass classes.
 
Pretty much a Duh piece of info, but easy to overlook!
GeneralRe: Just for general informationmemberDave Loeser18 Jul '03 - 14:29 
Hi Mazerim,
 
Good discovery... thanks for pointing this out Rose | [Rose]
 
Dave Dak Lozar Loeser
When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. - MSDN:C# Programmer's Reference

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 7 May 2002
Article Copyright 2002 by Dave Loeser
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid