This tip assumes that you are familiar with DLL usage and 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.
My advice is to 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 it's 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 and near to minimal - it might be useful if you are just about to find out how a simple DLL project looks 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. It's 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 the dynamic linking solution with delay loading. That one is the most elegant and 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 - it's 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.
- It's very easy to merge this DLL code into your application later as part of a refactorization if you decide to do so - it's a breeze.
- Let's say you are writing a plug-in interface for your program - it's 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 kinds are 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. Let's 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 the declaration of both the old and the new version of the interface 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 piece of code is nice for many reasons: If you have to load a DLL to find out whether it is your plugin or not, then during your
LoadLibrary() call the
DllMain() of the library may be executed inside your process even if it is not your plugin. Another advantage is faster execution that may count a lot if your app has lots of plugins. Here is another tip that contains the code that detects whether a DLL exports your desired function(s) without loading it with
LoadLibrary(): Checking for exported symbols/functions in a DLL without loading it.
Advices to Design the Interface of DLLs Written 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:
- 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 you 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 may seem to be troublesome but you can not avoid that if you are working with dynamic linking. You can actually declare the function pointer types directly inside the interface
struct if you like that more.
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.