Solved

Creating a Plug-in Architecture

Posted on 2002-04-21
24
510 Views
Last Modified: 2013-11-18
Hi,
I'm working on several projects that I'd like to design in a way that modules can be downloaded after the release of the program to extend it's functionality. I'm wondering what the general way of going about creating these systems is. Would I use something like a DLL? To give you a better idea of what I'm trying to do I'll give you an overview of the main project I'd like to use this for:

I'm working on a MS VC++6 MFC based dialog application. I want to set it up so that it uses a tabbed view (like a property sheet). I'd like to code a base application that just has the dialog box and the ability to load plug-ins. Then I want to make plug-ins that create their own tab on the dialog box. This way if I have 3 plug-ins, my dialog box will have 3 tabs. I'd also like to be able to code the controls (on each tab) and their functionality into the plug-ins. So each plug-in would contain the data to create a tab in the dialog box, create and implement the functionality of the controls on each page, and a few functions to implement the functionality of the plug-in. I'm providing 300 points (this may rise) for this question so I expect to get a little more than suggestions from the experts (I'm not asking you to write my program, but code samples would be great).

If you'd like any more information please feel free to ask.

Thank you!
0
Comment
Question by:xebra19
  • 10
  • 6
  • 6
  • +2
24 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 6958695
Um, two words: Use COM. That is what you are looking for.
0
 

Author Comment

by:xebra19
ID: 6958771
Um, two words: Elaborate Please. That is what I am looking for. :-)
0
 
LVL 22

Accepted Solution

by:
ambience earned 300 total points
ID: 6958798
Both ways are possible, you can use COM and you can even get away with simple dlls.

Basically what you need to define here is the protocol of communication between the host application and the plug-in. You need to document the functions that the host will call and thus should be implemented by the plug-in. Also the sequence of the calling and constraints etc.

For ex. your host app may call Initialize(HWND, LPVOID data) just after loading a plug-in dll, giving it the handle to the TabCtrl, the plug-in must export this function and should implement code here to initialize and create a tab etc.

Then maybe there can be functions for QueryKillActive() called by host to determine if it is okay to switch to another tab., etc.

This all depends upon what you require.

How to do this, well, if using dlls , then as you see all dlls must export the functions required by the host, so your host will dynamically link to that dll.

If using COM , you need to package these functions into interface (one or more). and your plug-ins must implement all these interfaces. your host creates a plug-in pbject and queryinterfaces for the required interfaces.

Hope this helps you get started, if you have finalized the desing and protocol post some info. here, would be much better to help that way.
0
 

Author Comment

by:xebra19
ID: 6958845
ok, i'll work on that. Just curious about COM. What would the actual plug-ins be? (if I used DLLs the DLL files would be the plug-ins).

Also, this project is pretty small (maybe just 5 or 6 plug-ins). Which approach makes sense for this size of project? Which is easier to use? Which has better preformance?

Thanks.
0
 
LVL 22

Expert Comment

by:ambience
ID: 6958880
COM objects are going to be dlls physically. That makes sense since you are not bothering about having exe COM servers.

Easier to use is COM definitely, coz much of the hassle of dynamic linking etc goes away, and its a scalable arhitecture, you can easily support more interfaces without breaking ties with existing plug-ins, always possible to be easily compatible with different versions of plug-ins etc.

Performance again i think there is not much difference between using a dll or a COM object. Though there is overhead of COM runtime involved at places, but there are so many other benefits that you get from using COM runtime like apartments, component lifetime management.

COM dll share the address space of hosts so it is also possible to directly pass custom pointers to the plug-in,
i.e. pointers to non-automation types. If you are looking for events firend from plug-ins to the host app, COM is a definite advantage.

But dlls are perhaps easy to plug-in, all you need do is place them in plug-ins folder and the host can detect. COM would require you to register components and also a means of letting the host know about the CLSIDs etc.

so it depends upon the complexity of your project ..
0
 

Author Comment

by:xebra19
ID: 6958913
ok, I heard what you said and you seem to be in favor with using COM. However this app will only have about 5 plug-ins, it's essential that i can just drag and drop them into a plug-ins folder and they will work. Also, this app will be running the background the whole time the system is on, so I want as low overhead as possible. Because of the low amount of plug-ins needed, the complexity of making each one, shouldn't be a major factor (i won't have to make many). I'll work on defining a communication interface.

Just to get a better idea of how DLLs work. Can I just have my app invoke an initialization function in the DLL, and have code in the DLL do all the work (adding the tab to the dialog, and running the functions), or is it more complicated.

Last note: Can I store the resources (dialog boxes used for the individual tab pages) directly in the DLL?

I'm sorry to be asking so many questions (i know you are all busy, but I'm trying to understnad more about how to do this and would like to really understand it). I will offer more points if you really feel it's necessary.
0
 
LVL 22

Expert Comment

by:ambience
ID: 6958938
>> it's essential that i can just drag and drop them into a plug-ins folder and they
will work.

Quite easy to do with dlls, with COM dlls possible but a lot of extra work required.

