Click here to Skip to main content
15,881,600 members
Articles / Desktop Programming / MFC
Article

Plug-in DLLs and Menu Interfaces

Rate me:
Please Sign up or sign in to vote.
4.76/5 (20 votes)
14 Sep 2005CPOL5 min read 78.4K   3.3K   104   13
An article on installing a menu interface for a load-on-demand DLL.

Before and After

Introduction

This article shows how to add a menu interface to an application from a DLL at any time.

This example was written using VC++.NET 2003 but it should translate to VC++ 6.0 easily, as much of this was developed using 6.0 early on. No managed code was harmed in the process of writing this article.

Background

I wanted a flexible way to load a DLL into my application to test it in-house and not leave any marks when the customer got the application. Future enhancements could include targeted computer-based training modules.

This is not meant to be a be-all end-all treatise, but, rather, a springboard for extending applications after the application has been written.

Using the code

There are two parts to this problem: the plug-in DLL and the target application. The target application needs to know how to call the plug-in DLL without the benefit of lib file. We accomplish this by standardizing the plug-in interface. The interface for this project is in the TestPlugin project in plugin_api.h. There are other ways of handling the interface. For instance, you could create a structure of function pointers and populate it after LoadLibrary and clean it out before FreeLibrary. In this example, you only have to store the HMODULE value. If you had more than one plug-in DLL, you would only need to store the HMODULE values and not have to carry many structures of the function pointers.

Let's talk about the plug-in DLL first.

The Interface: There are four methods defined publicly for this DLL in TestPlugin.def. They are InstallExtMenu and RemoveExtMenu which install and remove the menus respectively, GetExtMenuItemCount which gives the application the number of menu items installed, and GetExtMenuItem which serves to map the menu control ID to a Windows message identifier. More can be done to extend the interface but this appears to be the bare minimum to get this up and running. The file plugin_api.h handles the details of connecting to the correct DLL based on a save HMODULE value from LoadLibrary.

CTestPluginApp: There are two ways of introducing user-defined Windows messages. One is defining a value greater than WM_USER; the other is to get a value using RegisterWindowMessage. WM_USER is useful for messages internal to an application. RegisterWindowMessage is useful when a particular message may be used across applications. We are using RegisterWindowMessage because this DLL may be servicing more than one application and because other DLLs can also use the registered messages. The registered messages are really static UINTs, attached to the CTestPluginApp object and initialized when the DLL is loaded. CTestPluginApp also contains the menu ID registered to the message ID map which is used by GetExtMenuItem to return the registered message when the menu item is selected. You will notice that the map class used is MFC's CMap<> template. My only reason for using it here is to maintain the MFC framework and not to clutter the code by importing STL. My personal preference is to use std::map over CMap.

CCommandWnd: This window receives the registered message from the target application. When the application initializes the plug-in DLL, it passes an HWND to the DLL so that the DLL can set up the menus for that window. In addition to setting up the menus, the DLL also creates a CCommandWnd window as a child to the window passed in.

