Chinaunix首页 | 论坛 | 博客
  • 博客访问: 395164
  • 博文数量: 61
  • 博客积分: 2525
  • 博客等级: 少校
  • 技术积分: 455
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-24 13:22
文章分类

全部博文(61)

文章存档

2008年(4)

2007年(57)

我的朋友

分类: C/C++

2007-07-22 15:23:46

combuilder是matlab6.5才有的,也是mathworks公司推荐使用于混合编程的,这些日子我对他进行了全方位的摸索,感觉是爽呆了,下面我们一起来揭开它的神秘面纱,^_^。

此系列分为以下几块:

1.matlab下做com组件

2.vb,c#.net实现调用

3.vc实现调用

4.打包

5.优缺点评注

其中2,3部分可以选择一个看

有什么问题请发到邮箱:dkf03@mails.tsinghua.edu.cn,也可以上smth,ID:xxhn,bbfrog

后期补充:

6.vc中的数组和com中的VARIANT变量的相互转换(Bycapriccio)

7.vb,.net中的数组和com中的VARIANT变量的相互转换

8.Matlab与VC混合编程(用idl或dll来调用com组件)

9.combuilder与vc混编中复数类型的输出(ByLPCTSTR)





一.matlab下做com组件

com是componentobjectmodule的简称,它是一种通用的对象接口,任何语言只要按照这种接口标准,就可以实现

调用它。matlab6.5新推出来的combuilder就是把matlab下的程序做成com组件,供其他语言调用。

我们先准备两个测试文件,并copy一个图片到c盘下,起名叫1.jpg(这些你都可以改的,我这儿是为了程序方便)

第一个叫im_test.m如下:

functionim_test%这个文件不带输入与输出

I=imread('c:\1.jpg');%因为以前带有imshow的程序用mcc编成dll后用%不了,所以想试combuilder是否

imshow(I);%能支持这些函数

第二个叫split2rgb.m,就是前些日子Zosco发给我的那个程序,因为它用mcc编成dll后有问题,所以我在这儿继续将它进行测试,而且它也带有多个输入及输出参数,所以也正好拿来测试

在matlab的workspace下打comtool,就打开了matlabcombuilder,点击file-newproject,新建一个工程,在componentname里填上comtest,Classname里填上一个sgltest(并将自动生成classes里的comtestremove掉),complie

codein选c或c++都无所谓,将Complieroptions里的UseHandleGraphicslibrary的复选框画上,点击ok就行了。然后点击project--Addfiles,将im_test.m和split2rgb.m添加入工程,然后点Build-ComObject,就会在comtest\distrib\文件夹下生成一个comtest_1_0.dll(它就是做好
的com组件),Build时matlab已经自动将此dll在注

册表中注册,为了下面能用其他编译器调用,我们还需做一个准备工作,开一个dos窗口,进入/bin/win32目录下(matlabroot为你机器上matlab安装的路径),打regsvr32mwcomutil.dll,即对mwcomutil.dll进行注册(这个dll是matlab下作的任何com组件都要用到的dll),下面我们在其他编
译器下调用时就可以用了。

^_^,是不是觉得做起com组件来很简单呢,matlab下可以给com组件中的类添加成员、事件、方法等,我这儿其

实是给类sgltest添加了两个方法,怎么添加成员和方法可以参看matlab的combuilder的帮助。



附录:split2rgb.m的源代码

%%//测试文件

function[m_nHeight,m_nWidth,mOrigR,mOrigG,mOrigB]=Split2RGB(FileName)



%%//读入一个Jpg文件,

mOrigData=imread(FileName);

%mDestData=imresize(mOrigData,0.5);

imwrite(mOrigData,'c:\2.jpg');



%%//用三个变量保存其R,G,B分量

mOrigR=mOrigData(:,:,1);

mOrigG=mOrigData(:,:,2);

mOrigB=mOrigData(:,:,3);





%%//获得图象的高度,宽度

[m_nHeight,m_nWidth]=size(mOrigR);



figure(1);

