windows编程之动态链接库的使用

当我们进行程序的编写的时候,经常需要将一些资源进行重复利用等操作。于是,微软就提出了DLL(动态链接库),它可以很好的将资源进行重复利用。

当一个进程加载一个DLL的时候呢,一般都会执行DllMain这个动态链接库的入口点(有时候不会,后面再讲),来看下它的声明:

BOOL WINAPI DllMain(  
  HINSTANCE hinstDLL,  // dll模块的内存基址
  DWORD fdwReason,     // 加载本dll的原因
  LPVOID lpvReserved   // 系统保留
);
其中,fdwReason有四个原因,第一个是DLL_PROCESS_ATTACH,表示进程加载本dll时。第二个是DLL_THREAD_ATTACH,表示线程加载本dll时。第三个是DLL_THREAD_DETACH,进程卸载本dll时,第四个是DLL_PROCESS_DETACH,线程卸载本dll时。

因为它是被刚加载或卸载的时候调用的,因此我们可以在该函数内写一些初始化的工作和释放资源的工作。

在DLL中,我们编写完要共享的代码后,我们需要对这些代码进行导出,变成导出函数或类等才能被我们共享使用。下面来介绍两种DLL导出函数的两种方式。

第一种导出方式是使用def文件,里边的内容格式如下:

LIBRARY 项目名
EXPORTS
导出函数名1
导出函数名2
写完之后,将后缀名改为.def格式,将其添加到工程里边,然后在VS里边右键属性----配置信息/链接器/输入/模块定义文件这一栏添加该文件即可,然后我们生成解决方案,我们的函数就被导出了。我们可以使用Dependency Walker工具来查看是否已经被导出了。

第二种导出的方式是使用__declspec(export)的方式。我们在要导出的函数名前添加这样一句话即可。如果你使用的是VS的C++编译器,最好在该句话前在添加一句extern "C"来声明是以C语言的标准来进行编译的,为什们呢?因为VS的C++编译器会自动的将我们的函数名以微软的方式添加一些信息。以下是一个声明导出函数的例子:

extern "C" __declspec(dllexport) DWORD TestFun(int a, int b)
{
	return a + b;
}

知道了怎样在dll工程中导出一个函数后,我们在来看下如何在别的工程中使用我们导出的函数。调用导出函数,也有两种方式,一种是隐式调用,一种是显示调用。

先来看下隐式调用。在我们的dll工程中,生成解决方案后,会生成一个dll文件和一个.lib文件。其中.dll文件里边包含了代码的实现等信息。而.lib文件则包含了函数的内存地址信息,用于指明我们的函数在哪个内存地址。因此,我们可以链接这个lib文件来使用我们的函数。在此之前呢,我们还要声明一下我们要使用哪个函数,然后再来链接,声明时要在函数前加一句__declspec(dllimport)表示导入一个函数,下面是一个示例:

extern "C" __declspec(dllimport) int TestFun(int a, int b);<span style="white-space:pre">	</span>//声明一个导入函数
#pragma comment(lib,"testLib.lib")<span style="white-space:pre">	</span>//链接我们的lib文件
显式调用。假设我们没有.lib文件,那么我们应该使用显示调用。显示调用就是调用LoadLibrary函数来动态的加载一个dll文件,然后用GetProcAddress来确定导出函数的地址。它们的声明如下:

HMODULE LoadLibrary(  
  LPCTSTR lpFileName   // 模块名
);

FARPROC GetProcAddress(  
  HMODULE hModule,    // 上一个函数的返回值
  LPCSTR lpProcName   // 函数名
);
下面是一个显示调用的示例代码:

#include <stdio.h>
#include <windows.h>
//定义一个函数指针,返回值和参数要与导出的函数相同
typedef int(*lpTestFun)(int,int);

int _tmain(int argc, _TCHAR* argv[])
{

	HMODULE hDll;
	lpTestFun testFun;
	//加载一个dll
	hDll = LoadLibrary("testLib.dll");
	if (hDll)
	{
		//获取要导出函数的地址
		testFun = (lpTestFun)GetProcAddress(hDll, "testFun");
		if (testFun)
		{
			printf("testFun result:%d\n", testFun(3, 2));
		}
	}
	return 0;
}

在文章开头讲过我们可能不需要DllMain这个函数入口点,当我们动态的加载一个函数时,除了调用LoadLibrary这个函数外,我们还可以调用它的加强版LoadLibraryEx,其声明如下:

HMODULE LoadLibraryEx(  
  LPCTSTR lpFileName,  	  // 文件名
  HANDLE hFile,           // 系统保留,必须为NULL
  DWORD dwFlags           // 一些标志位
);
该函数的第三个参数里边,有一个DONT_RESOLVE_DLL_REFERENCES这个标志位,它表示当使用这个标志位的时候呢,加载一个Dll时,程序不会执行我们的DllMain这个入口点函数。为什么要使用这个函数呢,假如我们在网上下载了一个dll,它可能在DllMain里边写了一些邪恶的代码,如果我们调用LoadLibrary去加载时,就可能引发一些严重的后果。这时加强版就起到了作用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值