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.
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.
- Program initialized.
- On_Init looks for DLLs in a given directory, for example, plug-ins and so forth.
- Program calls a method named "load" on each proper DLL found.
- 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.
- 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.
- 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.
Comments
None
Posted by Legacy on 02/21/2004 08:00amOriginally 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.
Replydebugger error: user breakpoint called from code @...
Posted by Legacy on 02/20/2003 08:00amOriginally posted by: mahnaz saedy
delphi and plug-ins
Posted by Legacy on 12/26/2002 08:00amOriginally 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.
ReplyWOW IT IS GREAT
Posted by Legacy on 12/24/2002 08:00amOriginally posted by: Tarundeep Singh
real good article....
Replyvery informative indeed ...
especially the commects on GUI
...pls do publish such more informative articles in future
regards
tarun
Security Issues.
Posted by Legacy on 09/07/2002 07:00amOriginally 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,
ReplyAndrew
Basic Questions
Posted by Legacy on 07/31/2002 07:00amOriginally 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.
ReplyYou're good job!.
Posted by Legacy on 06/13/2002 07:00amOriginally posted by: jeff
Your explanation is very useful. Thanks a lot.
ReplyHave a good day :)