您的位置:首页 > 编程语言 > MATLAB

一个基于COM组件的Matlab与C#混合编程实例

2017-12-21 16:23 453 查看
把matlab与其他语言混合使用是一种挺实用的技巧,在前期使用matlab进行算法设计和计算仿真,而在需要编写程序原型的时候把matlab算法模块嵌入到C++或C#的程序里,一方面省的再用C++或C#重写代码,另一方面也可以直接调用matlab里的一些工具箱。

至于混合编程的手段也是多种多样的,最简单的可以调用matlab引擎或将matlab文件打包成动态链接库,不过官方比较推荐的是打包成COM组件,这种方法很适合于算法复杂,涉及很多matlab文件的情况。matlab提供了打包COM的编译器,使用起来也是很方便的。我在图书馆找的相关书籍都是讲matlab和C++混合编程的,说实话用C++写真的挺费劲的,最麻烦的是输入输出是矩阵序列的情况,需要写很多转换和处理的代码;不过如果用C#的话就方便很多了,根本不用管什么类型,.NET框架帮我们处理了。下面是我参考其他资料后自己写的一个matlab与C#混合的程序实例。

1 matlab程序编写

先写个matlab程序吧,我用的matlab比较老,2010b,程序的话就写个简单的绘制正弦波形函数,即绘制一个周期的y=Asin(ωt+θ) 波形,A 、ω 和θ 是三个输入参数,具体代码如下:

%  Paras[1]:Amp
%  Paras[2]:Omega
%  Paras[3]:Theta
function []=DrawSin(Paras)
Amp=Paras(1);
Omega=Paras(2);
Theta=Paras(3);
t=0:(2*pi/Omega/1000):2*pi/Omega;
value=Amp*sin(Omega*t+Theta);
plot(t,value);


函数的输入我用一个矩阵变量来存放三个参数,这是为了后面在C#程序里调用时展示特意这么做的。

2 生成COM组件

在matlab的”File”菜单里新建一个Deployment project:



在Deployment project对话框中将target选择为Generic COM Component:



点击”Add class”新建一个类,之后再点击“Add files”将写好的M文件添加进去:





然后就可以点击上面的“Build”图标进行编译,旁边那个图标是打包,如果需要把COM组件用在其他没有按照matlab的机器上的话就需要打包了,打包时会将matlab的编译运行时(MCR)包含进去并生成一个安装包,在别的机器上进行安装,不过如果只是在本机测试的话就没必要这么麻烦了。Build的时候要注意这个项目文件的目录不要有中文,不然会出错的。



编译完成后在项目目录中的distrib中已经生成了一个dll文件(这里的名字是DrawSinCOM_1_0.dll),这就是COM组件的生成结果。COM组件使用前是需要在本机上注册的,因为现在是在本机测试,生成过程中已经自动注册好了,所以可以直接调用这个COM对象了。

3 C#程序调用

新建一个winform程序:



把DrawSinCOM_1_0.dll复制到debug目录下,在winform程序中添加DrawSinCOM_1_0.dll的引用,在后台就可以初始化COM对象并调用其方法了,相关代码如下:

private DrawSinCOM.CDrawSin component;//定义COM组件对象
public Form1()
{
InitializeComponent();
this.textBox1.Text = "1";
this.textBox2.Text = "10";
this.textBox3.Text = "0";
component = new CDrawSin();//初始化
}

private void button1_Click(object sender, EventArgs e)
{
double[] input = new double[3];
input[0] = Convert.ToDouble(this.textBox1.Text);//读取输入
input[1] = Convert.ToDouble(this.textBox2.Text);
input[2] = Convert.ToDouble(this.textBox3.Text);
component.DrawSin(input);//调用方法
}


如果我们查看DrawSin的函数定义,可以看到其输入参数的类型是object,因此我们可以直接将double[]对象直接传进去,不需要管它内部具体怎么转换。运行结果:



4 绑定matlab窗口

虽然调用成功了,不过波形窗口是弹出的,这样还是很别扭的,因此需要把这个波形窗口嵌入到C#窗口中。要实现这种“窗口绑架”的效果似乎必须要用Win32 API实现,需要在C#代码中导入user32.dll中的函数。要掌握Win32 API中操作窗口相关的函数还是很费劲的,我在网络上搜罗了这里要用到的主要的几个函数:

FindWindow:查找并返回指定的窗口句柄

SetParent:设定窗口的父窗口

MoveWindow:移动窗口并设定大小

GetWindowLong:获取窗口相关参数

SetWindowLong:设定窗口参数

综合使用这些函数就可以绑定窗口了,相关代码如下:

using System.Runtime.InteropServices;//添加命名空间
...
...
//导入Win32函数
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SetParent")]
public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "MoveWindow")]
public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);
private const int GWL_STYLE = (-16);
private const long WS_CAPTION = 0xC00000;
private const long WS_MAXIMIZE = 0x01000000L;
private const long WS_THICKFRAME = 0x00040000L;
private const int SW_HIDE = 0;
private const int SW_SHOW = 0;
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern long SetWindowLong(IntPtr hwnd, int nIndex, long newLong);
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
public static extern long GetWindowLong(IntPtr hWnd, int nlndex);
...
...
private void button1_Click(object sender, EventArgs e)
{
double[] input = new double[3];
input[0] = Convert.ToDouble(this.textBox1.Text);
input[1] = Convert.ToDouble(this.textBox2.Text);
input[2] = Convert.ToDouble(this.textBox3.Text);
component.DrawSin(input);//调用方法

System.Threading.Thread.Sleep(100);
IntPtr handle = FindWindow(null, "figure 1");//获取波形窗口句柄
SetParent(handle, this.panel1.Handle);//把波形窗口的父窗口设为主窗口中的面板
long temp = GetWindowLong(handle, GWL_STYLE);//获取窗口参数
SetWindowLong(handle, GWL_STYLE, temp & (~WS_CAPTION) | WS_MAXIMIZE);//这里是为了去掉窗口边框
MoveWindow(handle, 0, 0, this.panel1.Width, this.panel1.Height, true);//移动、调整窗口
}


注意到代码中用Sleep进行了延时,如果不延时,FindWindow方法很可能会返回零从而绑定失败(也许是因为代码执行太快了)。此外我碰到过GetWindowLong调用失败的情况,好像改了项目的.NET版本后又行了。最终的效果:



窗口嵌入了主窗口,还可以方便地进行缩放、导出等功能。

总结

利用COM技术可以轻松地构建matlab和C#混合编程应用,我个人觉得这种方法很适合在快速搭建软件原型的时候使用,因为省去代码重构的时间了,不过如果是做产品或是对运行速度要求较高的场合,这种方式显然不太合适了。此外,我在试验的时候曾经试图加入多核计算的元素,因为matlab和C#都有并行计算库,不过后来发现混合编程时这是行不通的,我搜到了一个matlab的官方回复,大意是matlab的模块打包中并不支持并行模块,这个回复是很早之前的了,不知道最新版的matlab行不行。

参考文献

1、《精通Matlabb与C/C++混合程序设计》

2、http://www.cnblogs.com/dekevin/p/3583344.html?utm_source=tuicool

3、https://www.cnblogs.com/eniac12/p/4390845.html

4、http://blog.sciencenet.cn/blog-720524-785934.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  matlab c#