Plug-in Architecture Framework for Beginners

Environment: VC6

In a typical application, a design is implemented that meets the set of requirements at the time of development. Often, after a program is delivered, the user will want added functionality, or different users will require custom functionality based on their specific needs. In order to accommodate these situations without a complete re-write, or causing a develop/compile/test/ship scenario, a framework that allows for future additions of modules without breaking the existing code base needs to be implemented. A plug-in architecture will meet these needs.

What is a plug-in architecture? Simply speaking, it is a framework that will allow a program to "look for" add-in functionality at startup, and then allow that plug-in to cooperate with itself. Many applications, such as Microsoft Office, currently use similar technologies to allow third-party developers to integrate with their existing applications to add functionality or robustness otherwise missing from the application.

How can you make a plug-in architecture model work for you? A very simple way to implement plug-ins is to create an application that will use DLLs to access the plug-in's functionality. When the app starts, it looks to a pre-determined directory for any DLLs that have the desired functionality. After querying these DLLs, the program can call methods based on a generic interface to which all the DLLs subscribe.

The sequence of events are as follows.

  1. Program initialized.
  2. On_Init looks for DLLs in a given directory, for example, plug-ins and so forth.
  3. Program calls a method named "load" on each proper DLL found.
  4. After load is called, program caches the name of each module and creates a reference so that the functions in this plug-in can be called later.
  5. During the course of running the program, the user selects one of the menu items, and one of a set of clearly defined standard methods are invoked.
  6. Upon shutdown of program, a method called "unload" will be called to free the resources, which were allocated in the "load" method.

Here is an example of a C++ Plug-in framework.

To make things quite simple, design a DLL with the Visual Studio DLL wizard. Make an export to a function called fnPlug1 that takes no parameters, but returns an integer as follows.

#define PLUG1_API __declspec(dllexport)
extern "C" PLUG1_API int fnPlug1(void);

Now, let's give the DLL something to do so that we can see that it worked.

Add the implementation to your DLL function as follows.

PLUG1_API int fnPlug1(void)
{
  return 1234;
}

Of course, in a real-world scenario, you would want to make something more robust than just returning a static integer.

In order to get this DLL to behave as a plug-in, we will need to set up a client program that will drive that process. Our goal will be to find all the DLLs, call LoadLibrary() on them, and store the resulting HMODULE so that we can refer to it later.

Here is that example. (Note that we are using the extension .PLX instead of .DLL.)

void CPluginDriverDlg::OnLoad()
{
  char filepath[MAX_PATH];

  //who are we really? Get the Exe Path
  GetModuleFileName(AfxGetApp()->m_hInstance,filepath, 
MAX_PATH-1);
  SetCurrentDirectory(ExtractFilePath(filepath));
  CFileFind finder;
  CString strWildCard = _T("*.plx"); //look for the 
plugin files

  //call this to set up the finder to iterate through all 
  //the plugins
  BOOL bWorking = finder.FindFile(strWildCard);
  while (bWorking)
  {
    //have to call 
FindNextFile() before GetFileName() or GetFilePath()
    //because FindFile just sets the object up and returns
    //true if _ANY_ files were found
    bWorking = finder.FindNextFile();

    HMODULE hm = LoadLibrary(finder.GetFilePath());
    if ( !hm )
    {
      MessageBox("couldn't load");
    }
    else
    {    //loaded OK, so add each library's HMODULE to an array.
      //m_dwa is an MFC CDWordArray
      m_dwa.Add((DWORD)hm);
    }
  }
}

Then, when you want to iterate through your plug-ins, you refer back to the HMODULE that you stored (for each plug-in) and after getting the address of the function, you call that function.

void CPluginDriverDlg::OnRunPlugins()
{
  for(int i=0; i<m_dwa.GetSize() ; i++)
  {
  //Find a function and use it
    PFUNC pFunc = (PFUNC)GetProcAddress(
    (HINSTANCE)m_dwa.GetAt(i), _T("fnPlug1"));
    if (pFunc != NULL)
    {
      int n = pFunc();
      CString answer ;
      answer.Format("The answer is %d", n);
      MessageBox(answer);
    }
  }
}

In case you were wondering, PFUNC is declared as:

typedef int (*PFUNC)(void);

One thing that you will want to make sure and do is to clean up after yourself and release all of the DLLs from memory. That is accomplished by freeing the previously loaded DLLs with FreeLibrary(). I chose to do that on the DestroyWindow() method of my test dialog.

