C++ DLL 供 C# AnyCPU 调用 【 OpenCV onnxruntime】

news/2025/2/27 23:49:55/文章来源:https://www.cnblogs.com/yaksa777/p/18742360

背景

C++ 打包的DLL用到 OpenCV,用到 onnxruntime

C# 软件需要打包成Any CPU版本,即可以在 x86下使用,也可以在x64下使用

C# 前端想把 C++ dll与依赖放在单独的”Libs“文件夹中,不是"Dubug"下

难点:

在C++ 中, OpenCV 和 onnxruntime 分别有x64和x86两个版本,两个版本只能在对应环境下使用

DLL 和其依赖如果不放在"Dubug"下,不容易路径。

比如在C++ dll代码中,调用了"model.onnx"。将打包后的 DLL 和 model.onnx放在C# 项目中的 "Dubug/Libs"下。C# 调用DLL时,会在 Dubug下找model.onnx,即使在C#代码中指定DLL路径,也会报"找不到模型文件onnx 的错误。

系统环境

Windows 11

Visual Studio 2019

解决方案

  1. 先用 C++ 打包两个 DLL FungiIdentification_x64.dll 和 FungiIdentification_x86.dll
    1. 两个DLL使用不同的函数接口 start_x64() 和 start_x86()
    2. 在打包时使用 getdlldir()函数获得DLL所在文件夹路径,之后使用DLL路径构建onnx模型的绝对路径(原因:DLL C++代码中设置的相对路径,C#调用后,会变成以C# exe为基准的相对路径)
  2. C# 调用时加载两个不同版本的DLL
    1. 给start_x64() 和 start_x86()再封装一个高一层的start()接口
    2. 使用windows系统"kernel32.dll"中的SetDllDirectory()函数,根据当前系统环境设置不同的依赖文件夹

具体步骤

省略配置 OpenCV 环境和 onnxruntime 环境的步骤

C++ 打包 x64版本

关键代码:

省略处理函数

#include "nlohmann/json.hpp"#include <fstream>
#include <iostream>
#include <ctime>
#include <sstream>
#include <windows.h>
#include <filesystem>#include "opencv2/opencv.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core/utils/logger.hpp"std::string getdlldir()
{HMODULE hDllHandle = GetModuleHandleA("FungiIdentification_x64.dll");//是本代码片所属动态库名称char cDllPathBuff[MAX_PATH];memset(cDllPathBuff, 0, MAX_PATH);auto getdllpathstatus = GetModuleFileNameA((HMODULE)hDllHandle, cDllPathBuff,MAX_PATH);std::string strDllPathBuff = cDllPathBuff;auto nPathTruncLoc = strDllPathBuff.find_last_of('\\');strDllPathBuff = strDllPathBuff.substr(0, nPathTruncLoc + 1);//strDllPathBuff 为本动态库所在路径return strDllPathBuff;
}extern "C" _declspec(dllexport) const char* start_x64(char* img_path, char* img_p)
{cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR); // 取消警告std::string dlldir = getdlldir();nlohmann::json results;std::string img_path_str = img_path;process(img_path_str, dlldir, results);  // 处理函数,没放进来std::string results_s = results.dump(2);  //转储成字符串 可以使用dump(4)进行缩进char* return_char;return_char = strdup(results_s.c_str());return return_char;
}

C++ 打包 x86版本

关键代码:

省略处理函数

#include "nlohmann/json.hpp"#include <fstream>
#include <iostream>
#include <ctime>
#include <sstream>
#include <windows.h>
#include <filesystem>#include "opencv2/opencv.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core/utils/logger.hpp"std::string getdlldir()
{HMODULE hDllHandle = GetModuleHandleA("FungiIdentification_x86.dll");//是本代码片所属动态库名称char cDllPathBuff[MAX_PATH];memset(cDllPathBuff, 0, MAX_PATH);auto getdllpathstatus = GetModuleFileNameA((HMODULE)hDllHandle, cDllPathBuff,MAX_PATH);std::string strDllPathBuff = cDllPathBuff;auto nPathTruncLoc = strDllPathBuff.find_last_of('\\');strDllPathBuff = strDllPathBuff.substr(0, nPathTruncLoc + 1);//strDllPathBuff 为本动态库所在路径return strDllPathBuff;
}extern "C" _declspec(dllexport) const char* start_x86(char* img_path, char* img_p)
{cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR); // 取消警告std::string dlldir = getdlldir();nlohmann::json results;std::string img_path_str = img_path;process(img_path_str, dlldir, results); // 处理函数,没放进来std::string results_s = results.dump(2);  //转储成字符串 可以使用dump(4)进行缩进char* return_char;return_char = strdup(results_s.c_str());return return_char;
}

C# 调用代码

首先将 dll与依赖放置在C# 项目的"Debug/Lib"下

