qt/c++调用dll的方法实践
qt/c++调用dll的方法实践关于c++调用dll的方法,应该说是很成熟的,很多文章介绍的也很详细,可以直接套用。这里不在详述其原理,而只是根据实际使用做一个实践总结。主程序添加dll中的头文件声明,联合该dll编译,直接调用dll内部函数–这是隐式调用的方法这种方式下,生成dll的源代码文件必须要区分头文件和源文件。比如:如下testdllc.h文件中定义了一个Functions类,定义了6个
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);
else
qDebug()<<"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" {
#endif
FBC_API int library_add(int a, int b);
FBC_API int value;
#ifdef __cplusplus
}
#endif
template<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 __cplusplus
extern "C" {
#endif
__declspec(dllimport) int library_add(int, int);
__declspec(dllimport) int value;
#ifdef __cplusplus
}
#endif
int 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);
};
参考文献:
- gcc/g++ 链接库的编译与链接(https://blog.csdn.net/q_l_s/article/details/51313842)
- g++编译DLL文件(http://turbinee.blog.sohu.com/271861881.html)
- 如何使用g++编译调用dll的c++代码(https://www.bbsmax.com/A/lk5avA70d1/)
- MinGW(GCC)编译DLL文件(https://www.cnblogs.com/lichmama/p/4126323.html)
- 使用mingw制作dll文件(https://www.cnblogs.com/tonghaolang/p/9253995.html)
- MinGW编译dll并引用(http://www.bubuko.com/infodetail-3426585.html)
更清楚的微软的参考
(1)编译,调用dll中的头文件 - Walkthrough: Creating and Using a Dynamic Link Library (C++)(https://docs.microsoft.com/en-us/previous-versions/ms235636%28v%3dvs.140%29)
(2)调用或直接使用dll中的函数 - Creating a Simple Dynamic-Link Library(https://docs.microsoft.com/zh-cn/windows/win32/dlls/creating-a-simple-dynamic-link-library)
- Using Load-Time Dynamic Linking(https://docs.microsoft.com/zh-cn/windows/win32/dlls/using-load-time-dynamic-linking)
- Using Run-Time Dynamic Linking(https://docs.microsoft.com/zh-cn/windows/win32/dlls/using-run-time-dynamic-linking)
- Windows C++中__declspec(dllexport)的使用(https://blog.csdn.net/fengbingchun/article/details/78825004)
更多推荐
所有评论(0)