//1. DLL构建:选择 Win32 控制台应用程序 -> DLL(附加选项里 勾选上 空项目) //2. Windows 应用程序编程入口(API)提供的所有函数都包含在DLL中。其中三个最重要的DLL: Kernel32.dll:包含的函数用来管理内存、进程以及线程 User32.dll:包含的函数用于执行与界面相关的任务 GDI32.dll:包含的函数用来绘制图像和显示文字 //3. (A):DLL通常由一组可供任何应用程序使用的独立函数组成。在DLL中通常不包含处理消息循环或创建窗口的代码 (B):在创建DLL的时候,必须给编译器指定 /DLL 开关 (C):在应用程序能调用一个DLL里的函数前,必须将该DLL的文件映像映射到调用进程的地址空间中:隐式载入时链接、显示运行时链接 (D):系统一旦将一个DLL文件映射到调用进程的地址空间后,进程中所有的线程就可以正常调用该DLL中的函数了 (E):DLL中若提供了分配资源的接口,一定也要提供释放资源的接口。若DLL将资源释放的接口留给可执行文件,那么可执行文件可能链接不同版本DLL造成问题 比如:DLL实现了一个函数返回动态分配的内存的指针,则此指针最好不要由可执行文件中的代码去管理 //4. 构建DLL步骤: (A):必须创建一个头文件,在其中包含我们想在DLL中导出的函数原型、结构、符号。为了构建DLL,DLL的所有源文件必须包含此头文件。在构建可执行文件的时候也需要此头文件 (B):创建源文件来实现想在DLL中导出的函数和变量。构建可执行文件的时候并不需要这些源文件 (C):构建DLL模块时,编译器会对每个源文件生成一个.obj模块 (D):当所有.obj模块创建完成后,链接器会将所有.obj模块的内容合并起来,产生一个单独的DLL镜像文件。这个镜像文件包含DLL中所有二进制代码以及全局/静态变量 (E):如果链接器检测到DLL的源文件输出了至少一个函数或变量,那么链接器还会生成一个.lib文件,此文件包含了所有被导出的函数和变量的符号名,构建可执行文件时候需要此文件 //5. (A):应避免从DLL中导出变量,因为这样会破坏封装性,使得DLL难以维护 (B):只有当导出C++类的模块使用的编译器与导入C++类的模块使用的编译器是同一厂商提供的,我们才能导出C++类,否则应避免导出C++类 (C):DLL与可执行文件应该使用同一个头文件,这样才利于代码的维护 (D):如果编译器看到一个变量、函数或C++类是用 __declspec(dllexport) 修饰的,那么他就知道应该在生成的DLL模块导出该变量、函数或C++类 (E):extern "C" 修饰符:在编写C++代码的时候应该使用这个修饰符,在编写C代码的时候不应该使用此修饰符。C++编译器通常会对函数名和变量名进行改编,这会在链接的时候导致问题 (F):__declspec(dllimport) 让编译器知道可执行文件需要从DLL模块导入哪些变量或函数 (G):DumpBin.exe /exports 可以查看DLL的导出段 (H): DLL例子: //DLL项目:Tool.h #pragma once #ifndef SznDllTest #define SznDllTest __declspec(dllimport) #endif extern "C" SznDllTest void FunTest(); extern "C" SznDllTest int nValue; class SznDllTest CTest { public: CTest(){} public: void ClassFun(); }; //DLL项目:Tool.cpp #define SznDllTest __declspec(dllexport) #include "Tool.h" #include <cstdio> void FunTest() { printf("FunTest "); } int nValue = 1024; void CTest::ClassFun() { printf("ClassFun "); } //可执行项目: #include "Tool.h" #pragma comment(lib, "Test.lib") int main() { FunTest(); //输出FunTest int nTem = nValue; //nTem = 1024 CTest Test; //输出ClassFun Test.ClassFun(); return 0; } //6. 如果用 Microsoft virtual C++ 创建的DLL与其他厂家的工具包构建的可执行文件链接,需要做额外的工作,过程:略,详情见书