动态链接库构建的思路框架(生成和使用)

使用的编译器:vs2017.
注:以下例子的配置都是Debug.

1.动态链接库的创建:

1.1 :新建一个空项目,取名“TestDll";
1.2:在该项目下添加”TestDll“类。
1.3:TestDll.h添加以下代码:

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

#include<iostream>
// 将此类的符号导出到 DLL中 
class DLL_API CDll {
public:
	CDll(void);
	void Output(void);
	// TODO: 在此处添加方法。
};
//导出变量符号到DLL中
extern DLL_API int nDll;
//导出函数符号到DLL中
DLL_API int fnDll(void);

1.4: TestDll.cpp添加以下代码:

#include "TestDll.h"

// 这是导出变量的一个示例
DLL_API int nDll = 0;

// 这是导出函数的一个示例。
DLL_API int fnDll(void)
{
	std::cout << "this is fnDll function!"<<std::endl;
	return 0;
}

// 这是已导出成员函数的示例
CDll::CDll()
{
	return;
}
void CDll::Output()
{
	std::cout << "this is CDll::Output function!"<< std::endl;
	return;
}

 1.5:修改项目属性:
属性页->配置属性->常规中修改两项:
目标文件扩展名: .dll
配置类型: 动态库(.dll)

属性页->配置属性->C/C+±>预处理器中,预处理器定义中添加预处理宏:
DLL_EXPORTS;

 

然后ctrl+shift+b组合键生成dll.
1.6: 在生成的Debug文件中找到TestDll.dll文件和TestDll.lib文件.和头文件TestDll.h三个文件一起保存下来,这三个文件就是使用DLL的时候需要用到的.

头文件中为什么要有下面的代码呢?

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

 

__declspec(dllexport)意思是导出符号到dll.
__declspec(dllimport)意思是从dll导入符号.
我们生成的时候,在项目属性中定义了DLL_EXPORTS宏,所以生成时将符号导出到DLL中.
我们在使用DLL的时候,没有定义到DLL_EXPORTS宏,所以使用的时候从DLL中导入符号到.exe文件,以供.exe文件使用.

除了这种创建方式,还有一种是使用vs的模板创建的dll项目:

 该操作会生成一个dllmain.cpp文件,里面有以下代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

这是dll文件的入口,只有在dll中需要new和dll操作时才需要用到这个,假如是一些单纯的函数库,或者该dll没有涉及资源的管理的话,则无需理会该文件.
如果有,则在case DLL_PROCESS_ATTACH语句后面做初始化操作,在case DLL_PROCESS_DETACH语句后面做反初始化操作.

这个东西应该是和构造函数一样的,假如dll中没有定义应该会自动生成一个默认的.(不确定是否正确).

///

2.动态链接库的使用方法一:
2.1: 创建一个控制台项目,在.cpp文件中添加以下代码:

 

#include<iostream>
using namespace std;
#include"DLL/TestDll.h"
#pragma comment(lib,"./DLL/TestDll.lib")
int main(void)
{
	CDll TestDll;
	cout << "动态库中的成员函数:";
	TestDll.Output();
	cout << "动态库中的变量:" << nDll<<std::endl; 
	cout << "动态库中的函数:";
	fnDll();
	return 0;
}

 

2.2: 在源代码的文件夹下面新建一个DLL文件夹,然后将1.6步骤中保存的TestDll.lib文件和TestDll.h文件复制到DLL文件夹中,然后运行,这时候会提示错误,然后在生成的Debug文件夹中(也就是.exe文件所在的位置),将1.6步骤中保存的TestDll.dll文件复制到该目录下,再运行,输出:

动态库中的成员函数:this is CDll::Output function!
动态库中的变量:0
动态库中的函数:this is fnDll function!

我曾疑问,不是说.lib是静态库,在编译的时候会将所有的 信息都一起编译到.exe文件中,.dll是动态库,在运行时动态加载吗,那么为何在使用.dll的时候还需要.lib文件呢?
后来才明白,.lib文件有两种,一种是静态库,是静态编译出来的,索引和实现都在其中.另一种是与动态编译出来的dll文件对应的lib文件,一般是一些索引信息,具体的实现在dll文件中.
在我们编译的时候只需要.lib文件即可,即使没有.dll文件也是可以编译成功的.所以在使用的时候我们的源代码是包含头文件和链接.lib文件:
 

#include"DLL/TestDll.h"
#pragma comment(lib,"./DLL/TestDll.lib")

只有在程序中运行时才需要dll,所以我们的dll文件是和.exe文件同一目录的.

3.动态链接库的使用方法二:
另外调用dll还有另外一种动态调用的方式,需要使用一些api,还需要使用vs中的dumpbin工具,比较麻烦,但好处是更加灵活(动态加载卸载),不用将dll的信息编译到程序中,生成的程序也更小.
代码:
 

#include<iostream>
//包含头文件,以使用LoadLibrary,GetProcAddress,FreeLibrary函数.
#include<windows.h>
//只需包含头文件,无需连接.lib.
#include"DLL/TestDll.h"
//#pragma comment(lib,"./DLL/TestDll.lib")
using namespace std;

int main(void)
{
	//定义函数指针.该指针必须要和我们接下来要查询的函数的返回值和参数相等.
	typedef int (*pFunc)();

	//定义句柄.
	HINSTANCE hdll = NULL;

	//加载dll.需要设置属性->属性页->配置属性->常规->字符集->使用多字节字符集.使用Unicode不行.
	//参数是dll的地址.
	hdll = LoadLibrary("./TestDll.dll");

	//判断是否加载成功.
	if (NULL == hdll)
	{
		return 0;
	}

	//获得函数地址,将其赋给函数指针.第二个参数是函数名.函数名使用dumpbin工具查询得到.
	pFunc func =(pFunc)GetProcAddress(hdll, "?fnDll@@YAHXZ");

	//判断是否成功.
	if (NULL == func)
	{
		//如果不成功,需要先卸载该dll再返回.
		FreeLibrary(hdll);
		cout << "2";
		return 0;
	}

	cout << "动态库中的函数:";
	//用函数指针调用该函数.
	(*func)();
	//使用完成,卸载dll.
	FreeLibrary(hdll);
	return 0;
}

输出:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值