首页 > 编程语言 >C++ 调用 C# - C++/CLI 方案

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

时间:2024-09-07 17:04:11浏览次数:7  
标签:std patient CLI C# nativePatient C++ MedicalDbAccessWrapper include

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

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

C++/CLI 层

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

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

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

MedicalDbAccessWrapper.h

// MedicalDbAccessWrapper.h
#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
#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();

MedicalDbAccessWrapper.cpp

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

// MedicalDbAccessWrapper.cpp
#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 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 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> 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.dll(若有多个 dll 则以空格隔开)

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

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

// VisitByCli.h
#pragma once
#include <windows.h>
#include <iostream>
#include "MedicalDbAccessWrapper_Native.h"

class VisitByCli
{
public:
    int run();
};
// VisitByCli.cpp
#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 头文件来得方便。

标签:std,patient,CLI,C#,nativePatient,C++,MedicalDbAccessWrapper,include
From: https://www.cnblogs.com/jasongrass/p/18401905

相关文章

  • SpringBoot集成WebSocket实现后端向前端推送数据
    SpringBoot集成WebSocket实现后端向前端推送数据这里最好了解一定websocket参考地址:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications在此之前可以了解一下【轮询(Polling)、长轮询(LongPolling)、服务器发送事件(Server......
  • Docker 教程简介
    教程名:Docker与微服务实战B站地址:尚硅谷Docker实战教程(docker教程天花板)-哔哩哔哩讲师:周阳‍前提了解SpringBoot,SpringCloud了解一些中间件:Redis,Nginx,MySQL了解Linux的基本使用知道Git‍课程大纲基础篇(零基小白)Docker简介Docker安装Docker常用命令Do......
  • Docker 配置国内镜像源
    由于GFW的原因,在下载镜像的时候,经常会出现下载失败的情况,此时就可以使用国内的镜像源。什么是镜像源:简单来说就是某个组织(学校、公司、甚至是个人)先通过某种手段将国外的镜像下载下来,然后上传到国内的网站,这样我们在国内就可以通过这个网站下载到镜像源‍起因笔者有......
  • SciTech-Science: 纯色滤(分)光塑料片: 将光分解为BGR三原纯色(彩色CCD传感器原理) +
    彩色滤(分)光塑料片:将光分解为BGR三原纯色彩色CCD传感器原理透过一张彩色滤(分)光塑料片可以分解出光源的“与滤光片同颜色”的成份;例如“B(蓝色)滤光塑料片”:用"P紫"光源照射,其“蓝色成份”就会穿透过"蓝色滤光塑料片";用"G绿"或"R红"光源照射,则没有光线透过.......
  • Oracle数据库配置大页脚本
    编辑脚本su-oracle$vihugepages_settings.sh------------------------------------------#!/bin/bash##hugepages_settings.sh##Linuxbashscripttocomputevaluesforthe#recommendedHugePages/HugeTLBconfiguration#onOracleLinux##Note:Thisscri......
  • Mac 套壳截图软件有哪些?
    争取用一篇文章让你实现截图加壳自由,告别对套壳截图软件的依赖。点击查看未压缩透明背景超高清原图根据以往我所寻找的,Mac套壳截图软件里面并不完善,有些不尽人意,比如缺乏某种型号或设备的外壳,或分辨率不够,或没有透明背景。于是我恢复了以前Mac套壳截图的老方法,......
  • 鹏哥课程三子棋 全码C语言
    代码来自在b站上的鹏哥的课程,大部分为手打,少部分直接搬运了csdn博客其他学习者的代码。该三子棋代码内容全免费。这篇文章仅提供给认真上课的学习者参考使用,经过测试该代码能够正常运行使用。前言唯一美中不足的是,在开始游戏界面如果输入下棋坐标,会导致死循环。共包含三篇,......
  • GenRec论文阅读笔记
    GenRec:LargeLanguageModelforGenerativeRecommendation论文阅读笔记Abstract​ 本文提出了一种使用大型语言模型(LLM)的创新推荐系统方法,该方法纯粹基于原始文本数据,即使用项目名称或标题作为项目ID,而不是创建精心设计的用户或项目ID。更具体地说,我们提出了一种新颖的用......
  • Go - Web Application 10
    CreatingaunittestInGo,it’sstandardpracticetowriteyourtestsin*_test.gofileswhichlivedirectly alongsidethecodethatyou’retesting.So,inthiscase,thefirstthingthatwe’regoingtodo iscreateanewcmd/web/template_test.gofilet......
  • CF1991F Triangle Formation 题解
    Description你有\(n\)根棍子,从\(1\)到\(n\)编号。第\(i\)根棍子的长度是\(a_i\)。你需要回答\(q\)个问题。在每个查询中,你会得到两个整数\(l\)和\(r\)(\(1\lel<r\len,r−l+1\ge6\))。确定是否可以从编号为\(l\)到\(r\)的棒中选择\(6\)个不同的棒,形......