一、创建DLL模块
1、生成代码:通过VS2019 创建新项目,选择项目模板“具有导出项的(DLL)动态链接库”,项目名称MyLib。生成的项目中包含以项目名称命名的MyLib.h和MyLib.cpp文件如下。
1)MyLib.h头文件
// 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 MYLIB_EXPORTS // 符号编译的。在使用此 DLL 的 // 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 // MYLIB_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 // 符号视为是被导出的。 #ifdef MYLIB_EXPORTS #define MYLIB_API __declspec(dllexport) #else #define MYLIB_API __declspec(dllimport) #endif // 此类是从 dll 导出的 class MYLIB_API CMyLib { public: CMyLib(void); // TODO: 在此处添加方法。 }; extern MYLIB_API int nMyLib; MYLIB_API int fnMyLib(void); |
该文件注释中强调,DLL模块编译时需要定义宏MYLIB_EXPORTS,使用DLL的模块不需要定义这个宏。项目中自动创建的宏与项目名称相关,没有必要再去自定义。
通过宏来切换这里提到的类、变量和函数的输入和输出。DLL模块中定义宏,表示输出;使用DLL时,包含上述头文件,不再定义宏,表示输入。
2)MyLib.cpp文件
// MyLib.cpp : 定义 DLL 的导出函数。 // #include "pch.h" #include "framework.h" #include "MyLib.h" // 这是导出变量的一个示例 MYLIB_API int nMyLib=0; // 这是导出函数的一个示例。 MYLIB_API int fnMyLib(void) { return 0; } // 这是已导出类的构造函数。 CMyLib::CMyLib() { return; } |
源代码文件程序的编写,与编写可执行程序没有差别。
2、编译:编译项目后生成.lib文件和.dll文件。
DLL模块提供给调用方的内容包含三个部分:.h头文件 .lib文件 .dll文件。
3、extern
在MyLib.h头文件中,关键字extern用在输出变量前面,用于声明一个外部变量。变量nMyLib在DLL模块的MyLib.cpp中被声明和定义。当调用方Include DLL模块的MyLib.h文件时,nMylib就可以作为外部变量来使用了。而类和函数前面并没有使用extern,这是因为它们默认就是外部声明。
经常在DLL模块的头文件中见到extern “C”的用法。形如,
#ifdef MYLIB_EXPORTS #define MYLIB_API extern "C" __declspec(dllexport) #else #define MYLIB_API extern "C" __declspec(dllimport) #endif 此类是从 dll 导出的 //class MYLIB_API CMyLib { //public: // CMyLib(void); // // TODO: 在此处添加方法。 //}; MYLIB_API int nMyLib; MYLIB_API int fnMyLib(void); |
当进行C和C++混合编程时,才使用修改符extern “C”。并且,只在C++代码中使用,而不是在C代码中使用。使用了修改符号后,C++编码生成的DLL模块才能够被C、C++或任何其他语言调用。
代码中已经将类CMyLib注释掉了,修改符extern “C”作用在类上会报错,这是因为修改符无法让C++中的类被C调用。
二、创建可执行模块
可执行模块调用DLL模块有2种方式:加载时的隐含链接和运行期的显式链接。
1、隐含链接
1)调用模块编译时:包含MyLib.h头文件,添加#program comment(lib,”MyLib.lib”);
2)调用模块运行时:将MyLib.dll拷贝到调用方的执行目录下;
2、显式链接
显式链接通过LoadLibrary函数实现。
三、其他
1、.def文件
如果使用Microsoft Visual Studio来创建DLL和使用该DLL的可执行模块,可以忽略.def文件。如果使用Microsoft Visual Studio创建DLL被其他编译器供应商创建的可执行模块时,需要使用.def文件。
.def文件定义了Microsoft Visual Studio创建的DLL模块中函数的输出名。当DLL模块中的函数使用__stdcall(WINAPI)调用规则时,编译后会改变函数的名字,而.def文件会告诉Microsoft的编译器不要改变函数名。形如,
LIBRARY EXPORTS fnMyLib |
四、参考
extern和extern "C"_lv_Amelia的博客-优快云博客
《Windows核心编程》