创建和使用 C/C++ DLL动态链接库(2)

一、显式调用和隐式调用的区别

    我们知道,动态库相比静态库的区别是:静态库是编译时就加载到可执行文件中的,而动态库是在程序运行时完成加载的,所以使用动态库的程序的体积要比使用静态库程序的体积小,并且使用动态库的程序在运行时必须依赖所使用的动态库文件(.dll文件),而使用静态库的程序一旦编译好,就不再需要依赖的静态库文件了(.lib文件)。
    动态库的调用又分为显示和隐式两种方式,区别如下:
    1、 隐式调用需要调用者写的代码量少,调用起来和使用当前项目下的函数一样直接;而显式调用则要求程序员在调用时,指明要加载的动态库的名称和要调用的函数名称。
    2、隐式调用由系统加载完成,对程序员透明;显式调用由程序员在需要使用时自己加载,不再使用时,自己负责卸载。
    3、由于显式调用由程序员负责加载和卸载,好比动态申请内存空间,需要时就申请,不用时立即释放,因此显式调用对内存的使用更加合理, 大型项目中应使用显示调用。
    4、当动态链接库中只提供函数接口,而该函数没有封装到类里面时,如果使用显式调用的方式,调用方甚至不许要包含动态链接库的头文件,而使用隐式调用时,则调用方不可避免要加上动态库中的头文件,g++编译时还需要要用参数-I指明包含的头文件的位置。需要注意的是,当动态链接库中的接口函数是作为成员函数封装在类里面时,即使使用显式调用的方式,调用方也必须包含动态库中的相应头文件。
    5、显式调用更加灵活,可以模拟多态效果

二、隐式调用

上一篇博文就是显式调用。这里需要补充的是,在上一篇博文(5)如何将 DLL 与 C++ 应用程序链接使用的是将 DLL 引用到应用程序,还可以如下操作:

  1. 首先选择项目属性,在包含目录中包含头文件所在的目录:
    在这里插入图片描述
    2.然后在库目录中包含动态库所在的目录:
    在这里插入图片描述
    3.最后在链接器-》输入下的附加依赖项中包含动态库文件
    在这里插入图片描述
    这样就实现了将 DLL 与 C++ 应用程序链接。

注:上面的3也可以由#pragma comment(lib, "Dll1.lib")代替,只需要在测试代码文件中加入这一行即可。

// CalculationApplication.cpp :
#include "Calculation.h"
#pragma comment(lib, "Dll1.lib")  // 加入这行,则可以省掉上面的3
int main(int argc, char* argv[])
{
	CalculationApi obj;
	obj.Addition();
	obj.Subtraction();
	obj.Multiply();
	return 0;
}

三、显式调用

显式链接,操作系统在运行时按需加载 DLL。通过显式链接使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载 DLL 并访问由 DLL 导出的函数。与调用静态链接库中的函数不同,客户端可执行文件必须通过函数指针调用 DLL 中的导出函数。显式链接有时称为动态加载或运行时动态链接。

要通过显式链接使用 DLL ,应用程序必须进行函数调用以在运行时显式加载 DLL。要显式链接到 DLL,应用程序必须:

  • 调用LoadLibrary、LoadLibraryEx或类似的函数来加载 DLL 并获取模块句柄。
  • 调用GetProcAddress以获取指向应用程序调用的每个导出函数的函数指针。由于应用程序通过指针调用DLL 函数,编译器不会生成外部引用,因此无需链接导入库。但是,您必须有一个 typedef 或 using 语句来定义您调用的导出函数的调用签名。
  • 完成 DLL 后调用FreeLibrary。

(1)方法一(调用类的成员函数不成功):

  • 1.首先书写头文件Calculation.h
//Calculation.h
#pragma once
#ifdef CALCULATIONDLL_EXPORTS  
#define CALCULATION_API __declspec(dllexport)   
#else  
#define CALCULATION_API __declspec(dllimport)   
#endif  
class  CALCULATION_API CalculationApi
{
public:
	void Addition(void);
	void Subtraction(void);
	void Multiply(void);
};
extern "C" {
	int CALCULATION_API my_max(int, int);

}
  • 2.然后书写实现文件CalculationDll.cpp
