Dll即动态链接库,相当于Linux下的共享对象。Windows下的类ELF文件称为PE文件,它属于PE/COFF格式。
PE里面有两个常用的概念,基地址和相对地址(RVA);当一个PE文件被装载时,其进程地址空间中的起始地址就是基地址,任何一个PE文件都有一个优先加载的基地址,PE文件头中的Image Base。
通过简单示例,了解DLL的创建和使用,其中最基本的概念便是导出。
__declspec(export) double Add( double a, double b)
{
return a + b;
}
__declspec(export) double Sub( double a, double b)
{
return a - b;
}
__declspec(export) double Mul( double a, double b)
{
return a * b;
}
使用VS2008编译器cl进行编译:
$ cl /LDd Math.c
生成"Math.dll"、"Math.obj"、"Math.exp" 和 "Math.lib" 四个文件。通过dumpbin查看DLL的导出符号:
$ dumpbin /EXPORTS Math.dll
我们也可以采用模块定义文件(.def)声明DLL中某个函数为导出符号,以代替使用“__declspec(export)”扩展:
LIBRARY Math
EXPORTS
Add
Sub
Mul
$ cl /LD /DEF Math.def Math.c
该种方法和
使用“__declspec(export)”扩展作用相同:
当然,我们也可以使用这种方式将导出函数重新命名:
LIBRARY Math
EXPORTS
add = Add
Sub
Mul
使用DLL的过程其实就是引用DLL中的导出函数和符号的过程,即导入过程。对于从其他DLL导入的符号,我们需要使用“__declspec(dllimport)”显示地声明某个符号为导入符号:
#include <stdio.h>
__declspec(import) double Sub( double a, double b);
int main()
{
double result = Sub(3.0, 2.0);
printf("Result = %f\n", result);
return 0:
}
$ link TestMath.o Math.lib
......
Math.lib并不包含Math.c的代码和数据,它用来描述Math.dll的导出符号,它包含了TestMath.o链接Math.dll时所需要的导入符号以及一部分桩代码;Math.lib又被称为导入库。
与ELF文件类似,DLL也支持运行时链接,即运行时加载,一个简单示例:
#include <windows.h>
#include <stdio.h>
typedef (*Func)(double, double);
__declspec(import) double Sub( double a, double b);
int main()
{
Func func;
// Load DLL
HINSTANCE hinstLib = LoadLibrary("Math.dll");
if (NULL == hinstLib){
printf("ERROR: unable to load DLL\n");
return 1;
}
// Get function address
function = (Func)GetProcAddress(hinstLib, "Add");
if (NULL == function){
printf("ERROR: unable to find DLL function\n");
FreeLibrary(hinstLib);
return 1;
}
// Call function
result = function(1.0, 2.0);
// Unload DLL file
result = function(1.0, 2.0);
FreeLibrary(hinstLib);
// Display result
printf("Result is %f\n", result);
return 0:
}