matlab与vc++混合编程(续)
<P>组件对象模型(简称COM)是windows对象的二进制标准。为WINDOWS提供了统一的,面向对象的, 可扩充的通讯协议。这意味着描述一个对象的可执行代码(.dll 或.exe 文件的代码)可以被其它对象执行。即使两个对象使用不同语言来编写的, 他们可以用COM 标准来进行通信。Mathworks公司在Matlab6.5中推出了将Matlab 中的.m函数编译为COM组件的工具-
- COM Builder,使用这个工具可以方便的制作出想要的COM 组件,以供其它支持COM 的
编程语言调用, 实现Matlab 和其他编程语言的整合。
下面就穿插一个具体的例子介绍VC++6.0 和Matlab 通过COM 实现混合编程的具体实
现。
1 Matlab编译环境的设置
Matlab COM Builder 在编译生成COM组件的时候需要借助于外部的编译器。因为并不
是所有的编译器都能生成和Microsoft相兼容的COM 组件, 因此COM Builder 只支持
下列编译器:
Borland C++ Builder 3
Borland C++ Builder 4
Borland C++ Builder 5
Microsoft Visual Studio 5.0
Microsoft Visual Studio 6.0
Microsoft Visual Studio.NET
在你的Matlab 安装了COM Builder( 一般在安装Matlab 的时候只要选中COM Builder
就可以了)之后,要先设置Matlab COM Builder 所使用的外部编译器。可以通过mbuild
– setup实现。
在Matlab 的Command Window输入命令mbuild – setup,并根据提示选择合适的编译器。
笔者使用的是Microsoft Visual Studio 6.0, 过程如下:
>> mbuild -setup
Please choose your compiler for building standalone MATLAB applications:
Would you like mbuild to locate installed compilers /n?
Select a compiler:
Lcc C version 2.4 in D:\MATLAB6P5\sys\lcc
Microsoft Visual C/C++ version 7.0 in C:\Program Files\Microsoft Visual Studio .NET
Microsoft Visual C/C++ version 6.0 in D:\Microsoft Visual Studio
None
Compiler: 3
Please verify your choices:
COM 实现VC++6.0 与Matlab 混编学习总结( Ver 1.0)
Compiler: Microsoft Visual C/C++ 6.0
Location: D:\Microsoft Visual Studio
Are these correct?(/n):
具体的提示会因为不同的情况而变化。然后在Command Window 中输入命令
cd(prefdir);mccsavepath;设置Matlab 为编译器提供的搜索路径, 这些命令只输入一次
就可以了。
在这个过程中Matlab 会自动注册mwcomutil.dll 和mwcommgr.dll,因为这两个DLL 是
Matlab COM Builder 生成的COM组件的基础, 所有生成的COM 组件都会使用到这两
个DLL。
如有疑问, 可以参阅Matlab 的帮助Matlab Compiler\ Stand-AloneApplications\ Building
Stand-Alone Applications on PCs\ Preparing to Compile 一节。
2 使用Matlab COM Builder制作需要的COM 组件
使用Matlab 的COM Builder 制作COM 组件非常的容易, 首先在Matlab 的Command
Window 中输入命令comtool, 启动COM Builder 然后使用File->New Project… 建立新的工程, 会出现工程设置窗口。
在Component name 中输入你要生成的COM 组件的的名字, 例如我们输入名字
ljwtestcom, 最后生成的COM 组件的名字就是这个名字加上版本信息。通过add>>和
Remove按纽可以方便的为这个COM 组件添加和删除类( CLASS)。在这里, 我们为这
个组件添加一个类testcom。Project version 是为了用户方便的管理自己所生成的COM
组件而设置的, 通过这个版本号, 用户可以方便的区分不同时期制作的相同名字的COM 组件。Project directory 是整个工程存放的目录。在Compile Code in
里面推荐选择C, 因为C 代码性能较好, C++代码可读性好, 因为我们最后使用的是
DLL, 因此不需要看中间的代码, 所以选择性能较好的C代码。如果要使用Matlab 的
图形库, 那么就要在Compiler options中选中Use Handle Graphics library。选中Build
debug version会生成调试版本的COM组件, 调试版本的COM组件在调试的时候如果
有错误发生, 就会有相应的错误提示信息, 可以的方便的定位与错误发生的地方, 而不
是debug 版本的COM在出错时不会给出任何的错误提示信息。选中Show verbose output
可以在编译生成COM组件的时候输出详细的过程信息。
在工程创建之后, 仍然可以通过菜单Project->Settings… 打开此窗口进行修改。
一个COM 组件可以包含许多类( CLASS),添加的方法就是在Project Setting 里面添加。
对于每个类, 可以为其添加方法( methods), 属性( Properties) 和事件( events), 下面
一一道来。
( 1) 添加类的方法( methods)
给一个类添加一个方法非常的简单: 先用选中所要对其进行操作的类, 然后使用菜
单Project->Add File… 或者按纽Add File添加现成的( 预先编写好的) 文件就可以
了。注意* .m文件不能是脚本文件, 只能是函数文件。
例如我们现为这个类添加一个简单的方法, 测试一下函数Plot。文件如下:
function testplot
t=0:0.001:0.04;</P>
<P>x=sin(2*pi*50*t);
plot(t,x,'r--');
grid on;
title('COM 组件的绘图测试');
这样就为我们的类添加了一个简单的方法。容易吧^-^。
( 2) 添加类的属性( Properties)
Matlab COM builder 自动的把形成类的方法的* .m 函数中所包含的全局变量转换
为类的方法。* .m 中的全局变量是通过关键字global 定义的。
例如我们再为这个添加如下方法:
function result=testproperty()
global A;
if(isempty(A))
result=0;
return;
end
result=det(A);
经过编译A就变成了类testcom的一个属性。
( 3) 添加类的事件( events)
为类添加事件只需要用到语法% # event 就可以了。例如将下面的函数加入到类中,
经过编译就会形成这个类的一个事件。但是对时间的处理还要在可用应用程序中编
写, 不同的开发语言具体的操作不同。由于还没有用到, 暂时还不知道该如何使用
这个类的事件。
函数文件为:
function testevent(i)
%#event
i
在Matlab 的环境下执行的时候, %#event 语句就被当作是注释, 但在COM Builder
中就起作用了。
所有函数添加完毕之后, 就可以使用菜单Build->COM Object 或者按纽Build进行编译
了。编译完毕之后会在工程文件夹下生成两个文件夹: 一个是src, 里面存放的是一些
中间文件; 一个是distrib,里面就是我们想要的东西。
在编译完毕之后, Matlab 会自动组成生成的component, 我们可以使用菜单
Component->Component Info… 将系统注册表里面有关于ljwtestcom_1_0.dll 的详细信息
调出来看一看有关生成的component的信息。
3 VC中使用生成的COM组件
在使用组件之前一定要先对组件进行注册, 而注册工作Matlab 已经替我们完成了。
在VC++6.0中使用COM组件颇有一点麻烦。首先用Microsoft Visual Studio的工具OLE
Viewer 打开生成的组件。具体操作过程如下:启动工具OLE Viewer,展开Type Libraries,
从中找出刚才编译生成并进行注册的组建ljwtestcom 1.0 Type Library (Ver 1.0),双击打开
它。也可以通过菜单File->View Type lib… 或者工具栏直接打开刚才生成的组建
ljwtestcom_1_0.dll。然后将其保存为*.h 和*.c 文件, 我们主要通过这两个文件实现
VC++6.0对组件的使用。新建一个VC++6.0工程, 工程类型为MFC AppWizard( exe),
名称为comtest,类型选择Dialog based。然后把刚才生的*.h 和*.c文件( ljwtestcom_1_0.h
和ljwtestcom_1_0.c)拷贝到工程文件夹下并添加到工程之中。另外由于会使用到Matlab</P>
<P>提供的一些其他的头文件, 因此推荐对VC IDE 做如下设置:
在VC++ IDE 中选择Tools->Options->Directories。
在Show directories for:中选择Include files, 添加如下两个目录:
<Matlab>\extern\include\
<Matlab>\extern\include\cpp
在Show directories for:中选择Library files, 添加如下两个目录:
<Matlab>\extern\lib\win32
<Matlab>\extern\lib\win32\microsof\msvc6
这里假设<Matlab>为你的Matlab 的安装目录。
在VC++ IDE 中选择Project->Setting->C/C++
在Category中选择Precompiled Headers,选择Automatic use of precompiled headers,并且
在Through header 下面添加stdafx.h。
要使用COM组建有如下的几个步骤:
( 1) 包含必要的头文件
( 2) 初始化COM 库::CoInitialize(NULL)
( 2) 创建组件实例使用函数CoCreateInstance
( 3) 使用相应的类中的方法
( 5) 析构组件实例使用类的方法Release
( 6) 释放COM 库::CoUninitialize();
接下来就通过具体的例子来说明。
在要使用COM 组件的源文件中添加如下头文件:
#include "mwcomutil.h"
#include "mwutil.h"
#include "ljwtestcom_1_0.h"
注意, 顺序不能错。添加完毕之后, 我们会在ClassView 中看到多出来了几个类。
然后就是找个合适的地方注册和释放COM。这里我实在初始化函数OnInitDialog 和析
构函数中完成的。
先做个简单一点的, 没有参数传递的调用。在你的源文件中添加如下代码:
// 创建组件实例
Itestcom *tc=NULL;
// 其中CLSID_testcom 和IID_Itestcom 可以在ljwtestcom_1_0.h 中找到
HRESULT
hr=CoCreateInstance(CLSID_testcom,NULL,CLSCTX_ALL,IID_Itestcom,(void**)&tc);
if (FAILED(hr))
{
AfxMessageBox(" 创建组件实例失败");
return;
}
// 使用相应的类中的方法
tc->testplot();
// 析构组件实例
tc->Release();</P>
<P>接下来就来一个有实用价值的, 包含矩阵参数传递的例子。
Matlab 在编译生成COM 组件的时候,一律将所有的.m函数中的参数转换为VARIANT
变量。在生成的COM组件的类中的方法的格式如下:
函数名( 输出参数个数, 输出参数列表, 输入参数列表)
如果没有输出参数就相应的没有前两项。
在VC++6.0 中的使用VARIANT 类型的变量比较麻烦,必须通过Windows API函数,而且
要遵从以下规则:
(1) 在使用之前先用函数VariantInit 进行初始化
(2) For the types VT_UI1, VT_I2, VT_I4, VT_R4, VT_R8, VT_BOOL, VT_ERROR,
VT_CY, VT_DECIMAL, and VT_DATE, data is stored within the VARIANT structure.
Any pointers to the data become invalid when the type of the variant is changed.
(3) For VT_BYREF | any type, the memory pointed to by the variant is owned and freed by
the caller of the function.
(4) For VT_BSTR, there is only one owner for the string. All strings in variants must be
allocated with the SysAllocString function. When releasing or changing the type of a
variant with the VT_BSTR type, SysFreeString is called on the contained string.
(5) For VT_ARRAY | any type, the rule is analogous to the rule for VT_BSTR. All arrays in
variants must be allocated with SafeArrayCreate. When releasing or changing the type
of a variant with the VT_ARRAY flag set, SafeArrayDestroy is called.
常用API函数
VariantInit
VariantClear
VariantCopy
VariantChangeType
VariantCopyInd
VariantChangeTypeEx
在你的VC 的源文件中添加如下代码:
// 创建组件实例
Itestcom *tc=NULL;
// 其中CLSID_testcom 和IID_Itestcom 可以在ljwtestcom_1_0.h 中找到
HRESULT
hr=CoCreateInstance(CLSID_testcom,NULL,CLSCTX_ALL,IID_Itestcom,(void**)&tc);
if (FAILED(hr))
{
AfxMessageBox(" 创建组件实例失败");
return;
}
// 创建相应的VARIANT 变量
double xdata={1.0,2.0,3.0,4.0};
double ydata=0; // 记录计算结果
double tmpdata=0;
VARIANT x,y,tmp;
VariantInit(&x);
VariantInit(&y);</P>
<P>VariantInit(&tmp);
x.vt=VT_R8|VT_ARRAY;
SAFEARRAYBOUND bound;
bound.cElements=2;
bound.lLbound=0;
bound.cElements=2;
bound.lLbound=0;
x.parray=SafeArrayCreate(VT_R8,2,bound);
if (x.parray==NULL)
{
AfxMessageBox(" 内存分配失败");
return;
}
x.parray->pvData=xdata;
// 使用相应的类中的方法
tc->put_A(x);
tc->testproperty(1,&y);
// 显示类的属性的值
tc->get_A(&tmp);
memcpy(xdata,tmp.parray->pvData,4*sizeof(double));
CString str;
str.Format("%f,%f\n%f,%f",xdata,xdata,xdata,xdata);
MessageBox(str," 矩阵",MB_OK|MB_ICONINFORMATION);
// 显示计算结果
ydata=y.dblVal;
str.Format("%f",ydata);
MessageBox(str," 矩阵的行列式的值",MB_OK|MB_ICONINFORMATION);
// 析构组件实例
tc->Release();
这里面涉及到SAFEAEEAY 数据类型的使用。欲创建的矩阵的维数就是
SAFEARRAYBOUND 行数组的数组元素个数。具体的使用请见MSDN。这里传递的参
数都是实数类型, 如果要传递附属类型, 可以参阅Matlab 提供的帮助。不过建议将复
数的实部和虚部分开作为两个实数数组传递。
4 COM组件的打包与程序的发布
程序开发完毕就要发布了, 对于我们要使用的的COM 组件可以使用Matlab 的COM
Builder进行打包。具体的操作是通过菜单Component->Package Component 完成的。在
distrib 文件夹下会生成一个自解压文件ljwtestcom.exe, 通过运行它实现必要的动态链
接库的安装和COM 组件的注册。按照提示就可以了。主要操作有两个: 一个是输入解
压目录, 一个就是设置系统的搜索路径(path)。需要注意的问题就是你说输入的文件夹
目录不能包含中文( 真是遗憾, 毕竟这不是国人开发的)。
大功告成, 可以运行我们的程序了。呵呵 谢谢了 </P> <P>楼主使用的方法怎么说呢.可以简化.</P><P>既然是COM组件,那么直接用VC的#import来导入到应用程序或是动态库中就可以使用了.</P> 学习了,有所帮助,谢谢
页:
[1]