C++DLL编写一个类,通过接口显示链接

1. DLL显式链接知识学习

在上一篇介绍了DLL封装并隐式链接调用,这一篇介绍DLL显式链接调用

首先学习一下预备知识(Win32API):

一、LoadLibrary是Windows API中的一个函数,用于动态加载一个库(通常是DLL文件)到调用进程的地址空间中。当你需要在运行时动态地使用某个库中的函数或资源时,可以使用LoadLibrary来加载该库,然后使用GetProcAddress来获取库中函数的地址。

HMODULE LoadLibrary(
  LPCSTR lpLibFileName
);

lpLibFileName:要加载的库的名称。这可以是库的文件名,也可以是包含路径的文件名。如果只指定文件名,系统会按照特定的搜索顺序查找库。
返回值是加载的模块的句柄(HMODULE),如果函数调用失败,返回NULL。成功调用LoadLibrary后,你可以使用GetProcAddress来获取库中导出函数的地址。
使用LoadLibrary时需要注意以下几点:
1. 确保指定的库文件存在于搜索路径中,或者提供完整的路径。
2. 加载库后,记得在不再需要时使用FreeLibrary来卸载库,避免资源泄露。
3. 处理好错误情况,例如加载失败时,可以使用GetLastError获取错误代码。

二、GetProcAddress 是 Windows API 中的一个函数,用于动态获取某个已加载的动态链接库(DLL)中的函数或变量的地址。这允许程序在运行时决定调用哪些函数,而不是在编译时静态链接这些函数,从而提供了更大的灵活性。
在 Windows 的头文件 Winbase.h(包含于 Windows.h)中,GetProcAddress 的原型定义如下:

FARPROC GetProcAddress(
  HMODULE hModule,
  LPCSTR  lpProcName
);

参数:
hModule:指向包含函数或变量的 DLL 模块的句柄。这个句柄通常是通过调用 LoadLibrary 或 LoadLibraryEx 函数获得的。
lpProcName:指向以 null 结尾的字符串的指针,该字符串指定函数或变量的名称。或者,如果函数的导出序号已知,可以通过将序号的低位字传递给 lpProcName 参数(高位字为0)来指定函数。Dependency Walker (DLL依赖查看工具)可以查看DLL中的函数。
返回值:
如果函数成功,返回值是指定函数或变量的地址。如果函数失败,返回值为 NULL。要获取扩展的错误信息,可以调用 GetLastError。

三、FreeLibrary 函数用于从调用进程的地址空间中卸载指定的动态链接库(DLL)。这意味着当一个 DLL 不再被使用时,可以通过调用 FreeLibrary 来释放占用的资源和内存。

BOOL FreeLibrary(
  HMODULE hModule
);

参数
hModule: 这是要卸载的 DLL 模块的句柄。这个句柄是通过之前对 LoadLibrary 或 LoadLibraryEx 函数的调用获得的。
返回值
如果函数成功,返回值为非零。
如果函数失败,返回值为零。要获取扩展的错误信息,可以调用 GetLastError 函数。

2. DLL显式链接操作

参考资料:https://blog.csdn.net/xiamentingtao/article/details/51052925?spm=1001.2014.3001.5506

  1. 创建一个空的DLL
    在这里插入图片描述
  2. 添加Interface接口类头文件
    在这里插入图片描述
#pragma once
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 INTERFACE_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// INTERFACE_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef INTERFACE_EXPORTS
#define INTERFACE_API __declspec(dllexport)
#else
#define INTERFACE_API __declspec(dllimport)
#endif


class Interface
{
public:
	virtual void ShowMsg() = 0; // 将调用方需要调用的成员函数声明成纯虚函数
	virtual ~Interface() {};// 抽象类的虚析构函数
};
extern "C" INTERFACE_API Interface* Export(void);
  1. 添加test.h头文件,实现对Test类的声明。在这里插入图片描述
 #pragma once
#include "Interface.h"
#include <string>
class Test :public Interface
{
public:
	Test();
	virtual ~Test();
	virtual void ShowMsg(void);
private:
	std::string s;
};
  1. 添加test.cpp源文件,Test类的实现。在这里插入图片描述
 #include "pch.h"
#include "test.h"
#include <iostream>

Test::Test()
{
	s = "hello form dll";
}

Test::~Test()
{
	std::cout << "destroy";
}

void Test::ShowMsg()
{
	std::cout << s << std::endl;
}
  1. 新建Interface.cpp,通过导出函数形式向调用方提供指向派生类对象的基类指针
    在这里插入图片描述
// Interface.cpp : 定义 DLL 应用程序的导出函数。
//

#include "pch.h"
#include "Interface.h"
#include <iostream>
#include "test.h"


// 通过导出函数形式向调用方提供指向派生类对象的基类指针
Interface* Export(void)
{
	return (Interface*)new Test();
}
  1. 新建一个调用DLL的项目,DLL_Explicit_Call.cpp。
    在这里插入图片描述
    在这里插入图片描述
  2. 将Dll_Explicit中的Interface.h头文件复制到DLL_Explicit_Call文件夹下,并通过现有项添加。
    在这里插入图片描述
    在这里插入图片描述
  3. 添加调用文件Dll_Explicit_Call.cpp。
    在这里插入图片描述
#include <Windows.h>
#include <iostream>
#include "Interface.h" // 包含抽象类从而使用接口

// 在调用处添加如下代码
using pExport = Interface * (*)(void); // 定义指向导出函数的指针类型

int main()
{
	HINSTANCE hDll = LoadLibrary("Dll_Explicit.dll");// 加载DLL库文件,DLL名称和路径用自己的
	if (hDll == NULL)
	{
		std::cout << "load dll fail \n";
		return -1;
	}
	pExport Get = (pExport)GetProcAddress(hDll, "Export");// 将指针指向函数首地址
	if (Get == NULL)
	{
		std::cout << "load address fail \n";
		return -1;
	}

	Interface *t = Get();// 调用导出函数获得抽象类指针
	t->ShowMsg();// 通过该指针调用类成员函数
	delete t; // 释放DLL中生成的对象
	FreeLibrary(hDll); //释放库句柄
	system("pause");
	return 0;
}
  1. 设置启动项为DLL_Explicit_Call。
    在这里插入图片描述
    在这里插入图片描述
  2. 运行结果
    在这里插入图片描述
  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值