前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >C++ 调用 C# - C++/CLI 方案

C++ 调用 C# - C++/CLI 方案

作者头像
jgrass
发布2024-12-25 18:37:18
发布2024-12-25 18:37:18
15600
代码可运行
举报
文章被收录于专栏:蔻丁杂记
运行总次数:0
代码可运行

C++/CLI 方案 是通过托管 C++ 做中间层,来转发 C# 和 C++ 之间的调用和数据传递。

这个写法,C# 不用做任何特殊的处理,正常写就可以。

C++/CLI 层

新建一个 C++/CLI 项目,e.g. MedicalDbAccessWrapper,添加对 C# 项目的引用, 注意,输出目录最好就是原生 C++ 项目的输出目录,原生 C++ 项目可以直接调用。

托管 C++ 头文件,里面处理对 C# 的调用。

托管 C++ 中,引用类型后面会跟一个 ^,命名空间和静态方法的调用使用 ::

MedicalDbAccessWrapper.h

MedicalDbAccessWrapper.h

代码语言:javascript
代码运行次数:0
复制
#pragma once
// 里面有很多测试代码using namespace System;using namespace System::Collections::Generic;using namespace MedicalDbAccess;using namespace MedicalDbAccess::Models;using namespace MedicalDbAccess::Wrapper;
namespace MedicalDbAccessWrapper {    public ref class DbAccessWraper    {    public:        String^ Combine(String^ str, int num)        {            return Class1::Run();        }
        String^ Run()        {            return Class1::Run();        }
        Patient^ GetPatient(String^ uid)        {            return PatientWrapper::GetPatient(uid);        }
        IList<Patient^>^ GetPatients()        {            return PatientWrapper::GetPatients();        }    };    }

MedicalDbAccessWrapper_Native.h

原生 C++ 头文件,用于处理 C++ 的导出

MedicalDbAccessWrapper_Native.h

代码语言:javascript
代码运行次数:0
复制
#pragma once
#ifdef MEDICALDBACCESSWRAPPER_EXPORTS#define MEDICALDBACCESSWRAPPER_API __declspec(dllexport)#else#define MEDICALDBACCESSWRAPPER_API __declspec(dllimport)#endif
#include <string>#include <vector>
// CPP 端定义的业务数据结构,对应 C# 的数据定义struct PatientDto{    std::string Uid;    std::string Name;    int Age;};
// 单独导出函数的形式MEDICALDBACCESSWRAPPER_API std::string RunAccess(std::string str, int value);MEDICALDBACCESSWRAPPER_API PatientDto GetPatient(std::string uid);MEDICALDBACCESSWRAPPER_API std::vector<PatientDto> GetPatients();
// 另外,也可以考虑使用导出的 class 的方式,对导出的方法进行封装class MEDICALDBACCESSWRAPPER_API MedicalDBAccessWrapper{public:    std::string RunAccess(std::string str, int value);    PatientDto GetPatient(std::string uid);    std::vector<PatientDto> GetPatients();};

MedicalDbAccessWrapper.cpp

CPP 源文件,用于实现要导出的函数,里面完成中托管数据类型对原生 C++ 类型的转换

MedicalDbAccessWrapper.cpp

