|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIn a recent project I needed to be able to dynamically load 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
The DLL implementationAfter creating an MFC Extension DLL project go to the ResourceView in the Workspace and create a new Dialog Resource of type 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' applicationThere 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 Select the ResourceView tab in the Workspace, open the Menu treeitem then double-click on the 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 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 ExampleThe requirements in my implementation were that the menuitems were to be read from a database. The table in the database held the following information:
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 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
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 My thanks go out to Mr. Vigil for his
|
||||||||||||||||||||||