windows dll的def文件
DLL(testcase_1.dll )源码:myfun.h
#pragma once #ifdef TESTCASE_1_EXPORTS #define MY_API __declspec(dllexport) #else #define MY_API __declspec(dllimport) #endif #include <iostream> #include <string> namespace APICore { class MY_API ExportInterface { public: virtual void foo() = 0; }; extern "C" MY_API ExportInterface* getInstance(); #ifdef TESTCASE_1_EXPORTS class ExportClass :public ExportInterface { private: std::string m_str; public: void foo(); }; #endif };
mufun.cpp
#include <iostream> #include "myfun.h" //#include "myfun2.h" using namespace std; namespace APICore { extern "C" MY_API ExportInterface* getInstance() { //APICore2::printInfo(); static ExportClass ec; return &ec; } void ExportClass::foo() { cout << "Hi, I'm ExportClass::foo" << endl; } }
可执行文件(alltest.exe)源码:
main.cpp
#include <iostream> //#include "..\\testcase_1\\myfun.h" //#pragma comment(lib, "..\\Debug\\testcase_1.lib") #include <Windows.h> #include "myfun.h" int main(int argc, char *argv[]) { //// 动态加载dll,不需要lib文件, //HINSTANCE handle = GetModuleHandle("testcase_1.dll"); // //if (handle == NULL) //{ // handle = LoadLibrary("testcase_1.dll"); // typedef APICore::ExportInterface* (*FUNTYPE)() ; // FUNTYPE fun = (FUNTYPE)GetProcAddress(handle, "getInstance"); // if (fun) // { // fun()->foo(); // } // FreeLibrary(handle); //} // 静态加载调用,无法使用def文件,因为静态加载用的是lib文件 APICore::getInstance()->foo(); getchar(); return 0; }
如果都使用默认配置编译,只要设置好可执行文件的头文件包含目录(myfun.h所在目录),导入对应的lib(testcase_1.dll.lib)文件,然后把testcase_1.dll.dll放到alltest.exe同一目录。程序便可以正常运行。
那么def文件有什么用呢?
我们先改变一下testcase_1.dll的项目属性,调用约定由__cdecl改为__fastcall,可执行文件alltest.exe保持__cdecl调用约定不变
这个时候再次编译dll,生产成功
再重新编译生产可执行文件alltest.exe,编译失败,说找不到我们要使用的dll中的函数符号。
这是因为上面改了dll调用约定,而exe没有改。我们可以吧exe和dll的调用约定改成一样的,就没问题了。
这里我介绍另一种方法,就是使用我们的def文件。我们在testcase_1.dll这个项目中新添加一个def文件,
叫做sourc.def。内容如下
LIBRARY "testcase_1.lib" EXPORTS getInstance
EXPORTS下的getInstance告诉编译器要导出的符号是getInstance,而是其他的,比如@[email protected],然后修改
项目属性如下:
再重新编译生产testcase_1.dll。这时候我们再次编译可执行文件,发现还是找不到符号,编译不通过。然后我们对可执行文件alltest.exe的代码做一点修改,
把dll加载有静态加载改为动态加载。修改main.cpp如下
#include <iostream> //#include "..\\testcase_1\\myfun.h" //#pragma comment(lib, "..\\Debug\\testcase_1.lib") #include <Windows.h> #include "myfun.h" int main(int argc, char *argv[]) { // 动态加载dll,不需要lib文件, HINSTANCE handle = GetModuleHandle("testcase_1.dll"); if (handle == NULL) { handle = LoadLibrary("testcase_1.dll"); typedef APICore::ExportInterface* (*FUNTYPE)() ; FUNTYPE fun = (FUNTYPE)GetProcAddress(handle, "getInstance"); if (fun) { fun()->foo(); } FreeLibrary(handle); } //// 静态加载调用,无法使用def文件,因为静态加载用的是lib文件 //APICore::getInstance()->foo(); getchar(); return 0; }
然后重新编译运行,就可以了
总结
def文件可以用于当dll的调用约定(__fastcall),与宿主(本例的alltest.exe)程序的调用约定不一致时(__cdecl),导致可执行文件在
使用dll时的链接出错。不过要注意的是,def文件从实验来看,只会影响dll中的输出符号,而不会影响lib中的输出符号。这也是为什么
我们不能再使用静态加载的方式,而要改为动态加载的方式。因为动态加载只使用dll,而静态加载链接时使用的是lib中的符号。