You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#调用C++ DLL传入cv::Mat参数出现编组错误该如何解决?

问题背景

你遇到的问题是C++ DLL导出函数使用了C++专属类型作为跨语言调用的参数和返回值,导致C#互操作封送失败,具体疑问点如下:

  • 该问题如何修复?
  • 如何实现C#侧Mat类型与C++侧cv::Mat类型的匹配适配,避免报错?
  • 需要修改C侧函数,还是只需在C#侧做特殊处理即可读取Mat内部数据传入C函数?
报错根本原因

当前报错Cannot marshal 'return value': Generic types cannot be marshaled的直接原因是:

  • C的std::vector<std::string>属于C STL模板类型,C#互操作层无法识别和封送
  • 入参的cv::Mat和C# OpenCVSharp的Mat是不同语言实现的类,内存布局完全不一致,无法直接跨语言传递
解决方案

三个问题明确答复

  1. 修复方案:修改C++ DLL的导出函数签名为C风格的可互操作类型,C#侧对应调整P/Invoke声明和传参逻辑即可解决
  2. Mat适配方案:不直接传递Mat对象,而是传递Mat的核心元数据(宽、高、行步长、像素数据指针、像素格式枚举值),在C++侧手动组装为cv::Mat,无需依赖两侧Mat的二进制兼容性
  3. 改动范围:两侧都需要做少量修改,原有业务逻辑完全不需要改动,仅调整对外交互的入口代码即可

具体代码修改

C++侧修改

第一步:调整导出函数签名,新增内存释放函数

避免使用C++专属类型作为入参和返回值,改为C基础类型:

#include <cstring>

// 新增:用于释放返回字符串的内存,避免内存泄漏
extern "C" __declspec(dllexport) void __cdecl FreeString(char* str)
{
    if (str != nullptr)
        delete[] str;
}

// 修改导出函数签名,入参改为Mat的核心属性,返回值改为char*
extern "C" __declspec(dllexport) char* __cdecl ProcessFrame(int width, int height, int step, unsigned char* data, int cvType)
{
    // 手动组装cv::Mat,原有业务逻辑完全不变
    cv::Mat image(height, width, cvType, data, step);
    
    /****************************
    此处保留你原来的ProcessFrame所有业务代码,直到得到listName结果
    ****************************/
    
    // 将返回的字符串列表拼接为|分隔的字符串
    std::string joinStr;
    for (size_t i = 0; i < listName.size(); i++)
    {
        if (i != 0) joinStr += "|";
        joinStr += listName[i];
    }
    
    // 分配返回内存,C#侧调用FreeString释放
    char* res = new char[joinStr.size() + 1];
    strcpy(res, joinStr.c_str());
    return res;
}

C#侧修改

第一步:调整P/Invoke声明,适配C风格函数签名

using System.Runtime.InteropServices;
using OpenCvSharp;

[DllImport("detect-camera.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr ProcessFrame(int width, int height, int step, IntPtr data, int cvType);

[DllImport("detect-camera.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeString(IntPtr str);

第二步:调整调用逻辑

private void button1_Click(object sender, EventArgs e)
{
    Mat image = Cv2.ImRead("D:/c++/ImportCallFunction/ImportCallFunction/123.jpg");
    
    // 传递Mat的核心属性到C++
    IntPtr resPtr = ProcessFrame(image.Cols, image.Rows, (int)image.Step(), image.Data, image.Type());
    
    // 转换返回结果为C#字符串
    string resStr = Marshal.PtrToStringAnsi(resPtr);
    // 释放C++侧分配的内存,避免泄漏
    FreeString(resPtr);
    
    // 拆分得到人脸列表
    List<string> facelist = resStr.Split('|').ToList();
    
    foreach (var item in facelist)
    {
        listBox1.Items.Add(item);
    }
}

注意事项

  • 确保C++使用的OpenCV版本与C# OpenCVSharp绑定的OpenCV版本的cv::Mat::type()枚举值定义一致,避免像素格式解析错误
  • 若需要支持中文返回值,可将字符串编码改为UTF8,C#侧用Marshal.PtrToStringUTF8转换即可
  • 处理过程中不要提前释放C#侧的Mat对象,避免C++侧访问已释放的像素内存

内容的提问来源于stack exchange,提问作者reza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 03:36:04