参考资料:
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(); }