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

C++ 调用 C# - AOT 方案

时间:2024-09-07 17:05:11浏览次数:10  
标签:VisitByAot set C# C++ AOT include

一些 C# AOT 编译的笔记,整体感觉:简单很方便,但限制也很多,适用于比较单一的功能点。

跨语言调用C#代码的新方式-DllExport - InCerry - 博客园

在 .NET8 下,直接添加 <PublishAot>true</PublishAot> 就可以支持了,
需要注意一些限制,这里比较相关的是,不能使用 Newtonsoft.Json 做序列化,可以使用原生的 System.Text.Json 代替

更多说明和限制,可以看:

Native AOT deployment overview - .NET | Microsoft Learn

Create a single file for application deployment - .NET | Microsoft Learn

因为反射等特性受限,无法动态加载程序集,很多功能会用不了,如果是长期维护的项目,后续很有可能会遇到相关的坑。

比如 linq2db 就用不了

C# 端的 demo 代码

[UnmanagedCallersOnly(EntryPoint = "Combine")]
public static IntPtr Combine(IntPtr str, int num)
{
    var name = Class1.Run();
    string? myStr = Marshal.PtrToStringAnsi(str);
    string result = $"{myStr} -- {num} -- {name}";
    return Marshal.StringToHGlobalAnsi(result);
}

[UnmanagedCallersOnly(EntryPoint = "Free")]
public static void Free(IntPtr ptr)
{
    Marshal.FreeHGlobal(ptr);
}

csproj 设置

<PropertyGroup>
    <PublishAot>true</PublishAot>
    <IsAotCompatible>true</IsAotCompatible>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

CopyLocalLockFileAssemblies 是将依赖都输出到输出目录。

C# 编译结果:Accesser.dll 以及相关的依赖

C++ 端的 demo 代码

// VisitByAot.h
#pragma once

#define PathToLibrary L"SubFolder\\Accesser.dll"
#define PathToLibraryFolder L"SubFolder"

#include <windows.h>
#include <iostream>
#include "PathHelper.h"

class VisitByAot
{
public:
    int run();

private:
    // 定义函数指针类型
    typedef const char* (__stdcall* CombineFunc)(const char*, int);
    typedef void(__stdcall* FreeFunc)(const char*);
};

PathHelper.h 中的是一些辅助函数,见函数名知意。

PathToLibrary 和 PathToLibraryFolder 的配置,是为了将 C# 的 dll 放到 C++ 输出目录下的子文件夹中,让 DLL 更清晰一点。

// VisitByAot.cpp
#include "VisitByAot.h"

int VisitByAot::run()
{
    std::cout << "Hello World!\n";

    // 获取当前工作目录
    std::wstring currentDirectory = PathHelper::GetExecutableDirectory();

    // 生成绝对路径
    std::wstring pathToLibrary = PathHelper::CombinePath(currentDirectory, PathToLibrary);
    std::wstring pathToDllFolder = PathHelper::CombinePath(currentDirectory, PathToLibraryFolder);

    // 设置 DLL 搜索路径
    SetDllDirectory(pathToDllFolder.c_str());

    // 加载 DLL
    HINSTANCE hinstLib = LoadLibrary(pathToLibrary.c_str());
    if (hinstLib == NULL)
    {
        std::cerr << "Could not load the DLL." << std::endl;
        return EXIT_FAILURE;
    }

    // 获取 Combine 函数地址
    CombineFunc Combine = (CombineFunc)GetProcAddress(hinstLib, "Combine");
    if (Combine == NULL)
    {
        std::cerr << "Could not locate the function." << std::endl;
        FreeLibrary(hinstLib);
        return EXIT_FAILURE;
    }

    // 获取 Free 函数地址
    FreeFunc Free = (FreeFunc)GetProcAddress(hinstLib, "Free");
    if (Free == NULL)
    {
        std::cerr << "Could not locate the Free function." << std::endl;
        FreeLibrary(hinstLib);
        return EXIT_FAILURE;
    }

    // 调用函数
    const char* result = Combine("example", 123);
    if (result != NULL)
    {
        std::cout << "Result: " << result << std::endl;

        //// 使用 GlobalFree 释放内存
        //GlobalFree((HGLOBAL)result);

        // 使用 C# 导出的 Free 方法释放
        Free(result);
    }
    else
    {
        std::cerr << "Function returned NULL." << std::endl;
    }

    // 释放 DLL
    FreeLibrary(hinstLib);

    return EXIT_SUCCESS;

}

需要注意的是,需要 C++ 调用端释放不再使用的引用。

自动拷贝

可以看到,C# 端和 C++ 端是完全隔离的,C++ 端使用 LoadLibrary 的方式加载。所以就需要手动将 C# 的输出,拷贝到 C++ 端的调用目录。

以下是一个辅助脚本,供参考。

cd /d "%~dp0"

set currentDir=%cd%
echo current work dir: %currentDir%

set BuildRID=Debug

set AotPublishOutputPath=".\bin\%BuildRID%\net8.0\win-x64\publish"
set CppUseTargetPath="..\x64\%BuildRID%\SubFolder"

rd /s /q %AotPublishOutputPath%

:: 生成 AOT 编译结果
dotnet publish -p:NativeLib=Shared -r win-x64 -c %BuildRID%

if not exist "%CppUseTargetPath%" (
    mkdir "%CppUseTargetPath%"
)
xcopy "%AotPublishOutputPath%\*" "%CppUseTargetPath%\" /E /I /Y

标签:VisitByAot,set,C#,C++,AOT,include
From: https://www.cnblogs.com/jasongrass/p/18401896

相关文章

  • C++ 调用 C# - DllExport 方案
    3F/DllExport:.NETDllExportwith.NETCoresupport(aka3F/DllExportakaDllExport.bat)目前还不支持.NET6以及后续的版本,看起来作者生活上遇到了一些麻烦,暂时也不打算处理了无法使用.NET6工作·问题#197·3F/DllExport---Unabletogetworkingusing.NET6......
  • C++ 调用 C# - C++/CLI 方案
    C++/CLI方案是通过托管C++做中间层,来转发C#和C++之间的调用和数据传递。这个写法,C#不用做任何特殊的处理,正常写就可以。C++/CLI层新建一个C++/CLI项目,e.g.MedicalDbAccessWrapper,添加对C#项目的引用,注意,输出目录最好就是原生C++项目的输出目录,原生C++项目......
  • 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。更具体地说,我们提出了一种新颖的用......