通过使用类型库提高VB调用DLL函数的性能

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 chen3feng 的 blog

 
本文始发于水木清华bbs(smth.org),转载请保留有关信息,谢谢! 


通过使用类型库提高vb调用dll函数的性能


roachcock@smth 
mailto:chen3feng@163.com,
chen3fengx@163.com,
chen3fengx@hotmail.com
 
vb虽然是个很好玩的东西,但是自身功能有限,限制了vb在某些方面的应用.
ms提供了两种两种方式来增强vb的功能,一种是调用com组件,另一种就是调用dll函数
 
说到调用com,大家都很熟悉,在project->reference或者project->component
菜单弹出对话框中选择需要的类型库或者activex控件,就可以把它加入project.
 
调用dll函数也很简单,需要使用declare函数声明先:
declare function messagebox lib "user32" alias "messageboxa" ( _
byval hwnd as long, byval lptext as string, byval lpcaption as string, _
byval wtype as long) as long
 
然后就可以像内部函数一样调用了:
messagebox me.hwnd, "hehe", "test", 0
 
下面我分析一下在上面的调用过程中,到底发生了什么.
 
我们知道,vb的字符串是unicode字符串,每个字符占两个字节,但是翻开winuser.h
我们可以看到messageboxa的真实定义,
winuserapi int winapi messageboxa(hwnd hwnd ,lpcstr lptext,
 lpcstr lpcaption, uint utype);
 
lpcstr是一个const char *类型,是一个指向单字节ansi字符串的指针,
把调用这个函数的project编译成exe,文件,用vc带的depends.exe看到底到如何引用的函数
 
竟然除了msvbvm60.dll.什么也没有!
 
这中间到底发生了什么呢?
 
通过反汇编跟踪生成的代码可以知道,vb要先调用__vbastrtoansi把字符串转化成ansi字
符串,再把得到的ansi字符串传给一个内部函数(一般称为dllfunctioncall),这个函数再
真正去调用dll中的函数
 
如果函数调用后还要使用传给dll函数的字符串参数的话(比如getwindowtext什么的),就
更复杂了,vb编译器会调用一个叫做__vbastrtounicode的函数,把参数转化成unicode,再
传递给函数.
 
也许你会说,messagebox并不会改变传给它的参数,根本不需要在转换成unicode.是的,你
知道,我也知道但是vb编译器不知道,声明函数的declare语句里可没指出参数传递的方向.
 
也许你会说,我的程序只在winnt,win2k,winxp下运行,他们内部都是用的unicode字符串
,我直接调用unicode版本的messageboxw不是更好吗?
 
对不起,由于win9x只基本完全实现了ansi函数,而基本没有实现unicode版本的函数,而w
innt则两者都实现了.为了照顾win9x,vb把声明语句中的string都当成ansi字符串.
//需要unicode,却依赖于ansi操作系统,可怜的vb!
 
由此,我想你已经看到了declare语句到底在那里影响了效率.好吧.让我们开始新的历程
!
 
[idl语言和类型库]
类型库对vb程序员来说应该很熟悉了,这里我只简单的介绍一下idl语言.
idl (interface definition language)是主要用于com和corba的一种接口定义语言,用
来描述各种接口,只是一种描述语言而不是一种编程语言.idl的风格类似于c.idl语言不
但可以描述更多的数据类型还可以描述参数的传递方向,因此上面的两个问题都不再是问
题了.
 
一般使用idl语言都是为了描述com接口等.其实idl也一样可以用于描述全局函数.让我们
看一个例子,
 
还举那个messagebox吧
 
打开vc6,新建一个utitity project,名字就叫mytlb吧
新建一个文本文件,名字还叫mytlb.idl吧,输入以下内容:
 
[ // 方括号表示一个idl属性,用来描述一些额外的信息,要和下面的东西连起来看.
 uuid("9f7b1bc0-28c6-11d7-8f56-0080c8f0a08c") // 用vc带的guidgen生成一个新的
guid,别和我的这个一样哦
]
library mylib // library说明了一个类型库,mylib是库的名字
{
 
 [
  dllname("user32.dll") file://因为下面的模块描述了函数,所以需要dll库的名字
 ]
 module test // test是模块的名字
 {
  [
   entry("messageboxw") // entry属性表明下面函数的入口,可以是函数的名字或者序号
  ]
  long __stdcall message(hwnd hwnd, [in]lpcwstr msg, [in]lpcwstr title, long
 flags);
  // __stdcall表示函数的调用约定,后面是参数表
  // [in]表示参数只传入,[out][in]表示参数只传入,[in,out]表示既传入又传出,类推
 
  // 对于简单的值类型,可以省略,默认就是[in]了
  // 再有函数可以接着写
 }
}
 
按f7编译,一个崭新的类型库诞生了! // btw,直接调用midl编译器也可以,不过我觉得很
多人都不喜欢命令行了, :)
 
把它引入我们的工程,不需要声明直接就可以调用messagebox了.反汇编代码,可以发现没
有多余的转换.
 
而且因为明确的指出了参数传递方向,不需要假设参数会被改变,生成的代码会更高效,
避免了dllfunctioncall的开支,更节省时间.

 
其他函数也可以一样处理,windows 98 resource kit带了一个win.tlb,包含了常用wind
ows类型,常量,以及api的定义,非常方便,完全可以摆脱declared语句的使用.可以在天网
(http://e.pku.edu.cn)搜到.
 
不过这个tlb是ansi版本的(win98嘛),如果需要unicode版本的,就得自己写idl文件编译

 
至于其他不带类型库的的dll函数,你可以找这上面的例子自己来,函数数目比较多的情况
下完全划的来.
 
祝各位假期编程愉快!
 
--
 

本文关键:类型库 DLL VB
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top