由于C#编写的是托管代码,编译生成微软中间语言,而C++代码则编译生成本地机器码(这种C++也有叫做本地C++或者非托管C++,VC6.0就是用于开发非托管C++代码的平台),这两种语言进行混合编程就存在一定困难。比较常用的方法是使用DllImport的方法,这种方法在网上有很多介绍,这里就不详细叙述了。但是用过这种方法的人都知道这种方法对于导出函数还可以但是却没法导出非托管C++类!非常的要命。
然而,除了C#、非托管C++外,C系列中还存在一种语言叫做托管C++,这种语言语法上和非托管C++几乎一样,但是却和C#一样编译成为微软中间语言,这样托管C++就可以和C#良好地通信,即可以在C#中使用托管C++类。另外,托管C++还有两个及其重要的特性就是:可以调用非托管C++的类和函数!托管C++的程序集可以嵌套非托管C++编译的机器码!好强大的混合体。所以我们的技术路径也就明晰了:C#以托管C++为中介调用非托管C++的类和函数。换句话说也就是用托管C++给非托管C++代码做一个外壳包装供C#调用。
换种方式解释:C#调用C++的dll库,如果调用C++的全局函数还好,估计可以直接调用,如果需要调用C++类里面的函数,因为C#和C++是两种不同的语言,两者之间不能直接调用,他们之间需要一个桥梁,而托管C++可以充当这个桥梁,这时需要创建一个C++ CLR dll,C#直接调用这个dll,间接调用原dll。
下面讲解VS2017使用C#语言调用QT5.9.2 MSVC2017编译生成的dll的步骤。
第一步:准备普通的C++类库,这里使用QT5.9.2 MSVC2017生成的.h、.lib、.dll,64位。
这里不再详细叙述,dll导出的函数参数都是int、double等基本类型的。
第二步:VS2017创建C++ CLR项目,将普通C++dll重新封装。
一、创建C++ CLR空项目。
二、注意改为与原C++ dll相同的64位
三、原dll类库相关文件准备好,这里放在程序目录下library文件夹下
四、.h和lib引入项目,debug和release分别引入
选中项目名称,点击“项目》属性》VC++目录》包含目录”,设置.h所在文件夹
选中项目名称,点击“项目》属性》链接器》常规》附加库目录”,设置.lib所在文件夹
选中项目名称,点击“项目》属性》链接器》输入》附加库依赖项”,设置.lib名称
五、项目属性调整,调整为动态库dll项目:
选中项目名称,点击“项目》属性》常规》配置类型”,改为“动态库”
选中项目名称,点击“项目》属性》常规》公共语言运行时支持”,改为“公共语言运行时支持(/clr)”
“输出目录”、“目标文件名”等,根据需要进行修改。
选中项目名称,点击“项目》属性》C/C++》语言》符合模式”,确认为“否”
六、添加一个空的.h和一个.cpp文件,命名随意,这里命名为ManageCppDll.h,ManageCppDll.cpp。根据原dll的头文件编写这两个文件的内容。
.h文件内容
// ManageCppDll.h
#pragma once
#ifndef MANAGECPPDLL_H
#define MANAGECPPDLL_H
#include "sorter_filter.h"
/*
* 请先初始化配置数据和计算数据,然后再调用计算函数进行计算
*/
public ref class ManageCppDll
{
public:
ManageCppDll();
~ManageCppDll();
///
/// \brief Init_SrcData:初始化导入计算数据,OVector格式
/// \param data_count:in,数据数量
/// \param x_coordinate:in,X轴坐标
/// \param y_coordinate:in,Y轴坐标
/// \param sensor_up:in,上传感器值
/// \param sensor_down:in,下传感器值
///
void Init_SrcData(int data_count, double* x_coordinate, double* y_coordinate, double* sensor_up, double* sensor_down);//初始化导入计算数据
///
/// \brief Init_SrcData_3D:初始化导入3D计算数据,仅针对PCL点云移动最小二乘滤波,OVector<QVector>格式(Filter_PCL_MLS_3D函数)
/// \param lineCount:in,行数量
/// \param columnCountEveryLine:in,每行数据数量
/// \param x_coordinate:in,X轴坐标
/// \param y_coordinate:in,Y轴坐标
/// \param sensor_up:in,上传感器值
/// \param sensor_down:in,下传感器值
///
void Init_SrcData_3D(int lineCount, int* columnCountEveryLine, double* x_coordinate, double* y_coordinate, double* sensor_up, double* sensor_down);//初始化导入3D计算数据
//递归中值平均滤波参数设置
///
/// \brief Init_Median_ConfigData:递归中值平均滤波参数设置
/// \param layernum:滤波层数(一般设置为1)
/// \param windownum:窗口长度
/// \param masknum:掩码长度
///
void Init_Median_ConfigData(int layernum, int* windownum, int* masknum);//递归中值平均滤波参数设置
///
/// \brief Filter_Median:递归中值平均滤波,使用时先调用Init_SrcData导入待滤波数据,再调用Init_Median_ConfigData设置滤波窗口相关,最后调用此函数
/// \param data_count:out,数据数量
/// \param x_coordinate:out,X轴坐标
/// \param y_coordinate:out,Y轴坐标
/// \param sensor_up:out,上传感器值
/// \param sensor_down:out,下传感器值
/// \return
///
bool Filter_Median(int& data_count, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down);//递归中值平均滤波
//calman滤波
///
/// \brief Init_Calman_ConfigData:calman滤波参数设置
/// * 当我们更信任模型估计值时(模型估计基本没有误差),那么应该让Kg小一点,我们应该将R取大一点,Q取小一点
/// * 当我们更信任观测值时(模型估计误差较大),那么应该让Kg大一点,我们应该将R取小一点,Q取大一点
/// \param Q
/// \param R
///
void Init_Calman_ConfigData(double Q, double R);//calman滤波参数设置
///
/// \brief Filter_Calman
/// \param data_count:out,数据数量
/// \param x_coordinate:out,X轴坐标
/// \param y_coordinate:out,Y轴坐标
/// \param sensor_up:out,上传感器值
/// \param sensor_down:out,下传感器值
/// \return
///
bool Filter_Calman(int& data_count, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down);//calman滤波
//PCL-MLS(点云移动最小二乘滤波)相关
///
/// \brief Init_PCL_ConfigData:PCL移动最小二乘滤波参数设置
/// \param MLSSearchRadius:最小二乘滤波半径
///
void Init_PCL_ConfigData(double MLSSearchRadius);//PCL滤波参数设置
///
/// \brief Filter_PCL_MLS
/// \param data_count:out,数据数量
/// \param x_coordinate:out,X轴坐标
/// \param y_coordinate:out,Y轴坐标
/// \param sensor_up:out,上传感器值
/// \param sensor_down:out,下传感器值
/// \return
///
int Filter_PCL_MLS(int& data_count, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down);//PCL移动最小二乘滤波;返回:0正常,<0异常
///
/// \brief Filter_PCL_MLS_3D
/// \param lineCount:out,行数量
/// \param columnCountEveryLine:out,每行数据数量
/// \param x_coordinate:out,X轴坐标
/// \param y_coordinate:out,Y轴坐标
/// \param sensor_up:out,上传感器值
/// \param sensor_down:out,下传感器值
/// \return
///
int Filter_PCL_MLS_3D(int& lineCount, int* &columnCountEveryLine, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down);//PCL移动最小二乘滤波;返回:0正常,<0异常
private:
// 类SORTER_CALCULATION的指针,用来调用类SORTER_CALCULATION的成员函数
SORTER_FILTER* m_pImp;
};
#endif // MANAGECPPDLL_H
.cpp文件内容
// ManageCppDll.cpp
#include "ManageCppDll.h"
#include <vcclr.h>
// 在构造函数中创建类CPerson的对象并在析构函数中将该对象销毁
// 所有的成员函数实现都是通过指针m_pImp调用类CPerson的相应成员函数实现
ManageCppDll::ManageCppDll()
{
m_pImp = new SORTER_FILTER();
}
ManageCppDll::~ManageCppDll()
{
// 在析构函数中删除CPerson对象
delete m_pImp;
}
//初始化导入计算数据
void ManageCppDll::Init_SrcData(int data_count, double* x_coordinate, double* y_coordinate, double* sensor_up, double* sensor_down)
{
return m_pImp->Init_SrcData(data_count, x_coordinate, y_coordinate, sensor_up, sensor_down);
}
//初始化导入3D计算数据
void ManageCppDll::Init_SrcData_3D(int lineCount, int* columnCountEveryLine, double* x_coordinate, double* y_coordinate, double* sensor_up, double* sensor_down)
{
return m_pImp->Init_SrcData_3D(lineCount, columnCountEveryLine, x_coordinate, y_coordinate, sensor_up, sensor_down);
}
//递归中值平均滤波参数设置
void ManageCppDll::Init_Median_ConfigData(int layernum, int* windownum, int* masknum)
{
return m_pImp->Init_Median_ConfigData(layernum, windownum, masknum);
}
//递归中值平均滤波
bool ManageCppDll::Filter_Median(int& data_count, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down)
{
return m_pImp->Filter_Median(data_count, x_coordinate, y_coordinate, sensor_up, sensor_down);
}
//calman滤波参数设置
void ManageCppDll::Init_Calman_ConfigData(double Q, double R)
{
return m_pImp->Init_Calman_ConfigData(Q, R);
}
//calman滤波
bool ManageCppDll::Filter_Calman(int& data_count, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down)
{
return m_pImp->Filter_Calman(data_count, x_coordinate, y_coordinate, sensor_up, sensor_down);
}
//PCL移动最小二乘滤波参数设置
void ManageCppDll::Init_PCL_ConfigData(double MLSSearchRadius)
{
return m_pImp->Init_PCL_ConfigData(MLSSearchRadius);
}
//PCL移动最小二乘滤波
int ManageCppDll::Filter_PCL_MLS(int& data_count, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down)
{
return m_pImp->Filter_PCL_MLS(data_count, x_coordinate, y_coordinate, sensor_up, sensor_down);
}
//PCL移动最小二乘滤波
int ManageCppDll::Filter_PCL_MLS_3D(int& lineCount, int* &columnCountEveryLine, double* &x_coordinate, double* &y_coordinate, double* &sensor_up, double* &sensor_down)
{
return m_pImp->Filter_PCL_MLS_3D(lineCount, columnCountEveryLine, x_coordinate, y_coordinate, sensor_up, sensor_down);
}
最后编译生成dll。
第三步:在C#项目中调用,VS2017建立C#项目验证。
这里创建 Winform项目验证。注意原dll和CLR版的dll都要加入项目。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace cs_call_cpp_dll
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
unsafe private void button1_Click(object sender, EventArgs e)
{
ManageCppDll calculation_dll = new ManageCppDll();
calculation_dll.Init_ConfigData(4, 10, 2, 0, 0);
double[] array_x = { 0, 1, 2, 3, 4 };
double[] array_y = { 0, 1, 2, 3, 4 };
double[] array_up = { 2, 2, 2, 2, 2 };
double[] array_down = { 0, 1, 6, 3, 4 };
int coordinate_count = 5;
fixed (double* coordinate_x = &array_x[0]
, coordinate_y = &array_y[0]
, coordinate_up = &array_up[0]
, coordinate_down = &array_down[0])
{
calculation_dll.Init_CoordinateData(coordinate_count, coordinate_x, coordinate_y, coordinate_up, coordinate_down);
}
double thickness, thickness_center;
calculation_dll.Calculation_THICKNESS(&thickness, &thickness_center);
calculation_dll.Calculation_THICKNESS(&thickness, &thickness_center);
}
}
}
注意,如果用到了指针,需要加入unsafe,并设置“允许不安全代码”。double[]转为double*时用到了fixed,注意fixed的使用方法,这里不再多说。