收藏 分销(赏)

在Visual-C--中使用MATLAB-COM组件.doc

上传人:仙人****88 文档编号:9495441 上传时间:2025-03-28 格式:DOC 页数:15 大小:1.12MB
下载 相关 举报
在Visual-C--中使用MATLAB-COM组件.doc_第1页
第1页 / 共15页
在Visual-C--中使用MATLAB-COM组件.doc_第2页
第2页 / 共15页
点击查看更多>>
资源描述
在Visual C++中使用MATLAB COM组件 taohe Email: taohe@ 摘要:MATLAB COM Builder 是MATLAB v6.5以后才有的一个工具。COM Builder是对MATLAB编译器功能的一种扩展, 它能够把MATLAB的应用程序直接转变成Windows COM组件。COM组件是Windows操作系统的一种核心技术。标准的COM组件可以在多种编程语言中使用。这也就是说,MATLAB COM BUILDER给用户提供了另外一种代码重用以及程序发布的方式,它使得MATLAB应用程序更加容易发布和配置。虽然说COM组件可以在多种编程语言中使用。但是在C++中调用COM组件要比在其他语言比如Visual Basic中使用COM要复杂。MATLAB帮助文件中只给出了如何在Visual Basic里使用COM Builder产生的组件,可惜并没有给出在C++中使用这些组件的例子。这对于一般用户尤其是初学者来说,增加了学习难度。本文将利用一个具体的例子,以详尽的步骤说明如何在Visual C++里使用MATLAB COM组件。 关键字:MATLAB, COM Builder,COM, Component Object Model,C++, Visual C++,MFC。 简介 MATLAB逐渐成为一种工业界广泛使用的计算语言。和早期的MATLAB应用相比,现在的MATLAB不只是用于一般的数值计算,在复杂的科学与工程领域,比如嵌入式系统、实时系统的仿真与开发,复杂系统如汽车引擎控制单元等的仿真与开发等方面,MATLAB也逐渐成为研发技术人员的首选工具。有些商业公司甚至使用MATLAB开发应用程序,包括图形界面和具体算法开发。 这些应用对MATLAB提出了新的要求,那就是如何针对具体应用环境方便地发布和配置MATLAB应用程序。如果所有用MATLAB开发出来的应用程序在实际使用中必须要用户购买和安装MATLAB的话,对用户和开发者来说都是不合情理,是无法接受的。为了满足工业界的这些需要,MATLAB提供给开发者多种辅助工具,包括MATLAB编译器,Real-time workshop等。开发者利用这些工具可以方便的把MATLAB应用程序转变成容易发布和配置的形式。在某种程度上,这些辅助工具的出现,也增强了MATLAB的实用功能,推动了MATLAB在实际中的应用。 从MATLAB 6.5起,MATLAB又多了一项辅助工具,那就是COM Builder,它可以把一般的MATLAB应用程序转变成COM组件。这里COM代表Component Object Model。COM是Windows操作系统的核心技术,也是微软新一代平台“.Net"的核心技术。简单地讲,COM是个标准,任何COM组件必须具备一些特殊的性质。COM组件所能提供的功能都以接口形式实现。每个COM组件都必须具备一些基本的接口。COM组件可以是dll文件,也可以是exe文件。MATLAB COM Builder产生的COM组件是dll文件。 理论上讲,COM并不是只能在Windows操作系统使用,它也有可能在其他的操作系统中使用。另外,COM组件可以方便地在分布式环境中使用。所以使用COM Builder把MATLAB应用程序转变成COM组件,开发者将有可能解决更为广泛的问题。COM组件通常需要在其他编程语言中使用,比如C++, Visual Basic, ASP, VBA, Delphi, JAVA等。COM Builder的帮助文档只给出了如何在VB中使用MATLAB COM组件的例子。在C++中使用COM组件要稍微复杂一些。本文将利用一个具体的例子,详细解说如何在Visual C++中使用MATLAB COM组件。虽然本文以Visual C++ 6.0为例,不过所用的技术完全可以应用于其他的C++编译器。为了理解本文所给出的例子,你至少需要有基本的MATLAB,C++和COM概念。 1 使用MATLAB COM Builder COM Builder是MATLAB C/C++编译器的一种扩展,在使用COM Builder之前,需要运行“mbuilder -setup”命令来配置MATLAB编译器。MATLAB最终是调用外部编译器来产生COM组件。所以配置的主要任务是选择合适的外部的编译器。虽然MATLAB C/C++编译器支持多种外部编译器,但是COM Buildre只支持少数几种。在MATLAB 6.5,COM Builder的版本是1.0,它所支持的编译器有[1]: · Microsoft Visual C++ 5.0, 6.0, 7.0 (Visual Stutio .Net 2002), 7.1 (Visual Studio .Net 2003) · Borland C++ Builder, 3.0, 4.0, 5.0, 6.0 需要注意的是,虽然MATLAB C/C++支持免费的Borland C/C++编译器,但是COM Builder并不支持它。 关于mbuilder的设置很简单,在MATLAB命令行运行“mbuilder -setup”,然后回答屏幕上的问题就行了。这个配置工作只需在第一次使用mbuilder或者COM Builder时执行。 在MATLAB的命令行执行comtool就可以运行COM Builder,这是一个有图形界面的MATLAB应用程序,其运行界面如下图: 图1 程序运行图 现在以COM Builder的帮助文件中的实例magicdemo为例,来产生一个COM组件。首先创建一个m函数mymagic,其定义为: function y = mymagic(x) y = magic(x); 然后保存成mymagic.m。运行COM Builder,选择菜单File-->New Project来创建一个工程,你将会看到一个对话框,在其中填上如图所示的内容。 图2 对话框 然后点击“Ok”。回到COM Builder窗口,首先选择magic,然后点击“Add files”,在出来的文件选择对话框中选择刚才保存的mymagic.m。如下图所示: 图3 程序对话框 然后点击“Build”按钮开始编译。编译过程将完成两项任务:一是转变mymagic.m为dll文件;二是在操作系统中注册产生的COM组件。在这个例子里,产生的COM组件文件名为magicdemo_1_0.dll。 如果要想知道COM Builder产生的magicdemo_1_0.dll组件中有哪些接口,可以使用Windows的OLE/COM对象察看器。在Visual C++开发环境的菜单Tools里选择"OLE/COM Object Viewer"来启动它,一般来说,在其窗口左侧的树形显示里,找到一项叫做“MATLAB COM Builder Components”并且展开它,由MATLAB COM Builder产生的COM组件都会在先是在这个节点下。在那里找到我们刚才产生的magic类,如图4所示: 图4 我们可以看见,对于这个例子,MATLAB COM Builder产生的COM组件总共实现了五个接口:IUnknown, IDispatch, Imagic, IConnectionPointContainer, ISupportErrorInfo。其中IUnknown是任何一个COM组件都必须支持的一个接口,IDispatch给该COM组件提供另外一种可调用的方式,它可以由那些支持自动化的应用程序比如VB编写的程序来以另一种方式调用COM组件。Imagic从IDispatch继承,通过Imagic,用户可以调用mymagic函数,它就是我们希望调用的功能。另外两个接口很少会用到。不过值得一提的是IConnectionPointContainer接口。用户程序和这个接口建立通信后,我们的COM组件可以通过这个接口向外界触发事件,或者调用外部函数,类似CALLBACK函数那样。这在有些时候,比如COM组件在进行某些费时计算操作时,客户程序不必一直等待COM计算结束,而只是设定一个CALLBACK函数,然后就可以进行其他的操作,等到计算结束后,COM组件则会通过IConnectionPointContainer接口调用我们指定的函数,从而进行一些相关的操作。不过,关于这个接口的调用,需要编写大量的代码,限于篇幅,本文将只讨论如何调用Imagic接口。关于COM组件,以及组件接口,可以参阅微软的文档MSDN[2]。 2 在Visual C++使用MATLAB COM组件 中,我们将使用Visual C++ 6.0建立一个基于对话框的MFC图形界面程序,窗口界面类似COM Builder的文档中给出的关于magicdemo的VB程序。值得一提的是,关于COM组件的调用和客户程序的界面没有关系,也和所用的编译器没有关系,所以这里演示的调用技COM组件所涉及的内容非常复杂。幸运的是调用组件比编写组件要容易的多。可以说,调用组件是COM技术里最简单的一种任务。虽然在C++里调用COM组件要比起用其他编程语言比如Visual Basic等复杂一些,但是,当你完成本文的例子后就会发现,在C++里调用COM组件其实也很简单。 几乎所有的COM组件都满足这样一条原则:不管用何种方式获得接口及调用函数,组件的功能必须具有一致性。在本文中,我将给大家演示一种常用的调用方法。在下面的例子术,完全可以用在其他的C++程序里,比如ATL/WTL,Visual C++ .Net,或者Borland C++ Builder等。 首先在VC里建立一个新的基于对话框的MFC AppWizard(exe)工程,取名testmatlabcom,有关工程的选项都选择缺省的设置。然后重新设计对话框,增加一个Group Box,在其中创建一个静态文本, 一个编辑框,一个按钮,以及一个List Control。布局如下图5所示 图5 用鼠标右键点击List Control,选择Properties,然后点击"Styles",把View改成Report。把编辑框和列表控件的ID分别设成IDC_EDIT_SIZE和IDC_LIST_MATRIX。然后打开Class Wizard,根据提示,为对话框建一个新类,然后给Create按钮增加一个消息处理函数。当按下这个按钮后,程序将会运行着个函数,我们将在这个函数里调用magic组件。 接下来,我们编写调用magic组件的代码。第一部需要做的是使用#import来把magicdemo_1_0.dll导入工程中。对于MFC应用程序来讲,我们可以在工程的stdafx.h文件中完成这部操作。在stdafx.h里,加入下面两行: #import "magicdemo_1_0.dll" raw_interfaces_only using namespace magicdemo; 然后,我们就可以在Create按钮的消息响应函数中开始调用magic组件。在调用具体COM组件之前,需要调用系统函CoInitialize(),同样,在结束使用COM组件后,也要调用另一个系统函数CoUninitialize()。为了调用组件所提供的功能,我们需要获得相应的接口。在这个例子离我们需要获得Imagic接口。获得组件接口的方式很多,这里介绍一种常见的获得Imagic接口的方法: HRESULT hresult; CLSID clsid; hresult=::CLSIDFromProgID(OLESTR("magicdemo.magic.1_0"), &clsid); Imagic *pImagic; hresult=::CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER, __uuidof(Imagic),(LPVOID *) &pImagic); 在这里我略去了对错误的检查。在实际应用中,错误检查是很重要的。在上面的这段代码中,我们首先获得组件的CLSID,然后通过它获得指向Imagic的指针。几乎每个跟COM有关的系统函数都会返回HRESULT类型的变量。通过检查返回值,我们可以判断刚才的函数调用是否成功。 获得Imagic接口后,我们就可以直接调用这个接口所提供的mymagic函数了。不过在实现具体调用之前,我们还需要解决一个很重要的问题,那就是如何传递函数的参数表。使用前面提到的OLE/COM对象察看器,察看Imagic接口的定义我们可以发现,在这个接口里,函数mymagic是这样来定义的 void mymagic( [in] long nargout, [in, out] VARIANT* y, [in] VARIANT x); 第一个参数是输出变量的个数。第二个参数是指向VARIANT对象的指针,它是输出变量。第三个是一个VARIANT对象,是输入变量。在MSDN可以查到VARIANT的定义,它是一个结构(struct),其元素包括各种类型的变量,以及数组。在C++中可以通过系统函数来方便的存取VARIANT中的数据。例如: VARIANT x, out; ::VariantInit(&x); ::VariantInit(&out1); CEdit * pEdit = (CEdit*) GetDlgItem(IDC_EDIT_SIZE); CString szTemp; pEdit->GetWindowText(szTemp); long size = atoi(szTemp.GetBuffer(szTemp.GetLength())); szTemp.ReleaseBuffer(); x.vt = VT_I4; x.lVal=size; out1.vt = VT_R8 | VT_ARRAY; SAFEARRAYBOUND bound[1]; bound[0].cElements = x.lVal * x.lVal; // Set up size of array bound[0].lLbound = 0; out1.parray = ::SafeArrayCreate(VT_R8, 1, bound); // Create it pImagic->mymagic (1, &out1, x); //call method 要理解这段代码是很容易的,首先是申明两个VARIANT对象x和out1,在使用VARIANT变量之前,都需要调用VariantInit()函数来把该变量的vt类型初始化成VT_EMPTY。然后在具体使用该变量之前,根据该变量所代表的数据类型再设定VARIANT变量的vt类型,并作相应的附值。比如在上面的例子代码中,我们就是把x.vt设成VT_I4,也就是说x所代表的数据是个整数(int)。这里需要说明一下,magic组件的Imagic接口提供的mymagic函数,其实就是要完成我们在MATLAB函数mymagic里所设计的功能,也就是根据以输入变量作为矩阵大小,产生一个Magic方阵并且返回该方阵。我们可以回想一下,在mymagic.m里,mymagic函数是这样来定义的: function y = mymagic(x) MATLAB缺省的数据类型是双精度实数,也就是double。这也就是说,这个函数希望的输入是一个double型的标量,返回一个double型的方阵。但是这个输入变量x其实只能取自然数,他代表其他所要产生的magic矩阵的行数和列数。在MATLAB环境里测试后就会发现:当x为0或者负数时,mymagic(x)返回的结果是空矩阵。而如果x为正实数时,mymagic(x)只取x的整数部分。也就是说mymagic(4)和mymagic(4.9)的结果是一样的。了解了MATLAB中mymagic函数的特性以后,现在回到C++代码,测试后发现,如果x.vt不是double型(VT_R8),而是个整数(VT_I4, VT_I2)时,结果也是一样的。在这个例子里我使用int型。至于out1变量,我们定义其数据为double型的数组。然后根据输入来确定out1数组的大小,然后创建数组。这样我们就可以调用COM组件中的mymagic函数了。关于VARIANT变量,还有一点需要注意,那就是在使用完这个变量之后,也要记得调用另外一个函数VariantClear()函数。VariantClear并不会释放内存,而只是把该变量的vt类型复原成VT_EMPTY。 接下来的一个问题就是如何取出计算结果。从上面的代码实例中我们看到,计算结果存放在out1,而out1是一个VARIANT变量。如何从VARIANT变量中提取数据的方法很多,最基本的方法可能就是使用系统函数。另外也有一些包装这些函数调用的C++类可以使用。下面是使用系统函数调用的例子: double *nRes; double HUGEP* pDest; nRes = new double[size * size]; ::memset(nRes, 0, size * size * sizeof(double)); ::SafeArrayAccessData(out1.parray, (void HUGEP**)&pDest); ::memcpy(nRes, pDest, size*size*sizeof(double)); // Copy into array ::SafeArrayUnaccessData(out1.parray); ::SafeArrayDestroy(out1.parray); // destroy the array 这段代码从out1的数组里,把数据拷贝到一个普通的C++指针所指向的一块存储地方。接下来的事情就应该是很简单的了,因为从指针所指向的存储单元里处理数据是很简单的任务,任何一个C/C++编程人员闭着眼睛都能写出需要的代码。可惜,这里还有一个重要的问题我们需要注意。如果你已经动手测试过,你会发现,此时nRes里的结果和我们想要得并不相同。这是因为在MATLAB里,矩阵的存储方式是以矩阵的列为先,也就是数据是从第一例开始逐列存储的。而在C/C++,矩阵则是以第一行开始逐行存储的。所以我么还需要再做一点工作,把数据转换成我们希望的结果。 //The result is stored in column first format, now change it to row first. double *rowFirstMatrix; rowFirstMatrix = new double[size*size]; for (int j = 0; j < size; ++j) { for (i = 0; i < size; ++i) rowFirstMatrix[j*size +i] = nRes[j + i*size]; } 这里我们重新申明一个指针,分配合适的大小,然后把数据重新整理一下,使得计算得到的magic矩阵以行模式存储。至此,大功告成。我们可以把结果显示在程序窗口的列表控件。为了方便读者自己测试,下面是Create按钮的消息处理函数完整的代码: void CtestmatlabcomDlg::OnBnClickedButton1() { ::CoInitialize(NULL); //initialize COM library HRESULT hresult; CLSID clsid; //retrieve CLSID of component hresult=::CLSIDFromProgID(OLESTR("magicdemo.magic.1_0"), &clsid); Imagic *pImagic; hresult=::CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER, __uuidof(Imagic),(LPVOID *) &pImagic); if(FAILED(hresult)) { AfxMessageBox("Creation Failed"); return; } VARIANT x, out1; VariantInit(&x); VariantInit(&out1); CEdit * pEdit = (CEdit*) GetDlgItem(IDC_EDIT_SIZE); CString szTemp; pEdit->GetWindowText(szTemp); long size = atoi(szTemp.GetBuffer(szTemp.GetLength())); szTemp.ReleaseBuffer(); double *nRes; if (size <= 0) { ::AfxMessageBox(_T("Magic Square Size must be greater than 0")); return; } else if (size == 1) { x.vt = VT_I4; x.lVal=size; out1.vt = VT_R8; pImagic->mymagic (1, &out1, x); //call method nRes = new double[x.lVal * x.lVal]; nRes[0] = out1.dblVal; } else { x.vt = VT_I4; x.lVal=size; out1.vt = VT_R8 | VT_ARRAY; SAFEARRAYBOUND bound[1]; bound[0].cElements = size * size; // Set up size of array bound[0].lLbound = 0; out1.parray = ::SafeArrayCreate(VT_R8, 1, bound); // Create it pImagic->mymagic (1, &out1, x); //call method double HUGEP* pDest; nRes = new double[size * size]; ::memset(nRes, 0, size * size * sizeof(double)); ::SafeArrayAccessData(out1.parray, (void HUGEP**)&pDest); ::memcpy(nRes, pDest, size*size*sizeof(double)); // Copy into array ::SafeArrayUnaccessData(out1.parray); ::SafeArrayDestroy(out1.parray); // destroy the array. } pImagic->Release(); //call method if ( pImagic != NULL) pImagic = NULL; ::VariantClear(&x); ::VariantClear(&out1); ::CoUninitialize(); //Unintialize the COM library int i=0; //The result is stored in column first format, now change it to row first. double *rowFirstMatrix; rowFirstMatrix = new double[size*size]; for (int j = 0; j < size; ++j) { for (i = 0; i < size; ++i) rowFirstMatrix[j*size +i] = nRes[j + i*size]; } ShowMatrix(rowFirstMatrix, (int) size); delete [] nRes; delete [] rowFirstMatrix; } 其中IDC_EDIT_SIZE是编辑框控件的ID。在函数的最后调用ShowMatrix()函数来把结果显示在窗口的列表控件。ShowMatrix函数首先删除列表中现有的数据,然后再把新数据逐行加入控件。为了方便读者测试,下面给出ShowMatrix函数的完整代码: void CtestmatlabcomDlg::ShowMatrix(double *y, int n) { CListCtrl* pLstBox = (CListCtrl*) GetDlgItem(IDC_LIST_MATRIX); int nColumns; nColumns = pLstBox->GetItemCount(); if(nColumns != 0) { pLstBox->DeleteAllItems(); while(pLstBox->DeleteColumn(0)); } int columnWidth = 70; CString strText; int i, j; i = 1; pLstBox->InsertColumn(0, "", LVCFMT_LEFT, columnWidth); for (j = 1; jInsertColumn(j, strText, LVCFMT_LEFT, columnWidth); } for (i=0; iInsertItem( LVIF_TEXT|LVIF_STATE, i, strText, (i%2)==0 ? LVIS_SELECTED : 0, LVIS_SELECTED, 0, 0); // Initialize the text of the subitems. for (j=1;j < n+1;j++) { strText.Format(_T("%.0f"), y[i*n+j-1]); pLstBox->SetItemText(i, j, strText); } } } 其中IDC_LIST_MATRIX是List Control的ID。现在我们可以编译并运行我们的例子程序。程序运行后的界面如图6所示。 图6 运行界面 我们可以在MATLAB里调用mymagic函数来验证C++程序的结果。比如,同样对于x=5,在MATLAB里的结果是: >> mymagic(5) ans = 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9 可以看出,我们在C++程序里调用COM组件出来的结果和直接在MATLAB李运行的结果是一样的。 3 关于VC工程文件 如果需要完整的VC工程文件,请给我发e-mail (taohe@)。这些代码经测试,可以在VC 6.0, 7.0, 和7.1成功编译。 4 结束语 这篇文章通过一个实例,详细地讨论了如何在C++使用MATLAB COM Builder创建的COM组件。重点放在了一些值得注意的问题,比如如何传递参数,以及传递矩阵数据时所需要注意的问题等。这些问题和其他很多问题一样,如果知道怎么做以后,就会觉得很简单。可惜在公开领域包括Internet,有关在C++使用MATLAB COM组件的文献几乎没有,我在去年刚开始这方面的尝试的时候,就是因为找不到合适的文献,才摸索了很长时间,时至今日,我发现还是没有没有完整的资料。这篇文章的目的就是分享我自己学习心得,希望能够给初学者提供参考资料。 参考文献 1. The Mathworks, Supported compilders for MATLAB 6.5 2003 2. MSDN, 2004
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 教育专区 > 小学其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服