set(gcf,'MenuBar','none')

imshow(mOrigData);

title(['OrginalImage:',FileName],'Color','b','FontSize',14);

xlabel(['Height:',num2str(m_nHeight),'Width:',num2str(m_nWidth)],'Color','b','FontSize',12);





%%//写param文件

paraFName=[FileName(1:length(FileName)-4),'.param'];

fid=fopen(paraFName,'w');

fwrite(fid,m_nHeight,'uint32');

fwrite(fid,m_nWidth,'uint32');

fclose(fid);



%%//写R分量文件

RFName=[FileName(1:length(FileName)-4),'_R.rot'];

fid=fopen(RFName,'w');

fwrite(fid,mOrigR,'uint8');

fclose(fid);



%%//写G分量文件

GFName=[FileName(1:length(FileName)-4),'_G.rot'];

fid=fopen(GFName,'w');

fwrite(fid,mOrigG,'uint8');

fclose(fid);



%%//写B分量文件

BFName=[FileName(1:length(FileName)-4),'_B.rot'];

fid=fopen(BFName,'w');

fwrite(fid,mOrigB,'uint8');

fclose(fid);



二.vb,c#.net实现调用

这一部分讲vb,c#.net下怎么实现调用上一部分我们生成的comtest_1_0.dll(matlab下做的com组件),记得一定先要对mwcomutil.dll进行注册(怎么注册参看上一部分)

1.vb下实现调用

打开或新建一个vb工程,点project-Reference,在弹出来的窗口中找到comtest1.0TypeLibrary,将前面的复选框选上,点击ok,此时便将此com组件添加到工程里面去了,此时你可以用对象浏览器看到comtest下有个sgltest类,这个类里面有两个方法im_test,split2rgb,还有个MWFlags
成员(这个成员是自动生成的),vb下测试代码如下:

测试im_test方法的代码:

DimstAssgltest

Setst=Newsgltest

Callst.im_test

测试split2rgb方法的代码:

DimstAssgltest

Setst=Newsgltest

DimhAsVariant,wAsVariant,rAsVariant,gAsVariant,bAsVariant,filenameAsVariant

filename="c:\\1.jpg"

Callst.split2rgb(5,h,w,r,g,b,filename)

可见matlab下函数的输入输出参数在com组件里全是variant型的变量,测试大获成功,结果就跟matlab下运行的一摸一样,爽



2.c#.net下实现调用

打开或新建一个c#项目(我采用的是vs.net编辑器),选中右边的解决方案资源管理器中的引用,点鼠标右键,选添加引用,在弹出来的窗口中选com,然后也找到comtest_1_0.dll,点选择,然后确定就可,此时此com组件也添加到工程里面去了,同样我们可以选择对象浏览器来看comtest
及下面的sgltest类,c#测试项目如下:

测试im_test方法的代码:

comtest.sgltestClassst=newcomtest.sgltestClass();

st.im_test();

测试split2rgb方法的代码:

comtest.sgltestClassst=newcomtest.sgltestClass();

objecth=null,w=null,r=null,g=null,b=null;

objectfilename="c:\\1.jpg";

st.split2rgb(5,refh,refw,refr,refg,refb,filename);

可见输入参数是refobject型的,而输出参数是object型的,测试同样大获成功,与matlab下运行的结果一摸一样,爽呆了。





三.vc下调用

这一部分讲vc下实现调用第一部分我们生成的comtest_1_0.dll,同样要记得先对mwcomutil.dll进行注册(怎么注册参看第一部分),vb和.net下实现对com组件的调用很简单,而vc下实现这一步我可是摸索了好几天(主要是vc用的不好)

1.先做一些准备工作,用oleviewer工具

