參考資料:
https://blog.csdn.net/qq_34097715/article/details/79540933
https://www.cnblogs.com/RascallySnake/p/3182807.html
生成Dll三步走
第一步:先建一個Dll項目
New --> Project --> Dynamic-Link Library(DLL) --> 取名,選路徑 --> OK
第二步:編寫頭文件,例子是一個四則運算
selfTrainingDll.h
#pragma once #ifdef DLL_TRAINING_API #else #define DLL_TRAINING_API _declspec(dllimport) //當編譯時,頭文件不參加編譯,所以.cpp文件中先定義,后頭文件被包含進來,因此外部使用時,為dllexport,而在內部編譯時,則為dllimport #endif class DLL_TRAINING_API arithmetic_operation //需要被外界調用的類(父類) { public: double Add(double a, double b); double Sub(double a, double b); double Multi(double a, double b); double Div(double a, double b); }; int DLL_TRAINING_API export333();
第三步:編寫CPP文件,實現方法
selfTrainingDll.cpp
// selfTrainingDll.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #define DLL_TRAINING_API _declspec(dllexport) #include <iostream> #include "selfTrainingDll.h" using namespace std; double DLL_TRAINING_API arithmetic_operation::Add(double a, double b) { return a+b; } double DLL_TRAINING_API arithmetic_operation::Sub(double a, double b) { return a - b; } double DLL_TRAINING_API arithmetic_operation::Multi(double a, double b) { return a * b; } double DLL_TRAINING_API arithmetic_operation::Div(double a, double b) { return a / b; } int DLL_TRAINING_API export333() { return 333; }
第四步:生成Dll
Build --> Build Solution
至此,文件生成完畢
靜態方法調用Dll文件
第一步:創建一個控制台程序
省略
第二步:編譯運行,產生Debug文件夾
第三步:將之前Dll項目生成的selfTrainingDll.h和selfTrainingDll.lib放入項目文件夾下,將selfTrainingDll.dll放入Debug文件夾下
第四步:在項目中添加selfTrainingDll.h頭文件
第五步:在Cpp中調用Dll
UseSelfDll.cpp
// UseSelfDll.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include "pch.h" #include <iostream> using namespace std; #include "selfTrainingDll.h" #pragma comment(lib,"selfTrainingDll.lib") int main() { arithmetic_operation ao; cout << ao.Add(1,2) << endl; cout << ao.Sub(2,1) << endl; cout << ao.Multi(2,1) << endl; cout << ao.Div(6,4) << endl; cout << export333() << endl; cout << "Hello World!\n"; }
至此,調用成功
動態方法調用Dll文件
UseSelfDll.cpp
// UseSelfDll.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include "pch.h" #include <iostream> using namespace std; //#include "selfTrainingDll.h" //#pragma comment(lib,"selfTrainingDll.lib") #include <windows.h> int main() { typedef int(*_print)(); cout << "1" << endl; HINSTANCE hDll = LoadLibrary(L"selfTrainingDll.dll"); cout << "2" << endl; _print pAdd = (_print)GetProcAddress(hDll, (LPCSTR)MAKEINTRESOURCE(7)); cout << "3" << endl; int a = pAdd(); cout << a << endl; //arithmetic_operation ao; //cout << ao.Add(1,2) << endl; //cout << ao.Sub(2,1) << endl; //cout << ao.Multi(2,1) << endl; //cout << ao.Div(6,4) << endl; //cout << export333() << endl; cout << "Hello World!\n"; FreeLibrary(hDll); }
由於C++導出Dll時會出現名字更改的問題,因此這里用序列號代表函數,至於函數的序列號可以用如下方法查看:
用VS打開cmd窗口(Tools --> Visual Studio Command Prompt),運行dumpbin -exports xxx.dll 后面最好寫DLL的絕對路徑,否則可能會報錯LNK1181: cannot open input file 'XXX.dll'。
可在EXE所在的目錄下使用dumpbin -imports xxx.EXE來查看某EXE文件使用過哪些dll庫.
64位EXE盡量去調用64位DLL,同理32位盡量調用32位。
如果想要直接使用函數名,那么在生成DLL時要加extern "C"
#pragma once #ifdef __cplusplus // if used by C++ code extern "C" { // we need to export the C interface #endif #ifdef DLL_TRAINING_API #else #define DLL_TRAINING_API _declspec(dllimport) //當編譯時,頭文件不參加編譯,所以.cpp文件中先定義,后頭文件被包含進來,因此外部使用時,為dllexport,而在內部編譯時,則為dllimport #endif class DLL_TRAINING_API arithmetic_operation //需要被外界調用的類(父類) { public: double Add(double a, double b); double Sub(double a, double b); double Multi(double a, double b); double Div(double a, double b); }; int DLL_TRAINING_API export333(); #ifdef __cplusplus } #endif
// UseSelfDll.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include "pch.h" #include <iostream> using namespace std; //#include "selfTrainingDll.h" //#pragma comment(lib,"selfTrainingDll.lib") #include <windows.h> int main() { typedef int (*_print)(); HINSTANCE hDll = LoadLibrary(L"selfTrainingDll.dll"); _print pAdd = (_print)GetProcAddress(hDll, "export333"); int a = pAdd(); cout << a << endl; //arithmetic_operation ao; //cout << ao.Add(1,2) << endl; //cout << ao.Sub(2,1) << endl; //cout << ao.Multi(2,1) << endl; //cout << ao.Div(6,4) << endl; //cout << export333() << endl; cout << "Hello World!\n"; FreeLibrary(hDll); }
但是它仍然有局限性,只針對函數,不針對類。
另外對於帶參數的函數,怎么動態調用呢。
HINSTANCE hDLL; typedef DWORD(*GetPCICFG)(BYTE bBus, BYTE bDev, BYTE bFun, BYTE bIdx, PDWORD pdwPortVal, BYTE bSize); GetPCICFG getPCICFG; hDLL = LoadLibrary(L"PCI.dll"); if (hDLL == NULL) printf("Error!!!\n"); getPCICFG = (GetPCICFG)GetProcAddress(hDLL, "PCI_GetPCICFG"); FreeLibrary(hDLL);
另外,可以將dll和exe工具放在一起調試,會比較方便,當然,exe里面的用動態調試比較好。靜態的可以看前面的鏈接。
首先,我們寫了一個DLL,然后在Solution里面點擊添加-->新建項目,這個就是我們新建的exe程序。新建了exe程序后,可以看到Solution里面有了兩個項目,這時我們右鍵exe的項目將其設為啟動項目,這時每次調試時都會執行exe的項目,如果要讓exe隨着DLL的修改而修改,可以同步反應,那么就右鍵exe項目,為其添加依賴項,選擇DLL為他的依賴就好了。這樣exe和DLL就可以同步調試了。
靜態調用DLL--同步調試
參考鏈接:https://blog.csdn.net/cynophile/article/details/79749524
一、新建一個DLL項目,編譯。
二、添加主cpp對應的頭文件,然后添加一個“export.h”頭文件。

