前言:
今天,小何同学向我发起了一个挑战,他需要在C#下用PCL点云处理的一些方法;但是经过我慎重思考发现一个大问题,一般C#用来制作界面,但是像一些算法都是C++书写的,这两种语言中间的鸿沟仿佛有点难跨越,所以开启了这篇文章的书写,方法供同样和我奋斗于开发一些的难兄难弟参考使用。当然,我也推荐一篇优秀博客(分为了三篇,可以进该博主的主页查看)混合编程之一:c#调用c++pcl库,生成c++动态链接库文件_.dll工程调用pcl c++-CSDN博客
正文:
一、书写c++动态库
1、创建项目
说明:这个项目封装一个自己的pcl(点云库)动态库,也就是对第三方库的二次封装,我这里示例就是完成一个动态库函数来实现读取点云并返回点云个数,在C#中调用实现库函数。
2、添加源码和头文件
3、配置编译环境
说明:由于我们需要用到c++的一些开源项目,并把它们封装好供我们自己的c#使用,所以我们这里必须配置一些第三方库,如果你不需要用到第三方项目的库的话那就无所谓了;我这里示例是用到PCL(点云库)来做说明。部分内容可以参考我之前的博客在VS2022中配置PCL、VTK环境,实现点云的C++处理和软件打包_vs2022配置vtk-CSDN博客的第二点。
4、代码书写
说明:主要就是对上面增加的cpp和h文件的实现,我这里用pcl的io模块读取点云并返回点云个数来示例。(我是xs.cpp和xs.h文件的实现)
xs.h:
#ifndef _ENCRYPTBASE_H
#define _ENCRYPTBASE_H
#ifdef __cplusplus
extern "C" //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
{
#endif
#ifdef DLL_EXPORTS
#define DLL_EXPORTS __declspec(dllexport) //__declspec(dllexport)将一个函数声名为导出函数,
//就是说这个函数要被其他程序调用,即作为DLL的一个对外函数接口。
#else
#define DLL_EXPORTS __declspec(dllimport)
#endif
DLL_EXPORTS int getPointCloudNum(const char* fileName); //DLL_EXPORTS 就是上面的宏定义来声明可以被外部调用的函数
#ifdef __cplusplus
}
#endif
#endif
xs.cpp:
//这两个是模板头文件
#include "pch.h"
#include "framework.h"
//cpp头文件
#include "xs.h"
//PCL
#include <pcl/io/pcd_io.h>
/**********************************************
* @projectName getPointCloudNum
* @brief 得到pcd点云文件的点数
* @param fileName: 传入点云文件的位置和文件名
* @return 成功返回读到的点云个数,失败返回-1
* @author xs
* @date 2024-05-24
**********************************************/
int getPointCloudNum(const char* fileName) //最好用char*类型 不用string string C#没成功用起来
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile(fileName, *cloud) < 0)
return -1;
return cloud->size();
}
出现error C2589: “(”: “::”右边的非法标记解决办法:项目属性-> C / C++ -> 预处理器 -> 预处理器定义(此处添加预定义编译开关 NOMINMAX)
参考:error C2589: “(”: “::”右边的非法标记;error C2059: 语法错误 : “::”_qt中c2059: 语法错误:“const”-CSDN博客
5、编译移植:
编译(Ctrl+B)后会在你的项目位置生成对应的动态库,我这里就是F:\C++\xsDll\x64\Debug,xsDll是我的项目,x64/Debug是我的编译环境,我这里生成的就是xsDll.dll。把这个备份好,就可以供C#使用啦。
注意:编译如有问题,就在网上找方法一步步解决把,但是这个步骤应该是没有问题了。
二、创建C#动态库中间层
1、创建项目
说明:其实际上这个项目就是一个中间层,帮助我们去调用C++的动态库,让以后的C#项目调用方便,其实际上就是用这个动态库去调用另一个动态库。
2、代码书写
xs.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace xsCsharpDll
{
public class xs //我自定义了类名
{
[DllImport("xsDll.dll", EntryPoint = "getPointCloudNum", CharSet = CharSet.Auto)] //设置调用动态库路径
//(就是用该库时去调用xsDll库,所以后面用时需要将该库和xsDll放在一起)和入口函数
public static extern int getPointCloudNum([MarshalAs(UnmanagedType.LPStr)] string fileName); //把char*转化为string
}
}
3、编译移植
这里编译成功后就会生成对应的中间层动态库,我这里是xsCsharpDll.dll,位于F:\C#\xsCsharpDll\xsCsharpDll\bin\x64\Debug(我的项目路径)。
到此,你就有了两个动态库xsDll.dll(C++动态库)和xsCsharpDll.dll(C#中间层调用动态库),那么保存好,一起准备移植到你的C#项目中去。
三、创建你的C#项目
注意:项目创建好后把你的两个动态库先移植到项目中去。
1、创建项目
2、配置及编译
修改完成后,编译一下生成项目目录,将两个动态库移植到此处。
添加引用
完成后,就是正常的C#第三方库使用了哦。
3、代码书写及使用
Form1.cs
using System;
using System.Windows.Forms;
using xsCsharpDll; //添加引用完成后,就可以使用该模组
namespace xsCsharp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
xs x = new xs();//定义一个xs类对象 等价于 xsCsharpDll.xs x=new xsCsharpDll.xs();
int pointCloudNum = xs.getPointCloudNum("F:/weiPc/real.pcd");
MessageBox.Show("点云有: "+pointCloudNum.ToString()+" 个点");
}
}
}
编译,运行。
完美,实现...
总结:
本来可以直接C++算法加界面来实现我们想要的东西,可是总有需求想给他撮合在一起,呜呜呜...不过,经此一役我们的任督二脉又通了三寸,这不就可以起飞了吗,加油...
哎,还是Qt用的比较习惯,哈哈哈。