在第三章的基础上,接着添加一个显示调用项目
显示调用项目创建:
1.给解决方案添加一个新的控制台项目DisplayCall用于测试动态库,创建完成后设置为启动项目
2.DisplayCall.cpp添加相关代码 1 // DisplayCall.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include <windows.h> //需要包含windows.h
6
7 typedef int(*PFUNC_MathSub)(int,int); //定义函数指针
8
9 int _tmain(int argc, _TCHAR* argv[])
10 {
11 HMODULE hmdll = ::LoadLibrary(_T("../Debug/DynamicLibrary.dll"));//动态加载dll
12 if(!hmdll)
13 {
14 printf("LoadDll is fail");
15 }else
16 {
17 //获取动态库中的sub函数地址,强制类型转换为函数指针
18 PFUNC_MathSub pfMathSub = (PFUNC_MathSub)::GetProcAddress(hmdll,"MathSub");
19 int nResult = pfMathSub(5,3); //通过函数指针进行调用
20 printf("5 - 3 = %d",nResult);
21 ::FreeLibrary(hmdll);
22 }
23 getchar();
24 return 0;
25 }
3.编译运行DisplayCall,结果发现出现异常?
4. 点击中断发现断在第19行,鼠标移到pfMathSub发现它的值为0. 说明没有获取到MathSub函数地址
打开动态库的头文件进行查看是不是输错函数名字了?函数名正确...
5. 真正导致bug产生的原因在于,C++函数编译时产生的函数名称与代码中实际函数名是不一样的
这里动态库项目采用的是c++, 那它生成时就是按C++编译方式生成函数名
这里可以使用depends来查看下dll信息,可是vs2012工具里尽然没有,那就只能借用第三方工具了
原始c++函数:int MathSub(int a ,int b) 编译后函数名称:?MathSub@@YAHHH@Z
c++函数名称都是以?开头,后面跟上函数名,然后@@YA代表的是c++默认调用方式__cdecl,
后面二个H代表是参数型是int型,返回值是int型,最后以@z代表结尾
6. 为了测试是否正确,修改代码, 把GetProcAddress第二个参数函数名改为?MathSub@@YAHHH@Z
F5运行,发现能成功调用MathSub函数了
7. 如果每次调用动态库中的函数都使用这种方式太过于复杂,于是忽另外一种简单的方式应运而生
修改动态库头文件,在函数最前面加上extern "C", 告诉编译器不要使用默认的C++方式编译,
该为使用C语言方式编译. 最后重新编译下动态库工程
8. 回到DisplayCall.cpp中,把代码改回来. 然后F5运行,发现异常信息消失了
9. 最后再用工具看一下用extern "C" 修饰的函数编译出来的函数名变成什么样了?
C语言方式编译出来函数名是MathSub, 这样我们就可以很方便的使用GetProcAddress获取函数地址了
总结:
如果需要显示调用动态库,尽量在导出函数前面加上extern "C"
而当使用第三方动态库,去GetProcAddress函数地址发现获取不到函数地址时,
可以尝试用工具去查看导出函数,以确定函数名称,以及查看相关的调用约定