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初学者比较难理解的一个东西.我这里以通俗易懂的语言 ...
随机推荐
- DDNS
一.DDNS简介 DNS,域名系统,是因特网的一项服务,它作为将域名和IP地址相互映射的一个分布式数据库,能够使人们更方便的访问互联网. DDNS,动态域名系统,是域名系统(DNS)中的一种自动更新名 ...
- contiki在keil下的stm32平台移植
参考博客: http://www.aiuxian.com/article/p-705047.html http://blog.csdn.net/u013232419/article/details/4 ...
- WPF 程序Form自的控件自适应方式之一
<Window x:Class="MapEditor2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/ ...
- redis安装及基础操作(1)
============================================================= 编译安装 0.环境 Linux:centos6.5 redis:3.0.5 ...
- find 命令
1.当前目录下查找"test.cpp"文件 find ./ -name test.cpp 2.当前查找含有"abcdef"字符串的文件 find ./ | xa ...
- Install pyodbc in OpenSUSE
Install pyodbc in OpenSUSE: Install unixODBC-2.3.2 ./configure --prefix=/usr/local/unixODBC --enable ...
- Centos下mysql数据库备份与恢复的方法
一.mysqldump工具备份 mysqldump由于是mysql自带的备份工具,所以也是最常用的mysql数据库的备份工具.支持基于InnoDB的热备份.但由于是逻辑备份,所以速度不是很快,适合备份 ...
- IIS7 配合 vs2013内置 LocalDB使用
错误提示: 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接. (provide ...
- UVALive 5058 Counting BST --组合数
题意:排序二叉树按照数插入的顺序不同会出现不同的结构,现在要在1~m选n个数,使按顺序插入形成的结构与给出的结构相同,有多少种选法. 解法:先将给出的结构插入,构造出一棵排序二叉树,再dfs统计,首先 ...
- Js倒计时程序
Js倒计时程序 点击下载