关于DLL的调用方式有两种
①静态调用方式
在制作DLL时使用_declspec(dllexport)声明函数为导出函数,编译成功后会在Debug下生成.dll 和 .lib文件,其中.lib文件
存储的是DLL中函数的符号信息。 .dll文件中存储的是函数的具体实现。
静态调用方式下只需要在.exe引入.lib文件,而.dll文件要在程序可以搜索到的路径下。
eg.
//制作Dll,Dll.cpp
_declspec (dllexport) int [调用约定,可选] Add(int a, int b)
{
return a + b;
}
//使用Dll
#pragma comment(lib, "Dll.lib");
_declspec (dllimport) int [调用约定,可选] Add(int a, int b);//声明引入
int sum = Add(5, 3);
说明:红色字体这里要注意,DLL中定义时如果声明了调用约定,使用DLL时也要声明调用约定!
②动态调用方式
这种方式不必使用.lib文件,只需要使用.dll即可。 需要时动态获取函数地址,使用完后释放该DLL即可。
这种方式也有缺点,那就是获取函数地址时要对函数名事先知道,这里的函数名是指编译器处理过的函数名,由于C++函数
重载的需要,函数名不是标识函数唯一的标志,而是加上一些附近信息(参数), 而C语言没有函数重载的问题,所以在C语言中,
函数名为函数的唯一标志。
如果在制作DLL时对函数声明为C语言处理方式,并且不加其他显示地调用约定,那么生成的DLL中对函数的标识只有函数名,
在动态获取函数地址指定函数名时就方便多了。
eg.
//制作DLL, Dll.cpp
extern "C" _declspec (dllexport) int Add(int a, int b)
{
return a + b;
}
//由于指定了extern "C"方式,所以DLL中函数名为Add
//使用DLL
HINSTANCE hInst = NULL;
hInst =LoadLibrary("Dll.dll");
if(NULL == hInst)
{
MessageBox("加载DLL失败!");
return;
}
typedef int (*ADDPROC) (int a, int b); //函数指针,根据需要是否在*ADDPROC前加调用约定
ADDPROC add = (ADDPROC) GetProcAddress(hInst, "Add"); //"Add"为函数名
if(NULL == add)
{
MessageBox("找不到函数地址!");
return;
}
int sum = add(5, 3);
FreeLibrary(hInst);
关于DLL要注意的地方
调用约定
在制作DLL时要注意调用约定的不同产生的效果不同。
如果显示的加了调用约定,那么即使加上extern "C",也很可能生成的函数名中还有其他的信息。
不同的调用约定的使用场合有所不同,编译平台也有所不同。 用时再查!
注意在静态调用方式下,使用DLL时没什么区别,因为在声明导入函数时不需要声明调用约定。
更正:上面红颜色的这句话是错误的! 今天学习HOOK时使用的静态调用方式,在DLL中的导出函数的调用约定是_stdcall,
_declspec(dllexport) BOOL _stdcall StartHook(HWND hWnd);
在使用这个DLL时这样声明:_declspec(dllimport) BOOL StartHook(HWND hWnd); 结果报错:未定义的外部符号!
加上调用约定改为_declspec(dllimport) BOOL _stdcall StartHook(HWND hWnd);后就可以了!
所以,切记!!!
阿弥陀佛! 求凤姐包邮!
在动态调用方式下,在定义函数指针时要同时指明调用约定,如果上例为标准调用约定,则要这样定义函数指针:
typedef int (_stdcall *ADDPROC) (int a, int b);
同时由于函数名可能不再只是单纯的函数名字那么简单(可能有一些附近信息),所以也要注意! 可以使用VC的工具dumpbin来查看
DLL中的函数名具体是什么。 用法如下:
首先到该DLL路径下,然后
dumpbin -exports dllName.dll
回车即可查看。