The following code fragment shows an executable source code file that imports the DLL's exported symbols and references those symbols in the code.
/*************************************************************************** Module: MyExeFile1.cpp ***************************************************************************/ // Include the standard Windows and C-Runtime header files here. #include <windows.h> // Include the exported data structures, symbols, functions, and variables. #include "MyLib\MyLib.h" //////////////////////////////////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, LPTSTR pszCmdLine, int) { int nLeft = 10, nRight = 25; TCHAR sz[100]; wsprintf(sz, TEXT("%d + %d = %d"), nLeft, nRight, Add(nLeft, nRight)); MessageBox(NULL, sz, TEXT("Calculation"), MB_OK); wsprintf(sz, TEXT("The result from the last Add is: %d"), g_nResult); MessageBox(NULL, sz, TEXT("Last Result"), MB_OK); return(0); } ////////////////////////////// End of File ///////////////////////////////// |
When you develop executable source code files, you must include the DLL's header file. Without it, the imported symbols will not be defined and the compiler will issue a lot of warnings and errors.
The executable source code file should not define MYLIBAPI before the DLL's header file. When the executable source code file shown on the previous page is compiled, MYLIBAPI is defined using _ _declspec(dllimport) by the MyLib.h header file. When the compiler sees _ _declspec(dllimport) modifying a variable, function, or C++ class, it knows that this symbol is to be imported from some DLL module. It doesn't know which DLL module, and it doesn't care. The compiler just wants to be sure that you access these imported symbols in the right way. Now, in the source code, you can simply refer to the imported symbols and everything will work.
Next, the linker must combine all the .obj modules to create the resulting executable module. The linker must determine which DLLs contain all of the imported symbols that the code references. So you have to pass the DLL's .lib file to the linker. As mentioned before, the .lib file simply contains the list of symbols that a DLL module exports. The linker simply wants to know that a referenced symbol exists and which DLL module contains that symbol. If the linker resolves all the external symbol references, an executable module is born.
The previous section introduced the _ _declspec(dllimport) modifier. When you import a symbol, you do not have to use the _ _declspec(dllimport) keyword—you can simply use the standard C extern keyword. However, the compiler can produce slightly more efficient code if it knows ahead of time that the symbol you are referencing will be imported from a DLL's .lib file. So I highly recommend that you use the _ _declspec(dllimport) keyword for imported function and data symbols. Microsoft does this for you when you call any of the standard Windows functions.
When the linker resolves the imported symbols, it embeds a special section called the imports section in the resulting executable module. The imports section lists the DLL modules required by this module and the symbols referenced from each DLL module.
Using Visual Studio's DumpBin.exe utility (with the -imports switch), you can see what a module's import section looks like. The following is a fragment of Calc.exe's import section. (Again, I've removed some of DUMPBIN's output so that it would not occupy too many pages in this book.)
C:\WINNT\SYSTEM32>DUMPBIN -imports Calc.EXE Microsoft (R) COFF Binary File Dumper Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Dump of file calc.exe File Type: EXECUTABLE IMAGE Section contains the following imports: SHELL32.dll 10010F4 Import Address Table 1012820 Import Name Table FFFFFFFF time date stamp FFFFFFFF Index of first forwarder reference 77C42983 7A ShellAboutW MSVCRT.dll 1001094 Import Address Table 10127C0 Import Name Table FFFFFFFF time date stamp FFFFFFFF Index of first forwarder reference 78010040 295 memmove 78018124 42 _EH_prolog 78014C34 2D1 toupper 78010F6E 2DD wcschr 78010668 2E3 wcslen |
As you can see, the section has an entry for each DLL that Calc.exe requires: Shell32.dll, MSVCRt.dll, AdvAPI32.dll, Kernel32.dll, GDI32.dll, and User32.dll. Under each DLL's module name is the list of symbols that Calc.exe is importing from that particular module. For example, Calc calls the following functions contained in Kernel32.dll: lstrcpyW, LocalAlloc, GetCommandLineW, GetProfileIntW, and so on.
The number immediately to the left of the symbol name indicates the hint value of the symbol and is not pertinent to our discussion. The number on the far left of each symbol's line indicates the memory address where the symbol is located in the process's address space. This memory address appears only if the executable module is bound. You can see some additional binding information toward the end of DumpBin's output. (Binding is discussed in Chapter 20.)