DLL(testcase_1.dll )源碼:myfun.h mufun.cpp 可執行文件(alltest.exe)源碼: main.cpp 如果都使用預設配置編譯,只要設置好可執行文件的頭文件包含目錄(myfun.h所在目錄),導入對應的lib(testcase_1.dll.lib)文件,然後把 ...
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,而是其他的,比如@getInstance@0,然後修改
項目屬性如下:
再重新編譯生產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中的符號。