代码语言:javascript
代码运行次数:0
复制
#include "pch.h"
#define MEDICALDBACCESSWRAPPER_EXPORTS // 定义为导出逻辑,而不是导入
#include <msclr/marshal_cppstd.h> // 必须在自己的 Wrapper.h 之前定义
#include "MedicalDbAccessWrapper.h"#include "MedicalDbAccessWrapper_Native.h"
PatientDto ConvertToNativePatient(Patient^ patient){    PatientDto nativePatient;    // nativePatient.Id = patient->Id;    nativePatient.Uid = msclr::interop::marshal_as<std::string>(patient->Uid);    nativePatient.Name = msclr::interop::marshal_as<std::string>(patient->Name);    nativePatient.Age = patient->Age;    return nativePatient;}
std::vector<PatientDto> ConvertToNativePatientList(IList<Patient^>^ patientList){    std::vector<PatientDto> nativePatients;
    // 遍历每个 Patient 并转换为 NativePatient    for each (Patient ^ patient in patientList)    {        PatientDto nativePatient;        // nativePatient.Id = patient->Id;        nativePatient.Uid = msclr::interop::marshal_as<std::string>(patient->Uid);        nativePatient.Name = msclr::interop::marshal_as<std::string>(patient->Name);        nativePatient.Age = patient->Age;
        // 将转换后的对象添加到原生列表        nativePatients.push_back(nativePatient);    }
    return nativePatients;}
std::string /*MedicalDBAccessWrapper::*/RunAccess(std::string str, int value){    MedicalDbAccessWrapper::DbAccessWraper wrapper;    String^ result = wrapper.Run();    std::string stdstr = msclr::interop::marshal_as<std::string>(result);    // return stdstr.c_str();    return stdstr;}
PatientDto /*MedicalDBAccessWrapper::*/GetPatient(std::string uid){    MedicalDbAccessWrapper::DbAccessWraper wrapper;    String^ uid2 = msclr::interop::marshal_as<String^>(uid);    Patient^ patient = wrapper.GetPatient(uid2);    return ConvertToNativePatient(patient);}
std::vector<PatientDto> /*MedicalDBAccessWrapper::*/GetPatients(){    MedicalDbAccessWrapper::DbAccessWraper wrapper;    auto list = wrapper.GetPatients();    return ConvertToNativePatientList(list);}

原生 C++ 端调用

原生 C++ 端,只要引用 MedicalDbAccessWrapper_Native.h 头文件,就可以直接调用里面导出的函数了。

为此,需要添加对 MedicalDbAccessWrapper 的引用,方式如下:

  • 项目->属性->配置属性->VC++ 目录-> 在 “包含目录” 里添加头文件 MedicalDbAccessWrapper_Native.h 所在的目录
  • 项目->属性->配置属性->VC++ 目录-> 在 “库目录” 里添加 MedicalDbAccessWrapper.lib 所在的目录
  • 项目->属性->配置属性->链接器->输入-> 在 “附加依赖项” 里添加 MedicalDbAccessWrapper.lib(若有多个 lib 则以空格隔开)

C语言调用C#函数 – Coding Life

调用就比较简单了,引入头文件之后,就可以直接调用了。

VisitByCli.h

代码语言:javascript
代码运行次数:0
复制
#pragma once#include <windows.h>#include <iostream>#include "MedicalDbAccessWrapper_Native.h"
class VisitByCli{public:    int run();};

VisitByCli.cpp

代码语言:javascript
代码运行次数:0
复制
#include "VisitByCli.h"
int VisitByCli::run(){    std::cout << "Hello CLI!\n";    auto result = RunAccess("", 1);    auto patient = GetPatient("P_112827");    auto patients = GetPatients();    return EXIT_SUCCESS;}

遗留问题

Wrapper 中间层和 C# 所有的 DLL,都要放在和原生 C++ 执行文件一个目录,dll 会显得非常混乱。 想把这堆 DLL,或者至少 C# 的所有 DLL,都一个放在一个子文件夹中,还没有找到方法。

当时,使用 LoadLibrary 动态加载的方式是可以的,但是这样就必须手动获取函数地址,然后还要定义函数签名。 不如直接引用 MedicalDbAccessWrapper_Native.h 头文件来得方便。

参考

P/Invoke各种总结(十、C++调用C#代码的几种方法总结) - zhaotianff - 博客园

使用PInvoke互操作,让C#和C++愉快的交互优势互补 - 一线码农 - 博客园

C语言调用C#函数 – Coding Life

原文链接: https://cloud.tencent.com/developer/article/2481585

本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024年9月7日 |,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++/CLI 层
  • 原生 C++ 端调用
  • 遗留问题
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档