区别静态调用(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 <iostream>
#include <Windows.h>
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不需要重新编译。程序加载时内存和消耗时间相对减少。