Qt调用C#动态库:从创建CLR包装到工程实现的完整指南

Chloe
Chloe
发布时间:2026-10-16| 4人浏览过
原创

本文将详细介绍在Qt框架下调用C#生成的动态链接库(.dll)的完整流程。核心思路是通过Visual Studio中的CLR托管C++项目构建一个中间层,将C#代码转换为C/C++可识别的原生函数接口。教程涵盖环境准备、两层DLL的创建、关键文件的依赖配置以及Qt中的两种具体实现方案。

结果或目标

最终实现在Qt项目中,通过C++代码成功调用编译自C#的业务逻辑函数(如加法运算、字符串处理等),解决不同编程语言之间的二进制兼容性问题。

准备条件

  • 开发环境: Visual Studio 2019 + Qt 5.12(版本需匹配,且VS需集成Qt插件)。
  • 基础工具: Visual Studio Installer,用于检查组件安装状态。
  • 语言规范: C# (用于核心逻辑) 和 Managed C++ / CLI (用于桥接)。

第1步:确认并安装 C++/CLI 支持

创建CLR项目的前提是必须安装对应版本的 MSVC v142 - VS 2019 C++ x64/x86 build tools 及其子选项。

  1. 打开 Visual Studio Installer。
  2. 找到已安装的 VS2019,点击修改。
  3. 在左侧菜单选择“使用C++的桌面开发”,然后在右侧的功能列表中找到:V142生成工具的C++/CLI支持。
  4. 勾选该选项,点击右下角的修改进行安装。
  5. 安装完成后,重启软件即可。

真实操作顺序

第2步:创建业务逻辑C#类库

首先创建一个标准的 .NET Framework C#类库,用于编写具体的业务功能(非托管代码)。本例假设项目名为 ClassLibrary1。

此时解决方案的视图应如下所示:

在 Class1.cs 文件中添加测试方法。我们模拟几个场景:整数加减法和字符串传递。

using System;

namespace ClassLibrary1
{
    public class Class1
    {
        // 简单的数值计算
        public int myAdd(int a, int b)
        {
            return a + b + 5; // 返回值带偏移量5
        }

        // 引用参数传递减法结果
        public void mySub(int a, int b, ref int c)
        {
            c = a - b - 5;
        }

        // 接收字符串参数
        public void mySetText(string text)
        {
            Console.WriteLine("C# Received: {0}", text);
        }

        // 输出字符串(通过ref返回)
        public void myGetText(ref string text)
        {
            text = "Hello from C#";
        }
    }
}

编写完成后,生成项目。此时会在 bin\Debug 目录下得到 ClassLibrary1.dll。请将此文件保留好,它是后续步骤的依赖项。

第3步:创建CLR封装类库(桥接层)

C#程序集默认不能直接被Qt加载。我们需要创建一个CLR Class Library (.NET Framework) 项目作为“桥梁”。本例命名为 ClassLibrary2。

该项目的目的是将上一步的C#对象包装为 extern "C" 形式的原生函数导出。注意此时的解决方案视图:

关键在于头文件 ClassLibrary2.h 的写法。我们需要引用第一个项目,并使用 #using 指令。

#pragma once
#include <windows.h>
#include <msclr/marshal_cppstd.h> // 用于字符串转换的工具

// 引用第一步生成的C# dll
#using "../ClassLibrary1/bin/Debug/ClassLibrary1.dll"
using namespace ClassLibrary1;

// 定义导出的C接口函数
extern "C" __declspec(dllexport) int MyAdd(int a, int b)
{
    ClassLibrary1::Class1 obj;
    return obj.myAdd(a, b);
}

extern "C" __declspec(dllexport) void MySub(int a, int b, int *c)
{
    ClassLibrary1::Class1 obj;
    // 注意指针传递方式
    int temp = 0;
    obj.mySub(a, b, temp); // C#需要ref或out
    if(c) *c = temp;
}

extern "C" __declspec(dllexport) void MySetText(const char* text)
{
    ClassLibrary1::Class1 obj;
    // 类型转换:const char* -> String^ -> std::string (若需存储)
    pin_ptr<const wchar_t> wch = PtrToStringChars(gcnew String(System::Text::Encoding::UTF8->GetBytes(text)));
    // 更简单的 marshal 方法
    msclr::interop::marshal_as<String^> sStr = msclr::interop::marshal_as<String^>(text);
    obj.mySetText(sStr);
}

