• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

C#与matlab混合编程以及C#编程

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

把最近所做的C#与MATLAB混合编程,还有界面一些问题进行总结。MATLAB有非常强大的运算功能,C#有很多封装好的库可以用来做界面,所以利用两者的优势来制作一个c#调用MATLAB算法程序的展示界面。

C#与MATLAB混合编程是整个项目中比较难的一部分,主要可以采用两种方式:一是C#调用MATLAB编写的动态链接dll文件,这个方法我最后只用来调用画图程序,因为在利用这个方法来调用比较复杂的MATLAB算法程序时,会出现问题,因为有些比较复杂的库函数,不存在于MATLAB的运行环境MCR中,所以c#在调用含有这些库函数的dll文件时,会报错;另一种方法是直接在C#里调用MATLAB引擎来实现调用MATLAB的.m文件进行运算,我在项目中就是利用第二种方法来调用算法程序,就避免了第一种方法带来的问题。

除了c#与MATLAB混合编程,还有一些编写界面程序的总结。

一.c#调用MATLAB动态链接dll

这种方法,只需要MATLAB的安装环境,不一定要安装MATLAB,我在这里演示使用的是MATLAB2016,MCR安装可以在文件安装的文件目录下找到,如 MATLAB\R2016a\toolbox\compiler\deploy\win64,可以重新安装一下运行环境以保证没有错误。

1.编写MATLAB函数.m文件

首先编写一个函数的.m文件,编写自己的函数方法,如下图

实现的是,两个矩阵的相加减,并输出结果

2.创建制作dll文件

在MATLAB命令窗口输入如下图代码,选择编译dll的语言

出现如下图

我选择的是c++2015语言进行编译,然后打开打包软件,在命令窗口敲入deploytool,如下图所示

选择Libarary Compiler,如下图

myFunction是自定义的dll名称,myFunction_class是自定义的类名,将写好的f.m文件添加到工程内,f.m就是类下的方法,点击右上角的对勾进行打包就可以了。打包完成后文件输出如下

我们需要用的dll文件就在for_redistribution_files_only里

3.在c#中添加引用

如图所示,右键点击引用,选择添加引用,

点击浏览,找到逆所要添加的dll文件,我在这里需要用到的就是自己编写的myFunction.dll和MWArray.dll,MWArray是MATLAB数组矩阵的引用,主要用于MATLAB和c#之间的参数传递,文件的位置就在MATLAB的安装目录下,如C:\ProgramFiles\MATLAB\R2016a\toolbox\dotnetbuilder\bin\win64\v2.0\MWArray.dll

4.c#调用MATLAB编写的dll中的函数方法

如上图中所示,MWNumericArray是MWNArray和c#语言数据的中间转换类,是矩阵的转换类,还有如MWCharray是字符串的转换类,都包含在引用MWArray.dll中,还有具体自己编写的函数的使用可以在定义中查看

我在上面用的是定义中第七行的方法

注意事项:c#调用MATLAB的dll文件,主要问题出现在各种版本和编译上的问题,还有一个比较常见的问题,如下图所示

这个可能是编译平台的问题,我用的是vs2015和MATLAB2016,都是64位的,而且添加的MATLAB引用都是64位的,所以将平台改成64位就可以了,在配置管理器中,如下图

新添加64位的编译平台即可解决上述问题。

二.c#调用MATLAB dll画图将figure嵌入winform窗口中

需要用到Windows API来设置窗体,用FindWindow查找图像窗体Figure 1的句柄。使用SetParent设置Figure 1父窗体为Winform的控件Panel,这样就把figure放进Winform里了,之所以放到panel控件里,而不是作为子窗体在winform里,是为了把它当做

Winform的一个控件,便于布局。再使用MoveWindow移动到合适位置,setWindowLong去掉标题,不能通过边框改变大小,在Winform窗体SizeChanged事件里用MoveWindow改变Figure的大小,就能使figure的尺寸和窗体同步改变。

1.系统接口user32.dll

Windows API的代码,用于c#抓取MATLAB figure图像

 User32.dll是Windows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息。里面的函数都可以在网上找到很详尽的说明,这次界面的编写中用到的user32.dll里的MoveWindow涉及到了嵌图的效果,所以在c#界面部分的参数设置需要注意,将函数进行一下说明:

MoveWindow

函数功能:该函数改变指定窗口的位置和尺寸。对于顶层窗口,位置和尺寸是相对于屏幕的左上角的:对于子窗口,位置和尺寸是相对于父窗口客户区的左上角坐标的。

函数原型:BOOL MoveWindowHWND hWnd.int x.int y,int nWidth,int nHeight,BOOL BRePaint

参数:

hWnd:窗口句柄。

x:指定窗口的新位置的左边界。

y:指定窗口的新位置的顶部边界。

nWidth:指定窗口的新的宽度。

nHaight:指定窗口的新的高度。