>> Can I just have my app invoke an initialization function
in the DLL, and have code in the DLL do all the work (adding the tab to the dialog, and running the
functions), or is it more complicated.

Yes it can call the Initialization function and the function can do eveything, but it would kind of freeze the host application until the initialization returns.

If you require you can create a UI-thread inside the app for each plug-in loaded (although deosnt seems right). since you can have windows message loop within the dlls maybe you can use that to your advantage.

You can definitely store dialog boxes as resources in dlls. And even show and handle messages for dialogs.
0
 

Author Comment

by:xebra19
ID: 6958964
Each DLL will be controlling a serial port device connected to the computer (which in my current app (the one with no plug-ins) is handled using worker-threads). I don't think this would be to hard to modify to use DLLs.

Could you elaborate on what you mean by the program will wait until the initialization function returns? Would this mean that my thread should launch the init function of the DLL or the thread should be launched from inside the init function of the DLL.

Here's the structure i'm understanding so far (using a non-exsistant programming language, but you get the idea, i did this very quick so it probably makes no sense, but i'm just looking to see if i'm on the right track):

//Program
programmain()
{
createtabbeddialog();
findplug-ins();
loadplug-ins();
CreateThread(indll.pluginit);
}

//DLL
pluginit()
{
addtabbtodialog();
controlserialdevice();
}
0
 
LVL 22

Expert Comment

by:ambience
ID: 6959118
>> (adding the tab to the dialog, and running the
functions)

I thought that if your initialization function is going to be calling other functions , the control will only return to the host after initialize return , so probably freezing the UI. You dont need a thread for that if this is not so.

>> CreateThread(indll.pluginit);

can explain that ? heres another way, can you explain which one suits you best.

progmain()
{
..
plugin[i]->PluginInitialize(..)
}


PluginInitialize(...)
{
   // Create Tab
   // Create thread for controlling device.
}

this would require the plug-in to be multi-threaded.
0
 

Author Comment

by:xebra19
ID: 6959136
that's pretty much identical to what I did except the thread is created within the plug-in. If you look at my example the plug-in's init function does call other function: "controlserialdevice()". This function contains a loop that continuously feeds data to the device. So yes, if the rest of the program waits for the init function to return, the program would hang forever because the init function doesn't return until the application is quitted. Now do you understand what I was trying to say? Which makes more sense to you (starting the thread in the program or the plug-in)?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6959190
I have implemented a similar feature without COM.  Each plugin exports one or two defined entry points.  You simply search the directory for files named MyPlugIn*.Dll then do LoadLibrary and call the entry point.  That entry point can return additional entry points or other information useful to the main 'framework' program.

In my implementation, I did not provide property pages, but instead provided an entry point that did a DoModal() on a dialog within the DLL.  But I don't see any real problem with having each plugin export a CPropertyPage-derived object.

To handle the serial ports, just create a thread and call the DLL's exposed thread proc.

-- Dan
0
 
LVL 22

Expert Comment

by:ambience
ID: 6959249
>> Which makes more sense to you (starting the thread in the program or the plug-in)?

That is really difficult to say, you can have a better idea of that, considering what exactly you want to do.

But i say it doesnt really matters who starts the thread, the thread function has to be in the dll. so a plug-in shoulld itself start the thread, because only it knows when it has intialized enough to be able to control devices, or perhaps for the fact that something needs to be done only after a thread has been created, so it depends upon the situation.
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 32

Expert Comment

by:jhance
ID: 6959497
>> have implemented a similar feature without COM

In other words, you've implemented a scheme SIMILAR to COM but with a more limited feature set to better fit your needs.  The description:

"do LoadLibrary and call the entry
point.  That entry point can return additional entry points or other information useful to the main
'framework' program."

Sure sounds like COM.  But COM itself has a lot of "baggage" that may not be useful in a limited application and the performance hit from COM's overhead may be prohibitive...
0
 

Author Comment

