Dynamic Link Libraries are useful when many applications may wish to use the same code. This can be achieved by supplying programmers with the source code and allowing them to use it in their applications. The programmers would 'statically link' to this supplied source code just as they might link to standard io functions.
This has two disadvantages:
- The programmer must have access to the source code and must be using the same programming language with the same compiler setup.
- Memory and disk space will be wasted if several programs which use the same library of code are running simultaneously.
Dynamic Link Libraries (DLLs) are compiled libraries of code which programmers may link to without having access to the source code, and which can be shared by several applications. The operating system only loads the DLL into memory if at least one application is using it. That single instance of the DLL is then shared by any further applications which need to use it.
The writer of the DLL should have supplied documentation and a header file. However, you could use the Dumpbin utility with the /EXPORTS option to see which functions a DLL exports for your use.
Implicit linking uses an Import Library. These have a .lib extension. This .lib file is used by the Linker when building your application and provides a means to resolve calls made to DLL functions, along with code to manage the loading and unloading of the DLL.
Explicit linking must be used in those rare cases in which the programmer does not know until run-time which DLL must be used. Calls to the ::LoadLibrary() and ::FreeLibrary() API functions must be used. Also, before calling a function in the DLL you must use ::GetProcAddress() to get the address of that function. Of course when using these API functions you must do the usual awkward error checking. Note also that it is possible to call a function with the wrong the set of parameters and cause horrible memory problems.
Note that an MFC application explicitly linking to an DLL should use the AfxLoadLibrary and AfxFreeLibrary functions instead. These functions have additional code to cope with MFC Extension DLLs (see below).
The Entry Point
The DLL must have an entry point. This is function which is called by the OS to initialize the DLL after loading, or to terminate it before unloading. When building a DLL we can define an entry point, but the default is a pre-defined function called _DllMainCRTStartup(). This in turn calls DllMain(), which is what we usually think of as the entry point. Unless you are doing anything unusual you can forget about _DllMainCRTStartup() and just focus on DllMain().
DllMain() is passed a parameter which tells it whether the OS is loading or terminating the DLL, and this allows the function to perform the appropriate actions.
You should not use MFC in DLLMain() because there is no guarantee that MFC has been initialized.
The compiled DLL code includes an export table, which the OS uses to lookup the addresses of functions, including the entry point. The export table is built using the Module Definition File which lists each function which the programmer wishes to make available to others, along with information about the functions. Module definition files have a .def extension.
The OS may lookup the function by name or by number. Each function in the DLL has a number, called its ordinal. The ordinals may be generated automatically or may be defined by the DLL's programmer in the .def file. You can use the .def file to specify that others can only link to a function by its ordinal. This saves space and allows you to change the name of the function later.
Implicit linking looks up based on the ordinal, so ideally a function's ordinal should always stay the same. Unless you specify your own ordinals the linker may generate different ordinals each time you build your application. This in turn means that Applications which have already been built to use your DLL may cease to link correctly.
Obviously maintaining the .def file is a bit tedious, so you can use the _declspec() macro in your function definitions instead, which causes the .def file to be generated automatically. However, this technique does not allow you to specify the ordinals, leading to the problem just described. MFC provides more macros such as AFX_CLASS_EXPORT to make exporting even easier but still does not let you specify ordinals.
Unfortunately maintaining your own .def file in order to specify your own ordinals is not very easy in C++, because the names which should be used are not particularly obvious, and depend on the compiler which you are using. C++ compilers create 'decorated' function names in order to represent operator overloads and to distinguish between overloaded functions. You must discover what these names are in order to put them in the .def file. Luckily, in Visual C++, if you check the 'Generate mapfile' option in the project settings then a .map file is created which lists the decorated names. This .map file is not easy to read but with some effort you should be able to find the names you wish to export.
You should supply a header file with your DLL and .lib file, because just like any other functions called in a C++ program, the functions my be declared. If the DLL consists of C functions, then a C++ header file should declare them with an ' extern "C" ' prefix.
DLLs which use MFC
Regular MFC DLLs
Regular MFC DLLs can use MFC internally but cannot use MFC classes in the parameters of their exported functions. For instance, the calling application and the DLL cannot exchange information by means of a CString object. There are some rare exceptions to this rule.
Regular MFC DLLs do not require that the calling application use MFC also, so they are suitable for use by development environments other than Visual C++.
In order to initialize MFC, a Regular MFC DLL must have a CWinApp-derived object.
All exported functions in a Regular MFC DLL must use the AFX_MANAGE_STATE(AfxGetStaticModuleState()) macro at the start of the function. This macro protects the DLLs use of the MFC libraries from that of the calling application.
MFC Extension DLLs
MFC Extension DLLs can only be used by applications which also use MFC, which rules out applications written in Visual Basic. The advantage of this type of DLL is that the MFC application and the MFC Extension DLL can pass MFC objects between each other.
These DLLs do not need to have their own CWinApp-derived object. Instead the MFC state information is managed by a CDynLinkLibrary object, created by a call to AfxInitExtensionModule() in DllMain() when the DLL is being loaded. This function needs an AFX_EXTENSION_MODULE as a parameter, so you should make the following global declaration:
static AFX_EXTENSION_MODULE extExtensionName;
When the DLL is being unloaded you should call AfxTermExtensionModule() in DllMain().
Copyright © Murray Cumming. Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.