DLL的创建与使用
一、动态链接库(DLL)
动态链接库提供了一种方法,使进程可以调用不属于其执行代码的函数。函数的可执行代码位于一个.dll文件中,该文件包含一个或多个已被编译、链接并使用它们的进程分开存储的函数。
优点:
1.扩展了 应用程序的特性
2.可以使用多种编程语言来编写
3.简化项目的管理
二、依赖项
当某个程序或DLL使用其他DLL或DLL函数时,就会创建依赖项,因此程序就不会再独立了,依赖项如果被破坏,该程序可能出现问题。
三、入口点
在创建DLL时,可以有选择地指定入口点的函数。当进程或线程将它们自身附加到DLL或者将它们自身以DLL分离时,都会调用入口函数。入口函数应该只进行简单的初始化工作,而不应该调用其他DLL函数或者终止函数。
关于创建DLL,我的环境是VS2017,步骤为:文件->新建项目->选择动态链接库(DLL)->完成。在创建的项目中的源文件可以看到有一个已经创建好的dllmain.cpp,这个就DLL入口点的代码了。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule, //模块句柄
DWORD ul_reason_for_call,//调用原因
LPVOID lpReserved //参数保留,好像没什么用
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: //DLL被某个程序加载
case DLL_THREAD_ATTACH: //DLL被某个线程加载
case DLL_THREAD_DETACH: //DLL被某个线程卸载
case DLL_PROCESS_DETACH: //DLL被某个程序加载
break;
}
return TRUE;
}
四、如何导出(在DLL中实现的)
第一种:
步骤1:向所有需要导出的DLL函数中添加关键字__declspec(dllexport)。
步骤2:要在应用程序中使用导出的DLL函数,必须使用__declspec(dllexport)来声明要导入的各个函数。
本章使用的就是这种方法
通常最好使用一个包含define语句和ifdef语句的头文件,以便分隔导出语句和导入语句,代码如下。
说明:该DLL库实现的是简单的加减乘除运算,注意实现减法的这个函数,我这里特别使用了一个抽象类来实现,我发现有些企业代码都会有这种类似的抽象类,然后用纯虚函数做接口。
//__declspec(dllexport)修饰符指示编译器和链接器从
//DLL 导出函数或变量,以便其他应用程序可以使用它,在ExprotDll.h中
#pragma once
#ifdef EXPORTDLL_EXPORTS
#define EXPORTDLL_API __declspec(dllexport)
#else
#define EXPORTDLL_API __declspec(dllimport)
#endif //导出的类
class EXPORTDLL_API CExportDll {
public:
// TODO: 在此添加方法
//用纯虚函数做为接口
virtual int SUBTARCT(int a, int b) = ;
virtual ~CExportDll() {}; }; //导出函数,如果是在C++环境下,则执行"{" "}"中的内容
#ifdef __cplusplus
extern "C" {
#endif
EXPORTDLL_API CExportDll* Create();
EXPORTDLL_API void Destroy(CExportDll* ex);
#ifdef __cplusplus
}
#endif //也可用下列方法定义导出函数
extern "C" EXPORTDLL_API int nExportDll;
extern "C" EXPORTDLL_API int ADD(int a, int b);
extern "C" EXPORTDLL_API int MULTIPLY(int a, int b);
extern "C" EXPORTDLL_API float DIVIDE(float a, float b);
第二种:
创建模块定义文件.def以列出导出的DLL函数:
五、特别调用
需要注意以下一些情况:
1.如果使用了Win32 API,则应该使用关键字__stdcall修饰函数
2.如果将C++生成的DLL供标准C语言使用,输出文件用extern "C"来修饰,设置导出函数时采用.def文件形式,而不是__declspec(dllexport)
六、编写DLL
创建DLL工程并且定义了相应的导出函数、变量或类之后,接下来就是实现功能了。本章中因为CExportDll是一个抽象类,所以需要再创建一个它的子类(CExportDllChild.h中),实现相应的接口功能(EXportDll.cpp中),同时其他函数也在(EXportDll.cpp)中实现功能。
//ExportDllChild.h
#pragma once
#include "ExportDll.h" class CExportDllChild :public CExportDll
{
virtual int SUBTARCT(int a, int b);
virtual ~CExportDllChild() {};
};
// ExportDll.cpp : 定义 DLL 应用程序的导出函数。 #include "stdafx.h"
#include "ExportDllChild.h" //这是一个导出变量的一个示例
EXPORTDLL_API int nExportDll = ; //这是导出函数的几个示例
//当前使用的是C++编译出来的 //构造对象
EXPORTDLL_API CExportDll* Create()
{
return new CExportDllChild;
}
//析构对象
EXPORTDLL_API void Destroy(CExportDll* ex)
{
delete ex;
}
//加
EXPORTDLL_API int ADD(int a, int b)
{
return a + b;
}
//减
int CExportDllChild::SUBTARCT(int a, int b)
{
return a - b;
}
//乘
EXPORTDLL_API int MULTIPLY(int a, int b)
{
return a * b;
}
//除
EXPORTDLL_API float DIVIDE(float a, float b)
{
if (b != ) {
return a / b;
}
else
return -;
}
创建编写好后,进行生成,可以在项目Debugp目录下看到生成的动态库.dll和静态库.lib
七、调用DLL
调用DLL有两种方法,一种是显式链接方式,另一种是隐式链接方式。
在调用前,先创建一个Win32控制台应用程序或者MFC,这里创建的是一个简单的Win32程序(LoadDll)
1.显式链接:
在此项目中,需要将CExportDllChild.h放在LoadDll目录下。然后在cpp中添加以下代码
// LoadDll.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include "pch.h"
#include <Windows.h>
#include <iostream>
#include "ExportDll.h" /*-------------动态链接-------------*/
int main()
{
HMODULE hModule; //模块句柄
//定义函数指针
typedef int(*FUNC1)(int a, int b);
typedef CExportDll* (*CreateFun2)();
typedef void(*DestroyFun2)(CExportDll* ex);
typedef int(*FUNC3)(int a, int b);
typedef float(*FUNC4)(float a, float b);
//调用DLL,先找当前文件夹,如果没有,就会去system32下查找
hModule = ::LoadLibrary(L"ExportDll.dll"); if (hModule == NULL){
MessageBox(NULL, L"DLL加载失败", L"Mark", MB_OK);
}
//获取相应DLL函数的入口地址
FUNC1 add = (FUNC1)::GetProcAddress(hModule, "ADD");
CreateFun2 create = (CreateFun2)::GetProcAddress(hModule, "Create");
DestroyFun2 destroy = (DestroyFun2)::GetProcAddress(hModule, "Destroy");
FUNC3 multiply = (FUNC3)::GetProcAddress(hModule, "MULTIPLY");
FUNC4 divide = (FUNC4)::GetProcAddress(hModule, "DIVIDE");
//加
if (add != NULL){
std::cout << "a+b="<< add(, ) << std::endl;
}
//减
if (create&&destroy)
{
CExportDll* p = create();
std::cout << "a-b=" << p->SUBTARCT(, ) << std::endl;
destroy(p);
p = NULL;
}
//乘
if (multiply != NULL){
std::cout << "a*b=" << multiply(, ) << std::endl;
}
//除
if (divide != NULL)
{
std::cout << "a/b=" << divide(, ) << std::endl;
}
FreeLibrary(hModule); //释放句柄
return ;
}
调用结果:
2.隐式调用
在程序开始执行时就将DLL文件加载到应用程序中。隐式调用没用过,不做表述了。
最新文章
- 二、CoreAnimation之寄宿图详解
- 谈谈JPA-01-概述
- PL/SQL查询Oracle数据乱码/Oracle客户端乱码解决办法
- LEARUN 开发框架 /aspnetboilerplate ----上海力软信息技术有限公司
- POJ 1274 The Perfect Stall 水二分匹配
- java网络之udp
- ASP.NET Core MVC/WebAPi如何构建路由?
- 配置WampServer以及搭建WordPress的一些问题,持续总结。
- UNIX环境高级编程——system V消息队列
- 变量的值与判断的结果有关,使用Set Variable If
- VsCode插件开发之插件初步通信
- 深入JVM对象引用
- scrapy 异步存储mysql
- Estimation And Gain
- We&#39;re Chronos! ----- Meet the team 团队作业 #1
- 获取sevlet response值
- 如何删除Android studio中的注解代码
- nginx AIO机制与sendfile机制
- bzoj 3790 神奇项链(Manacher,DP+BIT | 贪心)
- Kafka剖析:Kafka背景及架构介绍
热门文章
- flask框架(十二):上下文管理***
- 【CUDA 基础】3.1 CUDA执行模型概述
- BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)
- vue-loader+webpack配置项目流程
- Leetcode题目22.括号生成(动态规划-中等)
- 20175215 2018-2019-2 第十周java课程学习总结
- 定时任务-Quartz(热部署、冷部署)
- rest 参数与扩展运算符
- 七十六:flask.Restful之flask-Restful插件的基本使用
- Kibana 创建索引 POST 403 (forbidden) on create index