在VB 中调用动态连接库
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在VB 中调用动态连接库
作为一种简单易用的Windows开发环境,Visual Basic从一推出就受到了广大编程人员的欢迎。它使程序员不必再直接面对纷繁复杂的Windows消息,而可以将精力主要集中在程序功能的实现上,大大提高了编程效率。但凡事有利必有弊。
VB中高度的封装和模块化减轻了编程者的负担,同时也使开发人员失去了许多访问低层API函数和直接与Windows交互的机会。因此,相比而言,VB应用程序的执行效率和功能比C/C++或Delphi生成的程序要差。为了解决这个问题,在一个大型的VB开发应用中,直接调用Windows API函数几乎是不可避免的;同时,还有可能需要程序员自己用C/C++等开发一些动态连接库,用于在VB中调用。本文主要讨论在32位开发环境Visual Basic 5.0中直接调用Windows 95 API函数或用户生成的32位动态连接库的方法与规则。
Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、 DLL、OCX 等)调用。动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用,而内部函数则只能在动态连接库内部使用。尽管动态连接库也能输出数据,但实际上它的数据通常是只在内部使用的。使用动态连接库的优点是显而易见的。将应用程序的一部分功能提取出来做成动态连接库,不但减小了主应用程序的大小,提高了程序运行效率,还使它更加易于升级。多个应用程序共享一个动态连接库还能有效地节省系统资源。正因为如此,在Windows系统中,动态连接库得到了大量的使用。
一般来说,动态连接库都是以DLL为扩展名的文件,如 Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多,如VisualC++、BorlandC++、Delphi等,具体方法可以参见相关文档。下面只以Visual C++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中的编译选项"结构成员对齐方式(structure member alignment)" 应设成4字节,其原因将在后文详细介绍。
4、由于在C中整型变量是4个字节,而VB中的整型变量依然只有2个字节,因此在C中声明的整型(int)变量在VB中调用时要声明为长整型(long),而C 中的短整型(short)在VB中则要声明成整型(integer);下表针对最常用的C语言数据类型列出了与之等价的Visual Basic 类型(用于32位版本的Windows)
C语言数据类型在VisualBasic中声明为调用时使用的表达式
ATOM ByVal variable As Integer 结果为Integer 类型的表达式
BOOL ByVal variable As Long 结果为Long 类型的表达式
BYTE ByVal variable As Byte 结果为Byte 类型的表达式
CHAR ByVal variable As Byte 结果为Byte 类型的表达式
COLORREF ByVal variable As Long 结果为Long 类型的表达式
DWORD ByVal variable As Long 结果为Long 类型的表达式
HWND, HDC, HMENU ByVal variable As Long 结果为Long 类型的表达式等Windows 句柄
INT, UINT ByVal variable As Long 结果为Long 类型的表达式
LONG ByVal variable As Long 结果为Long 类型的表达式
LPARAM ByVal variable As Long 结果为Long 类型的表达式
LPDWORD variable As Long 结果为Long 类型的表达式
LPINT, LPUINT variable As Long 结果为Long 类型的表达式
LPRECT variable As type 自定义类型的任意变量
LPSTR, LPCSTR ByVal variable As String 结果为String 类型的表达式
LPVOID variable As Any 任何变量(在传递字符串的时候使用ByVal)
LPWORD variable As Integer 结果为Integer 类型的表达式
LRESULT ByVal variable As Long 结果为Long 类型的表达式
NULL As Any 或ByVal Nothing 或
ByVal variable As Long ByVal 0& 或VBNullString
SHORT ByVal variable As Integer 结果为Integer 类型的表达式
VOID Sub procedure 不可用
WORD ByVal variable As Integer 结果为Integer 类型的表达式
WPARAM ByVal variable As Long 结果为Long 类型的表达式
5、VB中进行32位动态库的声明时,函数名是大小写敏感的。在获得了需要的动态连接库之后,就可以在VB中进行调用了。但是,由于VB不能验证应用程序传递到动态连接库中的参数值是否正确,因此VB程序中大量的API调用可能会降低整个应用程序的稳定性,也会增加以后维护的难度。所以,决定在VB程序中直接调用API函数时要慎重,但适当的使用API调用确实能够有效地提高VB程序的性能。这之间的平衡需要编程人员根据实际情况来掌握。下面就具体介绍一下在VB中调用API函数时需要做的工作。
要声明一个DLL过程,首先需要在代码窗口的"通用(General)"部分增加一个Declare 语句。如果该过程返回一个值,应将其声明为Function:
Declare Function publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])] As Type
如果过程没有返回值,可将其声明为Sub:
Declare Sub publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])]
缺省情况下,在标准模块中声明的DLL过程,可以在应用程序的任何地方调用它。在其它类型的模块中定义的DLL过程则是模块私有的,必须在它们前面声明Private关键字,以示区分。下面分别介绍声明语句的各个组成部分。