This tip assumes that you are familiar with DLL usage and the related terms like static/dynamic linking. If you have no idea how to use DLLs then read this tip after learning the DLL related basics here: http://msdn.microsoft.com/en-us/library/1ez7dh12%28v=vs.80%29.aspx.
My advice is that never use DLLs in your project if you don't have to because they are trouble. If you still have to fall back on putting some C/C++ code to DLLs (or you have to create a plug-in interface) and its up to you to design the DLL interface (that is used only by your C/C++ code) then read along. There are 3 zip files attached with Visual C++ 2008 solutions that demonstrate what I describe in this tip. You can download and use them as skeletons because their code is very simple an near to minimal - might be useful if you are just about to find out how does a simple DLL project look like in Visual C++.
What should a DLL interface look like in my opinion
First declare an interface (a class that has only pure virtual methods) in the public header file of your DLL. Its quite important to use only pure virtual methods! After this put the only exported function to your DLL that returns a pointer to this interface. After this in the private source files of your DLL you can derive a class from this interface and implement its methods. Now the users of the DLL just call the interface getter method of the DLL and then continue using it in a nice C++ish way.
DLL.h: (the public header of the DLL)
virtual void AddNumbersAndPrintResult(int a, int b) = 0;
__declspec(dllexport) I_DLLInterface* GetDLLInterface();
__declspec(dllimport) I_DLLInterface* GetDLLInterface();
Note that the above header file is from the zip file that contains the static linking solution. My personal favorite is in the zip file that contains the dynamic linking solution with delay loading. That solution is the most elegant, easy-to-port to other platforms, and that solution requires putting only the interface into the public header of the DLL!
Why is this good?
- It simply looks nicer.
- As a result of the thin linking interface between your DLL and the module that uses it - just one exported DLL function - its very easy to make this cross-platform because the ugly
ifdefs and platform dependent code is minimal. The thin interface makes it easy to implement platform independent delay loading.
- Its very easy to merge this DLL code into your application later as part of a refactorization if you decide to do so - its a breeze.
- Lets say you are writing a plug-in interface for your program - its very easy to put internal plug-ins to your executable by implementing the DLL interface inside your program. It becomes very easy to switch between an built in functionality and one that is implemented in a DLL, for your executable both kind is visible just as a simple interface pointer.
Versioning plug-in DLLs (my style)
There are a lot of ways to detect the version of plug-ins. For example by coding the version into the versioninfo resource of the DLL, coding the version to the filename, getting the version by calling a version getter exported function of the DLL... I use a totally different approach that has several advantages over the previously listed methods. Lets say I have a media player program and a codec plug-in for it with a specific interface. After some time I release a new version of the media player that has a modified interface but I decide to release the plug-in for older versions of the media player as well. I do this by writing a single DLL that supports both the old and the new version of the plugin interface. The public interface header of my plug-in DLL will contain both the old and the new version of the interface declaration, and it contains two exported functions: one of them returns the old, and the other returns the new interface pointer. The exported methods have different names of course:
GetMediaPlayerInterface2(). The DLL implements both interfaces and inside it can use a lot of shared code between the two implementations and I have to maintain only one project for that. I have a piece of code that can detect if a DLL exports some functions without actually loading the DLL. This is quite suitable to find out if a DLL is your plug-in DLL or not. Loading a library that is not a DLL is very dangerous anyway, because it executes its
DllMain() function inside your process while your
LoadLibrary() call is executing! So, here is another tip that contains the code that detects whether a DLL exports your desired function or not without loading the DLL: Checking for exported symbols/functions in a DLL without loading it
Some good advices for the usage of DLLs whose code is in C
I myself never use C in user applications and I'm generally against the use of C when a reasonably good C++ compiler is available on the specified platform. If you have the sources of a DLL that is written in C then you can do the following:
- You can create a C++ interface for that DLL with the method I mentioned above. After that you can see only a nice C++ interface from outside, the C stuff is wrapped in.
- If you are C fanatic or for some other reason you have to keep the C interface then you can still do this: In the public header of your DLL declare a function pointer type for each function that your want to export from the DLL. Declare a struct that contains all these pointers. Export a single function from the DLL that returns a const pointer to this struct. Somewhere in your DLL define an instance of this struct and fill it with the actual pointers to your function implementations. This method might be less comfortable than the usage of
__declspec(dllimport) but that is guaranteed to work only on windows with Visual C++. My struct + function pointer combination has all the advantages I described above with my C++ interface... (cross-platform, easy-refactorization, plug-ins, ...) The function pointer declarations might can be troublesome but you can not avoid that if you are working with dynamic linking...
About the attached files
Attached three zip files: they contain the same Visual Studio 2008 solution but two of them demonstrate this tip/trick with dynamic linking (normal and delay loaded versions) while the third uses Visual C++ specific static linking with
__declspec(dllexport)/__declspec(dllimport). From the dynamic linking versions I recommend using the code of the delay loaded version because that is much more elegant - you can find there a nice solution for delay loading.