目录
前言
一、C++ DLL 封装
二、C# 调用 DLL
1、创建 C# 控制台项目,调用
三、注意事项
前言
在实际工程开发中,跨语言调用是常见的需求,尤其是在性能要求较高的模块中,常常采用 C++ 实现核心算法逻辑,并通过封装为 DLL(动态链接库)的形式提供给其他语言调用,例如 C#。这种方式既能充分发挥 C++ 在执行效率、底层控制方面的优势,又可以借助 C# 在界面开发、快速迭代和平台整合方面的便利性,从而实现高效开发与运行的平衡。
本项目以“C++ 封装成 DLL,然后在 C# 中调用”为核心,详细介绍了如何封装成标准的 Windows 动态链接库(DLL),并在 C# 环境中成功调用
一、C++ DLL 封装
//ImageProcessing.h
#pragma once
#include <string>
#include <vector>
#ifdef IMAGEPROCESSING_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
extern "C" {
/**
* @brief 加载图像并进行反色处理
* @param inputPath 输入图像路径
* @param outputPath 输出图像路径
* @return 处理成功返回1,失败返回0
*/
DLL_API int InvertImage(const char* inputPath, const char* outputPath);
}
cpp源文件:
#include "ImageProcessing.h"
#include <opencv2/opencv.hpp>
void interpolateMat(const cv::Mat& input, cv::Mat& output)
{
//*******
//****
}
Mat fitPlane(const std::vector<cv::Point>& points, const cv::Mat& depthMap)
{
//*******
//****
}
int InvertImage(const char* inputPath, const char* outputPath)
{
//interpolateMat();
//****
//fitPlane();
}
二、C# 调用 DLL
1、创建 C# 控制台项目,完成调用,结束。
using System;
using System.Runtime.InteropServices;
namespace UseImageProcessingDll
{
class Program
{
[DllImport("MyTest.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int InvertImage(string inputPath, string outputPath);
static void Main(string[] args)
{
string input = "test.jpg";
string output = "inverted.jpg";
int result = InvertImage(input, output);
Console.WriteLine(result == 1 ? "图像处理成功!" : "图像处理失败!");
}
}
}
三、注意事项
-
确保
xxx.dll
和opencv_worldXXX.dll
(如opencv_world455.dll
)位于bin\Release
或bin\Debug
目录下。 -
推荐设置为 x64 平台(C++ 和 C# 都必须一致)。
2、另外如果遇到 C# 中没有 std::string&
这样的类型,这属于 C++ 的标准库类型,不能直接通过 P/Invoke(DllImport)跨语言传递。则:
-
在 C++ 侧提供一个
const char*
的 C 接口包装(因为直接 P/Invokestd::string&
不稳定)。 -
在 C# 中通过
[DllImport]
调用这个包装函数
extern "C"
{
DLL_API void ExtractionPathAndSave(std::string& FileName, double minThreshold = 0.15);
// 新增:接受 C 字符串
DLL_API void ExtractionPathAndSave_Ansi(const char* fileName, double minThreshold = 0.15);
}
void ExtractionPathAndSave(std::string& FileName, double minThreshold)
{
// 你的实际处理逻辑
}
extern "C" void ExtractionPathAndSave_Ansi(const char* fileName, double minThreshold)
{
std::string strFile(fileName);
ExtractionPathAndSave(strFile, minThreshold);
}
调用时C# 的 string
会自动通过 [MarshalAs(UnmanagedType.LPStr)]
转换为 const char*
,只要设置 CharSet = CharSet.Ansi
。
using System;
using System.Runtime.InteropServices;
namespace UseDLL_Test
{
class Program
{
// 方法1:导入 DLL 函数,注意命名要和导出的函数一致
[DllImport("DLL_API .dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void ExtractionPathAndSave_Ansi(string fileName, double minThreshold);
方法2:P/Invoke 声明:调用 C++ 的 Ansi 包装函数
//[DllImport("DLL_API.dll",EntryPoint = "ExtractionPathAndSave_Ansi", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
//public static extern void ExtractionPathAndSave(string fileName, double minThreshold);
static void Main(string[] args)
{
string filePath = @"C:\data\scan_data.bin";
double threshold = 0.2;
try
{
Console.WriteLine("调用 DLL 提取路径...");
ExtractionPathAndSave_Ansi(filePath, threshold); //方法1
//ExtractionPathAndSave(binFile, threshold);//方法2
Console.WriteLine("完成!");
}
catch (Exception ex)
{
Console.WriteLine("发生错误: " + ex.Message);
}
Console.ReadKey();
}
}
}