Qt程序中调用C#编写的dll

1、打开Visual Studio,新建一个C#的Class Library项目(这里选择的是.Net Framework 4),项目名为CSharpDll。

2、由于默认没有引入Forms等UI库,先在reference中添加引用System.Windows.Forms以便可以在测试中使用MessageBox等。

3、最终C#编写的dll的源代码如下图所示,命名空间为CSharpDll,公共类为CSharpClass。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace CSharpDll
{
    public class CSharpClass
    {
        public CSharpClass() { }
        public int add(int a , int b)
        {
            return a + b;
        }

        public void substract( int a , int b , ref int c)
        {
            c = a - b;
        }

        public static void showBox(string str)
        {
            MessageBox.Show("C#:" + str);
        }
    }
}

里面包含一个加法add,一个减法substract(为了测试指针,所以在减法的返回类型是void,而把计算结果通过ref参数c给返回),一个showBox方法(里面采用C#的MessageBox对话框显示用户输入的参数字串)

4、对project进行release build,在release目录下生成了CSharpDll.dll(待会用到)。

5、关闭CSharpDll项目,另外新建一个C++ CLR类型的Class Library项目(选择与C#项目相同的.Net Framework 4),项目名称为CppDll。

一开始我用的VS2019,发现VS2019好像无法新建 C++ CLR类型的Class Library项目了,所以学习微软的技术一定要小心,学习主流的支持很久的技术,尽量不要学习新出的技术,如果必须学新技术,一定要认真考量,一些边缘化的技术一定不要学习,没准哪天微软就不维护了。

6、选择Project->CppDll Properties…,在弹出的属性页面选择“Add New Reference..”,点击“browsing.”后选择CSharpDll项目中release目录下的CSharpDll.dll。

7、选择CSharpDll.dll后,可以看到在项目属性的References中出现了CSharpDll这个Library。

8、在CppDll项目中的CppDll.h中利用_declspec(dllexport)导出对应的3个接口函数add,substract,showBox。需要using namespace System::Reflection,对于这里的showBox,其参数不能采用CSharpDll里面的showBox参数的String类型,而是使用const char* 类型。

主要代码如下所示:

// CppDll.h

#pragma once

using namespace System;
using namespace System::Reflection;

__declspec(dllexport) int add(int a, int b)
{
	CSharpDll::CSharpClass obj;
	return obj.add(a, b);
}

__declspec(dllexport) void substract(int a, int b, int *c)
{
	CSharpDll::CSharpClass obj;
	obj.substract(a, b, *c);
}

__declspec(dllexport) void showBox(const char* content)
{
	CSharpDll::CSharpClass obj;
	String^ str = gcnew String(content);
	obj.showBox(str);
}

namespace CppDll {

	public ref class Class1
	{
		// TODO:  在此处添加此类的方法。
	};
}

9、选择release方式build CppDll项目,在release文件夹中生成了CppDll.dll文件,可以看到同时其也将引用的CSharpDll.dll也给拷贝到release文件夹中了。

10、接下来在Qt中进行调用, 在QtCreator中新建一个TestCSharpDll GUI项目,编译器选的mingw。通过VS自带的命令行工具中的dumpbin工具可以查看CppDll.dll导出的函数接口。

dumpbin -exports (+dll路径)

在TestCSharpDll工程中通过typedef定义函数指针,同时采用QLibrary动态加载并resolve函数。

在这里.dll的路径设为当前目录下“./CppDllMingW.dll”,也就是编译好的程序exe同一目录下的dll,去resolve由普通导出方式的接口即“?add@@YAHHH@Z”。

主要代码如下所示:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QLibrary>
#include<QMessageBox>
typedef int (*x_add)(int a , int b);
typedef void (*x_substract)(int a , int b , int* c);
typedef void (*x_showBox)(const char* content);

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

//add
void MainWindow::on_pushButton_clicked()
{
    int a = ui->lineEdit->text().toInt();
    int b = ui->lineEdit_2->text().toInt();
    QLibrary library("./CppDll.dll");
    if(library.load()){
        x_add add = (x_add)library.resolve("?add@@YAHHH@Z");
        if(add){
            QString str = QString::number(add(a , b));
            QMessageBox::information(this , "call add from dll" , str);
        }
    }
}

//sub
void MainWindow::on_pushButton_2_clicked()
{
    int a = ui->lineEdit_3->text().toInt();
    int b = ui->lineEdit_4->text().toInt();
    int c = 0;
    QLibrary library("./CppDll.dll");
    if(library.load()){
        x_substract sub = (x_substract)library.resolve("?substract@@YAXHHPAH@Z");
        if(sub){
            sub(a , b , &c);
            QString str = QString::number(c);
            QMessageBox::information(this , "call sub from dll" , str);
        }
    }
}

//showBox
void MainWindow::on_pushButton_3_clicked()
{
    QLibrary library("./CppDll.dll");
    if(library.load()){
        x_showBox showBox = (x_showBox)library.resolve("?showBox@@YAXPBD@Z");
        if(showBox){
            showBox("showBox!");
        }
    }
}

编译TestCSharpDll工程,将CppDll.dll和CSharpDll.dll复制到同一目录下,执行TestCSharpDll.exe,可看出点击按钮后,通过QLibrary进行动态resolve,均正常调用。

调用add函数
调用sub函数
调用showBox函数

最好是将相关dll置于同一目录下运行,不然会出现“未能加载文件或程序集”的异常。针对.lib链接方式,理应是置于同一目录下。而针对QLibrary进行resolve方式,可能通常一开始的想法是,CppDll.dll和CSharpDll.dll放在与程序不同目录的地方,程序中利用了QLibrary指定了CppDll.dll的方式进行加载,而CppDll.dll和CSharpDll.dll,因此程序调用CppDll.dll里面的函数时,CppDll.dll会找到与CppDll.dll同一目录下的CSharpDll.dll,然而CppDll.dll在被程序进行加载时,其继承了程序的环境变量,因此会从程序的当前目录下去查找,所以最好还是将CppDll.dll和CSharpDll.dll放置于程序同一目录下,同时QLibrary加载当前目录下的CppDll.dll。当然,部署到另外一台机器上时,目标机器还是需要安装.Net Framework,其版本至少不能低于当前CSharpDll.dll所使用的版本。

发表评论

电子邮件地址不会被公开。 必填项已用*标注