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

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);
};

参考文献:

  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)
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值