Now let's talk about the target application.

  1. CMainFrame: Okay, so making the CMainFrame window responsible for maintaining the menus and how they are handled is a bit arbitrary. You could do this either from the CView-based window or the CDocument object. I wanted to keep all of the code for handling the menus and the plug-in in one place and CMainFrame seemed to be the easiest way to handle it. There are some really good reasons for relocating the code to the CDocument or CView object and much of what I am presenting here can be translated with little work to the other CDocument and CView. When the function InstallExtMenu is called, the HWND for CMainFrame is sent along with a value which will be used to identify the CCommandWnd from the plug-in. The plug-in creates the CCommandWnd as a child window to CMainFrame and the CCommandWnd window can be retrieved using CWnd::GetDlgItem.
  2. CMainFrame::OnCommand: This is where we translate the menu command ID to the registered message used by the plug-in DLL (the red indicates the plug-in's interface):
    BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
    {
        // if wParam translates to our internal message
        // then send the internal message
        UINT nSendMsg = 0 ;
        if (::GetExtMenuItem(m_TestModule, (UINT)wParam, &nSendMsg) != FALSE)
        {
            CWnd * pWnd = GetDlgItem( CHILD_WINDOW_ID ) ;
            if ( pWnd != NULL && pWnd->GetSafeHwnd() != NULL )
            {
                // if ::GetExtMenuItem returns TRUE and we have the child 
                // window then send the message to the child
                return (BOOL)pWnd->SendMessage( nSendMsg, 0, 0 ) ;
            }
        }
        return CFrameWnd::OnCommand(wParam, lParam);
    }
  3. CMainFrame::OnCmdMsg: CFrameWnd contains a member variable called m_bAutoMenuEnable which is used to disable menus that do not have ON_COMMAND or ON_UPDATE_COMMAND_UI handlers. The menus that the plug-in installs do not have native handlers, so you would think that we need to set m_bAutoMenuEnable to FALSE to enable our menus. Unfortunately, this would also enable other menus that we may not want enabled. Fortunately, we don't need to bother with m_bAutoMenuEnable. By overriding CFrameWnd::OnCmdMsg, we can ask the plug-in (if it is loaded) if it owns this menu and enable it if it does:
    BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, 
                               AFX_CMDHANDLERINFO* pHandlerInfo)
    {
        if ( nCode == CN_COMMAND )
        {
            // if nID translates to our internal message
            // then enable the menu item
            // otherwise, let OnCmdMsg() handle nID.
            UINT nPostItem = 0 ;
            // does the plugin own this menu item?
            if ( ::GetExtMenuItem( m_TestModule, nID, &nPostItem ) != FALSE )
            {
                return TRUE ; // if yes, then enable it by returning TRUE
            }
        }
        // otherwise, let the CFrameWnd handle it 
        return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
    }

Points of Interest

I have set up a macro called _PLUGIN_ON_DEMAND in stdafx.h. If you undef this macro, the CTargetApp will try to load the DLL in its InitInstance method and unload it in the ExitInstance method. I have also included an alternate IDR_MAINFRAME menu (with the subtitle ALTMENU) that you can use when the _PLUGIN_ON_DEMAND is not defined to show how the menus can be added when the top-level Tests menu does not exist.

History

This is version 1.0!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAwesome Pin
Michael B Pliam14-Oct-11 7:34
Michael B Pliam14-Oct-11 7:34 
AnswerRe: Awesome Pin
Michael Bergman14-Oct-11 13:46
Michael Bergman14-Oct-11 13:46 
GeneralPlease traslate it into VC++6.0 Pin
Andrewpeter7-Feb-11 2:27
Andrewpeter7-Feb-11 2:27 
GeneralRe: Please traslate it into VC++6.0 [modified] Pin
Michael Bergman19-Feb-11 23:30
Michael Bergman19-Feb-11 23:30 
GeneralThank Pin
whfount15-Oct-06 21:03
whfount15-Oct-06 21:03 
I think the article is very good , I hope author give more.
GeneralExtension DLLs Pin
f26-Jan-06 20:43
f26-Jan-06 20:43 
GeneralRe: Extension DLLs Pin
Michael Bergman9-Jan-06 7:40
Michael Bergman9-Jan-06 7:40 
GeneralRe: Extension DLLs Pin
f29-Jan-06 8:04
f29-Jan-06 8:04 
GeneralRe: Extension DLLs Pin
Michael Bergman9-Jan-06 14:39
Michael Bergman9-Jan-06 14:39 
Generalback to Extension DLLs Pin
f29-Jan-06 20:24
f29-Jan-06 20:24 
GeneralNice Pin
Abu Mami15-Sep-05 7:19
Abu Mami15-Sep-05 7:19 
GeneralVery Nice! Pin
WREY15-Sep-05 6:34
WREY15-Sep-05 6:34 
GeneralRe: Very Nice! Pin
Michael Bergman15-Sep-05 15:50
Michael Bergman15-Sep-05 15:50 

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.