There are two methods for calling a function in a DLL. One way, known as load-time dynamic linking, is simply to call the function as you would any other function. Since the code for the DLL function is not located in your source code, this generates an external function reference in the object code when the source code is compiled. To resolve this external reference, you will need to link your application with the import library for the DLL or use an IMPORTS statement in the application's module definition file. The other method, known as run-time dynamic linking, involves using the functions in the Win32 API that allow you to specify at run-time the name of the DLL to be used and the name or ordinal value of the function within that DLL to execute.
Either method retains the advantages of DLLs: being able to revise the DLL without relinking the application; simultaneous use by multiple applications; and conserving use of memory and disk space.
Load-time dynamic linking is the method used by the Win32 API. When a source code file is compiled or assembled, the compiler or assembler generates an object module for the code. If the source code calls any external functions (that is, functions whose code is in another object module), the compiler or assembler adds an external function reference to the object module. The linker resolves these external references. If the linker finds the external function in a special library called an import library or in an IMPORTS statement in the application's module-definition file, the code for the external function is in a dynamic-link library. To resolve external references to dynamic-link libraries, the linker simply adds information to the executable file that tells the system where to find the DLL code when the application starts up. For more information about module-definition files and import libraries, refer to Section 0.1.2, “Files Used in Dynamic Linking.”
When the system starts a program that does not use dynamic linking, all the code and data are contained in a single executable file and the system can map it all into memory at once. If a program contains dynamically linked references, however, the system must process the information in the references. The executable modules for any DLLs used by a process are mapped into the address space of the process. The system then modifies the executable code of the process to provide the addresses needed to use the DLL functions. As with the rest of an application's code, DLL code is mapped into memory when the application starts up and only loaded into memory when needed.
When a process starts up, the system searches for its required DLLs using the following search path:
The directory in which the executable module for the current process is located
The current directory
The Windows directory
The Windows system directory
The path specified by the PATH environment variable
The LIBPATH environment variable is not used. If any DLL used by a process cannot be found when the process is starting up, the system terminates the process and reports the error. If all DLLs are found, the DLL modules are mapped into the memory space of the process, and the process attaches to each DLL by calling its LibMain function. If a DLL's LibMain function fails during process start up, the process is terminated.
Once a DLL is mapped into memory, the system notifies the process where to find the functions it needs in the DLL.
Note that the PRELOAD or LOADONCALL code attributes used in previous versions of Windows to control loading have no meaning in Win32. Instead the necessary executable modules for the DLL are mapped into the address space of the process, and the necessary pages are loaded as needed. Loading and unloading of code pages is handled by the memory manager as with any other pages in memory: If a page fault occurs, the code page is loaded; and when memory is needed, the least recently used page is unloaded.
With load-time dynamic linking, you must link your process with an import library or use an IMPORTS statement in the module definition file of the process, in order to resolve the external references to the DLL's functions. This is not necessary with run-time dynamic linking because your process does not explicitly call the DLL's functions and so does not generate the external references. Instead, your process uses LoadLibrary to specify the name of the DLL that it wants to use, and GetProcAddress to get the entry point of the function it wants to call in the DLL. The FreeLibrary function can be used to unmap the DLL module when your process is finished using it.
The DLL specified in the call to LoadLibrary may already be attached to the calling process, either due to load-time linking or to a previous call to LoadLibrary without a corresponding call to FreeLibrary. In this case, LoadLibrary simply returns a handle to the DLL. Otherwise, it attempts to locate the DLL using the same search path listed above for load-time dynamic linking. If successful in locating the DLL, the function maps the executable module into the address space of the process and the DLL's LibMain function is called to initialize the DLL for this process. LoadLibrary returns NULL to indicate failure if it cannot find the DLL or if the LibMain function fails.
If successful, LoadLibrary returns a handle to the DLL module. This handle can be used in the GetProcAddress function to get the entry point to one of the DLL's functions. The desired function is specified using its name or ordinal value as listed in the EXPORTS statement of the DLL's module definition file.
Both LoadLibrary and GetProcAddress return NULL to indicate that they were not successful. This allows your process to use some alternative method to accomplish its objective. This is one of the main advantages of run-time linking, since load-time linking simply terminates the process if the DLL is not available. For example, if a process is unable to locate one DLL, it could try to use another DLL or it could notify the user. If the user can provide the full path of the missing DLL, the process can use this information to load the library even though it is not in the normal search path.
Another advantage of run-time linking is that the programmer does not need to know the names of the DLLs the application will be using or the names of the functions within the DLL. The application can get this information from a configuration file or obtain it from the user.