开始--程序--Microsoftvisualstudio6.0--Microsoftvisualstudio6.0Tools--OLEviewer(这个工具也可以通过在vc下点Tools--OLE/COMObjectViewer来打开,在Oleviewer工具里,在右边选择Typelibraries,将他展开,找到comtest1.0TypeLibrary,选中它,点鼠标右键,
选view,便又弹出一窗口,点工具栏上的save按钮,分别将他保存为comtest_1_0.h,comtest_1_0.c(也可以存为comtest_1_0.Idl接口文件),我们就可以通过这两个文件实现对comtest_1_0.dll调用

2.vc下调用

新建或打开一个vc工程,注意,此时不用对编译器进行任何设置(而用mcc生成的dll我们么设置一大堆,我这儿只设置了Precompiledheaders,选Automaticuseofprecompiledheaders,写上stdafx.h),将comtest_1_0.h和comtest_1_0.c加入工程,并复制一个comtest_1_0.dll到工程目录
下,由于comtest_1_0.dll还要用到mwcomutil.dll,所以将/extern/include/下的mwcomutil.h也加入工程

(将这两个文件拷贝入工程路径下,如果设置了librarypath,可以不加)

此时可以通过classView看到多出了_IID、IMWUtil,Isgltest类,Isgltest就是我们在matlab下建起来的sgltest类

vc下代码如下

//这几个是引入dll和头文件

#import"mwcomutil.dll"

#import"comtest_1_0.DLL"

#i nclude"mwcomutil.h"

#i nclude"comtest_1_0.h"

#i nclude"comutil.h"//此文件是用来处理由char*向VARIANT类型的转换



测试im_test方法的代码:

if(FAILED(CoInitialize(NULL)))//初始化调用com

{

AfxMessageBox("unabletoinitializeCOM");

}

Isgltest*st=NULL;

//创建一个com组件,CLSID_sgltest和IID_Isgltest可以从comtest_1_0.h和comtest_1_0.c里找到

HRESULThr=CoCreateInstance(CLSID_sgltest,NULL,CLSCTX_ALL,IID_Isgltest,(void**)&st);

if(SUCCEEDED(hr))

{

st->im_test();

AfxMessageBox("succeed");

st->Release();

}

else

{

AfxMessageBox("unsucceed");

}

如果你的vc工程是consoleproject的话,上述的AfxMessageBox可改成printf或cout,

测试split2rgb方法的代码(类型转换我参照visualc的精华区也转换成功了)

if(FAILED(CoInitialize(NULL)))

{

AfxMessageBox("unabletoinitializeCOM");

}

Isgltest*st=NULL;

HRESULThr=CoCreateInstance(CLSID_sgltest,NULL,CLSCTX_ALL,IID_Isgltest,(void**)&st);

VARIANTm,n,r,g,b,filename;

VariantInit(&m);

VariantInit(&n);

VariantInit(&r);

VariantInit(&g);

VariantInit(&b);

VariantInit(&filename);

filename.vt=VT_BSTR;

filename.bstrVal=_com_util::ConvertStringToBSTR("C:\\1.jpg");

if(SUCCEEDED(hr))

{

st->split2rgb(5,m,n,r,g,b,filename);

st->Release();

AfxMessageBox("succeed");

}

else

{

AfxMessageBox("unsucceed");

}

同样,运行结果与matlab下的结果一摸一样,记得我们的im_test里面可是使用了imshow阿,以前用mcc生成的程序中用它可是有错哦,爽呆了。

关于VC下用com组件及其类型的转变请参看msdn及其VisualC的精华区。



四.打包:

^_^,combuilder系列可以结尾了

在matlab下的workspace里打comtool,点file-openproject将我们先前建好的comtest.cbl工程文件打开,再点

component--packagecomponent就实现了打包,此时到comtest\distrib文件夹里看,生成的comtest.exe就是打包后的解压程序,双击它会解压出一些文件,再点击解压出来的_install.bat就可以实现安装(按他的步骤去做就行了)



五.优缺点评注

这几天用这个combuilder可把我给爽死了,特别是在vc下调用成功时,记得精华区里曾讲combuilder没有什么实质性的突破,我可不这么认为,它的突破可大了

1.做出来的是com,通用的,任何编译器只要支持com,就可以实现调用,想c++builder,Delphi等的,我想只要按照调用com组件去做,也能成功的

2.支持imshow等一些原来混编用不了的函数,对图形库的支持也比以前强(这些还需各位大侠共同测试)

3.实现方法简单,没有像以前混编还要设置一大堆东东

4.能够在matlab下写自己的类,并为自己的类编写成员、方法和事件,管理工程也方

便(这个有点像vc、vb下管理工程一样)

用的太爽了,一下子还不知道怎么写缺点了,^0^,我想缺点还需大家一起用来找出

我这儿说一个缺点,感觉它的参数全是VARIANT型的,不怎么方便



后期补充:



六.vc中的数组和com中的VARIANT变量的相互转换(Bycapriccio,感谢)

用一个例子说明一下,

首先用matlab的m文件描述一下例子中com组件的功能:

functiony=hehe(x)

y=x*2;

把该文件保存为hehe.m



用combuilder中建立工程comparatest,加入类paratest,

然后加入文件hehe.m,再build成为com组件。



下面是vc中的代码:

#i nclude"stdafx.h"

#i nclude"stdio.h"

#import"mwcomutil.dll"

#import"comtest2_1_0.DLL"

#i nclude"mwcomutil.h"

#i nclude"comtest2_1_0.h"



#i nclude"comutil.h"



intmain(intargc,char*argv[])

{

doublea[4]={1,2,3,4};

inti=4;

if(FAILED(CoInitialize(NULL)))//初始化调用com

{

printf("unabletoinitializeCOM");

}

VARIANTx,y;

VariantInit(&x);

VariantInit(&y);

x.vt=VT_R8|VT_ARRAY;//声明x的类型为VT_ARRAY,

//Array中的元素为double型

SAFEARRAYBOUNDrgsabound[1];

rgsabound[0].cElements=4;//元素的个数

rgsabound[0].lLbound=0;//矩阵索引的下界

x.parray=SafeArrayCreate(VT_R8,1,rgsabound);//该函数新建

//一个SafeArray,并把这个SafeArray的描述

//符的指针传给x.parray。

x.parray->pvData=a;

Iparatest*st=NULL;

//创建一个com组件,CLSID_paratest和IID_Iparatest可以从

//comparatest2_1_0.h和comparatest2_1_0.c里找到

HRESULThr=CoCreateInstance(CLSID_paratest,NULL,CLSCTX_ALL,

IID_Iparatest,(void**)&st);

if(SUCCEEDED(hr))

{

st->hehe(1,&y,x);

memcpy(a,y.parray->pvData,4*sizeof(double));

//当matlab文件中返回的矩阵y是实数行的时候,com组件

//把得到的y的值存放再y.parray->pvData所指向的内存区域。

printf("%f\n%f\n%f\n%f\n",a[0],a[1],a[2],a[3]);

printf("succeed");

st->Release();

}

else

{

printf("unsucceed");

}

return0;

}



上面的方法解决了实矩阵参数在com组件和vc之间的传递,应该也能用到其他

类型的矩阵,比如字符和整型的矩阵。但是如果com组件是由matlabcombuilder

编译m文件得到的,而且m文件所返回的矩阵是复数型的,就不行了。因为好像

这时返回的矩阵并不是如上所述保存在y.parray中,至于到底保存在什么地方

就有待于大牛们把它找出来了,反正俺琢磨了一整天也没有一点头绪:(



七.vb,.net中的数组和com中的VARIANT变量的相互转换

vb和.net变量的相互转换也很简单,下面举例简单说一下:

在我们前面的sgltest里面增加一个函数叫y=paratest(x),x,y为输入输出,都为矩阵

1.vb下:

DimstAssgltest

Setst=Newsgltest

Dimx(2)AsVariant,yAsVariant

x(0)=3#

x(1)=5#

x(2)=6#

Callst.paratest(1,y,x)

y(1,0),y(1,1),y(1,2)就是输出的数组

2..net下:

comtest.sgltestClassst=newcomtest.sgltestClass();

objecty=null;

object[]x;

x=newObject[3];

x[0]=2.0;

x[1]=3.0;

x[2]=4.0;

pt.para_test(1,refy,x);

y[0,0],y[0,1],y[0,2]就是输出的数组



八.Matlab与VC混合编程(用idl或dll来调用com组件)

这儿再提供一种通过Idl或dll来调用com组件的方法(不用生成*.h和*.c文件)

其实这种方法比通过生成.h和.c文件来调用要简单一些:),还用精华区的例子

1.通过ole/comobject生成idl文件,将此idl文件加入工程,点菜单build-complie

comtest_1_0.tlb,生成tlb文件

2.打开classwizard,点右边的addclass-fromatypelibraryclass,选中刚生成的

comtest_1_0.tlb(应该在debug\目录下),其实这儿也可以直接选择用matlab的combuilder

生成的comtest_1_0.dll(这样就省去了第一步),后面将出现生成class的一些对话框,照

提示去做就可以了,这样就生成了comtest_1_0.h和comtest_1_0.cpp,多了一个Isgltest

类:)

