在vb中调用dll的方法.docx

上传人:b****8 文档编号:30710506 上传时间:2023-08-19 格式:DOCX 页数:25 大小:34.37KB
下载 相关 举报
在vb中调用dll的方法.docx_第1页
第1页 / 共25页
在vb中调用dll的方法.docx_第2页
第2页 / 共25页
在vb中调用dll的方法.docx_第3页
第3页 / 共25页
在vb中调用dll的方法.docx_第4页
第4页 / 共25页
在vb中调用dll的方法.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

在vb中调用dll的方法.docx

《在vb中调用dll的方法.docx》由会员分享,可在线阅读,更多相关《在vb中调用dll的方法.docx(25页珍藏版)》请在冰豆网上搜索。

在vb中调用dll的方法.docx

在vb中调用dll的方法

1制作好DLL之后,就可以用VB调用它,实现VB调用C程序。

VB程序要使用DLL中的函数,首先必须要有特殊的声明,用Declare声明语句在窗体级或模块级或全局模块的代码声明段进行声明,将动态链接库中的函数声明到VB中,供VB程序调用。

语句格式为:

DeclareSub过程名Lib[Alias"别名]([ByVal参数AS类型]),或为DeclareFunction函数名Lib[Alias"别名]([ByVal参数AS类型])AS类型在声明中首先用Declare关键字表示声明DLL中的函数。

在C语言中有的函数类型为VOID,它表示不具有返回值,则必须用关键字Sub将其声明成过程。

有的函数具有返回值,则必须用关键字Function将其声明成函数,并且在声明语句的最后要用AS关键字指明函数返回值的类型。

例如上面的ADD.DLL在VB中就可以声明为:

DeclareFunctionADDLib“c:

\ADD.dll”(ByValXASInteger,ByValYASInteger,ByValfileinasstring)ASInteger

通过此声明语句将函数ADD声明到VB中,便可直接调用。

2、dll文件中的函数好像是C语言写的,

//函数名:

int__stdcallGetMacNo(int*MacNo)

//功能:

获取卡机的卡机号(单机时)

//参数:

MacNo[0]-被读出的卡机号

//返回值:

0-成功,

//2-PC接收超时,

//3-应答错误

dll的文件名是COMM232.dll

函数的形参int*MacNo是指针吗?

在VB中应该怎么声明和调用该函数?

VB里也可以定义指针吗?

问题补充:

vb调用dll文件中的函数我是会的,但这儿的形参有一个星号才不知是怎么一回事,

我是这样声明的对吗?

PublicDeclareFunctionGetMacNoLib"COMM232.dll"(ByValMacNoAsInteger)AsInteger

又应该怎么调用呢?

要先定义一个指针的变量再传给*MacNo还是要怎么做?

都说了MacNo是被读出的卡机号,那么就是传址的了。

dimlasinteger

dimmasinteger

l=GetMacNo(m)

ifl=0then

label1.caption="卡机号:

"&m

elseifl=2then

msgbox"PC接收超时"

elseifl=3then

msgbox"应答错误"

endif

Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、DLL、OCX等)调用。

动态连接库包含两种函数:

输出(exported)函数和内部(internal)函数。

输出函数可以被其它模块调用,而内部函数则只能在动态连接库内部使用。

尽管动态连接库也能输出数据,但实际上它的数据通常是只在内部使用的。

使用动态连接库的优点是显而易见的。

将应用程序的一部分功能提取出来做成动态连接库,不但减小了主应用程序的大小,提高了程序运行效率,还使它更加易于升级。

多个应用程序共享一个动态连接库还能有效地节省系统资源。

正因为如此,在Windows系统中,动态连接库得到了大量的使用。

  一般来说,动态连接库都是以DLL为扩展名的文件,如Kernel32.dll、commdlg.dll等。

但也有例外,如16位Windows的核心部件之一GDI.exe其实也是一个动态库。

编写动态连接库的工具很多,如VisualC++、BorlandC++、Delphi等,具体方法可以参见相关文档。