extern "C" __declspec(dllexport) const char* MyGetText()
{
    ClassLibrary1::Class1 obj;
    String^ result;
    obj.myGetText(result);
    // 返回静态或全局缓存,注意内存管理
    static char buffer[256];
    array<byte>^ bytes = System::Text::Encoding::Default->GetBytes(result);
    pin_ptr<char> pch = &buffer[0];
    memcpy(pch, bytes->GetData(), bytes->Length);
    buffer[bytes->Length] = '\0';
    return buffer;
}

注:上述代码展示了概念流程。实际工程中推荐直接使用 msclr::interop::marshal_as 自动处理字符串转换。对于复杂返回值,建议采用输出缓冲区指针的方式避免生命周期问题。

关键风险提醒:依赖分发
生成 ClassLibrary2.dll 后,必须确保以下两个文件位于Qt程序的同一运行目录下,否则Qt无法加载时会导致崩溃:

  1. ClassLibrary2.dll (包含C接口的主DLL)
  2. ClassLibrary1.dll (C#业务逻辑DLL,因为ClassLibrary2引用了它)

Qt调用实现

方法一:QLibrary 动态加载(灵活但需谨慎)**

这种方法不需要修改 .pro 配置文件,适合运行时决定是否加载DLL。

#include <QCoreApplication>
#include <QLibrary>
#include <QDebug>
#include <type_traits>

// 根据导出的C函数定义函数指针类型
typedef int (*ADD_FUNC)(int, int);
typedef void (*SUB_FUNC)(int, int, int*);
typedef void (*SHOW_FUNC)(const char*);

typedef const char* (*GET_FUNC)();

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QLibrary mylib("ClassLibrary2.dll"); // 假设在exe同级目录
    
    if (!mylib.load()) {
        qDebug() << "Failed to load library:" << mylib.errorString();
        return -1;
    }

    // 获取函数地址
    ADD_FUNC add = (ADD_FUNC)mylib.resolve("MyAdd");
    SUB_FUNC sub = (SUB_FUNC)mylib.resolve("MySub");
    SHOW_FUNC show = (SHOW_FUNC)mylib.resolve("MySetText");
    GET_FUNC get = (GET_FUNC)mylib.resolve("MyGetText");

    if (add) {
        qDebug() << "Result of Add(3,2):" << add(3, 2); // Expected: 3+2+5=10
    }
    
    if (sub) {
        int res = 0;
        sub(10, 5, &res); // Expected: 10-5-5=0
        qDebug() << "Result of Sub(10,5):" << res;
    }

    if (show) {
        show("Hello World");
    }

    if (get) {
        const char* str = get();
        qDebug() << "Result of GetText:" << QString(str);
    }

    return a.exec();
}

方法二:链接静态库 .lib 并直接调用(更稳定)**

这是C++工程中的标准做法,利用VS生成的导入库(.lib)让编译器在链接阶段解析符号。

  1. 右键Qt项目 (.qmake/CMakeLists.txt) → 添加库 → 外部库。
  2. 浏览并选择 ClassLibrary2.lib 文件。
  3. 确认平台设置(通常选Windows)。

在代码中包含头文件并声明外部函数(或直接将头文件放入项目include路径):

#include <iostream>
#include <QString>

// 前置声明 (如果已将 .h 加入Qt Include 则无需重写)
extern "C" __declspec(dllimport) void MySetText(const char*);
extern "C" __declspec(dllimport) const char* MyGetText();

int main() {
    MySetText("Hello Qt");
    
    const char* ret = MyGetText();
    QString msg(ret);
    qDebug() << msg;
    
    return 0;
}

简要总结

Qt调用C#库的核心在于“CLR桥接”:纯C#不能被Win32 API加载,纯C++不能理解.NET对象。通过创建一个带有 __declspec(dllexport) 的CLR C++项目,我们将C#的行为映射为C风格的函数接口,从而实现了跨语言的无缝调用。只要注意 ClassLibrary1.dll 和 ClassLibrary2.dll 均在运行目录存在,并在字符串转换时处理好编码(建议使用 msclr 命名空间下的工具),即可高效集成。

以上就是Qt之调用C#的动态库的详细解决方法,更多关于Qt编程与C#集成的资料请关注本站其它相关文章!
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。