qt/c++调用dll的方法实践

关于c++调用dll的方法,应该说是很成熟的,很多文章介绍的也很详细,可以直接套用。
这里不在详述其原理,而只是根据实际使用做一个实践总结。

主程序添加dll中的头文件声明,联合该dll编译,直接调用dll内部函数–这是隐式调用的方法

这种方式下,生成dll的源代码文件必须要区分头文件和源文件。
比如:如下testdllc.h文件中定义了一个Functions类,定义了6个函数

class Functions
{public:static  void one();  static  void two();  static  void three();  static  int add(int a,int b);static  int sub(int a,int b);static  int mul(int a,int b);
};

在源文件testdllc.cpp中做了实现:

#include "testdllc.h"
#include <iostream>
using namespace std;  /* one */
void Functions::one(){  cout << "call one() function" << endl;
}  /* two */
void Functions::two(){  cout << "call two() function" << endl;
}  /* three */
void Functions::three(){  cout << "call three() function" << endl;
}  int Functions::add(int a,int b){return a+b;
}
int Functions::sub(int a,int b){return a-b;
}
int Functions::mul(int a,int b){return a*b;
}

这是通过编译命令:

g++ -shared -Wall -o libtestdllc.dll testdllc.cpp

可以生成一个libtestdllc.dll的dll文件。

于是在一个主程序中可以通过添加头文件的方式来使用这个dll内部的函数,比如:如下testdllc

#include <windows.h>
#include <iostream>
#include <stdio.h>
#include "testdllc.h"
using namespace std;  /* main.cpp */
int main(){int a=10,b=2;cout<<"a+b="<<Functions::add(a,b)<<endl;cout<<"a-b="<<Functions::sub(a,b)<<endl;cout<<"a*b="<<Functions::mul(a,b)<<endl;Functions::one();  Functions::two();  Functions::three();  return 0;
}

其中直接通过#include "testdllc.h",来引入dll中的函数,可以直接使用,当前其前提是在编译主程序时,要加入dll进行联合编译。
其编译命令为:

g++ libtestdllc.dll main-call-dll-implicit.cpp -o main-call-dll-implicit.exe

显然libtestdllc.dll作为必须的库文件参与了编译、链接。

对于dll不参与联合编译的方式,则是另外一种方式,称为显式调用方法,即需要在主程序中显式的指出dll文件并调用。
这种方法见下一节。

主程序调用dll,通过指针调用dll中的函数–这是显式调用的方法

其主程序中没有对dll内部头文件的声明,只有显式的dll调用。这种调用使用了windows提供的api,所以内部声明了#include <windows.h>
比如:main-call-dll-explicit.cpp文件

#include <windows.h>
#include <iostream>
#include <stdio.h>
using namespace std;  typedef int (*AddFunc)(int ,int);typedef int (*SubFunc)(int ,int);typedef int (*MulFunc)(int ,int);
// typedef int (*DivFunc)(int ,int);typedef void (*OutStr)();/* main.cpp */  int main(){int a=10,b=2;//动态加载Dlltest.dll文件//HMODULE hDll = LoadLibrary("testdll.dll");//HINSTANCE hDll=LoadLibrary("testdll.dll");HINSTANCE hDll;hDll= LoadLibrary(TEXT("testdll.dll"));//测试是否能够正确输出//printf("%s\n","absededeedd");//cout<<"absededeedd"<<endl;if (hDll != NULL){AddFunc add = (AddFunc)GetProcAddress(hDll, "add");if (add != NULL) {cout<<"a+b="<<add(a,b)<<endl;//printf("a+b=%d\n",add(a,b));}SubFunc sub = (SubFunc)GetProcAddress(hDll, "sub");if (sub != NULL){cout<<"a-b="<<sub(a,b)<<endl;//printf("a-b=%d\n",sub(a,b));}MulFunc mul = (MulFunc)GetProcAddress(hDll, "mul");if (mul != NULL) {//printf("a*b=%d\n",mul(a,b));cout<<"a*b="<<mul(a,b)<<endl;}//DivFunc div = (DivFunc)GetProcAddress(hDll, "div");//if (div != NULL) cout<<"a/b="<<div(a,b)<<endl;OutStr one=(OutStr)GetProcAddress(hDll,"one");if (one != NULL) {//printf("%s\n",one());one();  }OutStr two=(OutStr)GetProcAddress(hDll,"two");if (two != NULL) {//printf("%s\n",two());two();  }OutStr three=(OutStr)GetProcAddress(hDll,"three");if (two != NULL) {//printf("%s\n",two());three();  }//卸载Dlltest.dll文件;FreeLibrary(hDll);}return 0;
}