三、修改項目配置

四、寫主cpp的.h文件

五、寫主cpp里面的函數,然后再次編譯

六、右鍵單擊左側列表中“解決方案”,然后在彈出菜單中選擇“添加 > 新建項目”,向解決方案中添加一個新的控制台項目,用於測試Bluetooth中導出的printHello()函數是否可以正常訪問;


七、將控制台項目設為啟動項目

八、將exe與dll鏈接起來,需要對項目進行配置


九、在控制台(exe)屬性頁窗口中,將配置設置為“所有配置”,然后在左側“配置屬性”列表中,選擇“鏈接器 > 常規”,接着在右側屬性列表中選擇“附加庫目錄”屬性右方的編輯框,在彈出的下拉列表中選擇“編輯”;$(OutDir)




十、配置完成,在exe中調用dll中的函數。

十一、運行函數Debug\Start Without Debugging

十二、結果

大功告成。
另外,如果想要dll以c的方式導出,可以修改主cpp的.h文件
#pragma once #ifdef __cplusplus extern "C" { #endif #ifndef Bluetooth_H #define Bluetooth_H #include "export.h" EXPORT_BLUETOOTH void printHello(); #endif //!Bluetooth_H #ifdef __cplusplus } #endif
動態調用DLL--同步調試
參考鏈接:http://www.mamicode.com/info-detail-2949884.html
依次執行靜態調用DLL--同步測試的步驟:一、二、三、四、五、六、七、八
然后在Test_Bluetooth.cpp中調用printHello函數
// Test_Bluetooth.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include "pch.h" #include <iostream> #include <Windows.h> using namespace std; class WinDll { public: WinDll(const char* dll) : mLib(::LoadLibraryA(dll)) {} WinDll(const wchar_t* dll) : mLib(::LoadLibrary(dll)) {} ~WinDll() { FreeLibrary(mLib); } WinDll(const WinDll&) = delete; WinDll& operator=(const WinDll&) = delete; operator bool() { return !!mLib; } template <typename Ret, typename... Args> Ret Invoke(const char* name, const Args& ...args) { auto proc = GetProcAddress(mLib, name); typedef Ret(__stdcall* Func)(Args...); return (proc) ? reinterpret_cast<Func>(proc)(args...) : (Ret()); } private: HMODULE mLib; }; int main() { WinDll bluetooth("Bluetooth.dll路徑"); if (bluetooth) { printf("start\n"); bluetooth.Invoke<void>("printHello"); bluetooth.~WinDll(); } }
如此可行。
動態調用封裝模板
參考鏈接:https://www.cnblogs.com/wuyaSama/p/11510889.html
動態調用dll,用到一個函數就要寫那一串,寫得很煩,就想有沒有簡單的方法封裝一下,以便調用,之前找到一個封裝類WinDll,用一些簡單的還不錯,用得還蠻開心的,不過當有函數的參數是char*的話就會出現問題,因此又找了一種其他的封裝方式。
template<typename _T> class Api; template<typename _Res, typename... _ArgTypes> class Api<_Res(_ArgTypes...)> { public: Api(const char* dllName, const char* funcName) { _M_module = LoadLibraryA(dllName); _M_func = reinterpret_cast<_Func>(GetProcAddress(_M_module, funcName)); } ~Api() { if (_M_module) FreeLibrary(_M_module); } _Res operator()(_ArgTypes... __args) const { return _M_func(__args...); } private: typedef _Res(*_Func)(_ArgTypes...); _Func _M_func; HMODULE _M_module; }; int main(int argc, char* argv[]){ Api<int(char*, char*)> write_head("F:\\aaa.dll", "print_hello"); int result = write_head("123", "456"); write_head.~Api(); }
