Friday, November 27, 2009

Dynamic Link Libraries









Dynamic Link Libraries


We 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 Libraries


The 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.


  • The executable image may be large, consuming disk space and physical memory at run time and requiring extra effort to manage and deliver to users.

  • Each program update requires a rebuild of the complete program even if the changes are small or localized.

  • Every program in the system that uses the functions will have a copy of the functions, possibly different versions, in its executable image. This arrangement increases disk space usage and, perhaps more important, physical memory usage when several such programs are running simultaneously.

  • Distinct versions of the program, using different techniques, might be required to get the best performance in different environments. For example, the Asc2Un function is implemented differently in Program 2-4 (atou) and Program 5-3 (Asc2UnMM). The only method of executing different implementations is to decide which of the two versions to run based on environmental factors.


DLLs solve these and other problems quite neatly.


  • Library functions are not linked at build time. Rather, they are linked at program load time (implicit linking) or at run time (explicit linking). As a result, the program image can be much smaller because it does not include the library functions.

  • DLLs can be used to create shared libraries. Multiple programs share a single library in the form of a DLL, and only a single copy is loaded into memory. All programs map their process address space to the DLL code, although each thread will have its own copy of nonshared storage on the stack. For example, the ReportError function was used by nearly every example program; a single DLL implementation could be shared by all the programs.

  • New versions or alternative implementations can be supported simply by supplying a new version of the DLL, and all programs that use the library can use the new version without modification.

  • With explicit linking, a program can decide at run time which version of a library to use. The different libraries may be alternative implementations of the same function or may carry out totally different tasks, just as separate programs do. The library will run in the same process and thread as the calling program.


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 Linking


Implicit or load-time linking is the easier of the two techniques. The required steps, using Microsoft Visual C++, are as follows.






1.
The functions in a new DLL are collected and built as a DLL, rather than, for example, a console application.

2.
The build process constructs a .LIB library file, which is a stub for the actual code. This file should be placed in a common user library directory specified to the project.

3.
The build process also constructs a .DLL file that contains the executable image. This file is typically placed in the same directory as the application that will use it, and the application loads the DLL during its initialization. The current working directory is the secondary location, and the OS will next look in the system directory, the Windows directory, and the path specified with the PATH environment variable.

4.
Take care to export the function interfaces in the DLL source, as described next.


Exporting and Importing Interfaces

The 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:



_declspec (dllexport) DWORD MyFunction (...);


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:



extern "C"


For example, if MyFunction is defined as part of a DLL build in project MyLibrary, the header file would contain:



#if defined(MYLIBRARY_EXPORTS)
#define LIBSPEC _declspec (dllexport)
#elif defined(__cplusplus)
#define LIBSPEC extern "C" _declspec (dllimport)
#else
#define LIBSPEC _declspec (dllimport)
#endif
LIBSPEC DWORD MyFunction (...);


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:


  • The directory containing the loaded application.

  • The current directory, if different from the executable image directory.

  • The Windows system directory. You can determine this path with GetSystemDirectory; normally its value is c:\WINDOWS\SYSTEM32.

  • The 16-bit Windows system directory, which does not exist on 9x systems. There is no function to obtain this path, and it is obsolete for our purposes.

  • The Windows directory (GetWindowsDirectory).

  • Directories specified by the PATH environment variable, in the order in which they occur.


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 Linking


Explicit 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.



HINSTANCE LoadLibrary (LPCTSTR lpLibFileName)



HINSTANCE LoadLibraryEx (
LPCTSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags)


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.


  1. LOAD_WITH_ALTERED_SEARCH_PATH overrides the previously described standard search order, changing just the first step of the search strategy. The pathname specified as part of lpLibFileName is used rather than the directory from which the application was loaded.

  2. LOAD_LIBRARY_AS_DATAFILE allows the file to be data only, and there is no preparation for execution, such as calling DllMain (see the DLL Entry Point section later in the chapter).

  3. DONT_RESOLVE_DLL_REFERENCE means that DllMain is not called for process and thread initialization, and additional modules referenced within the DLL are not loaded.


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.



BOOL FreeLibrary (HINSTANCE hLibModule)


After loading a library and before freeing it, you can obtain the address of any entry point using GetProcAddress.



FARPROC GetProcAddress (
HMODULE hModule,
LPCSTR lpProcName)


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.









    No comments: