应用篇之DLL的静态调用和动态调用

区别静态调用(static call)和动态调用(dynamic call)。

静态调用

        静态调用,即 Load-time Dynamic Linking。正如我们常用的配置方式,同时需要头文件、LIB和DLL文件,缺一不可。

动态调用

        动态调用,即Run-time Dynamic Linking是一种隐式的调用方式,即程序运行过程中装载DLL。该方式只需要DLL,不需要头文件和LIB)。然后获取指定函数名称的接口函数,然后再调用之。
        具体做法如下:首先使用LoadLibrary函数获取DLL的句柄,如果LoadLibrary成功,则将返回的句柄传入GetProcAddress函数中,来获取DLL的需要调用的函数的地址。调用DLL函数后,程序将调用FreeLibrary函数以卸载DLL。
        示例如下,DLL中包含的如下:

// MathLibrary.h - Contains declarations of math functions
#pragma once

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif

// The Fibonacci recurrence relation describes a sequence F
// where F(n) is { n = 0, a
//               { n = 1, b
//               { n > 1, F(n-2) + F(n-1)
// for some initial integral values a and b.
// If the sequence is initialized F(0) = 1, F(1) = 1,
// then this relation produces the well-known Fibonacci
// sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

// Initialize a Fibonacci relation sequence
// such that F(0) = a, F(1) = b.
// This function must be called before any other function.
extern "C" MATHLIBRARY_API void fibonacci_init(
	const unsigned long long a, const unsigned long long b);

// Produce the next value in the sequence.
// Returns true on success and updates current value and index;
// false on overflow, leaves current value and index unchanged.
extern "C" MATHLIBRARY_API bool fibonacci_next();

// Get the current value in the sequence.
extern "C" MATHLIBRARY_API unsigned long long fibonacci_current();

// Get the position of the current value in the sequence.
extern "C" MATHLIBRARY_API unsigned fibonacci_index();

新建工程,使用LoadLibrary、GetProcAddress和FreeLibrary动态调用DLL.

#include 
#include 

int main()
{
	HMODULE hModule = LoadLibrary("MyDLL.dll");
	if (hModule == NULL || hModule == INVALID_HANDLE_VALUE) {
		return -1;
	}
	typedef void(*TYPE_init) (const unsigned long long, const unsigned long long);
	typedef unsigned long long(*TYPE_current) ();
	auto ProcInit = (TYPE_init)GetProcAddress(hModule, "fibonacci_init");
	auto ProcCurrent = (TYPE_current)GetProcAddress(hModule, "fibonacci_current");
	if (ProcInit != NULL && ProcCurrent != NULL)
	{
		ProcInit(5, 6);
		std::cout << ProcCurrent() << std::endl;
	 }
	FreeLibrary(hModule);
	system("pause");
	return 0;
}

可以看出,虽然没有包含dll对应的头文件,但是我们要能够知道调用函数的函数指针类型和函数名。

二者区别

静态调用

        当可执行文件exe在构建时链接到DLL时,链接器将不会插入目标代码,而是会插入一个存根,该存根基本上表示此名称的功能位于此DLL中。当可执行文件运行时,可执行文件的位将丢失(即ha函数存根),因此在允许程序运行之前,程序加载器通过将其替换为DLL文件的入口点来修复这些丢失的函数。只有在所有存根都已被替换(即已解决)之后,可执行文件才被允许运行。
所以,一旦DLL文件发生变化,exe需要重新编译。另外由于替换存根,程序加载时需要更多的内存和消耗时间。

动态调用

        在这种情况下,可执行文件exe未链接到任何DLL库文件,因此它不会包含任何存根,因此程序加载器运行该可执行文件没有问题。但是,从DLL中获取对函数的访问权的任务留给了可执行文件,可以使用GetProcAddress Windows API完成。
所以,一旦DLL文件发生变化,exe不需要重新编译。程序加载时内存和消耗时间相对减少。

参考资料

  • Using Load-Time Dynamic Linking

你可能感兴趣的:(C++,动态调用DLL,LoadLibrary,FreeLibrary)