您好,欢迎访问三七文档
VB调用动态链接库-[Dll编程]作为一种简单易用的Windows开发环境,VisualBasic从一推出就受到了广大编程人员的欢迎。它使程序员不必再直接面对纷繁复杂的Windows消息,而可以将精力主要集中在程序功能的实现上,大大提高了编程效率。但凡事有利必有弊。VB中高度的封装和模块化减轻了编程者的负担,同时也使开发人员失去了许多访问低层API函数和直接与Windows交互的机会。因此,相比而言,VB应用程序的执行效率和功能比C/C++或Delphi生成的程序要差。为了解决这个问题,在一个大型的VB开发应用中,直接调用WindowsAPI函数几乎是不可避免的;同时,还有可能需要程序员自己用C/C++等开发一些动态连接库,用于在VB中调用。本文主要讨论在32位开发环境VisualBasic5.0中直接调用Windows95API函数或用户生成的32位动态连接库的方法与规则。Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、DLL、OCX等)调用。动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用,而内部函数则只能在动态连接库内部使用。尽管动态连接库也能输出数据,但实际上它的数据通常是只在内部使用的。使用动态连接库的优点是显而易见的。将应用程序的一部分功能提取出来做成动态连接库,不但减小了主应用程序的大小,提高了程序运行效率,还使它更加易于升级。多个应用程序共享一个动态连接库还能有效地节省系统资源。正因为如此,在Windows系统中,动态连接库得到了大量的使用。一般来说,动态连接库都是以DLL为扩展名的文件,如Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多,如VisualC++、BorlandC++、Delphi等,具体方法可以参见相关文档。下面只以VisualC++5.0为例,介绍一下开发应用于VisualBasic5.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方,都以VC5为例;所有涉及VisualBasic的地方都以VB5为例)。作为一种32位Windows应用程序的开发工具,VB5生成的exe文件自然也都是32位的,通常情况下也只能调用32位的动态连接库。但是,并不是所有的32位动态库都能被VB生成的exe文件正确地识别。一般来说,自己编写用于VB应用程序调用的动态连接库时,应注意以下几个方面的问题:1、生成动态库时要使用__stdcall调用约定,而不能使用缺省的__cdecl调用约定;__stdcall约定通常用于32位API函数的调用。2、在VC5中的定义文件(.def)中,必须列出输出函数的函数名,以强制VC5系统将输出函数的装饰名(decoratedname)改成普通函数名;所谓装饰名是VC的编译器在编译过程中生成的输出函数名,它包含了用户定义的函数名、函数参数及函数所在的类等多方面的信息。由于在VC5中定义文件不是必需的,因此工程不包含定义文件时VC5就按自己的约定将用户定义的输出函数名修改成装饰名后放到输出函数列表中,这样的输出函数在VB生成的应用程序中是不能正确调用的(除非声明时使用Alias子句)。因此需要增加一个.def文件,其中列出用户需要的函数名,以强制VC5不按装饰名进行输出。3、VC5中的编译选项结构成员对齐方式(structurememberalignment)应设成4字节,其原因将在后文详细介绍。4、由于在C中整型变量是4个字节,而VB中的整型变量依然只有2个字节,因此在C中声明的整型(int)变量在VB中调用时要声明为长整型(long),而C中的短整型(short)在VB中则要声明成整型(integer);下表针对最常用的C语言数据类型列出了与之等价的VisualBasic类型(用于32位版本的Windows)。C语言数据类型在VisualBasic中声明为调用时使用的表达式ATOMByValvariableAsInteger结果为Integer类型的表达式BOOLByValvariableAsLong结果为Long类型的表达式BYTEByValvariableAsByte结果为Byte类型的表达式CHARByValvariableAsByte结果为Byte类型的表达式COLORREFByValvariableAsLong结果为Long类型的表达式DWORDByValvariableAsLong结果为Long类型的表达式HWND,HDC,HMENUByValvariableAsLong结果为Long类型的表达式等Windows句柄INT,UINTByValvariableAsLong结果为Long类型的表达式LONGByValvariableAsLong结果为Long类型的表达式LPARAMByValvariableAsLong结果为Long类型的表达式LPDWORDvariableAsLong结果为Long类型的表达式LPINT,LPUINTvariableAsLong结果为Long类型的表达式LPRECTvariableAstype自定义类型的任意变量LPSTR,LPCSTRByValvariableAsString结果为String类型的表达式LPVOIDvariableAsAny任何变量(在传递字符串的时候使用ByVal)LPWORDvariableAsInteger结果为Integer类型的表达式LRESULTByValvariableAsLong结果为Long类型的表达式NULLAsAny或ByValNothing或ByValvariableAsLongByVal0&或VBNullStringSHORTByValvariableAsInteger结果为Integer类型的表达式VOIDSubprocedure不可用WORDByValvariableAsInteger结果为Integer类型的表达式WPARAMByValvariableAsLong结果为Long类型的表达式5、VB中进行32位动态库的声明时,函数名是大小写敏感的。在获得了需要的动态连接库之后,就可以在VB中进行调用了。但是,由于VB不能验证应用程序传递到动态连接库中的参数值是否正确,因此VB程序中大量的API调用可能会降低整个应用程序的稳定性,也会增加以后维护的难度。所以,决定在VB程序中直接调用API函数时要慎重,但适当的使用API调用确实能够有效地提高VB程序的性能。这之间的平衡需要编程人员根据实际情况来掌握。下面就具体介绍一下在VB中调用API函数时需要做的工作。要声明一个DLL过程,首先需要在代码窗口的通用(General)部分增加一个Declare语句。如果该过程返回一个值,应将其声明为Function:DeclareFunctionpublicnameLiblibname[Aliasalias][([[ByVal]variable[Astype][,[ByVal]variable[Astype]]...])]AsType如果过程没有返回值,可将其声明为Sub:DeclareSubpublicnameLiblibname[Aliasalias][([[ByVal]variable[Astype][,[ByVal]variable[Astype]]...])]缺省情况下,在标准模块中声明的DLL过程,可以在应用程序的任何地方调用它。在其它类型的模块中定义的DLL过程则是模块私有的,必须在它们前面声明Private关键字,以示区分。下面分别介绍声明语句的各个组成部分。(一)、指定动态库:Declare语句中的Lib子句用来告诉VisualBasic如何找到包含过程的.dll文件。如果引用的过程属于Windows核心库(User32、Kernel32或GDI32),则可以不包含文件扩展名,如:DeclareFunctionGetTickCountLibkernel32AliasGetTickCount()AsLong对于其它动态连接库,可以在Lib子句指定文件的路径:DeclareFunctionlzCopyLibc:windowslzexpand.dll_(ByValSAsInteger,ByValDAsInteger)AsLong如果未指定libname的路径,VisualBasic将按照下列顺序查找该文件:①.exe文件所在的目录②当前目录③Windows系统目录④Windows目录⑤Path环境变量中的目录下表中列出了常用的操作系统环境库文件。动态链接库描述Advapi32.dll高级API服务,支持大量的API(其中包括许多安全与注册方面的调用)Comdlg32.dll通用对话框API库Gdi32.dll图形设备接口API库Kernel32.dllWindows32位核心的API支持Lz32.dll32位压缩例程Mpr.dll多接口路由器库Netapi32.dll32位网络API库Shell32.dll32位ShellAPI库User32.dll用户接口例程库Version.dll版本库Winmm.dllWindows多媒体库Winspool.drv后台打印接口,包含后台打印API调用。对于Windows的系统API函数,可以利用VB提供的工具APIViewer查找某一函数及其相关数据结构和常数的声明,并复制到自己的程序中。(二)、使用别名:Declare语句中的Alias子句是一个可选的部分,用户可以通过它所标识的别名对动态库中的函数进行引用。例如,在下面的语句中,声明了一个在VB中名为MyFunction的函数,而它在动态库Mydll.dll中最初的名字是MyFunctionX。PrivateDeclareFunctionMyFunctionLibMydll.dll_AliasMyFunctionX()AsLong需要注意的是,Alias子句中的函数名是大小写敏感的,也就是说,必须与函数在生成时的声明(如在C源文件中的声明)一致。这是因为32位动态库与16位动态库不同,其中的函数名是区分大小写的。同样道理,如果没有使用Alias子句,那么在Function(或Sub)后的函数名也是区分大小写的。通常在以下几种情况时需要使用Alias子句:A.处理使用字符串的系统WindowsAPI过程如果调用的系统WindowsAPI过程要使用字符串,那么声明语句中必须增加一个Alias子句,以指定正确的字符集。包含字符串的系统WindowsAPI函数实际有两种格式:ANSI和Unicode(关于ANSI和Unicode两种字符集的区别将在后面详细阐述)。因此,在Windows头文件中,每个包含字符串的函数都同时有ANSI版本和Unicode版本。例如,下面是SetWindowText函数的两种C语言描述。可以看到,第一个描述将函数定义为SetWindowTextA,尾部的A表明它是一个ANSI函数:WINUSERAPIBOOLWINAPISetWindowTextA(HWNDhWnd,LPCSTRlpString);第二个描述将它定义为SetWindowTextW,尾部的W表明它是一个Unicode函数:WINUSERAPIBOOLWINAPISetWindowTextW(HWNDhWnd,LPCWSTRlpString);因为两个函数实际的名称都不是SetWindowText,要引用正确的函数就必须增加一个Alias子句:PrivateDeclareFunctionSetWindowTextLibuser32_AliasSetWindowTextA(ByValhwndAsLong,ByVal_lpStringAsString)AsLong应当注意,对于VB中使用的系统WindowsAPI函数,应该指定函数的ANSI版本,因为只有WindowsNT才支持Unic
本文标题:VB调用动态链接库
链接地址:https://www.777doc.com/doc-4286379 .html