基于COM STA线程模型的Delphi与Matlab混合编程研究
2013-12-29李丰
摘要:针对传统的基于COM的Matlab与Delphi混合编程技术,界面容易出现冻结现象,详细讨论了其出现的原因,提出了一种基于COM STA线程模型的改进的混合编程方法,并以一信号包络提取为例给出了改进方法的具体实现。
关键词:Delphi;Matlab;混合编程;COM;单线程套间
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2013)12-2912-04
Delphi是一种基于Object Pascal语言的快速可视化开发工具,对于程序开发人员来讲,使用Delphi开发应用软件,将会极大地提高编程效率,但若直接利用Delphi开发一些复杂算法特别是科学计算,其效率并不会很高。Matlab作为一种高性能数值计算软件,提供了强大的数值分析、矩阵运算和图形显示等功能,被广泛应用于工程计算、数值分析、系统仿真等领域,但由于Matlab是一种解释型编程语言,执行效率低,程序界面开发能力差。如果将Delphi与Matlab相结合,将会克服彼此缺点,高效地完成编程任务。
Delphi和Matlab混合编程的常用方法有多种[1-3],其中基于COM组件技术的编程方法得到了广泛关注[4-5]。这种方法是利用Matlab提供的COM Builder工具将编写好的M文件编译生成DLL库,之后供Delphi调用,该方法可以脱离Matlab环境,因而极大的方便了应用程序的发布。但其也有缺点,其中之一是在应用程序中调用DLL库时容易出现用户界面冻结,使用户以为程序失去响应。为此,该文对此进行研究,提出一种基于COM STA线程模型的改进方法。
1 相关技术
1.1 COM组件
COM即组件对象模型(Componet Object Model, COM),是一种以组件为发布单元的对象模型,该模型使各应用程序组件可以用一种统一的方式进行通讯。
在COM标准中,COM对象被完美地封装起来,客户无法访问对象的实现细节,提供给用户的唯一访问途径是通过COM接口来实现。COM接口有两方面的含义:其一,它是一组可供调用的函数,客户可以让该对象做某些事情;其二,接口是组件及其客户程序之间的协议。使用COM编程实现了与编程语言无关的软件重用。
1.2 基于COM组件实现Delphi与Matlab混合编程
传统的基于COM组件实现的Delphi与Matlab混合编程[4-5],其第一步是编写M文件,之后在Matlab环境下输入命令comtool,启动Matlab COM Builder,设置好相关参数并添加好M文件后,就可以编译生成相应的COM组件,并将该COM组件注册到Windows的注册表中,最后在Delphi中直接调用该COM组件,实现混合编程。但这种方法基于的是单线程技术,所开发的应用程序在运行过程中容易出现界面冻结。原因分析如下:
假设有一个利用Matlab实现的COM服务器,MatrixServer,该服务器提供有一种方法Mul,功能是实现矩阵相乘运算。
IMatrix = interface
procedure Mul;
end;
假设该服务器同通常的应用程序一样,采用的是单线程技术。考虑3个客户(C1,C2,C3),每一客户都创建一个Matrix实例MatrixServer。这3个客户都si9MTIjjnM8zi+NTGBUJhg==同时调用IMatrix.Mul(假定第一个是C1,随后是C2,接着C3)。由于MatrixServer是单线程的,其只能按顺序一个一个执行命令,因此首先处理C1,然后C2,最后处理C3。
由于利用Matlab实现的大多是一些复杂的科学计算工作,其执行过程将花费较多的时间。假设执行完毕C1和C2各需要一分钟时间,那么最后完成C3工作将至少需要3分钟时间。这是因为C3必须等待C1和C2完成之后才能进行。这样就出现界面冻结。
1.3 COM线程模型
1.3.1 套间
“套间”是指存放一组对象的地方,该组对象共享相同的线程行为和要求。COM线程模型包括单线程套间STA、多线程套间MTA和中性套间(Neutral Apartment)。一个进程可以有多个STA,但最多只能有一个MTA。
1.3.2 单线程套间STA
STA是COM线程模型中一个重要的概念。在一个STA中,有且只有一个线程,驻留在该STA中的所有对象都服务于该线程。由于STA中仅有一个线程,因而被默认序列化,无需考虑多线程编程中的同步问题[7],如图1所示。
由于一个STA包含一个线程,多个STA包含多个线程,因而可以利用创建多个STA在一个服务中实现多线程。
假设在C1、C2、C3调用IMatrix.Mul时分别创建一个线程实例,这样C1、C2、C3将分别工作在服务线程T1、T2、T3上。这样C1、C2、C3三个客户可以同时调用IMatrix.Mul,而无需等待,从而可以消除界面冻结。图2所示为利用STA实现C1、C2、C3同时调用IMatrix.Mul。
在使用STA时需要注意的是,由于STA套间中所有的COM组件代码都运行于主STA(第一个调用CoInitialize函数的线程),如果主线程没有调用CoInitialize,那么第一个调用CoInitialize的工作线程就会成为主STA,而工作线程随时可能中止,这种情况下,一旦工作线程中止,主STA也就不复存在了,因此必须在主线程中调用CoInitialize初始化主STA。初始化STA也可以使用CoInitializeEx(nil,COINIT_APARTMENTTHREADED)函数,它与CoInitialize(nil)等效。
通过查阅Windows注册表键,由Matlab生成的COM组件,其线程模型ThreadingModel=Both,说明其既支持STA,也支持MTA。
2 利用STA线程模型实现Delphi调用COM组件
Delphi调用COM组件之前需要利用CoInitialize(nil)函数初始化COM组件,以便让COM知道如何同调用线程工作,最后需要调用CoUnInitialize函数关闭COM,卸载在该线程中已装载DLL,释放线程中使用的资源,以及关闭线程中所有打开的RPC连接。
假设有一个利用Matlab生成的COM组件signal.dll,在Delphi XE中调用该组件的方法如下:
1)打开Delphi XE集成开发环境,新建一个包,设该包名命名为signal,编译并安装该包,然后关闭该包;
2)选择Component→Import Component→Import a Type Library,出现Import Component对话框后,找到signal.dll,点击下一步,在Palette Page中选择ActiveX,再点击下一步,选择“Install to a Existing Package”,继续下一步,定位到在第(1)步中安装的包signal.bpl,单击完成,即可将signal.dll安装到Delphi XE中;
3)在所建的Delphi XE工程中,新建一个Thread Object文件,类名为TSignal,在该文件的Execute过程中输入以下代码:
FreeOnTerminate:=True;
try
CoInitialize(nil);
调用Form1中的Signal1组件;
finally
CoUnInitialize;
end;
在Form1中Button1的OnClick事件中输入以下代码:
try
TSignal.Create(False);
except
Application.MessageBox('系统出现异常!','错误');
end;
通过以上四个步骤,就可以实现Delphi与Matlab混合编程,而且在执行COM组件所提供函数的同时,还可以进行其它的操作,而不会出现界面冻结。
3 应用实例
下面用一个实际例子说明如何利用COM STA线程模型实现Delphi和Matlab混合编程。该实例实现的是利用S变换提取信号包络。首先编写Matlab函数如下:
%输入参数:fName—文件名,Len—文件长度
%输出参数:wav—信号原始波形,stenv—基于S变换提取的信号包络
function [wav, stenv] = stanaly(fName,Len)
[wav Fs] = wavread(fName);
if length(wav) > Len
wav = wav(1:Len);
end
stran = st(wav);
stenv = [];
for i=1:size(stran,2)
stran1 = max(abs(stran(: , i)));
stenv = [stenv stran1];
end
stenv = stenv*max(abs(wav));
stenv = stenv/max(stenv);
按照文献[4]介绍的方法,制作并注册COM组件。设组件名为TSignal,按上文介绍的方法将TSignal安装到Delphi IDE中。
在Delphi新建一个工程,在窗体上添加如下控件:一个Signal,一个Chart,两个Edit,两个Label,一个UpDown,五个Button,一个Timer,一个OpenDialog,一个AlWavePlayer,一个AlAudioOut,一个SlScope。设置UpDown控件的Associate属性为Edit1,Min=7000,Max=10000,设计的最终界面如图3所示。
新建一个Thread Object单元,单元名为Unit2,类名为TSt。在其Execute过程添加如下代码:
procedure TSt.Execute;
begin
FreeOnTerminate := True;
try
CoInitialize(nil);
try
fStatusText := '信号分析—波形分析中,请稍候...';
Synchronize(ShowStatus); //在Form1.Caption中显示相关信息
Form1.Signal1.stanaly(2, wav, stenv, FFName, FFLen);
except
Application.MessageBox('系统出现异常!','错误')
end;
finally
fStatusText := '信号分析—波形分析完成';
Synchronize(ShowStatus);
CoUnInitialize;
end;
end;
Unit1单元的部分代码如下:
procedure TForm1.BtnAnalyClick(Sender: TObject);
var
fName : string;
fLen : Integer;
begin
fName := Copy(EdtFName.Text, 1, Length(EdtFName.Text)-4);
fLen := StrToInt(EdtFLen.Text);
wav := VarArrayCreate([1, fLen], VarDouble);//传出COM的变体数据:原始波形
stenv := VarArrayCreate([1, fLen], VarDouble);//传出COM的变体数据:st变换包络
try
Timer1.Enabled := True;
SignalThread := TSt.Create(fName,fLen);
except
Application.MessageBox('系统出现异常!','错误');
BtnExit.Enabled := True;
end;
end;
procedure TForm1.PlotWave;
var
WavData, EnvData : array of double; //原始波形和信号包络
p1, p2 : Pointer;
i : Integer;
begin
SetLength(WavData,StrToInt(EdtFLen.Text));
SetLength(EnvData,StrToInt(EdtFLen.Text));
p1 := VarArrayLock(wav);
p2 := VarArrayLock(stenv);
Chart1.Series[0].Clear;
Chart2.Series[0].Clear;
try
Move(p1^, WavData[1], StrToInt(EdtFLen.Text) * sizeof(double));
Move(p2^, EnvData[1], StrToInt(EdtFlen.Text) * sizeof(double));
for i := Low(WavData) to High(WavData) do
begin
Chart1.Series[0].AddXY(i, WavData[i]);
Chart2.Series[0].AddXY(i, EnvData[i]);
end;
finally
VarArrayUnlock(wav);
VarArrayUnlock(stenv);
end;
由于篇幅所限,其余代码省略。
在Delhi IDE中编译、连接工程,运行效果如图3所示。点击“分析”按钮进行分析的同时,还可以执行其他操作,而不会出现界面冻结现象。
4 结束语
本文详细讨论了基于COM STA线程模型的Delphi和Matlab混合编程的实现方法。按照这种方法进行应用系统的开发,不仅可以充分利用Matlab强大的科学计算功能和Delphi灵活的可视化设计功能,而且开发出的系统在运行过程中不会出现界面冻结现象,避免给用户程序僵死的假象。采用这种方法可以提高软件性能,为科学研究和工程计算提供更强的技术支持。该文所介绍的应用实例在Windows XP、Delphi XE和Matlab 7环境下调试通过。
参考文献:
[1] 陈新.利用Delphi与Matlab进行科学计算的实现[J].四川兵工学报,2009,30(6):117-118.
[2] 王艳丽.Delphi与Matlab混合编程的5种方法[J].菏泽学院学报,2006,28(2):100-102.
[3] 蒋裕丰,何鲜峰,金永强.BP网络监测模型的Matlab&Delphi混合编程[J].水力发电,2008,34(1):88-91.
[4] 姜银方,陈建希,李路娜.基于COM的Delphi和Matlab接口编程研究[J].计算机应用与软件,2008,25(2):31-34.
[5] 吴小丽,丁维明,程力.Delphi动态调用Matlab COM组件实现二者混合编程[J].工业控制计算机,2011,24(3):1-3.
[6] Binh Ly.Multithreading in COM[EB/OL][2010-12-28].http://www.techvanguards.com/com/concepts/multithreading.asp.