下面只以VisualC++6.0为例,介绍一下开发应用于VisualBasic6.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方,都以VC为例;所有涉及VisualBasic的地方都以VB为例)。

  作为一种32位Windows应用程序的开发工具,VB生成的exe文件自然也都是32位的,通常情况下也只能调用32位的动态连接库。

但是,并不是所有的32位动态库都能被VB生成的exe文件正确地识别。

一般来说,自己编写用于VB应用程序调用的动态连接库时,应注意以下几个方面的

问题:

  1、生成动态库时要使用__stdcall调用约定,而不能使用缺省的__cdecl调用约定;__stdcall约定通常用于32位API函数的调用。

  2、在VC中的定义文件(.def)中,必须列出输出函数的函数名,以强制VC系统将输出函数的装饰名(decoratedname)改成普通函数名;所谓装饰名是VC的编译器在编译过程中生成的输出函数名,它包含了用户定义的函数名、函数参数及函数所在的类等多方面的信息。

由于在VC中定义文件不是必需的,因此工程不包含定义文件时VC就按自己的约定将用户定义的输出函数名修改成装饰名后放到输出函数列表中,这样的输出函数在VB生成的应用程序中是不能正确调用的(除非声明时使用Alias子句)。

因此需要增加一个.def文件,其中列出用户需要的函数名,以强制VC不按装饰名进行输出。

  3、VC中的编译选项"结构成员对齐方式(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&或VBNullString

  SHORTByValvariableAsInteger结果为Integer类型的表达式

  VOIDSubprocedure不可用

  WORDByValvariableAsInteger结果为Integer类型的表达式

  WPARAMByValvariableAsLong结果为Long类型的表达式

  5、VB中进行32位动态库的声明时,函数名是大小写敏感的。

在获得了需要的动态连接库之后,就可以在VB中进行调用了。

但是,由于VB不能验证应用程序传递到动态连接库中的参数值是否正确,因此VB程序中大量的API调用可能会降低整个应用程序的稳定性,也会增加以后维护的难度。

所以,决定在VB程序中直接调用API函数时要慎重,但适当的使用API调用确实能够有效地提高VB程序的性能。

这之间的平衡需要编程人员根据实际情况来掌握。

下面就具体介绍一下在VB中调用API函数时需要做的工作。

  要声明一个DLL过程,首先需要在代码窗口的"通用(General)"部分增加一个Declare语句。

如果该过程返回一个值,应将其声明为Function:

  DeclareFunctionpublicnameLib"libname"[Alias"alias"][([[ByVal]variable[Astype][,[ByVal]variable[Astype]]...])]AsType

  如果过程没有返回值,可将其声明为Sub:

  DeclareSubpublicnameLib"libname"[Alias"alias"][([[ByVal]variable[Astype][,[ByVal]variable[Astype]]...])]

  缺省情况下,在标准模块中声明的DLL过程,可以在应用程序的任何地方调用它。

在其它类型的模块中定义的DLL过程则是模块私有的,必须在它们前面声明Private关键字,以示区分。

下面分别介绍声明语句的各个组成部分。

  

(一)、指定动态库:

  Declare语句中的Lib子句用来告诉VisualBasic如何找到包含过程的.dll文件。

如果引用的过程属于Windows核心库(User32、Kernel32或GDI32),则可以不包含文件扩展名,如:

  DeclareFunctionGetTickCountLib"kernel32"Alias"GetTickCount"()AsLong

  对于其它动态连接库,可以在Lib子句指定文件的路径:

  DeclareFunctionlzCopyLib"c:

\windows\lzexpand.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查找某一函数及其相关数据结构和常数的声明,并复制到自己的程序中。

(二)、使用别名:

A.函数名是标准的名称

  Declare语句中的Alias子句是一个可选的部分,用户可以通过它所标识的别名对动态库中的函数进行引用。

例如,在下面的语句中,声明

了一个在VB中名为MyFunction的函数,而它在动态库Mydll.dll中最初的名字是MyFunctionX。

  PrivateDeclareFunctionMyFunctionLib"Mydll.dll"_

  Alias"MyFunctionX"()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子句:

PrivateDeclareFunctionSetWindowTextLib"user32"_

Alias"SetWindowTextA"(ByValhwndAsLong,ByVal_

lpStringAsString)AsLong

  应当注意,对于VB中使用的系统WindowsAPI函数,应该指定函数的ANSI版本,因为只有WindowsNT才支持Unicode版本,而Windows95不支持这个版本。

仅当应用程序只运行在WindowsNT平台上的时候才可以使用Unicode版本。

  B.函数名是不标准的名称

  有时,个别的DLL过程的名称不是有效的标识符。

例如,它可能包含了非法的字符(如连字符),或者名称是VB的关键字(如GetObject)。

在这种情况下,可以使用Alias关键字。

例如,操作环境DLLs中的某些过程名以下划线开始。

尽管在VB标识符中允许使用标识符,但是下划线不能作为标识符的第一个字符。

为了使用这种过程,必须先声明一个名称合法的过程,然后用Alias子句引用过程的真实名称:

DeclareFunctionlopenLib"kernel32"Alias"_lopen"_

(ByVallpPathNameAsString,ByValiReadWrite_

AsLong)AsLong

  在上例中,lopen是VB中使用的过程名称。

而_lopen则是动态连接库中可以识别的名称。

  C.使用序号标识DLL过程

  除了使用名称之外,还可以使用序号来标识DLL过程。

某些动态连接库中不包含过程的名称,在声明它们包含的过程时必须使用序号。

同使用名称标识的DLL过程相比,如果使用序号,在最终的应用程序中消耗的内存将比较少,而且速度会快些。

但是,一个具体的API的序号在不同的操作系统中可能是不同的。

例如GetWindowsDirectory在Win95下的序号为432,而在WindowsNT4.0下为338。

总而言之,如果希望应用程序能够在不同的操作系统下运行,那么最好不要使用序号来标识API过程。

