在VS2022中C++封装动态库在C#调用的混合编程

前言:

        今天,小何同学向我发起了一个挑战,他需要在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用的比较习惯,哈哈哈。

### 回答1: C语言创建动态链接库供C程序使用的步骤如下: 1. 写好要封装的C语言函数或者模块的代码,并将其保存为一个或多个C文件。 2. 编写一个头文件,将要使用的函数或者模块的声明放在该头文件。 3. 打开终端或者命令提示符,使用GCC编译器将C文件编译为目标文件。命令如下: `gcc -c 文件名.c -o 输出的目标文件名.o` 这样会将C文件编译为目标文件,目标文件是二进制文件,用来存储编译后的机器码。 4. 将编译好的目标文件打包成动态链接库。动态链接库的命名规则为"libxxx.so",其xxx为动态链接库的名字。命令如下: `gcc -shared 目标文件1.o 目标文件2.o -o libxxx.so` 这样会将目标文件打包成动态链接库。动态链接库是一个二进制文件,包含了函数或者模块的机器码。 5. 将生成的动态链接库文件放置到C程序的运行路径下,使用时可通过指定库的名字链接到该库。 6. 在C程序引入头文件,并调用动态链接库的函数或者模块。 以上是使用GCC编译器创建动态链接库供C程序使用的基本步骤。通过这种方式封装功能,可以实现代码的模块化和重用,提高程序的可维护性和可扩展性。 ### 回答2: 使用者使用动态链接库的主要目的是为了方便地重复使用某些功能代码。在C语言,可以使用如下步骤来创建并使用动态链接库。 首先,创建动态链接库的源代码文件,该文件包含了要提供的功能代码。可以使用C语言编写这个文件,其可能包含一些函数和全局变量等。 接下来,使用编译器将源代码文件编译成目标文件。在这个过程,需要使用适当的编译选项来指定生成动态链接库而不是可执行文件。比如,在GCC编译器,可以使用"-shared"选项来生成动态链接库。 然后,使用编译器将目标文件链接成动态链接库文件。这一步会生成扩展名为".so"(在Linux系统上)或".dll"(在Windows系统上)的文件。在该步骤,需要提供一些额外的链接选项,以确保正确地生成动态链接库。 最后,使用者可以在自己的C语言程序引用和使用动态链接库。可以通过在程序包含相应的头文件并使用相关的函数和变量来调用动态链接库的功能。 需要注意的是,使用者在编译和链接自己的程序时,需要指定动态链接库的位置和名称,以便在运行时正确加载和使用动态链接库。这可以通过编译选项和链接选项来实现。 总之,通过以上步骤,可以创建一个供C语言程序使用的动态链接库,并在程序使用其的功能代码。这样可以提高代码的复用性和可维护性,同时也便于程序的调试和更新。 ### 回答3: 编程语言C的动态链接库(Dynamic Link Library,简称DLL)是一个可重用的代码和数据集合,可以在不同的程序调用。以下是创建动态链接库供C使用的基本步骤: 1. 编写C代码:首先,编写包含所需功能的C代码。将这些代码组织成一个或多个函数,这些函数可以是库的接口。 2. 创建头文件:创建一个头文件(.h文件),其包含库的函数声明和必要的常量和类型定义。这个头文件将作为客户端程序与动态链接库之间的接口。 3. 编译动态链接库:使用C编译器(例如gcc)将C代码编译成目标文件,使用以下命令生成位置无关的目标文件: ``` gcc -c -fPIC library.c -o library.o ``` 其,`-c`选项表示只编译不链接,`-fPIC`选项表示生成位置无关的代码,`library.c`是你的源代码文件名,`library.o`是生成的目标文件名。 4. 创建动态链接库:使用以下命令将目标文件创建为动态链接库: ``` gcc -shared -o liblibrary.so library.o ``` 其,`-shared`选项表示生成动态链接库,`-o liblibrary.so`指定输出的库文件名为`liblibrary.so`。 5. 安装动态链接库:将生成的动态链接库文件(`liblibrary.so`)复制到系统的默认库目录(例如`/usr/lib`)。使用以下命令: ``` sudo cp liblibrary.so /usr/lib ``` 注意:根据操作系统和环境设置,可能需要提供管理员权限。 6. 使用动态链接库:在你的C程序,通过包含头文件(步骤2)并使用函数声明来调用动态链接库的函数。编译时需要链接动态链接库,可以使用以下命令: ``` gcc client.c -o client -llibrary ``` 其,`client.c`是你的客户端程序源代码文件名,`-llibrary`表示链接名为`liblibrary.so`的动态链接库。 这样,你就成功地创建了一个动态链接库,供其他C程序调用。在客户端程序,只需要包含头文件并链接动态链接库,就能使用其定义的函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值