DLL动态库的创建,隐式加载和显式加载
动态库的创建
打开VS,创建如下控制台工程,工程命名为DllTest:
在弹出的对话框中选择“DLL”后单击“完成”按钮:
在工程中新建DllTest.h和DllTest.cpp文件,在DllTest.h头文件中加入以下代码:
#ifndef _DLLTEST_H_
#define _DLLTEST_H_
extern "C" _declspec(dllexport) int Add ( int a, int b);
extern "C" _declspec(dllexport) int Sub ( int a, int b);
#endif
其中#ifndef和#define的配合使用可以防止头文件被重复引用。
declspec(dllexport)将一个函数声明为导出函数,就是说这个函数是要其他程序调用的,作为DLL的一个对外函数接口。
extern “C” 的主要作用是为了让C++代码能够正确调用其他用C语言编写的代码,加上这个限定之后,编译器会把其后的代码段按照C语言的编译方式编译,而不是按照C++语言的编译方式编译。
C++编译方式跟C编译方式的不同之处在于:C++支持函数的重载,编译器在编译函数时会附带将函数的参数个数、参数类型信息一起添加到编译之后的代码中,而不仅仅只是包含函数名称,C++正是通过这种编译机制实现函数重载的。而编译器对C语言代码中的函数在编译时只包含函数名称,不附带参数类型或参数个数信息。
所以extern “C”声明的主要作用是为了实现C语言代码在C++代码中的混合编程。
在DllTest.cpp文件中加入以下代码:
// DllTest.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "DllTest.h"
int Add(int a, int b)
{
return a+b;
}
int Sub(int a, int b)
{
return a-b;
}
以上操作相当于在DllTest.h文件中对所有DLL导出的函数进行导出声明,并且方便引用DLL的工程查看导出函数列表。DllTest.cpp文件中是对导出函数的具体实现。
编译DllTest工程,会在工程目录Debug/Release目录下生成DllTest.dll和DllTest.lib文件,加上DllTest.h文件,这三个文件就是导入DLL文件所需要的全部文件了。
在另一个工程中对DLL文件的加载有两种实现方式,分别是隐式加载(加载时动态链接)和显式加载(运行时动态加载)。
隐式加载
隐式加载是在系统启动时一次性把所有的DLL的导出函数加载到可执行文件中,需要用到.h和.lib文件。隐式加载的步骤为:
1. 新建一个测试工程,把DllTest工程中的头文件DllTest.h拷入工程目录下,并在工程的头文件中添加该头文件。
2. 在工程中的cpp文件中加入以下代码:
#include <iostream>
#include "DllTest.h"
using namespace std;
#pragma comment (lib,"DllTest.lib")
extern "C" _declspec(dllimport) int Add( int a, int b);
extern "C" _declspec(dllimport) int Sub( int a, int b);
int main(int argc,char *argv[])
{
int num1=5,num2=3;
cout<<num1<<"+"<<num2<<" = "<<Add(num1,num2)<<endl;
cout<<num1<<"-"<<num2<<" = "<<Sub(num1,num2)<<endl;
system("pause");
return 0;
}
_declspec(dllimport)指明从DLL文件中导出的函数。注意通过隐式链接加载的dll,也可以不用列出_declspec(dllimport),实际效果是一样的(可以尝试把这两段代码注释掉,也可以正常运行)。
所不同的是,加入_declspec(dllimport)后相当于明确告知编译器其后的函数是从外部dll加载的,调用的时候直接取dll文件的对应函数入口处调用,而不加_declspec(dllimport)则编译器不能分辨出当前函数是普通函数还是从别的dll文件中加载的函数,调用的时候需要在二进制代码中通过一个JMP指令跳转到dll文件中函数的入口地址。
3. 隐式加载需要使用到动态库的导入库——.lib文件,在工程中加入.lib文件主要有以下3中方式:
- 拷贝.lib文件到工程目录下,然后在程序中通过 #pragma comment(lib, "DllTest.lib")链接即可;
- 不直接拷贝.lib文件,而是通过.lib文件所在的绝对路径访问,在程序中通过 #pragma comment(lib, "D:\\XX\\DllTest.lib")链接绝对路径;
- 在工程上右键->属性->链接器->输入->附加依赖项,在附加依赖项栏中输入.lib文件所在的绝对路径,如“D:\\XX\\DllTest.lib”,在程序中就不再需要指令#pragma comment(lib, "DllTest.lib");
无论是.lib文件采取哪种方式加入到工程中,在编译成功生成.exe可执行文件之后就不再需要.lib文件了,发布的时候也不需要带着.lib文件发布。
4. 把.dll文件拷贝到程序根目录下的Debug/Release文件夹里,跟.exe文件同一目录,发布的时候也随.exe文件一起发布。
完成以上4个步骤之后就把Dll文件成功加载到程序中了,运行效果:
显式加载
显式加载不需要通过.lib和.h文件链接(当然前提是要知道.dll文件中包含的函数列表)。新建一个测试工程,在.cpp文件中加入以下代码:
#include <Windows.h>
#include <iostream>
using namespace std;
typedef int (*AddFunc) (int a, int b);
typedef int (*SubFunc) (int a, int b);
int main(int argc,char *argv[])
{
int numb1=5, numb2=3;
HMODULE hDll=LoadLibrary(L"DllTest.dll");
if(hDll!=NULL)
{
AddFunc add=(AddFunc)GetProcAddress(hDll,"Add");
SubFunc sub=(SubFunc)GetProcAddress(hDll,"Sub");
if(add)
{
cout<<numb1<<"+"<<numb2<<" = "<<add(numb1,numb2)<<endl;
}
if(sub)
{
cout<<numb1<<"-"<<numb2<<" = "<<sub(numb1,numb2)<<endl;
}
system("pause");
}
FreeLibrary(hDll);
return 0;
}
之后拷贝DllTest.dll文件到程序根目录下的Debug/Release文件夹中即可,在发布程序的时候也需要随着程序一起发布。
在显示加载中,程序会在需要的时候才去加载DLL文件,获取到DLL文件中相关的函数入口地址,然后执行,执行完之后可以立即释放掉资源。显示加载具有更好的灵活性,能更加有效的使用内存,在编写大型程序时往往使用显示加载方式。
DLL动态库的创建,隐式加载和显式加载的更多相关文章
- Windows下静态库与动态库的创建与使用
Windows下静态库与动态库的创建与使用 学习内容:本博客介绍了Windows下使用Visual C++ 6.0制作与使用静态库与动态库的方法. --------CONTENTS-------- 一 ...
- Java调用C/C++实现的DLL动态库——JNI
由于项目的需要,最近研究了java 调用DLL的方法,将如何调用的写于此,便于日后查阅: 采用的方法是JNI:Java Native Interface,简称JNI,是Java平台的一部分,可用于让J ...
- Windows下静态库、动态库的创建和调用过程
静态库和动态库的使用包括两个方面,1是使用已有的库(调用过程),2是编写一个库供别人使用(创建过程).这里不讲述过多的原理,只说明如何编写,以及不正确编写时会遇见的问题. //注:本文先从简单到复杂, ...
- Qt——动态库的创建和使用
一.动态库是什么 很多人写程序的人都见过.lib和.dll文件,对动态库也略有耳闻. 生成动态库后可以得到两个文件,后缀名分别是.lib以及.dll. 简而言之,.lib称为导入库,相当于头文件:.d ...
- Eclipse下生成.dll动态库及.a静态库使用 for Windows [z]
以后的主要工作就是做库了,将我们的C或者C++写的接口做成库,给客户端使用,因此有必要知道库的使用和制作方法.主要是在Eclipse下搞了搞,公司用的是Carbide,也差不多.库做好了,用SVN已提 ...
- Delphi调用C# 编写dll动态库
Delphi调用C# 编写dll动态库 编写C#dll的方法都一样,首先在vs2005中创建一个“类库”项目WZPayDll, using System.Runtime.InteropServices ...
- java使用JNA框架调用dll动态库
这两天了解了一下java调用dll动态库的方法,总的有三种:JNI.JNA.JNative.其中JNA调用DLL是最方便的. ·JNI ·JNA ·JNative java使用 JNI来调用dll动态 ...
- Delphi - Windows系统下,Delphi调用API函数和7z.dll动态库,自动把文件压缩成.tar.gz格式的文件
项目背景 应欧美客户需求,需要将文件压缩成.tar.gz格式的文件,并上传给客户端SFTP服务器. 你懂的,7-Zip软件的显著特点是文件越大压缩比越高,在Linux系统上相当于我们Windows系统 ...
- .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper
.NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper 參考演示样例代码,例如以下所看到的: /// <summary> /// MySql ...
随机推荐
- [WPF自定义控件库]排序、筛选以及高亮
1. 如何让列表的内容更容易查找 假设有这么一个列表(数据源在本地),由于内容太多,要查找到其中某个想要的数据会比较困难.要优化这个列表,无非就是排序.筛选和高亮. 改造过的结果如上. 2. 排序 在 ...
- 3、linux下Socket编程-TCP/UDP
1.什么是Socket 网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返 回一个整型的Socke ...
- 3930: [CQOI2015]选数|递推|数论
题目让求从区间[L,H]中可反复的选出n个数使其gcd=k的方案数 转化一下也就是从区间[⌈Lk⌉,⌊Hk⌋]中可反复的选出n个数使其gcd=1的方案数 然后f[i]表示gcd=i的方案数.考虑去掉全 ...
- POJ 3132 & ZOJ 2822 Sum of Different Primes(dp)
题目链接: POJ:id=3132">http://poj.org/problem?id=3132 ZOJ:http://acm.zju.edu.cn/onlinejudge/show ...
- matplotlib学习之函数积分图
# coding:utf-8 import numpy as np from matplotlib import pyplot as plt from matplotlib.patches impor ...
- Intellij IDEA中使用Debug
Intellij IDEA中使用Debug Debug用来追踪代码的运行流程,通常在程序运行过程中出现异常,启用Debug模式可以分析定位异常发生的位置,以及在运行过程中参数的变化.通常我们也可以启用 ...
- mysql 悲观锁 的运用
悲观锁: 它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数 ...
- html5+js压缩图片上传
最近在折腾移动站的开发,涉及到了一个手机里面上传图片.于是经过N久的折腾,找到一个插件,用法如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...
- c# 调用ArcEngine的GP工具
转自原文c# 调用ArcEngine的GP工具,AE调用GP工具 IAoInitialize m_AoInitialize = new AoInitializeClass(); esriLicense ...
- <a href='javacript:' title='{$str}'>是什么意思(多看学习视频)
<a href='javacript:' title='{$str}'>是什么意思(多看学习视频) 一.总结 一句话总结: 1.javascript:是什么? 伪协议,后面接javascr ...