动 态 链 接 库 的 使 用 有 两 种 方 式 , 一 种 是 显 式 调 用 , 一 种 是 隐 式 调 用 动态链接库的使用有两种方式,一种是显式调用,一种是隐式调用 动态链接库的使用有两种方式,一种是显式调用,一种是隐式调用
显式调用:使用LoadLibrary载入动态链接库、使用GetProcAddress获取某函数地址
隐式调用:可以使用#pragma comment(lib, “XX.lib”)的方式,也可以直接将XX.lib加入到工程中
实例
// dllUser.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "wuygDll.h"
#include <iostream>
#include <windows.h>
#pragma comment(lib, "wuygDll")
int _tmain(int argc, _TCHAR* argv[])
{
//隐式调用
wuygFun();
//显式调用
typedef int (__stdcall *Fun)(void);
HMODULE hDll = ::LoadLibrary(L"wuygDll");
Fun TestFun = ::GetProcAddress(hDll, "wuygFun");
TestFun();
system("pause");
return 0;
}
D L L 的 编 写 DLL的编写 DLL的编写
编写dll的时候,需要函数重命名
解决方式有两种:
- 直接在代码里解决采用
extent”c”
_declspec(dllexport)
#pragma comment(linker, "/export:[Exports Name]=[Mangling Name]")
- 采用def文件
编写dll时,为什么有 extern “C”
还不是C++不通用呗,例如:Borland C++跟Mircrosoft C++就不同
任何一个支持c语言的编译器,它编译出来的obj文件可以共享,链接成可执行文件。这是一种标准,如果DLL跟其使用者都采用这种约定,那么就可以解决函数重命名规则不一致导致的错误。
注意到extern “C”的作用是为了解决函数符号名的问题,这对于动态链接库的制造者和动态链接库的使用者都需要遵守的规则。
_declspec(dllexport)和_declspec(dllimport)的作用
_declspec(dllexport)
用在dll上,用于说明这是导出的函数。
_declspec(dllimport)
用在调用dll的程序中,用于说明这是从dll中导入的函数
因为dll中必须说明函数要用于导出,所以_declspec(dllexport)很有必要。
但是也可以使用def文件
来说明哪些函数用于导出,同时def文件里边还有函数的编号。
使用_declspec(dllimport)却不是必须的,但是建议这么做。因为如果不用_declspec(dllimport)来说明该函数是从dll导入的,那么编译器就不知道这个函数到底在哪里,生成的exe里会有一个call XX的指令,这个XX是一个常数地址,XX地址处是一个jmp dword ptr[XXXX]的指令,跳转到该函数的函数体处,显然这样就无缘无故多了一次中间的跳转。如果使用了_declspec(dllimport)来说明,那么就直接产生call dword ptr[XXX],这样就不会有多余的跳转了。
__stdcall的作用
这是一种函数的调用方式。
默认情况下VC使用的是__cdecl的函数调用方式,如果产生的dll只会给C/C++程序使用,那么就没必要定义为__stdcall调用方式,如果要给Win32汇编使用(或者其他的__stdcall调用方式的程序),那么就可以使用__stdcall。
*.def文件
指定导出函数,并告知编译器不要以修饰后的函数名作为导出函数名,而以指定的函数名导出函数(比如有函数func,让编译器处理后函数名仍为func)。这样,就可以避免由于microsoft VC++编译器的独特处理方式而引起的链接错误。
即,使用了def文件,那就不需要extern “C”了,也可以不需要__declspec(dllexport)了
(不过,dll的制造者除了提供dll之外,还要提供头文件,需要在头文件里加上这extern”C”和调用约定,因为使用者需要跟制造者遵守同样的规则,除非使用者和制造者使用的是同样的编译器并对调用约定无特殊要求)