VS2010 学习C++动态链接库的创建与使用(带实例代码)

本文只是记载下学习C++动态链接库的过程以及记录学习过程中的一些程序,好开始。

先用VS2010创建一个dll的工程,好,只看图不说话,点击finish就完成创建了。。。

创建头文件developer_dll.h和源代码developer_dll.cpp两个文件,里面可以写你喜欢的函数以及类。

以下是我的.h和.cpp文件代码:

developer_dll.h:

#pragma once
#ifdef dev_dll_api
#else
#define dev_dll_api _declspec(dllimport)
#endif

namespace developer_dll_namespace
{
	dev_dll_api void add(int a, int b);
	dev_dll_api void subtract(int a, int b);

	class dev_dll_api developer_dll_class
	{
	public:
		void class_add(int a, int b);
		void class_subtract(int a, int b);
	protected:
	private:
		int x;
		int y;
	};
}

developer_dll.cpp:

#include <iostream>

#define dev_dll_api _declspec(dllexport)
#include "developer_dll.h"

namespace developer_dll_namespace
{
	void add(int a, int b)
	{
		std::cout<<"两者之和(调用普通函数):"<<a+b<<std::endl;
	}

	void subtract(int a, int b)
	{
		std::cout<<"两者之差(调用普通函数):"<<((a>b)?(a-b):(b-a))<<std::endl;
	}

	void developer_dll_class::class_add(int a, int b)
	{
		std::cout<<"两者之和(调用类的成员函数):"<<a+b<<std::endl;
	}

	void developer_dll_class::class_subtract(int a, int b)
	{
		x = a;
		y = b;
		std::cout<<"两者之差(调用类的成员函数):"<<((x>y)?(x-y):(x-y))<<std::endl;
	}
}

好,搞定这两个就可以build生成.lib以及.dll文件了,看图:


然后就可以创建使用者的.h文件和.cpp文件来加载dll,在程序中加载DLL有两种方法,分别为隐式链接动态加载dll隐式链接需要(.lib、.dll、.h)文件,动态加载dll只需(.dll)文件就可以了。

1、然后下面先以隐式链接测试:

创建源代码user_dll.cpp来测试dll。


.lib、.dll、.h)文件复制进来并且创建相应的文件夹。


在这里将lib的路径加进去,不然编译链接会出错。

下面是我的user_dll.cpp:

#include <iostream>
#include <Windows.h>

#include "developer_dllh/developer_dll.h"
#pragma comment(lib, "developer_dll.lib") 

using namespace std;
using namespace developer_dll_namespace;

int main()
{
	add(2,3);
	subtract(2,3);

	developer_dll_class users;
	users.class_add(2,3);
	users.class_subtract(2,3);

	system("pause");
	return 0;
}

运行测试一把:我的结果是这样的:


那如果我们只想导出类的部分函数要怎么导出:


这里我们可以利用depends这个软件来查看dll导出了哪些函数:


这里为什么函数名会是这样子呢,主要是C++为了实现重载实现了函数倾轧,函数名就按照一定的规则发生的变化,只要都是C++是可以正常调用的。如果要用C语言调用dll,就要添加extern “C”,但是它不能用于导出类的成员函数。如果是给其他语言调用则使用extern “C”也没有卵用。要避免这个问题就得使用模块定义文件.def,待会就测试这个怎么使用。

	
	add(2,3);
	subtract(2,3);

	developer_dll_class users;
	users.class_add(2,3);
	//users.class_subtract(2,3); 这个成员函数没有导出,所以不能用了。

	system("pause");
	return 0;

好我们测试一把:



2、好下面来实现动态链接dll:

这里需要用到几个函数:

LoadLibrary:

[格式]:

  1. function LoadLibrary(LibFileName : PChar): Thandle;
[说明]:参数 LibFileName 指定了要装载的 DLL 文件名,如果 LibFileName 没有包含一个路径,系统将按照:当前目录、Windows 目录、Windows 系统目录、包含当前任务可执行文件的目录、列在 PATH 环境变量中的目录等顺序查找文件。

[功能]:加载由参数 LibFileName 指定的 DLL 文件。

如果函数操作成功,将返回装载 DLL 库模块的实例句柄,否则,将返回一个错误代码。假如在应用程序中用 LoadLibrary 函数装入某一个 DLL 前, 其他应用程序已把该 DLL 装入内存中了,则系统将不再装入该 DLL 的另一个实例,而是使该 DLL 的“引用计数”加 1

GetProAccess