其编译方式为:

g++ main-call-dll-explicit.cpp -o main-call-dll-explicit.exe

显然由于dll是不参与编译的。

那么对于dll文件的源代码,可以直接在一个文件中输入,而且与通常的c++文件基本一致,为调用做的修改非常简单,比如文件:testdll.cpp,编译也很简单,命令为:g++ -shared -Wall -o testdll.dll testdll.cpp

#include <windows.h>
#include <iostream>
using namespace std;  #define EOF (-1)#ifdef __cplusplus    // If used by C++ code,
extern "C" {          // we need to export the C interface
#endif/* one */  void one(){  cout << "call one() function" << endl;  }  /* two */  void two(){  cout << "call two() function" << endl;  }  /* three */  void three(){  cout << "call three() function" << endl;  }  int add(int a,int b){return a+b;}int sub(int a,int b){return a-b;}int mul(int a,int b){return a*b;}/*int div(int a,int b){return a/b;}*/#ifdef __cplusplus
}
#endif

需要注意的是,如果使用的是window vc这些微软提供的编译器,需要添加特定的声明。
比如:__declspec(dllexport)__declspec(dllimport),这个将在最后一节说明。

对于简单的函数,前面这种使用方式是非常方便的,但如果我们在dll中功能实现比较复杂,需要用类来实现,那么可以对dll做一定接口封装,即使用简单函数作为接口,然后在这个函数内部实现对类的调用。详见下一节。

显式调用方法如果遇到复杂类可以做一定的封装

下面给出的例子仅展示功能,类也是比较简单的。

如下testdllclass.cpp文件中,类完全按常规讨论写,仅在封装的funcadd函数内做了类的实例化,并调用其函数。

#include <windows.h>
#include <iostream>
using namespace std;  class Functions
{public:void one(){  cout << "call one() function" << endl;  }  void two(){  cout << "call two() function" << endl;  }  void three(){  cout << "call three() function" << endl;  }  int add(int a,int b){return a+b;}int sub(int a,int b){return a-b;}int mul(int a,int b){return a*b;}};#define EOF (-1)#ifdef __cplusplus    // If used by C++ code,
extern "C" {          // we need to export the C interface
#endif/* one */  int funcadd(int a,int b){  Functions tempa; return(tempa.add(a,b));}  #ifdef __cplusplus
}
#endif

而主程序与上一节是类似的:

#include <windows.h>
#include <iostream>
#include <stdio.h>
using namespace std;  typedef int (*AddFunc)(int ,int);/* main.cpp */  int main(){int a=10,b=2;//动态加载Dlltest.dll文件//HMODULE hDll = LoadLibrary("testdll.dll");//HINSTANCE hDll=LoadLibrary("testdll.dll");HINSTANCE hDll;hDll= LoadLibrary(TEXT("testdllclass.dll"));//测试是否能够正确输出//printf("%s\n","absededeedd");//cout<<"absededeedd"<<endl;if (hDll != NULL){AddFunc add = (AddFunc)GetProcAddress(hDll, "funcadd");if (add != NULL) {cout<<"a+b="<<add(a,b)<<endl;//printf("a+b=%d\n",add(a,b));}//卸载Dlltest.dll文件;FreeLibrary(hDll);}return 0;
}

编译方法与上一节也是相同的。

qt显式调用dll的方法

qt也是用c++写的所以,其调用方法是类似的,而且由于qt使用开源的g++编译器,所以调用也不会出现什么由于编译器导致的障碍。在windows中qt使用mingw中的g++编译,所以说不使用微软提供的编译器时,编译环境完全是相同的。