// CalculationDll.cpp : Defines the exported functions for the DLL application.
#include "Calculation.h"
#include <iostream>
using namespace std;
void CalculationApi::Addition(void)
{
	int x = 0;
	int y = 0;
	cout << "Enter the numbers for add" << endl;
	cin >> x >> y;
	cout << (x + y) << endl;
}
void CalculationApi::Subtraction(void)
{
	int x = 0;
	int y = 0;
	cout << "Enter the numbers for sub" << endl;
	cin >> x >> y;
	cout << (x - y) << endl;
}
void CalculationApi::Multiply(void)
{
	int x = 0;
	int y = 0;
	cout << "Enter the numbers for multiply" << endl;
	cin >> x >> y;
	cout << (x * y) << endl;
}
int my_max(int a, int b)
{
	return a > b ? a : b;
}
  • 3.测试代码
#include <windows.h>  
#include <cstdio>  
#include "Calculation.h"
#include <iostream>
int main(void)
{
	HMODULE hDLL;
	hDLL = LoadLibrary(L"Dll1.dll");//加载动态链接库Dll1.dll文件;  
	typedef int (*comp)(int, int);
	comp cmp = (comp)GetProcAddress(hDLL,"my_max");
	CalculationApi* obj = (CalculationApi*)GetProcAddress(hDLL,"CalculationApi");
	std::cout << cmp(1, 2) << std::endl;
	//obj->Addition();  //类名没有被修改,但是成员函数名字被修改,所以调用不成功
	//obj->Subtraction();
	//obj->Multiply();
	FreeLibrary(hDLL);//卸载MyDll.dll文件;  
	return 0;
}

我们发现调用类成员函数报错
在这里插入图片描述
表示找不到成员函数,那是因为成员函数名字被修改,所以调用不成功。要解决这样的问题,需要虚函数,请继续往下看。
(2)方法二:

  • 1.首先书写头文件
//Calculation.h
#pragma once
#ifdef CALCULATIONDLL_EXPORTS  
#define CALCULATION_API __declspec(dllexport)   
#else  
#define CALCULATION_API __declspec(dllimport)   
#endif  
class CALCULATION_API Base
{
public:
	virtual void Destroy()  //释放new创建的实例对象
	{
		delete this;
	}
	virtual void Addition(void)=0;
	virtual void Subtraction(void)=0;
	virtual void Multiply(void)=0;

};
class   CalculationApi:public Base
{
public:
	void Addition(void);
	void Subtraction(void);
	void Multiply(void);
};
// extern "C"中告诉 C++ 编译器链接器应该使用 C 调用约定。它是必需的,以防止对函数名称进行修改。
extern "C" CALCULATION_API int  my_max(int, int); 
extern "C" CALCULATION_API Base*  getInstance();

  • 然后书写实现文件CalculationDll.cpp
// CalculationDll.cpp : Defines the exported functions for the DLL application.
#include "Calculation.h"
#include <iostream>
using namespace std;
void CalculationApi::Addition(void)
{
	int x = 0;
	int y = 0;
	cout << "Enter the numbers for add" << endl;
	cin >> x >> y;
	cout << (x + y) << endl;
}
void CalculationApi::Subtraction(void)
{
	int x = 0;
	int y = 0;
	cout << "Enter the numbers for sub" << endl;
	cin >> x >> y;
	cout << (x - y) << endl;
}
void CalculationApi::Multiply(void)
{
	int x = 0;
	int y = 0;
	cout << "Enter the numbers for multiply" << endl;
	cin >> x >> y;
	cout << (x * y) << endl;
}
int my_max(int a, int b)
{
	return a > b ? a : b;
}
Base* getInstance()
{
	return new CalculationApi;
}
  • 3.测试代码
#include <windows.h>  
#include <cstdio>  
#include "Calculation.h"
#include <iostream>
int main(void)
{
	HMODULE hDLL;
	hDLL = LoadLibrary(L"Dll1.dll");//加载动态链接库Dll1.dll文件;  
	typedef int (__cdecl *comp)(int, int); //__cdecl是 C 和 C++ 程序的默认调用约定,这里可写可不写
	typedef Base*(*pClass)();
	comp cmp = (comp)GetProcAddress(hDLL,"my_max");
	pClass obj = reinterpret_cast<pClass>(GetProcAddress(hDLL,"getInstance"));// 也可以pClass obj = (pClass)GetProcAddress(hDLL,"getInstance");
	std::cout << cmp(1, 2) << std::endl;
	Base* ins = obj(); //使用结束时候要记得delete动态分配的内存
	ins->Addition();  
	ins->Subtraction();
	ins->Multiply();
	ins->Destroy(); //delete动态分配的内存
	FreeLibrary(hDLL);//卸载MyDll.dll文件;  
	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值