|-- FungiIdentification_x64.dll

|-- FungiIdentification_x86.dll

|-- dish.onnx

|-- model_2.0.onnx

|-- x64_FungiIdentificationDependency

| |-- concrt140d.dll

| |-- msvcp140_1d.dll

| |-- msvcp140d.dll

| |-- onnxruntime.dll

| |-- opencv_world455d.dll

| |-- ucrtbased.dll

| |-- vcruntime140_1d.dll

| |-- vcruntime140d.dll

|-- x86_FungiIdentificationDependency

| |-- concrt140.dll

| |-- concrt140d.dll

| |-- msvcp140.dll

| |-- msvcp140_1d.dll

| |-- msvcp140d.dll

| |-- onnxruntime.dll

| |-- opencv_world455d.dll

| |-- ucrtbase.dll

| |-- ucrtbased.dll

| |-- vcruntime140.dll

| |-- vcruntime140d.dll

OpenCV 和 onnxruntime 都有 x64 和 x86 两个版本所以两个库的DLL与依赖都是不同的,所以分为两个文件夹放置 x64_FungiIdentificationDependency 和 x86_FungiIdentificationDependency

  • opencv_world455d.dll 在 OpenCV 编译包找到(注意版本)
  • 如果 onnxruntime 是用NuGet安装的,onnxruntime.dll 可以在 C++ 工程下找到(也要注意版本)
  • 像 concrt140d.dll 等这些 dll 是 C/C++ 运行时所需要的库,64位的在"C:\Windows\System32", 32位的在"C:\Windows\SysWOW64"
  • C++运行库的版本和使用的 Visual Studio版本有关,我用的是 VS2019,不同的VS版本使用的版本号可能不同
  • ONNX模型文件不分 x64 和 x86 ,可以共用