qt显式调用dll的方法有两种,一种是使用windows提供的api,需要添加#include <windows.h>,另一种是使用自己的api,需要添加#include <QLibrary>

两者均需要定义指针函数:

typedef int (*AddFund)(double * ,int); //定义函数指针
typedef int (*AddFunc)(int,int); //定义函数指针

使用windows的方法:

    /*win的方法*///动态加载Dlltest.dll文件HINSTANCE hDll;hDll= LoadLibrary(TEXT("testdllsarjam32.dll"));if (hDll != NULL){qDebug() << "dll加载成功";AddFunc add = (AddFunc)GetProcAddress(hDll, "funcadd");if (add != NULL) {qDebug()<<"a+b="<<add(5,8)<<endl;}//卸载Dlltest.dll文件;FreeLibrary(hDll);}else {qDebug() << "dll加载失败";}

使用qt的方法:

    QLibrary mylib("testdllsarjam32.dll");if(mylib.load()){qDebug() << "dll加载成功";AddFunc avg = (AddFunc)mylib.resolve("funcadd");if (avg)qDebug()<<"res="<< avg(5, 8);elseqDebug()<<"has no pt to func\n";}else {qDebug() << "dll加载失败";}

使用微软提供的编译器时的设置

要使用__declspec(dllexport)__declspec(dllimport)

简单的函数的使用比如:

# define _DLLExport __declspec (dllexport) //;extern "C" long long _DLLExport dlltest();#include <iostream>long long dlltest()
{long long a = 1;int b = 0;while(b<1000000000){a=a+b;b++;}return a;
}

调用的主程序为:

#include <windows.h>
#include <iostream>
#include <stdio.h>
using namespace std;  typedef long long (*AddFunf)();/* main.cpp */  int main(){//动态加载Dlltest.dll文件//HMODULE hDll = LoadLibrary("testdll.dll");//HINSTANCE hDll=LoadLibrary("testdll.dll");HINSTANCE hDll;hDll= LoadLibrary(TEXT("testdlladd.dll"));//测试是否能够正确输出//printf("%s\n","absededeedd");//cout<<"absededeedd"<<endl;if (hDll != NULL){AddFunf dlladd = (AddFunf)GetProcAddress(hDll, "dlltest");if (dlladd != NULL) {long long res=dlladd();cout<<"jamming is finished "<<res<<endl;}//卸载Dlltest.dll文件;FreeLibrary(hDll);}return 0;
}

编译命令为:

:: clear temp files
del /q *.dll *.exe::compile dll
g++ -g -std=c++11 -shared -Wall -o testdlladd.dll testdlladd.cpp D:\mingw64\lib\gcc\x86_64-w64-mingw32\8.3.0\libstdc++.a ::compile exe
g++ -g -std=c++11 main-call-dll-explicit-add.cpp -o main-call-dll-explicit-add.exe::run exe to call dll
main-call-dll-explicit-add.exe

因为是使用g++做的测试,前述的设置其实没有影响。

对于windows编译器来说则是必须的,可以参考如下的示例:
其中对library_add函数,全局变量value和类Simple都做了声明,用于dll调用。

#ifndef FBC_LIBRARY_LIBRARY_HPP_
#define FBC_LIBRARY_LIBRARY_HPP_// reference: http://geoffair.net/ms/declspec.htm#ifdef _MSC_VER#ifdef FBC_STATIC#define FBC_API#elif defined FBC_EXPORT#define FBC_API __declspec(dllexport)#else#define FBC_API __declspec(dllimport)#endif
#endif#ifdef __cplusplus
extern "C" {
#endifFBC_API int library_add(int a, int b);
FBC_API int value;#ifdef __cplusplus
}
#endiftemplate<typename T>
class FBC_API Simple {
public:Simple() = default;void Init(T a, T b);T Add() const;private:T a, b;
};#endif // FBC_LIBRARY_LIBRARY_HPP_

其实现为:

#include "library.hpp"
#include <iostream>
#include <string>FBC_API int library_add(int a, int b)
{value = 11;fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);return (a+b);
}template<typename T>
void Simple<T>::Init(T a, T b)
{this->a = a;this->b = b;
}template<typename T>
T Simple<T>::Add() const
{fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);return (a + b);
}template class Simple<int>;
template class Simple<std::string>;

其调用的程序为:


#ifndef FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
#define FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_#include <library.hpp>namespace test_library_ {#ifdef __cplusplusextern "C" {
#endif__declspec(dllimport) int library_add(int, int);
__declspec(dllimport) int value;#ifdef __cplusplus}
#endifint test_library_1();
int test_library_2();} // namespace test_library_#endif // FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_

其实现为:

#include "test_library.hpp"
#include <iostream>
#include <string>#include <library.hpp>namespace test_library_ {int test_library_1()
{int a{ 4 }, b{ 5 }, c{ 0 };c = library_add(a, b);fprintf(stdout, "%d + %d = %d\n", a, b, c);fprintf(stdout, "value: %d\n", value);return 0;
}int test_library_2()
{Simple<int> simple1;int a{ 4 }, b{ 5 }, c{ 0 };simple1.Init(a, b);c = simple1.Add();fprintf(stdout, "%d + %d = %d\n", a, b, c);Simple<std::string> simple2;std::string str1{ "csdn blog: " }, str2{ "http://blog.csdn.net/fengbingchun" }, str3;simple2.Init(str1, str2);str3 = simple2.Add();fprintf(stdout, "contents: %s\n", str3.c_str());return 0;
}} // namespace test_library_

再来个主程序调用其中的参数即可。

而对于最开始的那个隐式调用的示例,也可以做类似的修改

#ifdef TESTDLLC_EXPORTS
#define TESTDLLC_API __declspec(dllexport)
#else
#define TESTDLLC_API __declspec(dllimport)
#endif  class Functions
{public:static TESTDLLC_API void one();  static TESTDLLC_API void two();  static TESTDLLC_API void three();  static TESTDLLC_API int add(int a,int b);static TESTDLLC_API int sub(int a,int b);static TESTDLLC_API int mul(int a,int b);
};

