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文件加载到应用程序中。隐式调用没用过,不做表述了。
DLL的创建与使用的更多相关文章
- simplest_dll 最简dll的创建与隐式调用(显式调用太麻烦,个人不建议使用)
首先需要有个头文件,名字随便写 假设test.h //test.h #ifndef _TEST_H #define _TEST_H #ifdef TEST_EXPORTS //通过宏定义控制是输入还 ...
- 关于DLL的创建与使用简单描述(C++、C#)
前言 前一段时间在学关于DLL的创建与调用,结果发现网络上一大堆别人分享的经验都有点问题.现在整理分享一下自己的方法. 工具 Microsoft Visual Studio 2017 depends ...
- Delphi中DLL的创建和使用(转)
Delphi中DLL的创建和使用 1.DLL简介: 2.调用DLL: 3.创建DLL: 4.两个技巧: 5.初始化: 6.例外处理. 1.DLL简介 ...
- 动态链接库DLL的创建生成及调用
一.背景 最近在做CANTOUSB底层驱动的调用,是调用别人已经封装好的库,看不到别人写的源程序.程序中调用的是隐式调用即 x.h+x.lib+x.dll,其中DLL即是动态链接库(Dynamic L ...
- Delphi DLL的创建、静态及动态调用
转载:http://blog.csdn.net/welcome000yy/article/details/7905463 结合这篇博客:http://www.cnblogs.com/xumenger/ ...
- Delphi中DLL的创建和使用
参考:http://blog.csdn.net/ninetowns2008/article/details/6311663 结合这篇博客:http://www.cnblogs.com/xumenger ...
- C#中调用c++的dll具体创建与调用步骤,亲测有效~
使用的工具是VS2010哦~其他工具暂时还没试过 我新建的工程名是my21dll,所以会生成2个同名文件.接下来需要改动的只有画横线的部分 下面是my21dll.h里面的... 下面的1是自动生成的不 ...
- 在Dll中创建对话框并调用
1.第一步创建一“MFC AppWizard (dll)”工程,接下来选择“Regular Dll using shared MFC DLL”,点击“Finish”. 2.添加一对话框资源到工程中,从 ...
- 在DLL中创建窗口时一个值得注意的地方 — UnregisterClass
背景描述: 今天要测试一份注入代码,拿以前写的创建窗口的DLL来做测试. 第一次注入时一切正常,窗口被成功创建并显示,但在第二次加载时窗口没有显示出来. 经过研究发现在第二次加载DLL时Registe ...
随机推荐
- BZOJ 4034 [HAOI2015]树上操作 线段树+树剖或dfs
题意 直接照搬原题面 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所 ...
- su: Authentication failure 的解决方法
su命令不能切换root,提示su: Authentication failure 原因是 没有设置过 root密码 sudo passwd root 会提示输入密码 重新输入密码 下次再su的时候只 ...
- (转)linux中wget未找到命令
转:https://blog.csdn.net/djj_alice/article/details/80407769 在装数据库的时候发现无法使用wget命令,提示未找到命令,如图所示 那是因为没有安 ...
- cdn and fallback
https://www.davepaquette.com/archive/2015/05/06/link-and-script-tag-helpers-in-mvc6.aspx It is a com ...
- mysql 更新存在就累加,不存在就插入语法
INSERT INTO tb_http_tomcat_monitor_1 (id,total_res_time,total_req_count,req_dispose_count,queue_size ...
- Zabbix - 实现对磁盘动态监控
回到目录 前言 zabbix一直是小规模互联网公司服务器性能监控首选,首先是免费,其次,有专门的公司和社区开发维护,使其稳定性和功能都在不断地增强和完善.zabbix拥有详细的UI界面和分组策 ...
- Linux的组成
1.内核:是系统的心脏,是运行程序和管理像磁盘和打印机等硬件设备的核心程序. 2.Shell:是系统的用户界面,提供了用户和内核进行交互操作的一种接口.它接收用户输入的命令并把它送入内核去执行,是一个 ...
- JavaScript中的bind,call和apply函数的用法和区别
一直没怎么使用过JavaScript中的bind,call和apply, 今天看到一篇比较好的文章,觉得讲的比较透彻,所以记录和总结如下 首先要理解的第一个概念,JavaScript中函数调用的方式, ...
- 内存数据库:memcached与redis技术的对比试验
本文以高性能nginx服务器为应用背景,想利用缓存技术来减轻系统负荷,加快响应时间,从而增加web服务器的吞吐量. redis是一种分布式内存数据库,memcached是一种内存缓存技术,它们都采用k ...
- 使用gimp画线、矩形、圆等
使用gimp画线.矩形.圆等 https://blog.csdn.net/tody_guo/article/details/7628508 2012年06月03日 19:08:47 Tody Guo ...