Dll的显式和隐式调用
#ifndef _DLL_TUTORIAL_H_ #define _DLL_TUTORIAL_H_ #include <iostream> #if defined DLL_EXPORT #define DECLDIR __declspec(dllexport) #else #define DECLDIR __declspec(dllimport) #endif extern "C" { DECLDIR int Add( int a, int b ); DECLDIR void Function( void ); } #endif
前面两行指示编译器只包含这个文件一次。extern "C"告诉编译器该部分可以在C/C++中使用。
在VC++中这里有两个方法来导出函数:
1、使用__declspec,一个Microsoft定义的关键字。
2、创建一个模块定义文件(Module-Definition File即.DEF)。
第一种方法稍稍比第二种方法简单些,但两种都工作得很好。
__declspec(dllexport)导出函数符号到在你的DLL中的一个存储类。当下面一行被定义时我定义DECLDIR来运行这个函数,
#define DLL_EXPORT
同时也导入函数.如果下面一行
#define DLL_EXPORT
没有在源文件中出现。在此情况下,你将导出函数Add(int a, int b)和Function()。
现在,你需要写一个将要称为DLLTutorial.cpp的源文件。
#include <iostream> #include "DLL_Tutorial.h" #define DLL_EXPORT extern "C" { DECLDIR int Add( int a, int b ) { return( a + b ); } DECLDIR void Function( void ) { std::cout << "DLL Called!" << std::endl; } }
这里你定义了(DLL中的)所有函数。Int Add(int a, int b)只简单地将两个数相加而void Function(void)只是在你的DLL被调用时(将信息)通知你。在我像你展示如何使用DLL前,我想告诉你一些关于模块定义文件(.def)的内容。
模块定义文件(.def)
模块定义文件是一个有着.def文件扩展名的文本文件。它被用于导出一个DLL的函数,和__declspec(dllexport)很相似,但是.def文件并不是Microsoft定义的。一个.def文件中只有两个必需的部分:LIBRARY 和 EXPORTS。让我们先看一个基本的.def文件稍后我将解析之。
LIBRARY dll_tutorial DESCRIPTION "our simple DLL" EXPORTS Add @ Function @
第一行,''LIBRARY''是一个必需的部分。它告诉链接器(linker)如何命名你的DLL。下面被标识为''DESCRIPTION''的部分并不是必需的,但是我喜欢把它放进去。该语句将字符串写入 .rdata 节[据MSDN],它告诉人们谁可能使用这个DLL,这个DLL做什么或它为了什么(存在)。再下面的部分标识为''EXPORTS''是另一个必需的部分;这个部分使得该函数可以被其它应用程序访问到并且它创建一个导入库。当你生成这个项目时,不仅是一个.dll文件被创建,而且一个文件扩展名为.lib的导出库也被创建了。除了前面的部分以外,这里还有其它四个部分标识为:NAME, STACKSIZE, SECTIONS, 和VERSION。我将不再在本文中涉及这些内容,但是如果你在Internet上搜索,我想你将找到一些东西(译注: MSDN2003上对模板定义文件各部分内容有详尽解释,请参阅)。
现在你已经创建了你的DLL,你需要学习如何在一个应用程序中使用它了。当这个DLL被生成后,它创建了一个.dll文件和一个.lib文件;这两个都是你需要的。
隐式链接
这里有两个方法来载入一个DLL;一个方法是捷径另一个则相比要复杂些。捷径是只链接到你.lib 文件并将.dll文件置入你的新项目的路径中去。因此,创建一个新的空的Win32控制台项目并添加一个源文件。将你做的DLL放入你的新项目相同的目录下。
#include <iostream> #include <DLLTutorial.h> int main() { Function(); std::cout << Add(, ) << "/n"; ); }
你必需要链接到DLLTutorial.lib文件。我在项目属性中设置了,但是你可能会用下面的语句代替:
#pragma comment(lib, "DLLTutorial.lib")
请注意我让编译器来查看我的DLL文件夹已获得.lib文件同时让它顺便看下该目录中的DLL头文件。如果你不想这么做,你可以总是把他们放入你的新项目的目录中并使用""(引号)而不是<>。这就是载入一个DLL的简单方法。
显示链接
难点的加载DLL的方法是有稍微有点复杂的。你将需要函数指针和一些Windows函数。但是,通过这种载入DLLs的方法,你不需要DLL的.lib或头文件,而只需要DLL。下面列出一些代码,我稍后将解析之。
#include <iostream> #include <windows.h> typedef int (*AddFunc)(int,int); typedef void (*FunctionFunc)(); int main() { AddFunc _AddFunc; FunctionFunc _FunctionFunc; HINSTANCE hInstLibrary = LoadLibrary("DLL_Tutorial.dll"); if (hInstLibrary == NULL) { FreeLibrary(hInstLibrary); } _AddFunc = (AddFunc)GetProcAddress(hInstLibrary, "Add"); _FunctionFunc = (FunctionFunc)GetProcAddress(hInstLibrary, "Function"); if ((_AddFunc == NULL) || (_FunctionFunc == NULL)) { FreeLibrary(hInstLibrary); } std::cout << _AddFunc(, ) << std::endl; _FunctionFunc(); std::cin.get(); FreeLibrary(hInstLibrary); ); }
首先你会注意到:这里包括进了文件“windows.h”同时移走了“DLL_Tutorial.h”。原因很简单:因为windows.h包含了一些Windows函数,当然你现在将只需要其中几个而已。它也包含了一些将会用到的Windows特定变量。你可以去掉DLL的头文件(DLL_Tutorial.h)因为-如我前面所说-当你使用这个方法载入DLL时你并不需要它。
下面你会看到:以下面形式的一小块古灵精怪的代码:
typedef int (*AddFunc)(int,int);
typedef void (*FunctionFunc)();
这是函数指针。因为这是一个关于DLL的自学指南,深入探究函数指针超出了本指南的范围;因此,现在我们只把它们当作DLL包含的函数的别名。我喜欢在尾部用“Func”命名之。(int,int)部分是这个函数的参数部分,比如,Add函数要获得两个整数;因此,你需要它们(译注:指(int,int)部分)作为函数指针的参数。Function函数没有参数,因此你让它为空。main()部分中的前面两行是声明函数指针以使得你可以认为它们等同于DLL内部的函数。我只是喜欢预先定义它们。
一个HINSTANCE是一个Windows数据类型:是一个实例的句柄;在此情况下,这个实例将是这个DLL。你可以通过使用函数LoadLibrary()获得DLL的实例,它获得一个名称作为参数。在调用LoadLibrary函数后,你必需查看一下函数返回是否成功。你可以通过检查HINSTANCE是否等于NULL(在Windows.h中定义为0或Windows.h包含的一个头文件)来查看其是否成功。如果其等于NULL,该句柄将是无效的,并且你必需释放这个库。换句话说,你必需释放DLL获得的内存。如果函数返回成功,你的HINSTANCE就包含了指向DLL的句柄。
一旦你获得了指向DLL的句柄,你现在可以从DLL中重新获得函数。为了这样作,你必须使用函数GetProcAddress(),它将DLL的句柄(你可以使用HINSTANCE)和函数的名称作为参数。你可以让函数指针获得由GetProcAddress()返回的值,同时你必需将GetProcAddress()转换为那个函数定义的函数指针。举个例子,对于Add()函数,你必需将GetProcAddress()转换为AddFunc;这就是它知道参数及返回值的原因。现在,最好先确定函数指针是否等于NULL以及它们拥有DLL的函数。这只是一个简单的if语句;如果其中一个等于NULL,你必需如前所述释放库。
一旦函数指针拥有DLL的函数,你现在就可以使用它们了,但是这里有一个需要注意的地方:你不能使用函数的实际名称;你必需使用函数指针来调用它们。在那以后,所有你需要做的是释放库如此而已。
Dll的显式和隐式调用的更多相关文章
- dll的加载方式主要分为两大类,显式和隐式链接
之前简单写过如何创建lib和dll文件及简单的使用(http://blog.csdn.net/betabin/article/details/7239200).现在先再深入点写写dll的加载方式. d ...
- 显式与隐式(ExplicitAndImplicit)
显式与隐式(Explicit And Implicit) 1.概念 1.1 显式 实现的单词Explicit意思是清楚的.明确的.详述的.所以,显式的“显”是指明显且清楚的实现,相对于接口来说,就是明 ...
- C++ 不具有继承关系的类之间的显式,隐式转换 2013-07-11 15:41
好久没有写blog了,今天在学习c#的时候看到某一章节 讲类的隐式与显式转换.特此留笔,以供后续参考之用. 关于显式,隐式转换有些争论,说什么不建议隐式转换.但是个人认为非必要,如果有良好的基础书写基 ...
- 【RS】CoupledCF: Learning Explicit and Implicit User-item Couplings in Recommendation for Deep Collaborative Filtering-CoupledCF:在推荐系统深度协作过滤中学习显式和隐式的用户物品耦合
[论文标题]CoupledCF: Learning Explicit and Implicit User-item Couplings in Recommendation for Deep Colla ...
- Scala中的Implicit(隐式转换,隐式参数,隐式类)
文章来自:http://www.cnblogs.com/hark0623/p/4196452.html 转发请注明 代码如下: /** * 隐式转换 隐式参数 隐式类 */ //隐式转换 class ...
- 实例理解scala 隐式转换(隐式值,隐式方法,隐式类)
作用 简单说,隐式转换就是:当Scala编译器进行类型匹配时,如果找不到合适的候选,那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型.话不多说,直接测试 ImplicitHel ...
- (转载)Android理解:显式和隐式Intent
Intent分两种:显式(Explicit intent)和隐式(Implicit intent). 一.显式(设置Component) 显式,即直接指定需要打开的activity对应的类. 以下多种 ...
- JavaScript 数据类型转换(显式与隐式)
一.数据类型 JS中有5中简单数据类型(也称为基本数据类型):Undefined.Null.Boolean.Number.String.还有一种复杂数据类型------Object,Object本质是 ...
- 【转】Android理解:显式和隐式Intent---- try catch
原文网址:http://blog.csdn.net/xiao__gui/article/details/11392987 Intent是Android初学者比较难理解的一个东西.我这里以通俗易懂的语言 ...
随机推荐
- 在Windows下配置Python+Django+Eclipse开发环境
一.配置开发环境我的开发环境是:Python2.6.7 + Django1.6.2 + Eclipse1.安装Python2.安装Eclipse的Python插件PyDev如上两步如何操作请点击此进行 ...
- android apk 防止反编译技术第四篇-对抗JD-GUI
又到周末一个人侘在家里无事可干,这就是程序员的悲哀啊.好了我们利用周末的时间继续介绍android apk防止反编译技术的另一种方法.前三篇我们讲了加壳技术(http://my.oschina.net ...
- 安装rpm包
下载好一个rpm包怎样安装? [root@localhost ~]# ls anaconda-ks.cfg install.log install.log.syslog jboss-as-7.1.1- ...
- MMORPG大型游戏设计与开发(客户端架构 part9 of vegine)
时间在人们的生活中是多么重要的东西,如果打乱了时间,不知道这个时间会成什么样子.在客户端中,自然也有时间模块,因为不同的时间可能会处理不同的事情,特别是在追求高度自由化的同时,时间也成为了一个很重要的 ...
- ZBrush中怎样对遮罩进行反选
通过对ZBrush的学习,我们知道了如何手动创建遮罩,手动创建遮罩相对来说是最简单有效的方法,在某些特定的使用场合会起到事半功倍的效果.创建遮罩我们可以结合Ctrl键在物体保持编辑的状态下来执行,您可 ...
- 2014 UESTC暑前集训图论专题解题报告
A.方老师和缘分 http://www.cnblogs.com/whatbeg/p/3765621.html B.方老师和农场 http://www.cnblogs.com/whatbeg/p/376 ...
- NOIP2015聪明的质检员[二分 | 预处理]
背景 NOIP2011 day2 第二题 描述 小T 是一名质量监督员,最近负责检验一批矿产的质量.这批矿产共有 n 个矿石,从 1到n 逐一编号,每个矿石都有自己的重量 wi 以及价值vi .检验矿 ...
- 理解Java中字符流与字节流的区别
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序 ...
- nmap脚本扫描使用总结
nmap的脚本默认目录为:/usr/share/nmap/scripts/ Nmap提供的命令行参数如下 -sC: 等价于--script=default,使用默认类别的脚本进行扫描 可更换其他类别 ...
- IOS第四课——Autolayout_View
这节课,我们要学习MVC.Selector.Access Control.Extension.Auto Layout.Delegate-Protocol.Custom View. Auto Layou ...