参考文献:

  1. gcc/g++ 链接库的编译与链接(https://blog.csdn.net/q_l_s/article/details/51313842)
  2. g++编译DLL文件(http://turbinee.blog.sohu.com/271861881.html)
  3. 如何使用g++编译调用dll的c++代码(https://www.bbsmax.com/A/lk5avA70d1/)
  4. MinGW(GCC)编译DLL文件(https://www.cnblogs.com/lichmama/p/4126323.html)
  5. 使用mingw制作dll文件(https://www.cnblogs.com/tonghaolang/p/9253995.html)
  6. MinGW编译dll并引用(http://www.bubuko.com/infodetail-3426585.html)
    更清楚的微软的参考
    (1)编译,调用dll中的头文件
  7. Walkthrough: Creating and Using a Dynamic Link Library (C++)(https://docs.microsoft.com/en-us/previous-versions/ms235636%28v%3dvs.140%29)
    (2)调用或直接使用dll中的函数
  8. Creating a Simple Dynamic-Link Library(https://docs.microsoft.com/zh-cn/windows/win32/dlls/creating-a-simple-dynamic-link-library)
  9. Using Load-Time Dynamic Linking(https://docs.microsoft.com/zh-cn/windows/win32/dlls/using-load-time-dynamic-linking)
  10. Using Run-Time Dynamic Linking(https://docs.microsoft.com/zh-cn/windows/win32/dlls/using-run-time-dynamic-linking)
  11. Windows C++中__declspec(dllexport)的使用(https://blog.csdn.net/fengbingchun/article/details/78825004)

qt/c++调用dll的方法实践相关推荐

  1. Delphi环境中编写调用DLL的方法和技巧

    Delphi环境中编写调用DLL的方法和技巧 第一章 为什么要使用动态链接库(DLL) top 提起DLL您一定不会陌生,在Windows中有着大量的以DLL为后缀的文件,它们是保证Windows正常 ...

  2. Delphi中动态调用DLL的方法

    Delphi中动态调用dll的方法如下: function CallFunc(dllname, funcname: string; const param: array of const): DWOR ...

  3. java dll 调用方法_关于Java调用dll的方法 | 学步园

    Java语言本身具有跨平台性,如果通过Java调用DLL的技术方便易用,使用Java开发前台界面可以更快速,也能带来跨平台性. Java调用C/C   写好的DLL库时,由于基本数据类型不同.使用字节 ...

  4. 用python读取身份证信息的功能分析与实现,兼述python调用dll的方法

    背景 有这样一个需求,要求能自动读取用户的身份证信息.如果是一代身份证,这个功能恐怕只能通过图像识别的办法来解决了.不过现在二代身份证已经很普及.客户要求能读二代身份证就可以了. 现在二代身份证阅读器 ...

  5. php调用c dll,PHP调用DLL的方法

    在PHP 4.2.0 至 4.2.3中,可以使用w32api_register_function 函数调用外部的DLL,前提是需要在php.ini中打开扩展的php_w32api.dll. 如果使用的 ...

  6. MFC创建、调用Dll的方法

    1. MFC创建DLL 1. 打开VS,新建项目,选择"MFC动态链接库",点击"下一步". 2. 设置项目名,路径等参数,点击"创建". ...

  7. VS C++生成dll和静态调用dll的方法

    一.动态链接库(dll)概述 在实际编程中,我们可以把完成某项功能的函数放在一个动态链接库里,然后提供给其他程序调用. 1.1 静态库和动态库 静态库: 函数和数据被编译进一个二进制文件(扩展名通常为 ...

  8. vue 获取安卓原生方法_VUE H5调用原生APP方法实践笔记

    最近做与app端的混合开发,了解到了H5与原生app端之间的方法互通 首先是我们给app端定义方法供他们使用(以vue为例) methods中定义一个方法名称 VContent() { console ...

  9. QT 调用 DLL 的三种方法

    Qt调用DLL方法一:使用Win32 API h文件 typedef bool (CALLBACK* SetKeyBoardHook)(HWND); HINSTANCE hDLL; // Handle ...

最新文章

  1. NASA 开放巨量 VICAR 源码:近 350 款应用程序
  2. 区别 和esc 打印指令tsc_Linux 下的这些高效指令,是你快速入门运维的神器
  3. 多线程系列之学习多线程下载的基本原理和基本用法(1)
  4. java 昨天今天明天
  5. MySQL JSON 类型数据操作
  6. 笨办法学 Python · 续 练习 8:`cut`
  7. 亚马逊自动驾驶小车上线啦:才卖1700元,请叫它“强化学习玩具”
  8. 【数学】十万个为什么(一) —— 为什么乘法会分为左乘和右乘,除法会分为左除和右除?
  9. Java Error(四)
  10. KL散度 JS散度 熵
  11. 传智播客python高级-2018年传智播客黑马python15期
  12. 0723Python总结-递归函数及练习
  13. 不要把敏感信息写在k8s的env上
  14. 领导力【管理学之五】
  15. JavaScript 进阶 - 第3天
  16. rk3288 安卓7.1显示正确的ICCID值
  17. 14.运算符(operator)
  18. 数字图像 - 图像隐写
  19. 一天发十万封邮件方法_网上投递简历,如何避免求职邮件成为垃圾?
  20. Redis系列漏洞总结

热门文章

  1. 音频文件格式有哪些?误删音频文件如何恢复?
  2. Amazon EC2实例的登录问题
  3. 51单片机的出租车计价器设计
  4. Weblogic EJB3开发及常见问题
  5. 一篇小短文,带你了解屏幕刷新背后的故事
  6. linux dd命令制作软盘,【Linux】dd命令操作磁盘与镜像
  7. 华兴银行 java 面试题_银行业务面试题,java代码。
  8. 支付业务稳健,到店电商快速放量,移卡迎来价值评估新锚点
  9. linux离线安装yum包
  10. vue扫描二维码,真机调试