【doc】用Visual Basic调用自定义Visual C++动态链接库的效率研究
用Visual Basic调用自定义Visual C,,动
态链接库的效率研究
第l9卷第2期长沙电力学院(自然科学版)Vo1.19No.2
2004年5月
J0URN^JLOFCHANGSHAUNIVERSITYOFELECTRICPOWER(NATURALSCIENCE)May2004
用VisualBasic调用自定义VisualC++
动态链接库的效率研究
龚红仿
(长沙理工大学数学与计算科学学院,湖南长沙410077)
摘要:通过Mandelbrot(曼德尔布罗特)集中计算不规则图形象素点的迭代算法,介
绍了一种用visualBasic调用
外部的VisualC++动态链接库的方法,并将其与纯VisualBasic应用程序的运行效
率进行了比较,同时描述了用vi.
sualC++制作动态链接库的一般方法和编程步骤.
关键词:VB语言;vc++语言;动态链接库;Mandelbrot集;效率分析
中图分类号:TP393.03文献标识码:A文章编号:1006.7140(2004)02.OOO5.04 EfficiencyResearchonInvokingUser-def'medVC++DLLbyVisualBasic GONGHong-fang
(C~egeofMath.&ComputingScience,ChangshaUniv.ofScience&Tech.,Chan
gsha410077,China)
Abstract:Themethodofinvokinguser—
definedVC++DLLbyVBisintroducedwit}Itheuseoftheiterative algorithmofcaculatingirregulargraphicsinMandelbrot.Themethodiscomparedt}Ithepure
applicationof
VBintheefficiency.andthemethodandstepsofdesigningVC++DLLaredescribed.
Keywords:VB;VC++;DLL;Mandelbrot;efficiencyanalysis
VisualBasic以其编程简单,快捷,界面友好等特 点,深受广大程序员的青睐.但VB是一种解释执行 的程序设计语言,运行速度没有编译执行的语言快, 对于庞大的科学计算问题,VB的这种慢速很不合 适.VisualC++以其灵活,高效,功能强大等优点而 获得广泛应用.如何发挥VB和VC++这两种语言 的优势,用VB设计一种良好的Windows用户界面来 调用C++程序呢?可以通过动态链接库(以下简称 DLL)将这两种语言结合起来.
1用vC++编写DLL程序的一般方
法
动态链接库就是执行代码库,是一种可执行的 "寄生"程序,主要通过使用别的应用进程,堆栈来执 行自己.因而,在用VB编程时,可以将部分功能用 VC++编写成DLL程序,再用VB调用该DLL,既提 收稿日期:20044)3-05
基金项目:国家自然科学基金资助项目(10171008) 作者简介:龚红仿(1968-),男,湖北天门人,长沙理工大学数学与计算科学学院讲师,
学士,主要从事软件测试及计算机应用等方面的研 究.
6长沙电力学院(自然科学版)2004年5月 高了VB程序运行速度,又可以供多个VB应用程序 共享.
最简单的DLL包含4个文件:C++语言模块定 义文件(.DEF),头文件(.H),源程序(.CPP)和
文件(.PRJ).这里要用VB编程,并用DLL提供专门 的支持,所以不必生成基于MFC的DLL,只需生成 比较简单的C/C++
的DLL.下面分别介绍这四
种文件的具体结构:
1)模块定义文件(.DEF)的结构和各段的意 义.
模块定义文件(.DEF)即定义文件,提供一列输 出函数,使其在已编译的DLL中以外部程序可以访 问的形式出现.尽管不用模块定义文件(.DEF)也可 以编译和连接DLL,但VB程序调用时,该DLL没有 可识别的进入点.因而,模块定义文件(.DEF)作为 "新的可执行文件头"的一部分,决定该模块的对外 输出及从其它模块的引入.定义文件的结构如下: //文件名:example.DEF,(文件名必须与.CPP 源程序名相同)
LIBRARY"example",//所定义的动态链接库名 DESCRIFrION'example.DLL',//对模块的描述 HEAPSIZE1024,//本模块的堆栈大小,VC++ 可忽略
EXPORTS,//本模块的输出函数
example@1
WEP@2
标准.DEF文件以Library和Description语句开 始.Library语句定义DLL库名;Description语句可选 用于描述保存模块的信息;HEAPSIZE语句用来定义 一
个DLL局部堆栈的初始规模,使DLL的局部堆初 始化,以完成有关DLL装入内存的工作;EXEPORTS 语句定义将被用作来自应用程序或来自其它DLL 人口点的程序,Windows利用这个信息建立一个优 化的序数人口值,允许动态连接机制更快操作且使 用较少的内存.
一
般来说,模块定义文件(.DEF)的结构除去动 态链接库的名字不同外,其它结构基本是固定的. 2)建立头文件(原形函数文件)(.H). 头文件的功能是进一步声明调用函数的函数名 和传递的参数,其形式为:
//文件名:example.H
#defineEXPORTextem"C"一
declspec(dllexport) EXPORTintFARPASCALexaFaple(intparaml,
…
,charparamn);
此处EXPORT用于说明该函数是外部函数,变 元采用C调用格式,库采用输出函数.
3)编写.CPP源程序框架.
C++源程序(设文件名为example.CPP)包括3 种函数:人口函数,输出函数和终止函数.其具体结 构如下:
//文件名:example.CPP
群include<windows.h> //——人口函数——
intFARPASCALLibMain(HINSTANCEhIn— stance,DWORDwDataSeg,DWORDcbHeapSize,
LPSTRlpszCmdLine) {retum(1);}
//——输出函数——
intFARPASCALexample(intparameterl,…,char parametem)
{..?//C++语言应用程序
}
//——终止函数——
imFARPASCALWEP(intnParameter)
{return(1);}
以上各组成部分参数的含义:windows.h头文 件,它包含有数据类型的定义,AP1人口点定义和其 它有用的参数信息.PASCAL说明符定义该程序的 传递参数和净化堆栈的协定.FAR说明DLL外部传 送远指针.LibMain()函数带4个参数:hInstance, wDataSeg,cbHeapSize和lpszCmdLine.hInstance参数是 DLL事例句柄,wDataSeg参数是数据段(DS)寄存器 值,cbHeatxSize参数是在模块定义文件中定义的堆 栈大小,LibMain()使用该值使本地堆栈初始化, lpszCmdLine参数包括命令行信息,但很少被DLL 使用.
DLL的输出函数实现用户所要完成的任务,这 部分是DLL的核心.它与一般C/C++语言程序不 同之处在于无scanf函数,所有的外部指针都是远指 针FAR.DLL还包括1个终止函数,即退出函数,它 的名字必须是WEP,被包括在DLL模块定义文件的 EXPORTS段中.
4)建立工程文件(.PRJ).
工程文件中包含有example.CPP,example.DEF2
个文件,然后编译连接生成DLL.
2用VB调用外部的VC++的DLL
(VB/DLL代码)
为了更好地说明DLL在VB编程中的高效率, 采用Mandelbmt集中需要计算大量浮点数的不规则 图形象素点的迭代算法,实现对VB图片框的着色, 以充分显示DLL的优势.
1)算法描述.
Mandelbrot集就是利用复平面上非常简单的复
第l9卷第2期龚红仿:用visu丑lBasic调用自定义vi$ualC++动态链接库的效率
研究7
解析迭代式:
+.=z+c(c为一复常数).
通过将象素点反复迭代,可以计算出非常精确 的象素点,但浮点数的计算量大,花费时间长.如:计 算4OO×4OO共16万个象素的图形,假设每个象素 需要大约50次重复,则总共要800万次重复,而且 每次重复要用到多个浮点运算.用VB编程来计算 要几分钟的时间,如果用VC++的DLL来运算,速 度就快得多.
2)编写DLL程序.
用VC++构造DLL是一个相对简单的过程,启 动VC++并在IDE中打开"New对话框",在"Project"
标签中选择"Win32Dyna~cLinkLibrary",输入文件 名FractalFill,就可以生成工程文件,编译后可生成 标准DLL文件.为了说明问题的方便,我们省略. DEF文件的定义.
(I).H文件.
与可选的模块定义文件(.DEF)不同,.H头文 件是必需的,因为头文件包含可输出的函数名称和
,其他要调用该DLL的C/C++程序要用头文 件,VB也要有对应的API函数定义语句,也可以通 过头文件看出DLL中提供了哪个函数,需要或返回 哪个参数和变量.FractalFiH.H头文件定义如下: #defineE?)oRT,extem"C"
一
declspec(dhexport)
EXPORTintCAH.RACKDUMandelbrot(double, double);
EXPORTintCALLBACKDllPaintMandelbrot(HDC *,im,int,double,double,double,double); EXPORT
示每个函数为外部函数,变元采用C
调用格式,库采用输出函数.每个函数返回int值,
C~H.RACK宏在VC++中已经定义为FARPASCAL.
(2)CPP文件.
每个DLL程序都有一个人口点,习惯上取名为
DlIMain,也有的取名为LibMain.
这里采用LibMain函数作为入口点.程序Frac.
talFiU.CPP如下:
#include<windows.h>
#include"C:\FractalFiU\Debug\FractalFil1. h"IIDLL的头文件
intCAI,T.RACKLibmain(HINSTANCEhIstance, DWORDwDataSeg,DWORDcbHeapSize,LPSTRlp—
szCmdLine)IIDLL人口函数
{return(I);}
EXPORTintCAI.I.RACKDUMandelbrot(doubledX. Pos,doubledYP)
IIDLL的输出函数,计算象素点
{//迭代计算象素点的代码
if(nSteps<nLimit)return(nSteps%15);//VB 中QbColor()函数参数为0—15
elsereturn(15);}
EXPORTintCALLBACKDllPaintMandelbrot(HDC pDC,intnXSize,intnYSize,doubledXStart,double
dYStart,doubledXEnd,doubledYEnd) IIDLL输出函数,根据象素点颜色值对VB图
片框着色
1..…?//变量声明
SetMapMode(pDC,MM—ANISOTROPIC);
//设置消息映射模式,第二个参数将逻辑单位
换算为应用程序定义的值
SetWindowExtEx(pDC,nXSize,nYSize,NULL);
SetViewportExtEx(pDC,nXSize,nYSize,NULL);
//CDC类的成员函数
for(inti=0;i<nXSize;i++)//循环生成Man.
delbrot集迭代常数dXPo6,dYPos
{dXPos=dXSmrt+i*dXStep;
fl0r(intj=0;j<nYSize;j++) {dYPb8=dYStart—j*dYStep;nIndex=DllMan—
delbrot(dXPo6,dYPos);
if(nIndex<15)SetPixelV(pDC,i,j,PALET.
TEINDEx(nIndex));//着色}
}return(1);}
EXPORTintCALLBACKWEP(intn_Parameter)//
DLL的终止函数
{t~tum(1);}
以上就是VC++工程文件FractalFil1.dsp添加
的文件,用"Build"菜单的"BuildFractaIFiU.dll"命令,
即可编译生成DLL文件,供VB程序调用.
3)用VB调用DLL程序.
就像C/C++的DLL用.H文件声明局部函数
与输出函数一样,VB程序也要事先声明DLL可访
问的函数.但VB不能使用.H文件,而是在窗体级
模块FmctalFil1.fnn文件的开头声明DLL的输出函
数如下:
PrivateDeclareFunctionDllPaintMandelbrotLib "C:\FractalFiU\Debug\FractalFil1.dll"(ByRefhDC AsAny,ByValnXSizeAsLong,ByValnYSizeAsLong, ByValdXStartAsDouble,ByValdYStartAsDouble, ByValdXEndAsDouble,ByValdYEndAsDouble)As LDl1g
DllPaintMandelbrot过程声明为函数,要符合VB
声明API函数的语法规则.由于VC++的int型数据
是32位的,故在VB中声明API函数时,要转换成
Long型.VB中没有指针,在DLL中的指针类型的参
数,在VB中用ByRef(传址)关键字声明参数的传递
8长沙电力学院(自然科学版)2OO4年5月
方式,指定AsAny来禁止类型检查,从而允许将任
意数据类型传递给该参数.
在图片框Picture1的Click事件过程中输入如下
代码:
Pfiv~eSubPicturelClick()
vbResuh=DllPaintMandelbrot(Picture1.hDC. XSize,YSize,XOrg,YOrg,XMax,YMax)'调用API
函数
EndSub
通过调用API函数重复迭代计算象素点并对
Picturel进行着色.
3用VB编程实现内部计算(VB代
码)
单纯用VB编程实现上述功能,在窗体级模块
vbFractalFil1.frm文件中只用一个事件过程实现.图 片框Picture1与"VB/DLL代码"中的图片框Picture1 完全一样,便于比较计算量.其Click事件过程如下: Pfiv~eSubPicture1.Click() ……
'有关初始化代码
Fori=0ToXSize
XPos=XOrg+i*XStep
ForJ=0ToYSize
Do'迭代计算象素点
If(XSqr+YSqr)>=4#3"henbDone=
Tme
IfnSteps>:nLimitThenbDone=Tme
LoopUntilbDone=Tme
Picture1.PSet(i,J),QBColor(nStepsMod15)
'根据QBColor函数的颜色值设置指定区域的 象素点的颜色
NextJ
Nexti
EndSub
以上程序均在VB6.0和VC++6.0环境下运
行通过.
4效率分析
将2种不同的编程方法实现同一功能的应用程 序进行比较,可以明显得出"VB/DLL代码"比"VB 代码"的效率要高.
I)执行速度快.
VB中调用外部的DLL的主要原因是速度的需 要.用装有奔腾?处理器的计算机运行上述程序进
行比较.在VB的IDE环境下直接运行"VB代码", 进行内部计算,大约需要4min17S,而直接运行 "VB/DLL代码",调用外部DLL进行计算,只需要约 10S,比"VB代码"快了近26倍,这是一个巨大的进 步.但这对VB并不公平,DLL包含的本地代码已经 过VC++编译器的优化.如果生成应用程序的可执 行文件再执行,则VB编译器也能做得很漂亮.运行 FractalFill文件夹中的vbFractalFil1.EXE文件,则"VB 代码"大约需要37S.运行FractalFil1.EXE文件,则 "VB/DLL代码"仍需要约10S,还是比"VB代码"快 了近4倍.尽管VB编译器对"VB代码"大有帮助, 对DLL却无济于事,只是调用DLL中的函数而已. 2)节省空间.
在VB调用DLL时,不管应用程序如何编译或 者解释,DLL与应用程序都是独立和分开的,DLL提 供的函数并不放到应用程序的.EXE文件中,其所有 函数仍然留在动态链接库内,只有需要时才调用.如 果有多个应用程序共享服务时,也只有一份DLL,因 而节省了复制代码所需的内存空间.
3)易于修改和维护.
DLL可以随时修改以增加功能,修改DLL是独 立的,并不影响调用它的应用程序.
5结论
本文介绍了VC++环境下制作动态链接库的 一
般框架,通过实例详细描述了用VB调用外部 VC++动态链接库的方法,步骤,并与纯VB应用程 序的运行效率进行了比较,阐明了VB调用DLL的 优势.本文介绍的方法具有广泛的意义,也完全适用
于其它高级语言程序.
参考文献:
[1]杜大鹏.VisualBasic6.0高级编程技巧[M].北京:电子工业出版 社,2OOO.
[2]刘炳文,李风华.VisualBasic6.0Win32API程序设计[M].北京: 清华大学出版社,2001.
[3j官章全,唐晓卫.Visualc++6.0编程实例详解[M].北京:电子 工业出版社.2OOO.
[4]陈天洲.c语言高级程序设计[M].北京人民邮电出版社,2002. [5]丁香荣.Visualc++编程技巧指南[M].成都:电子科技大学出 版社,1998.
[6]刘华杰.分形艺术(电子版)[M].长沙:湖南电子音像出版社, 1997.
[7]龚红仿.VisualBasic应用软件系统集成的窗体调控技术的研 究[J].长沙电力学院(自然科学版),2004,19(1):19-22.