C#调用C++的dll及MATLAB的dll的方法
Posted fantasia_fang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#调用C++的dll及MATLAB的dll的方法相关的知识,希望对你有一定的参考价值。
为了记录踩坑的过程,避免以后再踩坑,居然专门开通了这么专业的技术博客,正好督促自己以后好好研究技术。
最近需要做一个界面系统来包装一下之前做的人脸属性识别的模型,希望用户随机选取一张图像(后面会实现摄像头拍现场图像),系统自动给出该图像中所包含的人脸属性(有没有戴眼镜,有没有戴帽子之类的)。其中人脸属性预测之前需要进行人脸识别以及人脸对齐等操作,人脸属性识别是由C++写的,人脸对齐是由MATLAB写的,考虑到界面的友好性以及开发的难易性,最终选了C#作为开发语言。(不要问我为啥不用Java,因为我没装eclipse,没配Java环境,并且C++和C#都是用vs编译,俺个人觉得这样方便些,其实主要是受某位师兄鼓动。。。他说Java也不会比C#容易到哪去)。然后说说为什么要用C#调用C++的dll和MATLAB的dll,直接混编不行么。其实也行,就是更麻烦。。。代码易读性非常差,并且不好调试,相信我,我也试过,但最终放弃了。。。dll相对来说简易许多,虽然封装过程纠结了好几天,但是使用起来像是黑盒子,你只要按照给定的参数类型输入,就可以得到理想输出,一两句话搞定,也基本不需要配置环境啥的,如果有同学遇到和我类似的需求,可以考虑一下我的做法。
下面开始详细详细再详细的记录一下这几天走过的各种坑,希望在帮助自己记录的同时也能帮到一些人。
本机环境:win7 64位 + vs2013 + MATLAB2015
请大家看下面的调用过程之前一定看好你的本机环境是不是和我的一样,如果不一样,那我成功的方式你走一遍不一定成功。我当初Google的时候没一个博客或者资料和我的环境一样,所以都是摸着石头过河,慢慢瞎鼓捣吧,不要放弃,总能搞出来的O(∩_∩)O。
- 如何用C#调用C++的dll
托管代码 (managed code) :由公共语言运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。
非托管代码 (Unmanaged Code) :在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务;它与托管代码不同,后者从公共语言运行库中获得这些服务。非托管代码的英文名是Unmanaged Code ,它是在公共语言运行库环境的外部,由操作系统直接执行的代码。
好啦,现在具体讲一下怎么做:
假设我有一个人脸检测加5点定位的函数,希望可以在C#里使用这个函数。函数的输入是两个字符串,分别表示输入的人脸图像路径以及想要保存的5点定位路径(一个txt里面存着5点的坐标,用于后面人脸对齐)。首先,我们要把这函数包装成一个类里的成员函数,以便后面再次包装为托管类。我们称这个包装函数的类为native类,为了区别后面的托管类clr类。Native写好之后大概是长成这样的:
图片中的#pragma once是为了防止重复定义,后面的一些include文件你需要什么就写什么。这里有个地方需要注意,就是C#和C++的数据类型是不太一样的。C++里的string参数包装好之后在C#那里显示的并不是string,而是C#里的一种数据格式(忘记截图了,反正看起来还挺复杂的),所以并不推荐大家用string作为输入参数,用char*吧,这样在C#那里就会显示为你需要输入sbyte*的参数,这个类型的可以很容易将C#里的string转换过来,后面会有例子。
好了,有了h文件,根据需求实现一下cpp文件:
现在native类已经准备好了,可以进行托管类的包装了。为了实现托管类,我们需要在在项目属性那里设置一下,不然编译器识别不出来托管类的标志。
主要是改一下画红线的两处,这样生成解决方案后,不会像普通的项目一样生成一个exe文件,而是生成一个dll文件,也就是后面我们在C#里调用的dll文件。第二个画红线的地方可以帮助编译器知道我们要做一个托管类。这样设置好项目属性后,就可以写托管类了。(在这一步设置属性的时候,我发现一个特别奇怪的事情,就是有时我一把这个属性设置好保存之后,原来高亮提示的关键字全都没有高亮提示了,好像是编译器识别不出来了一样,这个我也不知道怎么回事,有时高亮消失,有时就不会。如果有人碰到和我一样的问题,我只能说,亲,你多试几次吧。。。逮着哪次高亮没消失就赶紧接着写,机不可失啊!其实貌似不提示高亮也不影响生成dll文件或者后续调用,不用没有高亮,写代码就木有提示啊!对或不对以及成员函数神马的您就都自己写吧。。。所以还是有提示比较好一点)。
好嘞,下面是最重要的写托管类啦!其实很简单哦!这一步在网上资料也比较多,如果我的不适合你的需求,你可以Google别人的呀!
这里的public可以让这个托管类在C#里被看见,ref表示这是一个托管类。Public成员函数里写上你想要的函数(名字最好不一样),然后private那里一定要有一个native类的对象,这样才可以通过托管类调用非托管类的函数呀!千万别忘了!
然后,实现一下cpp文件:
两处划红线的地方很重要,第一个红线表明在托管类的构造函数里实例化一个native类的对象,一定要实例化,不然后面用不了。第二个红线表明在托管类的函数里调用非托管类的函数。
如此一来,就可以点击“生成解决方案”来生成一个dll啦。比如我的配置管理器里写的是release, x64,那生成的dll文件就在工程目录下的x64目录下的release目录下。
这里画红线的与你工程名字相同的dll就是你后面在C#里需要用到的dll啦!如果你的C++项目还会额外用到什么dll,lib之类的,在后面C#使用时也要一起拷贝过去,不然后提示dll依赖缺失之类的问题。
好啦!准备好dll,我们就可以转眼去看看C#如何使用这个dll。
首先,你要把刚刚生成好的dll以及这个dll依赖的其他dll一起拷贝到C#的工程目录下。比如我的C#配置管理器里选的是release,Any Cpu,那应该是把这些dll一起拷贝到工程目录下的bin目录下的release目录下。像这样:
当然,我所说的C++的dll所依赖的dll不是指的那些opencv配置环境时的dll,而是你在函数里需要用到的外部dll,比如我的函数用了人脸识别dll。像是配置opencv添加在项目附加文件那里的dll以及opencv的头文件好的,都会随着你生成dll后一起包装起来,所以是不是很神奇!是不是很省事!如果你连外部的dll都没用到,那你直接把C++生成的dll拷贝过去就可以直接用啦!
除了把dll拷贝到上面说的C#的文件夹里,还要将其放到C#当前工程目录下,然后在C#项目的引用那里选中这个dll将其引用到C#项目里就可以使用啦!
接下来说说怎么在C#代码里使用C++的dll。像我这边需要给C++的函数传string的参数(当然,为了方便数据类型的转换,我把C++函数里的string换位了char*,前面已经提到了),然后我需要把两种语言的数据类型统一起来,C++的char*对应的是C#里的sbyte*,因此我先讲C#里的string转为sbyte*,像这样:
在这里,如果想使用Marshal,C#编译器会提示你这是一段不安全的代码,如何避免错误提示呢,我们需要在这段代码的外面加上unsafe标志,告诉编译器我们知道不安全但我们就是要用!
如果此时它还提示你不安全,那么就可以到项目属性那里设置一下:
在项目属性的“生成”那里会有“允许不安全代码”的选项,勾上就可以啦!另外,换红线的地方,提示一下各位童鞋。如果你的C++的dll编译生成的时候是选的x64配置环境,那么在这里一定一定要把“首选32位”这个选项去掉,不然运行C#就会报错。好了,下面讲一下如何调用。
简单吧!只有三行,第一行创建一个C++的dll封装的托管类的对象(你托管类叫啥这里就写啥,不是非托管类的名字哦),然后第二行就是调用函数,第三行就是把sbyte*再转会string。(不过int参数好像可以直接传,不需要改类型,其他类型的参数我就不清楚了)。
至此,C#调用C++的dll就大功告成啦!!!鼓掌!!!虽说现在说起来好像不难,但是自己也是鼓捣了两天,碰到各种问题,踩了各种坑才成功,如果大家按照我的方法没成功,一是可能环境和我不一样,二嘛,可能是你人品不好,哈哈,开玩笑。网上的资料千千万,不一定会有你需要的,参考着来吧,自己慢慢试慢慢研究总会搞出来的。
本来想一篇写好C++和MATLAB的调用。不过由于废话多,篇幅过长,MATLAB的调用放到下一篇讲。
以上是关于C#调用C++的dll及MATLAB的dll的方法的主要内容,如果未能解决你的问题,请参考以下文章
从 C# 调用 C++ dll 中的方法,没有任何信息,只有头文件