3.测试代码如下:

头文件中只需添加:

#i nclude"comtest_1_0.h"

测试代码:

Isgltestst;

AfxOleInit();

if(st.CreateDispatch(_T("comtest.sgltest")))

{

st.im_test();

AfxMessageBox("Haha,Succeeded");

st.ReleaseDispatch();

}

else

AfxMessageBox("UnSucceeded");



上述代码的具体意义参看msdn,我也是刚刚摸索到:)



九.combuilder与vc混编中复数类型的输出(ByLPCTSTR)

今天瞎捣鼓了一下,在前辈们的基础上解决了这个问题;)

调用com的方法和前面的几位略有不同,个人感觉比他们的简单、正规一点;)



step1建立m文件,build成com

m文件写的很简单

functioncplx=getcplx()

cplx=1+2i

方便后面看代码,写出我建立com工程时组建名称:AMyCom类:MyTst



step2导出必要的头文件

1.打开OLEviewer(vc6.0菜单:tools--〉OLE/COMObjectViewer

2.OLEviewer菜单:file-->viewTypeLib

3.打开combuilder生成的dll:AMycom_1_0.dll

4.File-->saveas选择生成.h文件

5.将生成的头文件拷到vc工程路径下

6.重复以上步骤生成mwcomutil.dll的头文件



step3vc下的简单测试源代码如下:



#i nclude

#i nclude"stdio.h"

#i nclude"mwcomutil.h"

#i nclude"Amycom_1_0.h"



voidmain()

{

CoInitialize(NULL);//初始化com



//创建Com实例

CLSIDCLSID_MyTst;

CLSIDFromProgID(L"AMyCom.MyTst.1_0",&CLSID_MyTst);

//这里的ProgID可以在combiulder的component->componentinfo里面看到

//或者搜索注册表得到

IIDIID_IMyTst=__uuidof(IMyTst);



IMyTst*pMyTst=NULL;

CoCreateInstance(CLSID_MyTst,NULL,CLSCTX_ALL,IID_IMyTst,(void**)&pMyTst

);



////////

VARIANTr[5];

for(inti=0;i<5;i++)

{

VariantInit(&r[i]);

}

pMyTst->getcplx(1,&r[0]);//调用函数接口



IMWComplex*pTemp=(IMWComplex*)r[0].pdispVal;//复数类型的接口

pTemp->get_Real(&r[1]);

pTemp->get_Imag(&r[2]);

doubledr=r[1].dblVal;//获取实部

doubleimg=r[2].dblVal;//获取虚部



printf("real:%f\nimg:%f\n",dr,img);//

getchar();



CoUninitialize();

return;

}



个人感觉matlab的一部分比较复杂的数据类型都做成了com组建封装在mwcomutil.dll中,当

用OLEviewer打开该文件时可以看到好多接口,其中包括IWMComplex即复数类型的接口,co

m参数的传递通过VARIANT类型来实现,可以在MSDN上查到详尽的用法

阅读(4504) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~