Windows下调用C++动态库,即dll文件有两种方式:隐式调用和显式调用。以下以VS2013为编译环境说明。
假如有一个项目TestDLL,产生的可执行文件名为TestDLL.exe,在这个工程中调用mydll.dll,mydll.dll对应的
.lib文件名为mydll.lib。
一 隐式调用
这种调用方式需要编译动态库时用到的头文件、静态库文件(.lib),动态库文件(.dll)。
这里的.lib文件只包含一些索引,即.dll文件中导出的变量或函数的符号名和可选的标识号,不包含具体的代码实现,
具体的代码实现都在.dll文件中, 应用程序中对函数或变量的调用与.lib文件中的符号相匹配,这些符号或标识号被
写入最终的可执行文件中,可执行文件运行时,就按照这些符号或标识号到.dll中寻找定义。.lib文件中也包含了对
应的.dll文件名,但不是完全路径,链接程序会把这个.dll文件名写到可执行文件中,当应用程序运行过程中需要加
载.dll文件时,windows根据这些信息发现并加载.dll,然后通过符号名或标识符实现对dll函数的动态链接。所有被
应用程序调用的.dll文件都会在应用程序加载时被加载到内存中。
如果要完成源代码的编译,只需要.lib文件就可以了;如果要使可执行文件运行起来,只要有.dll文件就可以了,在
编译和测试阶段,这两个文件最好都有。
隐式调用步骤如下:
1 将编译mydll.dll时用到的头文件拷贝到TestDLL的项目目录下,保证TestDLL项目在编译时可以找到这些头文件。
在VS2013中,右键点击项目->Properties->Configuration Properties->VC++ Directories->Include
Directories,然后将头文件的路径添加到项目配置中。
2 将mydll.lib的文件路径添加到TestDLL项目的依赖库路径下,保证TestDLL项目在编译时能够找到这个mydll.lib。
在VS2013中,右键点击项目->Properties->Configuration Properties->VC++ Directories->Library
Directories,然后将mydll.lib的文件路径添加到项目配置中。
3 将mydll.lib文件添加到TestDLL项目的附加依赖中,告诉程序要使用mydll.lib。
在VS2013中,右键点击项目->Properties->Configuration Properties->Linker->Input->Additional
Dependencies, 将mydll.lib添加进去。
4 将要用到的mydll.dll文件拷贝到可执行文件所以的目录,即TestDLL.exe所在的目录,或系统环境变量配置的
目录中(系统环境变量配置可到网上查找),保证TestDLL.exe运行时能够找到mydll.dll。
在TestDLL项目中,需要用到mydll.dll的地方,包含相应的头文件以后,就可以使用mydll.dll中定义的变量或函数了。
二 显示调用
这种方式不需要头文件,也不需要相应的.lib文件,只要.dll文件就可以了。
这种方式在应用程序编译前并不知道会调用哪些.dll文件,在运行过程中才确定,运行过程中,应用程序调用系统函数,
在需要的时候加载.dll文件,然后获取指定变量的址或指定函数的入口地址后,就可以使用变量或进行函数调用了。
显示式调用步骤如下:
1 调用系统函数LoadLibraryEx加载mydll.dll。示例代码片段:
string dllPath = "D:\\Project\\lib\\mydll.dll"; //mydll.dll的路径 TCHAR dynLibPath[MAX_PATH_LEN]; //如果使用的是ANSI字符集,这一步不需要 swprintf(dynLibPath, MAX_PATH_LEN, L"%S", dllPath.c_str()); //如果使用的是ANSI字符集,这一步不需要 //如果使用的是ANSI字符集,LoadLibraryEx的第一个参数,直接使用dllPath.c_str(),即ANSI字符串即可。 HMODULE dllHandler = LoadLibraryEx(dynLibPath, NULL, DONT_RESOLVE_DLL_REFERENCES); if(dllHandler) { cout << "Load mydll.dll successfully!" << endl; } else { cout << "Load mydll.dll failed!" << endl; }
2 调用系统函数GetProcAddress获取变量或函数的地址。示例代码片段:
FARPROC pFun = NULL; pFun = GetProcAddress(dllHandler, "TestFun"); //TestFun是mydll.dll中导出的函数 pFun(); //调用TestFun FARPROC pVar = NULL; pVar = GetProcAddress(dllHandler, "TestVar"); //TestVar是mydll.dll中导出的整型变量 int num = *(int*)pVar; //将pVar转换成整形 cout<<num<<endl; //打印TestVar的值
注意: 有时候,dll中确定有相应的函数,调用GetProcAddress却返回NULL,例如: mydll.dll中确定导出了函数
“ AnotherTestFun",在Dependency Walker下看到相应的函数名为?AnotherTestFun@3HA,
调用GetProcAddress(dllHandler, "AnotherTestFun")始终返回NULL;
而调用GetProcAddress(dllHandler, "?AnotherTestFun@3HA")却可以返回有效地址值。
像这种情况,可能是因为在定义AnotherTestFun函数中,函数声明前忘了加 extern "C"。
如果没有加extern "C",编译器在编译AnotherTestFun这个函数时,会按照C++的规则翻译这个函数名,
即可能把函数名字改掉,例如改成上面的"?AnotherTestFun@3HA",具体改成什么样的名字视编译器而定,
这样一来,以TestDLL项目中再以"AnotherTestFun"为参数获取该函数的地址,肯定会失败。
如果加上了extern "C",则是告诉编译器在编译函数时按照C语言的规则去翻译相应的函数名,即“请保持函数
名称,不要生成中间函数名"。