using Newtonsoft.Json.Linq;
using System;
using System.Runtime.InteropServices;namespace ConsoleApp2._1
{class Program{[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]static extern bool SetDllDirectory(string lpPathName);[DllImport("Libs/FungiIdentification_x64.dll", CallingConvention = CallingConvention.Cdecl)]extern static IntPtr start_x64(string img_path);[DllImport("Libs/FungiIdentification_x86.dll", CallingConvention = CallingConvention.Cdecl)]extern static IntPtr start_x86(string img_path);static IntPtr start(string img_path){if (Environment.Is64BitOperatingSystem){return start_x64(img_path);}else{return start_x86(img_path);} }static void setDllDirectory(){if (Environment.Is64BitOperatingSystem){string dllpath = $"{AppDomain.CurrentDomain.BaseDirectory}/Libs/x64_FungiIdentificationDependency";  //BaseDirectory 有分隔符结尾bool ret = SetDllDirectory(dllpath);if (!ret) throw new System.ComponentModel.Win32Exception();}else{string dllpath = $"{AppDomain.CurrentDomain.BaseDirectory}/Libs/x86_FungiIdentificationDependency";  //BaseDirectory 有分隔符结尾bool ret = SetDllDirectory(dllpath);if (!ret) throw new System.ComponentModel.Win32Exception();}}static void Main(string[] args){try{string img_path = @"D://微信图片_20250227203052.jpg";// 1. AnyCPU 测试(生成平台选择 AnyCPU)//    因为我的笔记本是 x64位,所以【去掉“首选32位”的勾选】setDllDirectory();IntPtr result = start(img_path);string result_str = Marshal.PtrToStringAnsi(result);// 2. x64 测试 (生成平台选择 x64)// string dllpath = $"{AppDomain.CurrentDomain.BaseDirectory}/Libs/x64_FungiIdentificationDependency";  //BaseDirectory 有分隔符结尾// bool ret = SetDllDirectory(dllpath);// if (!ret) throw new System.ComponentModel.Win32Exception();// IntPtr result_x64 = start_x64(img_path);// string result_str = Marshal.PtrToStringAnsi(result_x64);// 3. x86 测试(生成平台选择 x86)//string dllpath = $"{AppDomain.CurrentDomain.BaseDirectory}/Libs/x86_FungiIdentificationDependency";  //BaseDirectory 有分隔符结尾//bool ret = SetDllDirectory(dllpath);//if (!ret) throw new System.ComponentModel.Win32Exception();//IntPtr result_x86 = start_x86(img_path);//string result_str = Marshal.PtrToStringAnsi(result_x86);// 输出Console.WriteLine(result_str);JObject result_json = JObject.Parse(result_str);Console.WriteLine(result_json["err"]);Console.WriteLine(result_json["err_info"]);// ERR = 0; 正常// ERR = 1; 找不到图片// ERR = 2; 找不到dish.onnx// ERR = 3; 找不到model_2.0.onnx// ERR = 4: 没有检测到菌}catch (Exception ex){Console.WriteLine($"ex:{ex}");}Console.ReadLine();}}
}

最后一个坑:

测试 Any CPU 时,如果你的电脑是x64,在选择生成平台时,需要去掉"首选32位"的选项

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/890913.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac本地部署DeepSeek(简洁版)

1. 下载ollama2. 安装ollma 无脑安装那么最小的1.5b模型就拥有15亿的参数量,而最大的671b则有6710亿个参数,我们要使用的是前面这6个规模较小的模型。ollama run deepseek-r1:8b要等一会我不相信黑夜将至 因为火把就在我的手中。

使用OpenLPA编辑eUICC卡片

使用OpenLPA编辑eUICC卡片原文地址: [https://github.com/EsimMoe/MiniLPA/blob/main/README_zh-CN.md] 原作者:EsimMoe 许可: AGPL-3.0 license精美的现代化 LPA UI特性良好的跨平台支持 (Windows, Linux, macOS) 更友好的用户界面 i18n 多语言支持 搜索与快捷跳转 自由地管理…

UltraRAG 框架全家桶

转载:清华等团队推出 UltraRAG 框架全家桶,让大模型读懂善用知识库!引言 RAG 系统的搭建与优化是一项庞大且复杂的系统工程,通常需要兼顾测试制定、检索调优、模型调优等关键环节,繁琐的工作流程往往让人无从下手。 近日,针对以上痛点,清华大学 THUNLP 团队联合东北大学…

2024/2/27日 日志 第一次测试案例分析(4)--登录与注册和仓库方面

UserMapper点击查看代码 package com.Moonbeams.mapper;import com.Moonbeams.pojo.User; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.ResultMap; import org.apache.ibatis.annotation…

基于Web的图书管理系统全面解析与实践指南

@目录一、项目背景与目标二、技术选型与开发工具(一)后端技术:Java 与 Servlet(二)前端技术:HTML、CSS 与 JavaScript(三)数据库:MySQL(四)开发工具:Eclipse三、系统架构设计(一)表示层(二)业务逻辑层(三)数据访问层四、功能模块详解(一)用户角色与权限管理…

Juniper SRX1500 防火墙 Internet与SDWAN分流配置

Chassis 管理接口fxp0 大多数SRX设备都有独立的管理接口,名称为fxp0。 低端一些的设备,比如SRX300/320是没有独立的fxp0的。 需要从接口中挑一个作为管理接口。 Interface Renumbering Juniper的堆叠叫Chassis, 堆叠的节点分别叫node0和node1。 堆叠建立以后, 在查看配置的…

逆向软件设计和开发

在软件技术迅猛发展的今天,逆向工程已成为理解、分析和改进现有软件系统的重要手段。旨在深入剖析目标软件的内部结构、功能逻辑和数据流,为软件维护、安全评估和功能扩展提供可靠依据。 在本次实验中,本人基于同学提供的一份C++课程设计的员工信息管理系统进行逆向工程开发…

如何构建多模态AI知识库?

转载:如何构建多模态AI知识库?摘要 在科技飞速发展的当下,人工智能(AI)已成为推动各行业变革的关键力量。其中,多模态 AI 知识库作为 AI 领域的重要创新,正引领我们迈向一个全新的智能时代。它打破了传统数据处理的单一模式,融合文本、图像、音频、视频等多种数据模态,…

使用 DeepSeek R1 和 Ollama 开发 RAG 系统

1.概述 掌握如何借助 DeepSeek R1 与 Ollama 搭建检索增强生成(RAG)系统。本文将通过代码示例,为你提供详尽的分步指南、设置说明,分享打造智能 AI 应用的最佳实践。 2.内容 2.1 为什么选择DeepSeek R1? 在这篇文章中,我们将探究性能上可与 OpenAI 的 o1 相媲美、但成本却…

[NCTF 2022]qrssssssssqrssssssss_revenge WP

[NCTF 2022]qrssssssss&qrssssssss_revenge WP[NCTF 2022]qrssssssss&qrssssssss_revenge WP 本来最近是想练习写 python 脚本的,无意之中刷到了这道题,看到是关于二维码的,于是正好研究一下。 看了官方 WP有些概念没怎么懂,又去借鉴了别人的二维码学习笔记,不过…

昆明理工大学25年材料工程考研预计调剂158人

--材测材料物理与化学材料学材料表征与分析材料工程F001现代材料测试技术F002材料成型技术基础864材料科学基础

7.4.1 Inception 块

白色的\(1\times 1\)卷积层是用来降低通道数的,蓝色的卷积层是用来学习特征的 大致解释一下这些数字是怎么分配的。现在我们的通道数是\(192\),于是我们假设最后输出的通道数是\(256\);考虑这些通道如何分配:我们知道\(3\times 3\)的卷积层性质很好(参数不算太多,学习能力…