bRepaint: 确定窗口是否被刷新。如果该参数为TRUE,窗口接收一个WM_PAINT消息;如果参数为FALSE,不发生任何刷新动作。它适用于客户区,非客户区(包括标题栏和滚动条),及由于移动子窗口而露出的父窗口的区域。如果参数为FALSE,应用程序就必须明确地使窗口无效或重画该窗口和需要刷新的父窗口。

返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。

2.定义全局变量,窗体载入事件

这当中主要用到了多线程的处理,还有c#引用在工作线程中更新界面线程的显示

3.线程执行的方法

每隔50ms查找一下figure窗体,找到嵌入到winform的panel控件里

注意当中使用引用更新界面的方式,还有抓图即FindWindow方法需要一定的时间,找到MATLAB的figure的句柄需要一定的时间,所以必须使用Thread.sleep()让线程休眠一段时间让操作系统找到MATLAB figure句柄。

4.其他

Radiobutton:切换不同画图效果

 

注意Form1_SizeChanged函数需要自己添加,下图是InitializeComponent()方法(即初始化所有组件,这部分不需要自己写,在你拖动控件时就会自动生成)下添加的函数声明,然后需要自己在编写部分添加定义

定义函数部分,如下图所示,该部分主要是使嵌图随窗体大小移动而移动

三.c#调用MATLAB引擎

该方法相当于直接调用MATLAB的计算引擎在后台进行运算执行,本人的开发环境是Matlab2016a和VS2015,由于是调用matlab引擎,所以必须安装Matlab。

 

1.添加com引用

在工程中添加引用,选择com,添加Matlab Application Type Library,如下图

MATLAB的版本可能不同。添加引用后,引用目录下多了一个MLApp文件

2.引用代码

如下图所示,下图中的代码其实就是打开后台MATLAB计算引擎,注意需要设置MATLAB的当前路径,否则会出现错误

如果在编程是采用MLAppClass的话还需要将文件的属性Embed Interop Types的true改为FALSE,不然会报错,如下图所示

Matlab.Execute()括号内的执行内容,其实相当于是MATLAB程序的命令窗口执行内容,因此我们可以直接在里面执行MATLAB语句,或者是自己的.m文件

3.运行自己的.m文件

首先要清除MATLAB中的变量,以防影响到要运行的MATLAB程序,需要注意,传递变量时只能传标量,不能传矩阵和数组,而且有字符串变量时,在c#里面要加单引号。

.m文件,input相当于输入的地址,里面存了x,y变量

需要将m文件放在工作目录下,然后在c#里调用的MATLAB就可以运行该程序了,如下图所示

4.注意事项:

  1. 一定记得设置MATLAB工作目录,不一定要在界面目录下,可以设置一个相对目录,并将需要运行的m文件放在该目录下;
  2. matlab.Execute(command),command相当于在命令窗口内容,“clear all”就是保证现在MATLAB内的变量不会影响之后的操作,但是要记得利用这个方法进行c#与MATLAB传参时,只能用标量,不能涉及到数组和矩阵,否则会报错;
  3. 记得在向MATLAB传递字符串时,记得加单引号。

 

 

 

四.c#编程部分

1.多线程的使用

除了界面部分的操作,还有比较耗时和耗内存的算法运算部分,所以我就多开了一个工作线程去单独做算法运算,这样能减轻界面部分的负担,并且两部分可以并发执行。主要代码如下

由于工作线程不能直接访问界面线程的控件,如果需要在工作线程中去更新界面线程(防止界面“假死”,即在运行工作线程时,界面没有任何输入输出,像卡住了一样),则需要用到引用(delegate)方法去在工作线程更新界面线程的内容。主要方法在代码中都有注释,控件更新的方法有Invoke和beginInvoke两种方法,区别在于,Invoke会使得工作线程等待执行完成,避免了竞争条件(如其他线程直接操作界面线程所属控件),避免了不可预料的结果。BeginInvoke不会等待委托方法的执行结束,不会使工作线程等待执行完毕,不会造成工作线程阻塞。

多线程的处理还需要注意资源抢占问题,不同线程同时访问同一资源时,可能会导致线程崩溃,这个需要特别注意,尽量不要有过多的共享资源,并且确保不会在同一时间访问。

2.输入输出流的使用

在这次界面项目的过程中,主要是用到了二进制文件的输入输出方法,如下图所

无论是二进制流还是文本流都必须建立在文件流的基础上。

3.其他控件的使用

这次主要用到了gridControl、button、textbox、layoutcontrol等一些其他控件,由于控件的使用太过细节,具体需要用到可以到网上去找,一般都会有。

4.效率问题

要注意代码局部性,这样可以节省时间和空间资源;

还要注意尽量不要使用二维数组,这样会导致访问速度的降低。

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
delphiwebbrowser执行js---转发布时间:2022-07-18
下一篇:
delphiMD5加密,BASE64加解密发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap