论文部分内容阅读
摘要:工程中往往需要VC 编写的程序进行大量的数学计算,如能在程序中使用Matlab的例程这些问题则能迅速解决。在Windows平台上Matlab 提供了两个实现这一目标的接口——Matlab Engine和Automation Server,两者在本质上都基于COM技术。引擎库通过其输出函数对用户屏蔽了底层的COM细节,这大大方便了使用。而使用自动化服务器就要使用COM的自动化接口IDispatch,不过MFC库的COleDispatchDriver类已使这一过程大大简化了。两种方式的差别主要体现在参数传递和返回值处理上,如文中代码所示。无论哪种方法,都可以将具有强大工程计算能力的Matlab接入VC 程序,从而快捷方面地解决许多数值计算和图形输出问题。
关键词:Matlab外部接口;混合编程;VC ;COM;自动化
中图分类号:TP311文献标识码:A文章编号:1009-3044(2008)36-2792-04
Program with Matlab COM Server in VC
SHEN Zhi-juan1, YANG Hei-lai1, WANG Shu-fen2, ZHANG Wei-min3
(1.Mechanical Engineering and Automation Department, Beijing University of Aeronautics and Astronautics, Beijing 100083, China; 2.Computing Center, Hebei University of Science and Technology, Tangshan 063009, China; 3.Beingjing Aeronautic Manufacturing Reseach Center, Beijing 100024, China)
Abstract: Programs written in VC sometimes need to do calculation intensive jobs, which can be solved swiftly if some Matlab function can be called inside the program. Matlab provides two methods to achieve this: Engine Library and Automation Server, both of which are based on COM. Engine Library made the custom COM interface IEngine transparent to the Client through its export functions while MFC class COleDispatchDriver encapsulates the detail of interacton with the COM Automation interface IDispatch. The given code in this article shows differences of the two method come mainly from the parameter passing and result retrieving process. With either method, Matlab can be linked into the program and thus make the calculation and figure output tasks be achieved faster and easier.
Key words: matlab external interface; hybrid programming; VC ; COM; automation
1 引言
Matlab提供的編程语言——M语言——是以方便数值计算编程为目的设计的,所以其基本数据结构就是矩阵,且其变量的具体类型和大小可在执行时动态确定,这对编写数值计算程序提供了很大的便利。另外,由于M语言可直接调用Matlab提供的各种数学和面向工程应用的工具箱函数,所以使用M语言还可以迅速地解决许多计算量很大的工程问题。但另一方面,由于M语言是一种解释型编程语言,所以它的执行效率较低,另外其在开发应用程序界面和对外围设备的控制能力上也较差。另一方面,其他的编程环境,如VC ,则在这些方面提供了很好的支持,但如用其进行数值计算或解决工程问题又会非常不便。故如果对软件界面、外围设备控制、数值计算三方面都有较高的要求,一个理想的方案是在像VC 这样的编程环境中实现界面及外设控制功能,并在其中调用Matlab环境中的模块,如其工具箱函数,作为计算工具。
在VC 程序中调用Matlab有两种方法:一是通过Matlab提供的引擎库(Engine Library),它的本质是一个拥有定制接口IEngine的COM服务器;二是通过Matlab提供的COM Automation Server。本文将首先介绍COM和Automation技术的原理和应用方法,之后介绍这两个接口,并随后给出在C 中使用两者的具体方法。
2COM与Automation
2.1 COM概述
组件对象模型(Component Object Model,COM) 由Microsoft于1993年创建,它规定了dll、exe等应用模块之间进行交互的步骤和方法。在COM之前,一个应用程序(Client)如果要使用dll文件(Server)输出的功能就要使用Win32 API函数LoadLibrary,GetProcAddress,要使用exe文件(Server)输出的功能除了需使用相关的Win32 API函数外,还要使用基于Windows消息机制的DDE协议。COM则是Microsoft新推出的这些方法的换代品,它解决了不少在使用老方法时产生的问题,如他们本质上是面向过程的技术,使用起来比较零散复杂,且对系统来说,他们是无法管理的。COM则提供了一个面向对象的Server功能管理及Client/Server的通讯方法。但这需要应用编程者的配合,即COM不仅是Windows操作系统新增的一系列数据结构和函数,而且是一系列应用与系统交互方式的定义。
在COM中,Server模块(dll或exe)要向系统注册其输出的COM类,即在注册表中保存COM类的GUID(Global Unique Identifier),Server模块完整的路径及文件名等相关信息。一个Server模块可拥有多个COM类。于是,Client只要向系统提供其所需COM类的GUID系统就能找到包含这个类的模块并加载它。在注册COM类的同时,Server还要注册每个COM类所包含接口(interface)的GUID。从功能上说,一个接口就是Server输出的一个功能单元,它由一组功能上密切相关的函数指针构成;而用C 语言表达,COM接口就是一个仅包含虚函数的结构体,它本质上是一个包含一个函数指针数组的内存结构,这一内存结构是由Microsoft定义的标准。Client则是通过GUID向系统提交自己的请求,通过获得由Server提供的接口指针来调用Server的输出函数[1]。
事实上,接口是COM技术的核心要素,其最基础的接口是Microsoft定义的标准接口IUnknown,它包含QueryInterface、AddRef和Release三个函数,这些函数是Client与Server通信的基础。而所有其他的接口均需继承自IUnknown。这也是C 多态技术的一个应用。除了IUnknown,Microsoft还定义了许多其它接口,这些接口事实上是许多编程技术的底层基础,如IDispatch接口就是Automation技术的根本核心。同时需要说明的是,对其制定的大部分接口而言,Microsoft只是定义了接口的数据结构,接口函数功能的实现要由Server开发者完成。
可见,COM是一个Client、Server和操作系统协力合作。从操作系统的角度看COM是一个记录COM类的方式和一组用于Client和Server通信的API函数。对Client来说COM是利用API向系统发出请求并利用接口指针使用COM类的功能。对Server来说则是利用API注册自己,实现所有接口函数,这包括由Microsoft定义的接口函数,如Server必须实现IUnknown接口的QueryInterface函数,其作用是为Client提供其所要求的所有接口指针。
2.2 Automation概述
由于接口本质上是一个包含一个函数指针数组的内存结构,这一内存结构是由Microsoft定义的标准,而所有支持COM的编译系统和语言对接口的处理都符合这一标准,所以通过COM,就可实现不同编译器、不同语言编写的应用顺利通信。但是仍有一个问题,就是想Visual Basic这样的解释性语言无法学习C 定义的接口。举例来说,如果一个C或C 程序要使用一个组件定义的接口IMath,它就需要声明接口的结构,如:
struct IMath : IUnknown
{
virtual void Add(int a, int b) = 0;
virtual void Multiply(int a, int b) = 0;
virtual void Square(int a) = 0;
};
这样,在获得了接口指针,不妨叫pIMath,之后,就可以用“pIMath->Add(a, b);”这样的语句来使用Server的功能。但Visual Basic却不支持这样的语法,即它的解释程序无法以函数指针的形式学习新的接口定义。考虑到接口将会不断增长的事实,让Visual Basic把对所有接口的支持固化在解释程序中又是不可能的。所以,为了解决这类问题,Microsoft定义了IDispatch接口,其思想是,像VB这类语言的解释程序只需要支持IDispatch接口,就可以在执行时通过Server获得其需要函数的相关信息,从而完成调用。
整个接口的机制是Server要为输出的函数分配一个数字ID和一个相对应的标识字符串。Client只知道标识字符串,为了调用相应的函数,Client在获取IDispatch接口指针后,先调用其函数GetIDsOfNames()得到和标识字符串相对应的数字ID,再调用IDispatch的函数Invoke(),该函数专门负责根据输入的数字ID找到Client要求的函数,并调用它。Invoke()在调用函数时需要传递参数,而参数格式是无法由解释程序预知的,所以Invoke()使用了一种特殊的数据类型VARIANT来解决这一问题。VARIANT是一个结构体,它可以表示任何类型的数据。VARIANT主要由两部分组成,一是标识数据类型的vt,二是存储实际数据的union结构。函数的输入参数就被存储在一个VARIANT数组里被传递给Invoke。当然,之后解析各个变量并传递给输出函数的任务就交由Invoke()完成了。函数的返回值同样是以VARIANT形式回传的。这样,就解决了高级语言使用COM组件的问题。
所以,自动化(Automation)是建立在COM基础上的。一个自动化服务器实际上就是一个实现了IDisPatch接口的COM组件。而一个自动化控制器则是一个通过IDisPatch接口同自动化服务器进行通信的COM客户。
3 Matlab 提供的COM Server接口
由于出现了Automation技术,访问COM组件就有了两种方式:①通过普通的自定义的COM接口访问组件;②通过调度接口IDisPatch访问组件;如果一个组件同时实现了这两种接口,则称为双重接口,那么客户就可自由的选择其中的一种方式访问组件的功能。Matlab就提供了这两种接口。
3.1 IEngine 接口
IEngine是Matlab提供的定制接口,但通常在程序中不会直接使用它,而是使用Matlab提供的引擎庫(Engine Library)函数。事实上,COM Server及其接口IEngine可看作是引擎库在Windows平台上的实现手段。当然,也可以把引擎库看作Matlab为方便IEngine的使用而提供的函数库。在使用引擎库时,完全不需要COM的知识,只需要把它看成一个非标准的C语言静态函数库即可。
引擎库共输出了9个函数,通过它们可以方便的开始和结束与Matlab Server的对话,设置命令窗口(缺省状态是显示命令窗口),与Matlab的工作空间交换变量,执行任何可在Matlab命令行执行的命令。使用引擎库和直接在Matlab命令行输入的方法是极为类似的。
3.2 IDispatch 接口
IDispatch是Matlab为Visual Basic,C#等语言提供的接口,但在C 程序中也可以使用,只不过,这需要进行较多和COM相关的编程。如果使用VC 提供的MFC类COleDispatchDriver来使用Matlab 自动化服务器,则可以大大简化这一过程。这些内容后面会详细讨论。
通过IDispatch接口获得的函数功能和引擎库类似,但功能更丰富更灵活些。例如,如果要调用统计工具箱的方差计算函数求某一双精度数组的方差,使用引擎库函数的流程必须是先通过engPutVariable()将数组存入Matlab,比如起名叫“a”;再用engEvalString()命令执行一条语句“b = std ( a )”,这在执行命令的同时又在Matlab里建立了变量“b”;最后通过engGetVariable()将变量"b"取回。Matlab的自动化服务器则除了拥有支持上面流程的函数外,还提供了另一种可能性,即只需调用函数Feval()就可以了,因为Feval()可同时接受函数名,参数作为输入,并将Matlab的计算结果输出。
4 Matlab COM 接口的VC 调用
在VC 中使用Server的关键问题有两类,一类是如何启动和释放Server,另一类是如何与Matlab进行数据交互。下面将分别针对这两方面,以一个具体的问题——方差的计算,待计算的数据保存在变量double Array[20]中——为例,说明调用Matlab 两种COM接口的具体操作。下面的代码均经过实际测试使用,其环境为WindowXP,Visual C 6.0和Matlab 7.4.0。
4.1 Server的启动和释放
4.1.1 引擎库
使用引擎库在创建工程时不需要特别设置,但之后第一要在源文件中包含engine.h,它其中声明了引擎库函数的原型。engine.h中还包含头文件matrix.h,其最主要的内容是定义了数据类型mxArray及对它进行操作的函数原型。mxArray类型是许多引擎库函数的参数类型,它是C 程序和Matlab进行数据交互的工具。而matrix.h中又包含tmwtypes.h,这一头文件中也定义了一些数据类型。三个头文件的路径均为Matlab安装目录下的extern\include。将engine.h包含入源文件有三种方法,其一是在源文件中包含完整的路径和文件名,如“#include "D:\matlab\extern\include\engine.h”;其二是仅包含文件名,但把上述三个头文件全部拷贝到工程目录中;其三也是只包含文件名,但要在VC 6.0的Tool菜单的Option命令对话框的Directory选项卡中,把头文件所在路径添加到include搜索路径中。这之后,还要为工程加入两个库文件——libeng.lib和libmx.lib,其中libeng.lib实现了引擎库函数,而libmx.lib则实现了操作mxArray需要的各个函数。它们路径均为Matlab安装目录下的extern\lib\win32\microsoft。具体操作为右键单击VC 6.0左侧Workspace里FileView的工程文件,在弹出菜单中选择“Add File to Project”,之后在弹出的对话框中转到上述目录后将文件过滤设为“Library Files”就可以选择所要的库文件了。为方便代码的移植,则可以先将上述文件复制到工程所在目录再进行添加。
在做好这些准备工作后,就可以正式使用引擎库了。引擎库启动可使用如下代码:
Engine *ep;
if (!(ep = engOpen("\0"))) {
fprintf(stderr, "\nCan’t start MATLAB engine\n");
return;
}
结束与Matlab的对话可使用:
engClose(ep);
4.1.2 自动化服务器
在C 程序中直接用Win32 API操作自动化服务器是比较繁琐的,所以笔者对自动化服务器的操作是通过MFC的COleDispatchDriver类完成的。这就要求在创建工程时要加入对MFC类的支持。之后,启动Class Wizard,单击其新建类按钮中的“从类库(type library)”命令从而打开“从类库导入”为标题的对话框。在对话框中转入Matlab安装目录下的bin\win32文件夹,选择其中的mlapp.tlb文件就可以生成继承自COleDispatchDriver类的DIMApp类。
完成上述工作,就可用下面代码启动Matlab自动化服务器:
DIMLApp MatlabAuto;
COleException *e = new COleException;
try{
if (!MatlabAuto. CreateDispatch("matlab.application", e))
throw(e);
}
// Exception handling code.
其中,函数CreateDispatch()是从基类COleDispatchDriver继承的。结束自动化服务器可用如下代码:
MatlabAuto.ReleaseDispatch();
函数ReleaseDispatch()也是从基类COleDispatchDriver继承的。
4.2 数据交换
无论是采用哪种调用方式,Client都需要一种中间数据类型来与Matlab交互数据。当要把参数传递给Matlab时,Client需要先把自己的数据转换成中间类型;当从Matlab获取结果时,Client要从中间类型中提取原始数据。对引擎库来说,这种中间类型是mxArray;对自动化服务器来说,这种中间类型是VARIANT。下面通过代码来说明这两种方式。
4.2.1 引擎库
使用引擎库的代码如下:
double standard_deviation = 0;
// Create a variable for our data
mxArray *a = mxCreateDoubleMatrix(1, 20, mxREAL);
mxArray *b = NULL;
memcpy((void *)mxGetPr(a), (void *)Array, sizeof(Array));
// Place the variable a into the MATLAB //workspace
engPutVariable(ep, "a", a);
// Evaluate function
engEvalString(ep, "b =std(a);");
// Get Result
b = engGetVariable(ep, "b");
standard_deviation = mxGetPr(b)[0];
cout << "The standard deviation is " << standard_deviation << endl;
// Release memory
mxDestroyArray(a);
a= NULL;
mxDestroyArray(b);
b= NULL;
4.2.2 自动化服务器
使用自动化服务器的代码如下:
double standard_deviation = 0;
VARIANT a; // 用于向Matlab传递参数
VariantInit(
关键词:Matlab外部接口;混合编程;VC ;COM;自动化
中图分类号:TP311文献标识码:A文章编号:1009-3044(2008)36-2792-04
Program with Matlab COM Server in VC
SHEN Zhi-juan1, YANG Hei-lai1, WANG Shu-fen2, ZHANG Wei-min3
(1.Mechanical Engineering and Automation Department, Beijing University of Aeronautics and Astronautics, Beijing 100083, China; 2.Computing Center, Hebei University of Science and Technology, Tangshan 063009, China; 3.Beingjing Aeronautic Manufacturing Reseach Center, Beijing 100024, China)
Abstract: Programs written in VC sometimes need to do calculation intensive jobs, which can be solved swiftly if some Matlab function can be called inside the program. Matlab provides two methods to achieve this: Engine Library and Automation Server, both of which are based on COM. Engine Library made the custom COM interface IEngine transparent to the Client through its export functions while MFC class COleDispatchDriver encapsulates the detail of interacton with the COM Automation interface IDispatch. The given code in this article shows differences of the two method come mainly from the parameter passing and result retrieving process. With either method, Matlab can be linked into the program and thus make the calculation and figure output tasks be achieved faster and easier.
Key words: matlab external interface; hybrid programming; VC ; COM; automation
1 引言
Matlab提供的編程语言——M语言——是以方便数值计算编程为目的设计的,所以其基本数据结构就是矩阵,且其变量的具体类型和大小可在执行时动态确定,这对编写数值计算程序提供了很大的便利。另外,由于M语言可直接调用Matlab提供的各种数学和面向工程应用的工具箱函数,所以使用M语言还可以迅速地解决许多计算量很大的工程问题。但另一方面,由于M语言是一种解释型编程语言,所以它的执行效率较低,另外其在开发应用程序界面和对外围设备的控制能力上也较差。另一方面,其他的编程环境,如VC ,则在这些方面提供了很好的支持,但如用其进行数值计算或解决工程问题又会非常不便。故如果对软件界面、外围设备控制、数值计算三方面都有较高的要求,一个理想的方案是在像VC 这样的编程环境中实现界面及外设控制功能,并在其中调用Matlab环境中的模块,如其工具箱函数,作为计算工具。
在VC 程序中调用Matlab有两种方法:一是通过Matlab提供的引擎库(Engine Library),它的本质是一个拥有定制接口IEngine的COM服务器;二是通过Matlab提供的COM Automation Server。本文将首先介绍COM和Automation技术的原理和应用方法,之后介绍这两个接口,并随后给出在C 中使用两者的具体方法。
2COM与Automation
2.1 COM概述
组件对象模型(Component Object Model,COM) 由Microsoft于1993年创建,它规定了dll、exe等应用模块之间进行交互的步骤和方法。在COM之前,一个应用程序(Client)如果要使用dll文件(Server)输出的功能就要使用Win32 API函数LoadLibrary,GetProcAddress,要使用exe文件(Server)输出的功能除了需使用相关的Win32 API函数外,还要使用基于Windows消息机制的DDE协议。COM则是Microsoft新推出的这些方法的换代品,它解决了不少在使用老方法时产生的问题,如他们本质上是面向过程的技术,使用起来比较零散复杂,且对系统来说,他们是无法管理的。COM则提供了一个面向对象的Server功能管理及Client/Server的通讯方法。但这需要应用编程者的配合,即COM不仅是Windows操作系统新增的一系列数据结构和函数,而且是一系列应用与系统交互方式的定义。
在COM中,Server模块(dll或exe)要向系统注册其输出的COM类,即在注册表中保存COM类的GUID(Global Unique Identifier),Server模块完整的路径及文件名等相关信息。一个Server模块可拥有多个COM类。于是,Client只要向系统提供其所需COM类的GUID系统就能找到包含这个类的模块并加载它。在注册COM类的同时,Server还要注册每个COM类所包含接口(interface)的GUID。从功能上说,一个接口就是Server输出的一个功能单元,它由一组功能上密切相关的函数指针构成;而用C 语言表达,COM接口就是一个仅包含虚函数的结构体,它本质上是一个包含一个函数指针数组的内存结构,这一内存结构是由Microsoft定义的标准。Client则是通过GUID向系统提交自己的请求,通过获得由Server提供的接口指针来调用Server的输出函数[1]。
事实上,接口是COM技术的核心要素,其最基础的接口是Microsoft定义的标准接口IUnknown,它包含QueryInterface、AddRef和Release三个函数,这些函数是Client与Server通信的基础。而所有其他的接口均需继承自IUnknown。这也是C 多态技术的一个应用。除了IUnknown,Microsoft还定义了许多其它接口,这些接口事实上是许多编程技术的底层基础,如IDispatch接口就是Automation技术的根本核心。同时需要说明的是,对其制定的大部分接口而言,Microsoft只是定义了接口的数据结构,接口函数功能的实现要由Server开发者完成。
可见,COM是一个Client、Server和操作系统协力合作。从操作系统的角度看COM是一个记录COM类的方式和一组用于Client和Server通信的API函数。对Client来说COM是利用API向系统发出请求并利用接口指针使用COM类的功能。对Server来说则是利用API注册自己,实现所有接口函数,这包括由Microsoft定义的接口函数,如Server必须实现IUnknown接口的QueryInterface函数,其作用是为Client提供其所要求的所有接口指针。
2.2 Automation概述
由于接口本质上是一个包含一个函数指针数组的内存结构,这一内存结构是由Microsoft定义的标准,而所有支持COM的编译系统和语言对接口的处理都符合这一标准,所以通过COM,就可实现不同编译器、不同语言编写的应用顺利通信。但是仍有一个问题,就是想Visual Basic这样的解释性语言无法学习C 定义的接口。举例来说,如果一个C或C 程序要使用一个组件定义的接口IMath,它就需要声明接口的结构,如:
struct IMath : IUnknown
{
virtual void Add(int a, int b) = 0;
virtual void Multiply(int a, int b) = 0;
virtual void Square(int a) = 0;
};
这样,在获得了接口指针,不妨叫pIMath,之后,就可以用“pIMath->Add(a, b);”这样的语句来使用Server的功能。但Visual Basic却不支持这样的语法,即它的解释程序无法以函数指针的形式学习新的接口定义。考虑到接口将会不断增长的事实,让Visual Basic把对所有接口的支持固化在解释程序中又是不可能的。所以,为了解决这类问题,Microsoft定义了IDispatch接口,其思想是,像VB这类语言的解释程序只需要支持IDispatch接口,就可以在执行时通过Server获得其需要函数的相关信息,从而完成调用。
整个接口的机制是Server要为输出的函数分配一个数字ID和一个相对应的标识字符串。Client只知道标识字符串,为了调用相应的函数,Client在获取IDispatch接口指针后,先调用其函数GetIDsOfNames()得到和标识字符串相对应的数字ID,再调用IDispatch的函数Invoke(),该函数专门负责根据输入的数字ID找到Client要求的函数,并调用它。Invoke()在调用函数时需要传递参数,而参数格式是无法由解释程序预知的,所以Invoke()使用了一种特殊的数据类型VARIANT来解决这一问题。VARIANT是一个结构体,它可以表示任何类型的数据。VARIANT主要由两部分组成,一是标识数据类型的vt,二是存储实际数据的union结构。函数的输入参数就被存储在一个VARIANT数组里被传递给Invoke。当然,之后解析各个变量并传递给输出函数的任务就交由Invoke()完成了。函数的返回值同样是以VARIANT形式回传的。这样,就解决了高级语言使用COM组件的问题。
所以,自动化(Automation)是建立在COM基础上的。一个自动化服务器实际上就是一个实现了IDisPatch接口的COM组件。而一个自动化控制器则是一个通过IDisPatch接口同自动化服务器进行通信的COM客户。
3 Matlab 提供的COM Server接口
由于出现了Automation技术,访问COM组件就有了两种方式:①通过普通的自定义的COM接口访问组件;②通过调度接口IDisPatch访问组件;如果一个组件同时实现了这两种接口,则称为双重接口,那么客户就可自由的选择其中的一种方式访问组件的功能。Matlab就提供了这两种接口。
3.1 IEngine 接口
IEngine是Matlab提供的定制接口,但通常在程序中不会直接使用它,而是使用Matlab提供的引擎庫(Engine Library)函数。事实上,COM Server及其接口IEngine可看作是引擎库在Windows平台上的实现手段。当然,也可以把引擎库看作Matlab为方便IEngine的使用而提供的函数库。在使用引擎库时,完全不需要COM的知识,只需要把它看成一个非标准的C语言静态函数库即可。
引擎库共输出了9个函数,通过它们可以方便的开始和结束与Matlab Server的对话,设置命令窗口(缺省状态是显示命令窗口),与Matlab的工作空间交换变量,执行任何可在Matlab命令行执行的命令。使用引擎库和直接在Matlab命令行输入的方法是极为类似的。
3.2 IDispatch 接口
IDispatch是Matlab为Visual Basic,C#等语言提供的接口,但在C 程序中也可以使用,只不过,这需要进行较多和COM相关的编程。如果使用VC 提供的MFC类COleDispatchDriver来使用Matlab 自动化服务器,则可以大大简化这一过程。这些内容后面会详细讨论。
通过IDispatch接口获得的函数功能和引擎库类似,但功能更丰富更灵活些。例如,如果要调用统计工具箱的方差计算函数求某一双精度数组的方差,使用引擎库函数的流程必须是先通过engPutVariable()将数组存入Matlab,比如起名叫“a”;再用engEvalString()命令执行一条语句“b = std ( a )”,这在执行命令的同时又在Matlab里建立了变量“b”;最后通过engGetVariable()将变量"b"取回。Matlab的自动化服务器则除了拥有支持上面流程的函数外,还提供了另一种可能性,即只需调用函数Feval()就可以了,因为Feval()可同时接受函数名,参数作为输入,并将Matlab的计算结果输出。
4 Matlab COM 接口的VC 调用
在VC 中使用Server的关键问题有两类,一类是如何启动和释放Server,另一类是如何与Matlab进行数据交互。下面将分别针对这两方面,以一个具体的问题——方差的计算,待计算的数据保存在变量double Array[20]中——为例,说明调用Matlab 两种COM接口的具体操作。下面的代码均经过实际测试使用,其环境为WindowXP,Visual C 6.0和Matlab 7.4.0。
4.1 Server的启动和释放
4.1.1 引擎库
使用引擎库在创建工程时不需要特别设置,但之后第一要在源文件中包含engine.h,它其中声明了引擎库函数的原型。engine.h中还包含头文件matrix.h,其最主要的内容是定义了数据类型mxArray及对它进行操作的函数原型。mxArray类型是许多引擎库函数的参数类型,它是C 程序和Matlab进行数据交互的工具。而matrix.h中又包含tmwtypes.h,这一头文件中也定义了一些数据类型。三个头文件的路径均为Matlab安装目录下的extern\include。将engine.h包含入源文件有三种方法,其一是在源文件中包含完整的路径和文件名,如“#include "D:\matlab\extern\include\engine.h”;其二是仅包含文件名,但把上述三个头文件全部拷贝到工程目录中;其三也是只包含文件名,但要在VC 6.0的Tool菜单的Option命令对话框的Directory选项卡中,把头文件所在路径添加到include搜索路径中。这之后,还要为工程加入两个库文件——libeng.lib和libmx.lib,其中libeng.lib实现了引擎库函数,而libmx.lib则实现了操作mxArray需要的各个函数。它们路径均为Matlab安装目录下的extern\lib\win32\microsoft。具体操作为右键单击VC 6.0左侧Workspace里FileView的工程文件,在弹出菜单中选择“Add File to Project”,之后在弹出的对话框中转到上述目录后将文件过滤设为“Library Files”就可以选择所要的库文件了。为方便代码的移植,则可以先将上述文件复制到工程所在目录再进行添加。
在做好这些准备工作后,就可以正式使用引擎库了。引擎库启动可使用如下代码:
Engine *ep;
if (!(ep = engOpen("\0"))) {
fprintf(stderr, "\nCan’t start MATLAB engine\n");
return;
}
结束与Matlab的对话可使用:
engClose(ep);
4.1.2 自动化服务器
在C 程序中直接用Win32 API操作自动化服务器是比较繁琐的,所以笔者对自动化服务器的操作是通过MFC的COleDispatchDriver类完成的。这就要求在创建工程时要加入对MFC类的支持。之后,启动Class Wizard,单击其新建类按钮中的“从类库(type library)”命令从而打开“从类库导入”为标题的对话框。在对话框中转入Matlab安装目录下的bin\win32文件夹,选择其中的mlapp.tlb文件就可以生成继承自COleDispatchDriver类的DIMApp类。
完成上述工作,就可用下面代码启动Matlab自动化服务器:
DIMLApp MatlabAuto;
COleException *e = new COleException;
try{
if (!MatlabAuto. CreateDispatch("matlab.application", e))
throw(e);
}
// Exception handling code.
其中,函数CreateDispatch()是从基类COleDispatchDriver继承的。结束自动化服务器可用如下代码:
MatlabAuto.ReleaseDispatch();
函数ReleaseDispatch()也是从基类COleDispatchDriver继承的。
4.2 数据交换
无论是采用哪种调用方式,Client都需要一种中间数据类型来与Matlab交互数据。当要把参数传递给Matlab时,Client需要先把自己的数据转换成中间类型;当从Matlab获取结果时,Client要从中间类型中提取原始数据。对引擎库来说,这种中间类型是mxArray;对自动化服务器来说,这种中间类型是VARIANT。下面通过代码来说明这两种方式。
4.2.1 引擎库
使用引擎库的代码如下:
double standard_deviation = 0;
// Create a variable for our data
mxArray *a = mxCreateDoubleMatrix(1, 20, mxREAL);
mxArray *b = NULL;
memcpy((void *)mxGetPr(a), (void *)Array, sizeof(Array));
// Place the variable a into the MATLAB //workspace
engPutVariable(ep, "a", a);
// Evaluate function
engEvalString(ep, "b =std(a);");
// Get Result
b = engGetVariable(ep, "b");
standard_deviation = mxGetPr(b)[0];
cout << "The standard deviation is " << standard_deviation << endl;
// Release memory
mxDestroyArray(a);
a= NULL;
mxDestroyArray(b);
b= NULL;
4.2.2 自动化服务器
使用自动化服务器的代码如下:
double standard_deviation = 0;
VARIANT a; // 用于向Matlab传递参数
VariantInit(