BOOL CPluginDriverDlg::DestroyWindow()
{
  for (int i=0; i<m_dwa.GetSize() ; i++)
  {
    // Free all the libs we used
    FreeLibrary((HMODULE)m_dwa[i]);
  }

  return CDialog::DestroyWindow();
}

That is really about all there is to it. You can compile as many plug-ins as you like, and place them in the same folder as your executable; then, when you are ready, you invoke the procedure that executes the functions in the plug-ins. Keep in mind that this is a very simple example, and could easily be extended to a more robust model that lets you pick which plug-in you would like to run.

Downloads

Download source and demo project - 46 Kb


Comments

  • None

    Posted by Legacy on 02/21/2004 12:00am

    Originally posted by: Achille Komla

    Thanks for your point made in this article. I have been looking for some explaination like this one. But one thing that i will beg u to provide me is how to use "Visual Studio DLL wizard" to set up this type of Plugin application. I tried to put your code together and get it working but the executable could not be found when the program run. Please provide me with your insight.

    Reply
  • debugger error: user breakpoint called from code @...

    Posted by Legacy on 02/20/2003 12:00am

    Originally posted by: mahnaz saedy

    Hi,
    
    First, I would like to thank you for your great article on Plug-in Architecture Framework for Beginners. I also have a question. I created my own PluginDriverDlg and added the following code to call the function "fnPlug1".

    HMODULE hm = LoadLibrary(dlg.GetPathName());
    typedef int (*PFUNC)(void);
    if ( !hm )
    {
    AfxMessageBox("couldn't load");
    }
    else
    {
    AfxMessageBox("loaded it");
    //Find a function and use it
    PFUNC pFunc = (PFUNC)GetProcAddress(hm, _T("fnPlug1"));
    if (pFunc != NULL)
    {
    int n = pFunc();
    CString answer ;
    answer.Format("The answer is %d", n);
    AfxMessageBox(answer);
    }


    When I load Plugin1.plx there is no problem but when I load MFCPlugin1.plx I get debugger error: User breakpoint called from code at 0xbff76a0.

    I think something is wrong when I load a DLL with a dialog.
    Thanks for any help.

    Mahnaz Saedy


    Reply
  • delphi and plug-ins

    Posted by Legacy on 12/26/2002 12:00am

    Originally posted by: benedito r. almeida filho

    hi,

    i use delphi and i'd like to know if it's possible to use plug-ins in a delphi system.

    thanks.

    Reply
  • WOW IT IS GREAT

    Posted by Legacy on 12/24/2002 12:00am

    Originally posted by: Tarundeep Singh

    real good article....
    very informative indeed ...
    especially the commects on GUI
    ...pls do publish such more informative articles in future
    regards
    tarun

    Reply
  • Security Issues.

    Posted by Legacy on 09/07/2002 12:00am

    Originally posted by: Andrew

    I am interested in creating a system similar to the one designed here, but I'm concerned with the potential security issues involved in allowing third party programmers to design plug-ins for my application, since these plug-ins may likely be run on a machine other then the plug-in's creator the potential to do damage is there.

    Have you addressed any of these issues? Or could you tell me how, or where to find information regarding over coming these issues?

    The risk I am conserned with most is the potential for the plug-in to modify files or execute other programs.

    Thanks,
    Andrew

    Reply
  • Basic Questions

    Posted by Legacy on 07/31/2002 12:00am

    Originally posted by: Chilih

    Thanks for the article on writing plug-ins, I am having trouble compiling the examples because with VisualStudio they start your project off with many files other than the ones you want to create. Should I just create an empty .dll project and just have one .cpp and one .h file. Thanks for any help.

    Reply
  • You're good job!.

    Posted by Legacy on 06/13/2002 12:00am

    Originally posted by: jeff

    Your explanation is very useful. Thanks a lot.
    Have a good day :)

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • A global data storage provider whose business is booming needed a best-in-class data center to serve as the backbone of its technical operations going forward—and it needed it delivered within a year.

  • Live Event Date: September 16, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you starting an on-premise-to-cloud data migration project? Have you thought about how much space you might need for your online platform or how to handle data that might be related to users who no longer exist? If these questions or any other concerns have been plaguing you about your migration project, check out this eSeminar. Join our speakers Betsy Bilhorn, VP, Product Management at Scribe, Mike Virnig, PowerSucess Manager and Michele …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds