创建DLL动态库
1、新建Dll项目
文件->新建->项目->Visual C++ win32控制台程序(填写项目名MyDll)->下一步->(应用程序类型)勾选:DLL,(附加选项)勾选:空项目->完成
2、添加新建项MyDll.h
#pragma once
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
class MyDll
{
public:
MYDLL_API int Add(int a, int b);
MYDLL_API void myPrint(const char* s);
};
extern "C" MYDLL_API void printStr(const char* s);
dllimport与dllexport用于声明导入导出属性,其中_declspec(dllexport)用在dll程序中,用于说明这是导出的函数。而_declspec(dllimport)用在调用dll的程序中,用于说明这是从dll中导入的函数。
MYDLL_EXPORTS宏在新建Dll项目时自动生成,命名规则为 项目名+EXPORTS,宏声明位置如下图所示。正因如此,我们可以通过这个宏自动区分是创建dll的场景还是使用dll的场景(使用dll时需要用到该头文件),从而分别将函数声明为导出、导入类型。
3、添加新建项MyDll.cpp
#include "MyDll.h"
#include <iostream>
int MyDll::Add(int a, int b)
{
return a + b;
}
void MyDll::myPrint(const char* s)
{
std::cout << s << std::endl;
}
void printStr(const char* s)
{
std::cout << s << std::endl;
}
4、编译生成
生成结果如下所示
后文隐式加载方式将使用到MyDll.h、MyDll.lib、MyDll.dll。MyDll.lib需放置于项目的库搜索路径下,MyDll.dll需放置于项目生成的可执行文件同一目录下。
使用Dependency Walker工具可以打开dll文件查看导出的函数格式
从上图中可以看出,导出的函数根据声明方式按一定规则进行了重命名。如果使用显示加载方式这种重命名会影响很大,显示加载方法将会在下一篇博客讲解。因本文中使用的是隐式加载方式,需要使用到创建dll时使用的接口头文件MyDll.h。因为使用的头文件相同,接口函数的声明的声明方式相同,函数的重命名方式也就一样,所以这种函数名修改方式也就不会有影响。
同时有人可能注意到printStr()函数为何没有重命名?这是因为使用extern “C” + __cdecl(C/C++默认的函数调用协议)声明的缘故。extern “C”用于指示编译器按C语言标准编译该部分代码,因此不能在类中使用该声明方式。
隐式加载方式使用DLL动态库
隐式加载是在系统启动时一次性把所有的DLL的导出函数加载到可执行文件中,需要用到.h和.lib文件。隐式加载的步骤为:
1、新建测试项目
文件->新建->项目->Visual C++ win32控制台程序(填写项目名testDll)->下一步-> (附加选项)勾选:空项目->完成
2、添加新建项main.cpp文件
#include <iostream>
#include "MyDll.h"
int main(void)
{
MyDll test;
std::cout<<test.Add(2, 5)<<std::endl;
test.myPrint("Func myPrint()");
printStr("Func printStr()");
return 0;
}
3、项目属性中添加头文件路径
替代方法:将头文件拷贝至项目文件同一目录下,即项目默认的头文件搜索路径中。
4、项目属性中添加dll对应.lib路径
5、项目属性中附加依赖项里添加dll对应的.lib文件名
步骤4、5可使用以下代码替代。其中的路径可以使用相对路径(这里为绝对路径)。
#pragma comment (lib,"D:/Project Files/MyDll/Debug/MyDll.lib")