首页 VB调用动态链接库(DLL)

VB调用动态链接库(DLL)

举报
开通vip

VB调用动态链接库(DLL)VB调用动态链接库(DLL)作为一种简单易用的Windows开发环境,VisualBasic从一推出就受到了广大编程人员的欢迎。它使程序员不必再直接面对纷繁复杂的Windows消息,而可以将精力主要集中在程序功能的实现上,大大提高了编程效率。但凡事有利必有弊。VB中高度的封装和模块化减轻了编程者的负担,同时也使开发人员失去了许多访问低层API函数和直接与Windows交互的机会。因此,相比而言,VB应用程序的执行效率和功能比C/C++或Delphi生成的程序要差。为了解决这个问题,在一个大型的VB开发应用中,直接调...

VB调用动态链接库(DLL)
VB调用动态链接库(DLL)作为一种简单易用的Windows开发环境,VisualBasic从一推出就受到了广大编程人员的欢迎。它使程序员不必再直接面对纷繁复杂的Windows消息,而可以将精力主要集中在程序功能的实现上,大大提高了编程效率。但凡事有利必有弊。VB中高度的封装和模块化减轻了编程者的负担,同时也使开发人员失去了许多访问低层API 函数 excel方差函数excelsd函数已知函数     2 f x m x mx m      2 1 4 2拉格朗日函数pdf函数公式下载 和直接与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就按自己的约定将用户定义的输出函数名修改成装饰名后放到输出函数列 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 中,这样的输出函数在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:DeclareFunctionpublicnameLib"libname"[Alias"alias"][([[ByVal]variable[Astype][,[ByVal]variable[Astype]]...])]AsType如果过程没有返回值,可将其声明为Sub:DeclareSubpublicnameLib"libname"[Alias"alias"][([[ByVal]variable[Astype][,[ByVal]variable[Astype]]...])]缺省情况下,在 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 模块中声明的DLL过程,可以在应用程序的任何地方调用它。在其它类型的模块中定义的DLL过程则是模块私有的,必须在它们前面声明Private关键字,以示区分。下面分别介绍声明语句的各个组成部分。、指定动态库:Declare语句中的Lib子句用来告诉VisualBasic如何找到包含过程的.dll文件。如果引用的过程属于Windows核心库(User32、Kernel32或GDI32),则可以不包含文件扩展名,如:DeclareFunctionGetTickCountLib"kernel32"Alias"GetTickCount"()AsLong对于其它动态连接库,可以在Lib子句指定文件的路径:DeclareFunctionlzCopyLib"c: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。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,LPCSTRIpString);第二个描述将它定义为SetWindowTextW,尾部的"W”表明它是一个Unicode函数:WINUSERAPIBOOLWINAPISetWindowTextW(HWNDhWnd,LPCWSTRIpString);因为两个函数实际的名称都不是"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"_(ByValIpPathNameAsString,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,从而取消类型限制。例如,下面的声明中的第三个参数(l ppt 关于艾滋病ppt课件精益管理ppt下载地图下载ppt可编辑假如ppt教学课件下载triz基础知识ppt AsAny)既可以传递一个POINT结构的数组,也可以传递一个RECT结构:DeclareFunctionMapWindowPointsLib"user32"Alias_"MapWindowPoints"(ByValhwndFromAsLong,_ByValhwndToAsLong,lpptAsAny,_ByValcPointsAsLong)AsLongAsAny子句提供了一定的灵活性,但是,由于它不进行任何的类型检查,风险也随之增力口。因此在使用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)的参数传递要复杂得多,这主要是Windows98API和VB使用的字符串类型不同的缘故。VB使用被称为BSTR的String数据类型,它是由自动化(以前被称为OLEAutomation)定义的数据类型。一个BSTR由头部和字符串组成,头部包含了字符串的长度信息,字符串中可以包含嵌入的。^]值。大部分的BSTR是Unicode的,即每个字符需要两个字节。BSTR通常以两字节的两个null字符结束。下图表示了一个BSTR类型的字符串。FunctionGetCharByte(ByValOneCharAsInteger,ByValIsHighByteAsBoolean)AsByte注释:该函数获得一个字符的高字节或低字节IfIsHighByteThenIfOneChar>=0ThenGetCharByte=CByte(OneChar256)注释:右移8位,得到高字节ElseGetCharByte=CByte((OneCharAnd&H7FFF)256)Or&H80EndIfExitFunctionElseGetCharByte=CByte(OneCharAnd&HFF)注释:屏蔽掉高字节,得到低字节ExitFunctionEndIfEndFunctionSubStrToByte(StrToChangeAsString,ByteArray()AsByte)注释:该函数将一个字符串转换成字节数组DimLowBound,UpBoundAsIntegerDimi,count,lengthAsIntegerDimOneCharAsIntegercount=0length=Len(StrToChange)LowBound=LBound(ByteArray)UpBound=UBound(ByteArray)Fori=LowBoundToUpBoundByteArray(i)=0注释:初始化字节数组NextFori=LowBoundToUpBoundcount=count+1Ifcount<=lengthThenOneChar=Asc(Mid(StrToChange,count,1))If(OneChar>255)Or(OneChar<0)Then注释:该字符是非ASCII字符ByteArray(i)=GetCharByte(OneChar,True)注释:得到高字节i=i+1Ifi<=UpBoundThenByteArray(i)=GetCharByte(OneChar,False)注释:得到低字节Else注释:该字符是ASCII字符ByteArray(i)=OneCharEndIfElseExitForEndIfNextEndSubSubChangeStrAryToByte(StrAry()AsString,ByteAry()AsByte)注释:将字符串数组转换成字节数组DimLowBound,UpBoundAsIntegerDimi,count,StartPos,MaxLenAsIntegerDimTmpByte()AsByteLowBound=LBound(StrAry)UpBound=UBound(StrAry)count=0ReDimByteAry(0)Fori=LowBoundToUpBoundMaxLen=LenB(StrAry(i))ReDimTmpByte(MaxLen+1)ReDimPreserveByteAry(count+MaxLen+1)CallStrToByte(StrAry(i),TmpByte)注释:转换一个字符串StartPos=countDoByteAry(count)=TmpByte(count-StartPos)count=count+1IfByteAry(count-1)=0ThenExitDoLoop注释:将每一个字符串对应的字节数组按顺序填入结果数组中ReDimPreserveByteAry(count-1)NextiEndSub下面看一个转换的例子:DimResultAry()asByteDimSomeStr(2)asStringSomeStr(0)="测试1"SomeStr(1)="测试222"SomeStr(2)="测试33”CallChangeStrAryToByte(SomeStr,ResultAry)注释:转换字符串数组当转换完成以后,查看字节数组ResultAry,其中包含了21个元素,依次是:178,226,202,212,49,0,178,226,202,212,50,50,50,0,178,226,202,212,51,51,0。其中,[178,226]是"测"的字节码,[202,112]是"试"的字节码,49,50,51分别为字符1、2、3的ASCII码。可见,经过转换后,字符串数组中的各个元素按顺序放在了字节数组中,相互间以终止符0分隔。这样,字符串数组就全部转换成了字节数组,然后只要将字节数组的第一个元素以传址的方式传入动态连接库,DLL过程就可以正确地访问数组中的所有字符串了。但是,使用这种方法,当DLL过程处理结束返回VB时,VB得到的仍然是字节数组。如果需要在VB中再次得到该字节数组表示的字符串,还要把整个字节数组重新以0为分割符分成多个子数组(每个子数组都对应原来字符串数组中的一个元素),然后使用VB函数StrConv将每个子数组转换成字符串(转换时第二个参数选vbUnicode),就可以显示或进行其它操作了。例如,其中一个子数组的名字是SubAry,则函数StrConv(SubAry,vbUnicode)就返回了它所对应的字符串。总之,VB应用程序和动态库间字符串参数的传递是一个比较复杂的过程,使用时要非常谨慎。同时应尽可能避免传递字符串数组类型的参数,因为这很容易引起下标越界、堆栈溢出等严重错误。(3)、用户自定义类型(User-definedType)参数的传递用户自定义类型在VB中是一种重要的数据类型,它为编程者提供了很大的灵活性,使开发人员可以根据需要构造自己的数据结构。它相当于C/C++中的结构类型(structure)o在VB中,允许程序员以传址的方式将自定义数据类型参数传入动态库,DLL过程也可以将修改后的参数返回VB程序。但是,在VB中仍然不支持以传值的方式传递用户自定义类型参数。传递用户自定义类型参数时,必须确保VB中的数据类型的成员与动态库中的结构成员是一一对应的,所占空间也必须严格一致。这里所说的一一对应,不仅是指VB中的所有结构成员在动态库的结构中都必须有对应的元素,而且它们在数据结构中定义的顺序也必须严格一致,这是VB中使用的"数据结构成员对齐方式"决定的。在VB中,数据结构使用双字对齐方式(4-bytealignment),因此,在用户自己生成用于VB调用的动态连接库时,也必须把编译选项"structurememberalignment"设为4字节(如前文所述)。所谓结构成员对齐方式是指一个数据结构内部,其成员的排列方式。譬如,在VB中,其对齐方式是4字节,这就好象在一个数据结构内部分成了很多个4字节大小的小单元,如果相邻两个或多个数据成员的大小可以放在一个单元中,那么就放在一起;否则这些小单元中可能会出现未用的空字节。我们来看下面一个数据类型:TypeTestTypemlasIntegerm2asBytem3asLongEndType它的三个成员的大小加起来是2+1+4=7。但是,由于ml和m2的字节总长度是3,小于4,它们就存放于一个单元中;但该单元剩下的一个字节不足以放下一个Long型的成员m3,于是m3就被放在下一个单元中,它们之间就有了一个未用的空字节;因此,整个结构所占实际长度是8字节。同理,如果将m3和m2的位置交换一下,它所占的尺寸就变成了9字节。可见,成员在结构中的声明顺序也是非常重要的。通常,当一个用户自定义类型中不包含字符串时,向动态连接库中传递该类型的参数是没有什么问题的。如果只传递一个自定义类型变量,则既可以传递该变量名,也可以传递该变量的第一个成员,它们的效果是一样的,都是将该变量的地址传进了动态库;同样,如果要传递一个自定义类型的数组,则既可以传递该数组的第一个元素,也可以传递第一个元素的第一个成员。但是,如果用户自定义类型中包含字符串类型时,又该如何与动态连接库传递参数呢?答案是令人遗憾的:在VB中,你无法将一个包含字符串成员的用户自定义类型变量或数组安全、正确地传入动态库中。如果你这样做了,即使某次侥幸得到了正确的结果,在其背后也隐藏着许多致命的危险。因此,如果一定要在用户自定义类型中包含字符串变量,并且该类型的变量又要作为参数传入动态库时,你最好修改类型定义,把其中的字符串成员用相应的字节数组类型替换掉(转换方法可参见前文),这样就可以在VB和动态库间传递这种类型的参数了。另外,在VB中还可以把一个函数的指针传递到动态库中,方法也并不复杂。但笔者强烈建议最好不要这么做,因为这样一来VB应用程序就几乎完全丧失了它所应有的安全性。如果确实需要传递函数指针的话,那么还是编一个C/C++的程序来完成这项工作吧。总之,在VB中调用DLL过程是一个比较复杂的问题,编程人员必须很好地把握,才能达到既提高了程序效率,开拓了程序功能,又不降低程序安全性的目的。另外需要特别指出的一点是,在本文中提到的所有动态连接库,都是指没有使用自动化(OLEAutomation)技术的动态库,WindowsAPI和大多数用户自编的动态连接库都是这种类型的。对于使用了OLEAutomation技术的动态连接库,其参数传递的方式有所不同,读者可以参阅有关OLE技术的书籍,在此不再涉及。FunctionGetCharByte(ByValOneCharAsInteger,ByValIsHighByteAsBoolean)AsByte注释:该函数获得一个字符的高字节或低字节IfIsHighByteThenIfOneChar>=0ThenGetCharByte=CByte(OneChar256)注释:右移8位,得到高字节ElseGetCharByte=CByte((OneCharAnd&H7FFF)256)Or&H80EndIfExitFunctionElseGetCharByte=CByte(OneCharAnd&HFF)注释:屏蔽掉高字节,得到低字节ExitFunctionEndIfEndFunctionSubStrToByte(StrToChangeAsString,ByteArray()AsByte)注释:该函数将一个字符串转换成字节数组DimLowBound,UpBoundAsIntegerDimi,count,lengthAsIntegerDimOneCharAsIntegercount=0length=Len(StrToChange)LowBound=LBound(ByteArray)UpBound=UBound(ByteArray)Fori=LowBoundToUpBoundByteArray(i)=0注释:初始化字节数组NextFori=LowBoundToUpBoundcount=count+1Ifcount<=lengthThenOneChar=Asc(Mid(StrToChange,count,1))If(OneChar>255)Or(OneChar<0)Then注释:该字符是非ASCII字符ByteArray(i)=GetCharByte(OneChar,True)注释:得到高字节i=i+1Ifi<=UpBoundThenByteArray(i)=GetCharByte(OneChar,False)注释:得到低字节Else注释:该字符是ASCII字符ByteArray(i)=OneCharEndIfElseExitForEndIfNextEndSubSubChangeStrAryToByte(StrAry()AsString,ByteAry()AsByte)注释:将字符串数组转换成字节数组DimLowBound,UpBoundAsIntegerDimi,count,StartPos,MaxLenAsIntegerDimTmpByte()AsByteLowBound=LBound(StrAry)UpBound=UBound(StrAry)count=0ReDimByteAry(0)Fori=LowBoundToUpBoundMaxLen=LenB(StrAry(i))ReDimTmpByte(MaxLen+1)ReDimPreserveByteAry(count+MaxLen+1)CallStrToByte(StrAry(i),TmpByte)注释:转换一个字符串StartPos=countDoByteAry(count)=TmpByte(count-StartPos)count=count+1IfByteAry(count-1)=0ThenExitDoLoop注释:将每一个字符串对应的字节数组按顺序填入结果数组中ReDimPreserveByteAry(count-1)NextiEndSub下面看一个转换的例子:DimResultAry()asByteDimSomeStr(2)asStringSomeStr(0)="测试1"SomeStr(1)="测试222"SomeStr(2)="测试33”CallChangeStrAryToByte(SomeStr,ResultAry)注释:转换字符串数组当转换完成以后,查看字节数组ResultAry,其中包含了21个元素,依次是:178,226,202,212,49,0,178,226,202,212,50,50,50,0,178,226,202,212,51,51,0。其中,[178,226]是"测"的字节码,[202,112]是"试"的字节码,49,50,51分别为字符1、2、3的ASCII码。可见,经过转换后,字符串数组中的各个元素按顺序放在了字节数组中,相互间以终止符0分隔。这样,字符串数组就全部转换成了字节数组,然后只要将字节数组的第一个元素以传址的方式传入动态连接库,DLL过程就可以正确地访问数组中的所有字符串了。但是,使用这种方法,当DLL过程处理结束返回VB时,VB得到的仍然是字节数组。如果需要在VB中再次得到该字节数组表示的字符串,还要把整个字节数组重新以0为分割符分成多个子数组(每个子数组都对应原来字符串数组中的一个元素),然后使用VB函数StrConv将每个子数组转换成字符串(转换时第二个参数选vbUnicode),就可以显示或进行其它操作了。例如,其中一个子数组的名字是SubAry,则函数StrConv(SubAry,vbUnicode)就返回了它所对应的字符串。总之,VB应用程序和动态库间字符串参数的传递是一个比较复杂的过程,使用时要非常谨慎。同时应尽可能避免传递字符串数组类型的参数,因为这很容易引起下标越界、堆栈溢出等严重错误。(3)、用户自定义类型(User-definedType)参数的传递用户自定义类型在VB中是一种重要的数据类型,它为编程者提供了很大的灵活性,使开发人员可以根据需要构造自己的数据结构。它相当于C/C++中的结构类型(structure)o在VB中,允许程序员以传址的方式将自定义数据类型参数传入动态库,DLL过程也可以将修改后的参数返回VB程序。但是,在VB中仍然不支持以传值的方式传递用户自定义类型参数。传递用户自定义类型参数时,必须确保VB中的数据类型的成员与动态库中的结构成员是一一对应的,所占空间也必须严格一致。这里所说的一一对应,不仅是指VB中的所有结构成员在动态库的结构中都必须有对应的元素,而且它们在数据结构中定义的顺序也必须严格一致,这是VB中使用的"数据结构成员对齐方式"决定的。在VB中,数据结构使用双字对齐方式(4-bytealignment),因此,在用户自己生成用于VB调用的动态连接库时,也必须把编译选项"structurememberalignment"设为4字节(如前文所述)。所谓结构成员对齐方式是指一个数据结构内部,其成员的排列方式。譬如,在VB中,其对齐方式是4字节,这就好象在一个数据结构内部分成了很多个4字节大小的小单元,如果相邻两个或多个数据成员的大小可以放在一个单元中,那么就放在一起;否则这些小单元中可能会出现未用的空字节。我们来看下面一个数据类型:TypeTestTypem1asIntegerm2asBytem3asLongEndType它的三个成员的大小加起来是2+1+4=7。但是,由于ml和m2的字节总长度是3,小于4,它们就存放于一个单元中;但该单元剩下的一个字节不足以放下一个Long型的成员m3,于是m3就被放在下一个单元中,它们之间就有了一个未用的空字节;因此,整个结构所占实际长度是8字节。同理,如果将m3和m2的位置交换一下,它所占的尺寸就变成了9字节。可见,成员在结构中的声明顺序也是非常重要的。通常,当一个用户自定义类型中不包含字符串时,向动态连接库中传递该类型的参数是没有什么问题的。如果只传递一个自定义类型变量,则既可以传递该变量名,也可以传递该变量的第一个成员,它们的效果是一样的,都是将该变量的地址传进了动态库;同样,如果要传递一个自定义类型的数组,则既可以传递该数组的第一个元素,也可以传递第一个元素的第一个成员。但是,如果用户自定义类型中包含字符串类型时,又该如何与动态连接库传递参数呢?答案是令人遗憾的:在VB中,你无法将一个包含字符串成员的用户自定义类型变量或数组安全、正确地传入动态库中。如果你这样做了,即使某次侥幸得到了正确的结果,在其背后也隐藏着许多致命的危险。因此,如果一定要在用户自定义类型中包含字符串变量,并且该类型的变量又要作为参数传入动态库时,你最好修改类型定义,把其中的字符串成员用相应的字节数组类型替换掉(转换方法可参见前文),这样就可以在VB和动态库间传递这种类型的参数了。另外,在VB中还可以把一个函数的指针传递到动态库中,方法也并不复杂。但笔者强烈建议最好不要这么做,因为这样一来VB应用程序就几乎完全丧失了它所应有的安全性。如果确实需要传递函数指针的话,那么还是编一个C/C++的程序来完成这项工作吧。总之,在VB中调用DLL过程是一个比较复杂的问题,编程人员必须很好地把握,才能达到既提高了程序效率,开拓了程序功能,又不降低程序安全性的目的。另外需要特别指出的一点是,在本文中提到的所有动态连接库,都是指没有使用自动化(OLEAutomation)技术的动态库,WindowsAPI和大多数用户自编的动态连接库都是这种类型的。对于使用了OLEAutomation技术的动态连接库,其参数传递的方式有所不同,读者可以参阅有关OLE技术的书籍,在此不再涉及。
本文档为【VB调用动态链接库(DLL)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
个人认证用户
唐伯虎
暂无简介~
格式:doc
大小:28KB
软件:Word
页数:33
分类:
上传时间:2023-05-01
浏览量:1