1. Why .h file is needed
If we are developing several cpp files in a project, the compiler will compile one cpp file at a time. If A.cpp refers B.cpp symbols(variable/function), there must be a way to tell A.cpp what this symbol looks like, this is what "declaration" does.
For efficiency purpose, we will place all the "cross-cpp" symbol "declaration"s in a header file. And any cpp files need the cross-cpp symbol will include this header file.
Note: header file is used for referring a symbol outside of the cpp, and is used to compiler, not linker.
2. Why the dll project .h file is needed in exe project?
This time, certain cpp file in exe project will refer the external symbol in the dll. So the Exe project compiler will recognize this first, so we should provide the dll external symbol declaration(which is included in dll .h file) to the exe project compiler. That is: we should include dll .h file in this Exe cpp file.
Note: with the .h file, the compiler only knows what this symbol looks like, however, it still did not resolve this symbol reference to its definition.
3. Why the import lib generated by dll project is needed by exe project?
With the dll .h file, the exe compiler will compile successfully and generate the obj files.
As stated in #2 note section, compiler still needs to resolve the symbol references in the objs to the definitions.
In the stage of linking, the linker needs the import lib to resolve this external symbol definition.(Yes, there is no real definition existed in import lib, the real definition exists in the dll). Linker will see this and use the import lib to generate the import section(IAT) in the final PE exe file.
4. How to export symbols(data or functions) definition from DLL?
There are 3 ways:
Using __declspec(dllexport)
Using appname.def Files
Using /EXPORT linker option
All these 3 ways use /EXPORT linker option in the end.
Using __declspec(dllexport)
The *compiler* recognizes this statement and generate a linker directive in .drectve section of the obj files. We can get the content like this: dumpbin /directives exportbydllexport.obj
Linker Directives
-----------------
/DEFAULTLIB:"uuid.lib"
/DEFAULTLIB:"uuid.lib"
/DEFAULTLIB:"LIBCMTD"
/DEFAULTLIB:"OLDNAMES"
/EDITANDCONTINUE
/EXPORT:?g_nResult@@3HA,DATA
/EXPORT:?Add@@YAHHH@Z
Using appname.def Files
The first statement is "LIBRARY dllname", linker will places this dllname in the generated import library.
LIBRARY exportbyDEFfile
EXPORTS
g_nResult @10 CONSTANT(or DATA)
Add @12
Note: there are 2 ways to exporting data, CONSTANT and DATA modifier. Please refer to #5 for the difference.
Using /EXPORT linker option
We normally do not use this hard way because of the compiler symbol decoration. See #4.1 linker directive.
5. How to static import exported DLL symbols(data or functions) from EXE?
There are 2 ways:
Using __declspec(dllimport)
NOT Using __declspec(dllimport)
Importing function
When exporting function from dll, the linker will generate 2 symbols in the lib file: decorated symbol and decorated symbol with __imp_ prefix before it. For example, "dumpbin /linkermember:1 exportbydllexport.lib" results in the following exported symbols:
6 public symbols
24A __IMPORT_DESCRIPTOR_exportbydllexport
488 __NULL_IMPORT_DESCRIPTOR
5B6 exportbydllexport_NULL_THUNK_DATA
77E __imp_?g_nResult@@3HA
70A ?Add@@YAHHH@Z
70A __imp_?Add@@YAHHH@Z
If the EXE source file does NOT use __declspec(dllimport) , the *compiler* does NOT know that this function comes outside of the Exe module, so it generates the normal calling instructor, like this: CALL XXXXXXXX, which XXXXXXXX waits for linker cross obj(not cross module) resolution. Also, it will only generate the reference symbol based on the C/C++ compiler decoration, which does NOT place a "__imp_" prefix.
If the EXE source file uses __declspec(dllimport) , the *compiler* knows that this function resides in DLL module, so it will generate the efficient form of calling instructors: CALL DWORD PTR [XXXXXXXX]. Also, the compiler will associate this function with "__imp_"+ "decoration name". The "XXXXXX" address(that is __imp_function_decoration_name symbol) just points to the corresponding IAT entry of EXE.
Finally, when the *linker* resolves this symbol reference with all the input obj files, it knows NOTHING about the symbol definition exists in DLL module. It will just look in the .lib file for the symbol definition matching the name(the decorated_name or the __imp_decorated_name, based on compiler generated symbol). As we can see in lib public symbol output, the decorated_name and both resolve to the same address: IAT entry.
So, it is the COMIPLER that decided whether to use the nature decorated symbol name or decorated symbol name with "__imp_" prefix. To proof this, we can use the following command to get the symbol table of the EXE obj files(obj is generated by compiler without linker touch):
dumpbin /symbols importDLLExe.obj