by:xebra19
ID: 6960557
ok, I think I've got the idea here. I will award the points in a little bit. I will post a 50 point question (after the fact = "Points for MemberX") later, for someone who can in this question post code similar to what Dan said: "You simply search the directory for files named MyPlugIn*.Dll then do LoadLibrary and call the entry
point". Thanks!
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6960930
void CD02Dlg::OnButton1()
{
     CStringArray asDllNames;

     CFileFind cFF;
     BOOL fWorking= cFF.FindFile( "c:\\temp\\MYXMLGen*.Dll" ); // wildcard
     while (fWorking ) {
          fWorking= cFF.FindNextFile();
          CString sDllFile= cFF.GetFilePath();
          HMODULE hMod= ::LoadLibrary( sDllFile );
          if (hMod == 0 ) {
               // handle error, not a valid DLL
          }
          void* pFn= ::GetProcAddress( hMod, "MYXML_GenerateXmlToOpenDb" );
          if ( pFn == 0 ) {
               // handle error, DLL is missing the entry point
          }
          asDllNames.Add( sDllFile );
          ::FreeLibrary( hMod );
     }
     //------------- now to call an entry point:

     HMODULE hMod= LoadLibrary( asDllNames[0] );
     LPFN_GenerateXmlToOpenDb pfnMyEntryPoint;
     pfnMyEntryPoint= (LPFN_GenerateXmlToOpenDb)GetProcAddress( hMod, "MYXML_GenerateXmlToOpenDb" );
     int nRet= pfnMyEntryPoint( 17, 18,19, 20 );

...

==-=-=-=-=-=-
jhance,
Yes, it is COM without the 6-month learning curve and without all of the complexities that are required by the need to allow multiple programs to access a module simultaneously or to query for new interfaces; that is, it is COM for proprietaty API plugins.

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6960935
I forgot to put this above the fn:

typedef int  (CALLBACK* LPFN_GenerateXmlToOpenDb)( int, int, int, DWORD );

-- Dan
0
 

Author Comment

by:xebra19
ID: 6961571
thanks Dan. I will post your points question in a little bit.

Just curious: I'm using VC++6, i haven't worked with DLLs for quite some time (i understand how to use them though). What I'm asking is, which option should I specify when I start a new project (MFC AppWizard (dll), or Win32 Dynamic-Link Library).
0
 
LVL 22

Expert Comment

by:ambience
ID: 6961830
if yur going to use MFC in your plug-ins , you have to use MFC dll, otherwise Win32 regular dll would have a smaller foot print and more efficient.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6962131
Choose
    MFC AppWizard (dll)
and choose
   Regular DLL using chared MFC DLL
in the second page.  The generated app provides a CWinApp-derived class and a resource file that will come in handy.  Also heed the comments in the cpp file:  Use
   AFX_MANAGE_STATE(AfxGetStaticModuleState());
at the top of all fns that could be called... including constructors.

-- Dan



0
 

Author Comment

by:xebra19
ID: 6964345
ok, i added that plugin loading code to my apps init code and it creates a Debug Assertion Failure. It said it was in file: afxcoll.inl, on line 233. I'm not sure if i'm doing it wrong.

If we can't get this code working with cutting and pasteing, could you just send me a basic Win32 Dialog Application with this code already in it (just make a new project and put that code in so that it causes no errors). Once i get that working so that I have a basic application that loads all DLLs in a specified folder and calls their init functions i will close this question out (probably a split between DanRollins and ambience)
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6964633
That assert occurs when you request an element of a CStringArray that is out of bounds.  Odds are there were no matches with the wildcard so the asDllNames[0] fails.  You need to place some breakpoints in the code and single-step through it, watching the action and monitoring the variables.

No, I won't build a project for you.  But I'll be glad to help you get one running.

-- Dan
0
 

Author Comment

by:xebra19
ID: 6964674
thanks. that was it. However the program now preforms an illegal operation when it does this line:
int nRet= pfnMyEntryPoint( 17, 18,19, 20 );

I'd like to implement some error checking that will skip loading plug-ins if none are present. I just changed:
HMODULE hMod= LoadLibrary( asDllNames[0] );
    LPFN_GenerateXmlToOpenDb pfnMyEntryPoint;
    pfnMyEntryPoint= (LPFN_GenerateXmlToOpenDb)GetProcAddress( hMod, "MYXML_GenerateXmlToOpenDb" );
    int nRet= pfnMyEntryPoint( 17, 18,19, 20 );

to
if(fWorking)
{
HMODULE hMod= LoadLibrary( asDllNames[0] );
    LPFN_GenerateXmlToOpenDb pfnMyEntryPoint;
    pfnMyEntryPoint= (LPFN_GenerateXmlToOpenDb)GetProcAddress( hMod, "MYXML_GenerateXmlToOpenDb" );
    int nRet= pfnMyEntryPoint( 17, 18,19, 20 );
}

That seems to run the program if no valid plug-ins are found. However i've made no plug-ins yet, so i can't test. Does if statment make sense to you?

Note: Most experts can't identify those errors, and it takes forever (a premade project is easier), but you were able to diagnose the problems, so i'd prefer to implement the code into my current project.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6964785
>>Most experts can't identify those errors
Most experts and many raw beginners can solve that.  Had you taken the 30 seconds required to open the file afxcoll.inl and scroll to line 233 (or had you single-stepped through the program code), you could have had the intense satisfaction of solving this little problem yourself.

>> if (fWorking)...

Probably the easiest way to know if you have found any valid plugins is if asDllNames.GetSize() > 0 (that assumes you do something sensible in those places where I put
  // handle error,
in the example code.

Yes, it is critical that you not use:

    int nRet= pfnMyEntryPoint( 17, 18,19, 20 );

(or whatever you set up as your entry point params) if hMod is 0 (not a dll) or if pfnMyEntryPoint is 0 (does not export the required function).

-- Dan
0
 

Author Comment

by:xebra19
ID: 6964897
ok, i got it working. Thanks. I'll get the points taken care of soon!
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
In this article you will learn how to create a free basic website on Bitbucket, a git service provider. Polymer creates dynamic HTML components, which allow more flexibility than static HTML. This tutorial uses Ubuntu Linux but can also be done on W…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now