Not Only Algorithm,不仅仅是算法,关注数学、算法、数据结构、程序员笔试面试以及一切涉及计算机编程之美的内容 。。
你的位置:NoAlGo博客 » 技术 » ,

在Visual Stuido中使用DLL

前面在在Visual Stuido中创建DLL这篇文章中介绍了如何在Visual Studio中创建自己的DLL,本文将继续讨论如何在Visual Studio中调用自己新建的DLL或者使用别人提供的DLL。
由于DLL调用有显示调用和隐式调用之分,适用于不同情况的需求,以下将分别进行详细介绍。

一 显式调用

显式调用指在程序中显式地加载DLL文件,然后读取文件中对应的内容进行使用。对于某些只有DLL文件而没有相应的LIB文件和.H头文件的情况下,只能使用显式调用。

首先在需要的时候使用Win32 API函数LoadLibrary动态加载DLL文件内容,然后使用GetProcessAdress函数获取想要调用的函数的指针,得到函数指针之后可以进行对应的函数调用。最后使用完毕后使用FreeLibrary函数释放DLL的内容。

GetProcessAdress函数可以通过函数的符号获取函数指针

GetProcAddress(hDll, "addFun")

也可以利用函数在导出文件中的序号进行获取

GetProcAddress (hDll, MAKEINTRESOURCE (1))

这里需要注意DLL是否使用了C++编译器导致函数的符号发生了改变。

GetProcessAdress函数也可以获取变量的指针,从而可以访问DLL中导出的变量。但是GetProcAddress不能直接支持显式获取类的信息,不过可以采用其他的技巧得到,这里不详细讨论。

另外,查看dll的内容可以使用VS自带的dumpbin.exe工具,用法为

dumpbin.exe /EXPORTS xxx.dll

整体的DLL显式调用方法如下所示(因为使用了win32的API函数,所以要包含windows.h头文件)。

#include <iostream>
#include <Windows.h>
using namespace std;

int main()
{
	//加载DLL
	//需要把路径表示转换成宽字符(ANSI字符串转换成unicode字符串),每个字符占两字节
	HINSTANCE hDll = LoadLibrary(L"..\\myDll\\Debug\\myDll.dll"); 
	if (hDll == NULL) //加载失败
	{
		cout << "Load Dll ERROR!" << endl;
		return 0;
	}

	//获取函数
	typedef int(*lpAddFun)(int, int); ///定义函数指针类型
	lpAddFun addFun = (lpAddFun)GetProcAddress(hDll, "addFun");
	if (addFun == NULL)
		cout << "Get function addFun ERROR!" << endl;
	else
		cout << "1 + 1 = " << addFun(1, 1) << endl;


	//获取变量
	int* globalVar = (int*)GetProcAddress(hDll, "globalVar");
	if (globalVar == NULL)
		cout << "Get variable globalVar ERROR!" << endl;
	else
 		cout << "Global value is: " << *globalVar << endl;

	//释放DLL
	FreeLibrary(hDll);	

	/*程序输出如下:
	======Process attach of dll======
	1 + 1 = 2
	Global value is: 123
	======Process detach of dll======
	*/
}

二 隐式调用

隐式调用不需要用户手动加载DLL文件,该方法需要使用DLL对应的LIB文件,系统会在程序开始时自动加载对应的DLL文件,程序运行时可以直接访问DLL的内容,程序结束时系统自动释放已加载的DLL的内容。如果多个程序使用了DLL的话,系统只需把该DLL的使用计数减一。

隐式调用需要在程序中指明使用到的DLL文件信息,比如使用以下预处理指令指明DLL对应的lib文件。

#pragma comment(lib,"xxx.lib")

或者可以在Visual Stuido的项目设置中进行依赖项的设置。

添加以上指令之后系统会自动载入DLL的内容,然后我们需要做的就是导入需要用到的DLL函数即可。具体使用的是__declspec(dllimport)关键字进行导入,参考以下代码。

#include <iostream>
using namespace std;

#pragma comment(lib,"..\\myDll\\Debug\\myDll.lib") //注意修改成你的文件目录

extern "C" __declspec(dllimport) int addFun(int a,int b);					//获取函数
extern "C" __declspec(dllimport) int globalVar;								//获取变量
extern __declspec(dllimport) class myClass{ public: void showMessage(); };	//获取类

int main()
{ 
	myClass m; m.showMessage();						 //测试类
	cout << "1 + 1 = " << addFun(1, 1) << endl;		 //测试函数
	cout << "Global value is: " << globalVar << endl;//测试变量

	/*程序输出如下:
	======Process attach of dll======
	Hello World!
	1 + 1 = 2
	Global value is: 123
	======Process detach of dll======
	*/
}

但是以上需要自己导入所需变量的方法还是不够方便,回到Visual Stuido中DLL的创建这篇文章中,我们建立DLL头文件时已经考虑上了导入变量的关键字的情形了(使用预定义的宏进行导入还是导出的判断)。

所以这里可以直接包含对应的头文件,然后已经导入所有DLL包含的内容,就可以向直接调用相关的函数了。

代码如下所示。

#include <iostream>
using namespace std;

#include "../myDll/myLib.h" //注意修改成你的.h文件目录
//以下设置可以改成在VS项目属性设置中进行
#pragma comment(lib,"..\\myDll\\Debug\\myDll.lib") //注意修改成你的.lib文件目录

int main()
{ 
	myClass m; m.showMessage();						 //测试类
	cout << "1 + 1 = " << addFun(1, 1) << endl;		 //测试函数
	cout << "Global value is: " << globalVar << endl;//测试变量

	/*程序输出如下:
	======Process attach of dll======
	Hello World!
	1 + 1 = 2
	Global value is: 123
	======Process detach of dll======
	*/
}

注意这样使用的前提是必须在头文件里有进行dllimport的相应设置。如果头文件中只有dllexport的设置,此时会出现无法解析的外部变量错误。

这种方法非常简单,实际中一般也是直接这样使用。在Visual Stuido使用的时候可以进行相应的配置

  1. 在项目->属性->C/C++->常规->附加包含目录中加入需要使用到的DLL的头文件。
  2. 在项目->属性->连接器->常规->附加库目录中设置DLL和LIB 文件路径。
  3. 在项目->属性->连接器->输入->附加依赖项中添加要加载的Lib文件名(加载多个Lib文件时,以回车为分隔符)。进行了这一步不需要在代码中添加#pragma comment指令。

三 简单比较

在使用方面:动态调用需要程序员自己把握DLL的操作,使用起来比较复杂。而一般的隐式调用使用起来非常方便,就跟调用本地函数的方式一样。

在使用要求和模块更新方面:在一般的隐式调用中,需要有.H、LIB、DLL三个文件,并不能有效支持模块的更新。而显式调用只需要有DLL文件即可,且能够更好地支持模块的更新。

在资源占用和运行效率方面:动态调用中程序会动态地加载及释放DLL内容,所以程序会比较灵活,可以根据需要进行DLL的选择,并且内存的占用会比较少,但是如果频繁使用的话会影响程序的运行效率。隐式调用中程序在运行期间DLL始终加载在内存之中,所以程序需要在一开始就选择好所有需要的DLL,导致内存的占用会比较高,但是频繁使用对运行效率影响不大。

上一篇: 下一篇:

我的博客

NoAlGo头像编程这件小事牵扯到太多的知识,很容易知其然而不知其所以然,但真正了不起的程序员对自己程序的每一个字节都了如指掌,要立足基础理论,努力提升自我的专业修养。

站内搜索

最新评论