[格式]:

  1. function GetProcAddress(Module:Thandle; ProcName:PChar): TfarProc;
[说明]:参数 Module 包含被调用函数的 DLL 句柄,这个值由 LoadLibrary 返回, ProcName是指向含有函数名的以 nil 结尾的字符串指针,或者可以是函数的次序值,但大多数情况下,用函数名是一种更稳妥的选择。这里如果要使用函数名字如果不采用.def的方式会比较麻烦。

int main()
{
	HMODULE load_ex = LoadLibrary(_T("developer_dll.dll"));//返回一个句柄
	if (load_ex)
	{
		addprt add = (addprt)GetProcAddress(load_ex,"?add@developer_dll_namespace@@YAXHH@Z");
		if (add)
		{
			add(2,3);
		}
		subtractprt subtract = (subtractprt)GetProcAddress(load_ex,"?subtract@developer_dll_namespace@@YAXHH@Z");
		if (subtract)
		{
			subtract(2,3);
		}
	}

如果该函数执行成功,则返回 DLL 中由参数 ProcName 指定的过程或函数的入口地址,否则返回 nil 。
FreeLibrary:

[功能]:返回参数 Module 指定的模块中,由参数 ProcName 指定的过程或函数的入口地址

[格式]:

  1. procedure  FreeLibrary(Module: Thandle);

[说明]:将由参数 Module 指定的 DLL 文件从内存中卸载 1 次。
[说明]:Module 为 DLL 库的句柄。这个值由 LoadLibrary 返回。由于 DLL 在内存中只装载一次,因此调用 FreeLibrary 首先使 DLL 的引用计数减 1,如果计数减为 0 则卸载该 DLL。

上面函数说明是从这里摘下来的,感兴趣可以去阅读https://www.cnblogs.com/westsoft/p/5936092.html

现在测试以导出文件(.def)的方式导出dll,在工程里面添加.def文件。


下面是我的源文件:

//.cpp文件
#include <iostream>
#include "developer_dllmd.h"
namespace developer_dll_namespace
{
	void add(int a, int b)
	{
		std::cout<<"两者之和(调用普通函数):"<<a+b<<std::endl;
	}
	void subtract(int a, int b)
	{
		std::cout<<"两者之差(调用普通函数):"<<((a>b)?(a-b):(b-a))<<std::endl;
	}
#if 0
void developer_dll_class::class_add(int a, int b)
	{
		std::cout<<"两者之和(调用类的成员函数):"<<a+b<<std::endl;
	}

	void developer_dll_class::class_subtract(int a, int b)
	{
		x = a;
		y = b;
		std::cout<<"两者之差(调用类的成员函数):"<<((x>y)?(x-y):(y-x))<<std::endl;
	}
#endif
}
//.h文件
#pragma  once
namespace developer_dll_namespace
{
	void add(int a, int b);
	void subtract(int a, int b);
	#if 0
class developer_dll_class
	{
	public:
		void class_add(int a, int b);
		void class_subtract(int a, int b);
	protected:
	private:
		int x;
		int y;
	};
#endif
}
//.def文件
LIBRARY "developer_dllmd"
EXPORTS
add @1
export_subtract=subtract @2

下面编写user函数实现动态调用:

#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <winuser.h>

using namespace std;
typedef void (*addprt)(int a, int b);
typedef void (*subtractprt)(int a, int b);

int main()
{
	HMODULE load_ex = LoadLibraryA("developer_dllmd.dll");//返回一个句柄
	if (load_ex)
	{
		addprt add = (addprt)GetProcAddress(load_ex,"add");
		if (add)
		{
			add(2,3);
		}

		addprt add1 = (addprt)GetProcAddress(load_ex,MAKEINTRESOURCEA(1));
		if (add1)
		{
			cout<<"通过序号调用:"<<"\t";
			add1(2,3);
		}
		else
		{
			cout<<"获取地址失败"<<endl;
		}

		subtractprt subtract = (subtractprt)GetProcAddress(load_ex,"export_subtract");
		if (subtract)
		{
			subtract(2,3);
		}
		subtractprt subtract1 = (subtractprt)GetProcAddress(load_ex,MAKEINTRESOURCEA(2));
		if (subtract1)
		{
			cout<<"通过序号调用:"<<"\t";
			subtract1(2,3);
		}
	}
        FreeLibrary(load_ex);
	system("pause");
	return 0;
}

好测试一把,我们看下结果如何:


对于类的动态导出其实文中方法不太好,还有更好的方法下会解说。

好,文章到此结束,谢谢观看,欢迎提问,共同讨论和提高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章