随手笔记之VC++(四)

来源:互联网 发布:重庆seo入门基础教程 编辑:程序博客网 时间:2024/04/30 13:45

DLL : 动态链接库( Dynamic Link Library

动态链接库 (DLL) 是作为共享函数库的可执行文件。

一般来说,DLL是一种磁盘文件,以.dll.DRV.FON.SYS和许多以.EXE为扩展名的系统文件都可以是DLL

动态链接库的调用可以分为两种:一种是隐式调用,一种是显示调用。

在操作系统中使用动态链接库(DLL)有很多优点,最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个DLL文件,真正实现了资源"共享",大大缩小了应用程序的执行代码,更加有效地利用了内存;使用DLL的另一个优点是DLL文件作为一个单独的程序模块,封装性、独立性好,在软件需要升级的时候,开发人员只需要修改相应的DLL文件就可以了,而且,当DLL中的函数改变后,如果没有修改参数,程序代码并不需要重新编译。这在编程时十分有用,大大提高了软件开发和维护的效率。 

我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。

一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。

动态链接库中的导出接口可以使用Visual C++Depends工具进行查看。

/* 文件名:lib.h */ 

#ifndef LIB_H

#define LIB_H 

extern "C" int __declspec(dllexport)add(int x, int y);

 #endif

 /* 文件名:lib.cpp */ 

#include "lib.h" 

int add(int x, int y) 

{

`return x+y;

}

#include <stdio.h>

 #include <windows.h> 

typedef int(*lpAddFun)(int, int); //+]) 

HINSTANCE hDll; //DLL句柄 

lpAddFun addFun; //函数指针

 hDll = LoadLibrary("..\\Debug\\dllTest.dll"); 

if (hDll != NULL) 

addFun = (lpAddFun)GetProcAddress(hDll, "add");

 if (addFun != NULL) 

{

 int result = addFun(2, 3); 

printf("%d", result); 

}

 FreeLibrary(hDll); 

}

 return 0;

 }

add的声明前面添加了__declspec(dllexport)语句, 。这个语句的含义是声明函数add为DLL的导出函数.

DLL内的函数分为两种:

(1) DLL导出函数,可供应用程序调用;

(2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。 

DLL中导出函数的声明有两种方式:

1. __declspec(dllexport) 

2. 采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。 

“DLL加载-DLL函数地址获取-DLL释放,这种调用方式称为DLL的动态调用。

动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 文件。

静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。

静态调用的例子:

#pragma comment(lib,"dllTest.lib") //.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息

 extern "C" __declspec(dllimport) add(int x,int y); 

int main(int argc, char* argv[]) 

int result = add(2,3); 

printf("%d",result);

return 0;

}

由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:

1.告诉编译器与DLL相对应的.lib文件所在的路径及文件名,

#pragma comment(lib,"dllTest.lib")就是起这个作用。 

程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件,该文件包含了DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。

2声明导入函数,extern "C" __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。

_stdcall约定:

__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;

如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@符号和参数的字节数,形如_functionname@number;而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。 

在lib.h中,应这样声明add函数: 

int __stdcall add(int x, int y); 

在应用工程中函数指针类型应定义为: 

typedef int(__stdcall *lpAddFun)(int, int); 



原创粉丝点击