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。示例代碼片斷:windows
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語言的規則去翻譯相應的函數名,即「請保持函數名稱,不要生成中間函數名"。測試