如果过程不属于API,或者应用程序使用的范围很有限,那么使用序号还是有好处的。

  要使用序号来声明DLL过程,Alias子句中的字符串需要包含过程的序号,并在序号的前面加一个数字标记字符(#)。

例如,Windowskernel中的GetWindowsDirectory函数的序号为432;可以用下面的语句来声明该DLL过程:

DeclareFunctionGetWindowsDirectoryLib"kernel32"_

Alias"#432"(ByVallpBufferAsString,_

ByValnSizeAsLong)AsLong

  在这里,可以使用任意的合法名称作为过程的名称,VB将用序号在DLL中寻找过程。

  为了得到要声明的过程的序号,可以使用Dumpbin.exe等实用工具(Dumpbin.exe是MicrosoftVisualC++提供的一个实用工具,它的使用说明可以参见VC的文档)。

利用Dumpbin,可以提取出.dll文件中的各种信息,例如DLL中的函数列表,它们的序号以及与代码有关的其它信息。

  (三)、使用值或引用传递

  在缺省的情况下,VB以引用方式传递所有参数(ByRef)。

这意味着并没有传递实际的参数值,VB只传递了数据的32位地址。

另外有许多DLL过程要求参数以值方式传递(ByVal)。

这意味着它们需要实际的数据,而不是数据的内存地址。

如果过程需要一个传值参数,而传递给它的参数是一个指针,那么由于得到了错误的数据,该过程将不能正确地工作。

要使参数以使用值方式传递,在Declare语句中需要在参数声明的前面加上ByVal关键字。

例如InvertRect过程要求第一个参数用传值方式传递,而第二个用引用方式传递:

DeclareFunctionInvertRectLib"user32"Alias_

"InvertRectA"(ByValhdcAsLong,lpRectAsRECT)AsLong

  动态连接库的参数传递是一个复杂的问题,也是VB中调用动态连接库时最容易出现错误的地方。

参数类型或传递方式的声明错误都可能导致应用程序出现GPF(通用保护错误),甚至使操作系统崩溃,因此我们将在后面专门详细地讨论这个问题。

  (四)、灵活的参数类型

  某些DLL过程的同一个参数能够接受多种数据类型。

如果需要传递多种类型的数据,可以将参数声明为AsAny,从而取消类型限制。

例如,下面的声明中的第三个参数(lpptAsAny)既可以传递一个POINT结构的数组,也可以传递一个RECT结构:

DeclareFunctionMapWindowPointsLib"user32"Alias_

"MapWindowPoints"(ByValhwndFromAsLong,_

ByValhwndToAsLong,lpptAsAny,_

ByValcPointsAsLong)AsLong

  AsAny子句提供了一定的灵活性,但是,由于它不进行任何的类型检查,风险也随之增加。

因此在使用AsAny子句时,必须仔细检查所有参数的类型。

正确的函数声明是在VB中调用动态连接库的前提,但要想在VB中用对、用好动态库中的函数,仅仅有声明还是远远不够的。

前面已经说过,由于VB不能验证应用程序传递到动态连接库中的参数值是否正确,因此就要求程序员应对参数类型有非常详细的了解,否则很容易引起应用程序发生通用保护错或导致潜在的Bug,降低软件的可靠性。

下面将参数类型分为简单数据类型、字符串、和用户自定义类型三种分别进行讨论。

  

(1)、简单数据类型:

  简单数据类型是指Numeric数据类型(包括Integer、Long、Single、Double、Currency类型)、Byte数据类型和Boolean数据类型。

它们的共同的特点是结构简单,操作系统在处理时不必进行特殊的转换。

  简单数据类型参数的传递比较简单。

我们知道,在VB中传递参数的方式有两种:

传值(Byval)和传址(ByRef),缺省的方式是传址。

所谓传值,就是对一个变量的具体值进行传递;而传址则是传递变量的地址。

例如,在VB程序中需要将一个整型变量m=10的值传进动态库,如果用传值方式,那么传进动态库的值就是10,而在传址方式下,传入的则是变量m的地址,相当于C/C++中&m的值。

需要注意的是,以传值方式传进动态连接库的变量,其值在动态库中是不能被改变的;如果需要在动态连接库中修改传入参数的值,则必须使用传址方式。

一般来说,在VB和动态连接库之间传递单个的简单数据类型,只要注意了以上几个方面就可以了。

当需要将一个简单数据类型的整个数组传进动态库时,必须将相应参数声明为传址方式,然后把数组的第一个元素作为参数传入,这样在动态连接库中就得到了数组的首地址,从而可以对整个数组进行访问。

例如,声明了一个名为ReadArray的DLL过程,要求传入一个整型数组aArray:

DeclareFunctionReadArrayLib"mydll.dll"_

(aArrayAsInteger)AsInteger

在调用时可以采用如下方式:

Dimret,I(5)asInteger

……

ret=ReadArray(I(0))'

将整个数组传入动态连接库

(2)、字符串参数的传递:

  与简单数据类型相比,字符串类型(String、String*n)的参数传递要复杂得多,这主要是Windows95API和VB使用的字符串类型不同的缘故。

VB使用被称为BSTR的String数据类型,它是由自动化(以前被称为OLEAutomation)定义的数据类型。

一个BSTR由头部和字符串组成,头部包含了字符串的长度信息,字符串中可以包含嵌入的null值。

大部分的BSTR是Unicode的,即每个字符需要两个字节。

BSTR通常以两字节的两个null字符结束。

下图表示了一个BSTR类型的字符串。

  (前缀)aTest\0

  头部BSTR指向数据的第一个字节

  另一方面,大部分的DLL过程(包括Windows95API中的所有过程)使用LPSTR类型字符串,这是指向标准的以null结束的C语言字符串的指针,它也被称为ASCIIZ字符串。

LPSTR没有前缀。

下图显示了一个指向ASCIIZ字符串的LPSTR。

  aTest\0

  LPSTR指向一个以null结尾的字符串数据的第一个字节

  如果DLL过程需要一个LPSTR(指向以null结束的字符串的指针)作为参数,可以在VB中将一个字符串以传值的方式传

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 自考

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1