Dynamic Link LibrariesWe have now seen that memory management and file mapping are important and useful techniques in a wide class of programs. The OS itself also uses memory management, and DLLs are the most visible and important use of file mapping. DLLs are used extensively by Windows applications. DLLs are also essential to higher-level technologies, such as COM, and many software components are provided as DLLs. The first step is to consider the different methods of constructing libraries of commonly used functions. Static and Dynamic LibrariesThe most direct way to construct a program is to gather the source code of all the functions, compile them, and link everything into a single executable image. Common functions, such as ReportError, can be put into a library to simplify the build process. This technique was used with all the sample programs presented so far, although there were only a few functions, most of them for error reporting. This monolithic, single-image model is simple, but it has several disadvantages.
DLLs solve these and other problems quite neatly.
DLLs, sometimes in limited form, are used in nearly every OS. For example, UNIX uses the term "shared libraries" for the same concept. Windows uses DLLs to implement the OS interfaces, among other things. The entire Windows API is supported by a DLL that invokes the Windows kernel for additional services. Multiple Windows processes can share DLL code, but the code, when called, runs as part of the calling process and thread. Therefore, the library will be able to use the resources of the calling process, such as file handles, and will use the calling thread's stack. DLLs should, therefore, be written to be thread-safe. (See Chapters 8, 9, and 10 for more information on thread safety and DLLs. Program 12-4 and 12-5 illustrate techniques for creating thread-safe DLLs.) A DLL can also export variables as well as function entry points. Implicit LinkingImplicit or load-time linking is the easier of the two techniques. The required steps, using Microsoft Visual C++, are as follows.
Exporting and Importing InterfacesThe most significant change required to put a function into a DLL is to declare it to be exportable (UNIX and some other systems do not require this explicit step). This is achieved either by using a .DEF file or, more simply, with Microsoft C, by using the _declspec (dllexport) storage modifier as follows:
The build process will then create a .DLL file and a .LIB file. The .LIB file is the stub library that should be linked with the calling program to satisfy the external references and to create the actual links to the .DLL file at load time. The calling or client program should declare that the function is to be imported by using the _declspec (dllimport) storage modifier. A standard technique is to write the include file by using a preprocessor variable created by appending the Microsoft Visual C++ project name, in uppercase letters, with _EXPORTS. One further definition is required. If the calling (importing) client program is written in C++, __cplusplus is defined, and it is necessary to specify the C calling convention, using:
For example, if MyFunction is defined as part of a DLL build in project MyLibrary, the header file would contain:
Visual C++ automatically defines MYLIBRARY_EXPORTS when invoking the compiler within the MyLibrary DLL project. A client project that uses the DLL does not define MYLIBRARY_EXPORTS, so the function name is imported from the library. When building the calling program, specify the .LIB file. When executing the calling program, ensure that the .DLL file is available to the calling program; this is frequently done by placing the .DLL file in the same directory as the executable. As mentioned previously, there is a set of DLL search rules that specify the order in which Windows searches for the specified .DLL file as well as for all other DLLs or executables that the specified file requires, stopping with the first instance located. The following standard search order is used for both explicit and implicit linking:
Note that the standard order can be modified, as explained in the Explicit Linking section. For some additional detailed information on the search strategy, see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/loadlibrary.asp and the SetDllDirectory function, which was introduced with NT 5.1 (i.e., XP). LoadLibraryEx, described in the next section, also alters the search strategy. The standard search strategy is illustrated by the Utilities project on the book's Web site, and the utility functions, such as ReportError, are used by nearly every example project. It is also possible to export and import variables as well as function entry points, although this capability is not illustrated in the examples. Explicit LinkingExplicit or run-time linking requires the program to request specifically that a DLL be loaded or freed. Next, the program obtains the address of the required entry point and uses that address as the pointer in the function call. The function is not declared in the calling program; rather, you declare a variable as a pointer to a function. Therefore, there is no need for a library at link time. The three required functions are LoadLibrary (or LoadLibraryEx), GetProcAddress, and FreeLibrary. Note: The function definitions show their 16-bit legacy through far pointers and different handle types. The two functions to load a library are LoadLibrary and LoadLibraryEx.
In both cases, the returned handle (HINSTANCE rather than HANDLE) will be NULL on failure. The .DLL suffix is not required on the file name. .EXE files can also be loaded with the LoadLibrary functions. Pathnames must use backslashes (\); forward slashes (/) will not work. Since DLLs are shared, the system maintains a reference count to each DLL (incremented by the two load functions) so that the actual file does not need to be remapped. Even if the DLL file is found, LoadLibrary will fail if the DLL is implicitly linked to other DLLs that cannot be located. LoadLibraryEx is similar to LoadLibrary but has several flags that are useful for specifying alternative search paths and loading the library as a data file. The hFile parameter is reserved for future use. dwFlags can specify alternate behavior with one of three values.
When you're finished with a DLL instance, possibly to load a different version of the DLL, you free the library handle, thereby freeing the resources, including virtual address space, allocated to the library. The DLL will, however, remain loaded if the reference count indicates that other processes are still using it.
After loading a library and before freeing it, you can obtain the address of any entry point using GetProcAddress.
hModule, despite the different type name (HINSTANCE is defined as HMODULE), is an instance produced by LoadLibrary or GetModuleHandle (see the next paragraph). lpProcName, which cannot be Unicode, is the entry point name. The return result is NULL in case of failure. FARPROC, like "long pointer," is an anachronism. It is possible to obtain the file name associated with an hModule handle using GetModuleFileName. Conversely, given a file name (either a .DLL or .EXE file), GetModuleHandle will return the handle, if any, associated with this file if the current process has loaded it. The next example shows how to use the entry point address to invoke a function. |
Friday, November 27, 2009
Dynamic Link Libraries
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment