VB如何处理返回为LPSTR的DLL调用子程序的步骤

   由于VB与VC之间的数据类型没有相同嘚关键字为了方便,下面列出常用数据类型在VC与VB中分别使用的关键字以及它们之间的对应关系:


能不能做一个子程序调用子程序嘚步骤如 TestAll(Value)所有都直接调用子程序的步骤如:

生素A和维生素C丰富的食品。3、适量限奶类制品、多饮水同时以【何氏消喘方】综合调理机體,全面调理肺部内环境健康从而达到养肺、护肺的目的,增强肺部免疫力

你好,出现了咳嗽咳白痰的症状,可能患急性气管炎主要是细菌感染引起的。如肺炎链球菌感冒杆菌。

建议做个血常规和胸片检查多喝水,避免受凉多吃蔬菜水果,多休息忌吃上火喰品,可以口服阿齐霉素肺宁颗粒,必嗽平治疗

支气管炎必须用抗菌素治疗,疗程一般十天左右常用青霉素、头孢霉素、大环内酯類抗菌素治疗。在儿童光服用止咳药没有多大效果

下载百度知道APP,抢鲜体验

使用百度知道APP立即抢鲜体验。你的手机镜头里或许有别人想知道的答案

等)调用子程序的步骤动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用子程序的步骤而内部函数则只能在动态连接库内部使鼡。尽管动态连接库也能输出 数据但实际上它的数据通常是只在内部使用的。使用动态连接库的优点是显而易见的将应用程序的一部汾功能提取出来做成动态连接库,不但减小了主应用程序的大小提高了程序 运行效率,还使它更加易于升级多个应用程序共享一个动態连接库还能有效地节省系统资源。正因为如此在Windows系统中,动态连接库得到了大量的使用

  一般来说,动态连接库都是以DLL为扩展名嘚文件如Kernel32.dllcommdlg.dll等。但也有例外如16Windows的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多如VisualC++BorlandC++Delphi等,具体方法可以参见相關文档下面只以Visual C++6.0为例,介绍一下开发应用于VisualBasic6.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方都以VC为例;所有涉忣Visual Basic的地方都以VB 为例)。

  作为一种32Windows应用程序的开发工具VB生成的exe文件自然也都是32位的,通常情况下也只能调用子程序的步骤32位的动态連接库但是,并不是所有的32位动态库都能被VB生成的exe 文件正确地识别一般来说,自己编写用于VB应用程序调用子程序的步骤的动态连接库時应注意以下几个方面的

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

  2、在VC中的定义文件(.def)中,必须列出输出函数的函数名以强制VC系统将输出函数的装饰名(decoratedname)改成普通函数名;所谓装饰名是VC的编译器在编译过程中生成的输出函数名,它包含了用户定义的函数名、函数参数及函数所在的类等多方面的信息由于在VC中定义文件不是必需的,因此工程不包含定义文件时VC就按自己的约定将用户定义的输出函数名修改成装饰名后放到输出函数列表Φ这样的输出函数在VB生成的应用程序中是不能正确调用子程序的步骤的(除非声明时使用Alias子句)。因此需要增加一个.def文件其中列出用戶需要的函数名,以强制VC不按装饰名进行输出

应设成4字节,其原因将在后文详细介绍

  4、由于在C中整型变量是4个字节,而VB中的整型變量依然只有2个字节因此在C中声 明的整型(int)变量在VB中调用子程序的步骤时要声明为长整型(long),而C中的短整型(short)在VB中则 要声明成整型(integer);丅表针对最常用的C语言数据类型列出了与之等价的Visual Basic 类型(用于32位版本的Windows

C语言数据类型在VisualBasic中声明为调用子程序的步骤时使用的表达式

类型的表达式等Windows 句柄

  5VB中进行32位动态库的声明时,函数名是大小写敏感的在获得了需要的动态连接 库之后,就可以在VB中进行调用子程序的步骤了但是,由于VB不能验证应用程序传递到动态连接库中的参 数值是否正确因此VB程序中大量的API调用子程序的步骤可能会降低整个應用程序的稳定性,也会增加以 后维护的难度所以,决定在VB程序中直接调用子程序的步骤API函数时要慎重但适当的使用API调用子程序的步驟确实 能够有效地提高VB程序的性能。这之间的平衡需要编程人员根据实际情况来掌握下面就具体介绍一下在VB中调用子程序的步骤API函数时需要做的工作。

  要声明一个DLL过程首先需要在代码窗口的"通用(General"部分增加一个Declare语句。如果该过程返回一个值应将其声明为

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

  缺省情况下在标准模块中声明的DLL过程,可以在应用程序的任何地方调用子程序的步骤它在其咜类型的模块中定义的DLL过程则是模块私有的,必须在它们前面声明Private关键字以示区分。下面分别介绍声明语句的各个组成部分

  ()、指定动态库:

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

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

Basic将按照下列顺序查找该文件:

  ①.exe文件所在的目录

  ⑤Path环境变量中的目录

  下表中列出了常用的操作系统环境库文件

  Advapi32.dll高級API服务,支持大量的API(其中包括许多安全与注册方面的调用子程序的步骤)

  Mpr.dll多接口路由器库

  Winspool.drv后台打印接口包含后台打印API调用子程序的步骤。

  对于Windows的系统API函数可以利用VB提供的工具API Viewer查找某一函数及其相 关数据结构和常数的声明,并复制到自己的程序中

A.函数名昰标准的名称

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

  需要注意的是,Alias子句中的函数名是大小写敏感的也就是说,必须与函数在生成时的声明(如在C源文件中的声明)一致这是因為32位动态库与16位动态库不同,其中的函数名是区分大小写的同样道理,如果没有使用Alias子句那么在Function(或Sub)后的函数名也是区分大小写的。

  通常在以下几种情况时需要使用Alias子句:

  如果调用子程序的步骤的系统Windows API过程要使用字符串那么声明语句中必须增加一个Alias 子句,鉯指定正确的字符集包含字符串的系统Windows 关于ANSIUnicode两种字符集的区别将在后面详细阐述)。因此在Windows头文件中,每个包含字符串的函数都同時有ANSI版本和Unicode版本例如,下面是SetWindowText函数 的两种C语言描述可以看到,第一个描述将函数定义为SetWindowTextA尾部的"A" 表明它是一个ANSI函数:

  因为两个函數实际的名称都不是"SetWindowText",要引用正确的函数就必 须增加一个Alias子句:

  应当注意对于VB中使用的系统WindowsAPI函数,应该指定函数的ANSI版本因为只

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

  有时,个别的DLL过程的名称不是有效的标识符例如,它可能包含了非法的字符(如连 字符)或者名称是VB的關键字(如GetObject)。在这种情况下可以使用Alias关键字。例 如操作环境DLLs中的某些过程名以下划线开始。尽管在VB标识符中允许使用标识符但是丅划线不能作为标识符的第一个字符。为了使用这种过程必须先声明一个名称合法的过程, 然后用Alias子句引用过程的真实名称:

  在上唎中lopenVB中使用的过程名称。而_lopen则是动态连接库中可以识别的名

  C.使用序号标识DLL过程

  除了使用名称之外,还可以使用序号来标識DLL过程某些动态连接库中不包含过程的名称,在声明它们包含的过程时必须使用序号同使用名称标识的DLL过程相比,如果使用序号在朂终的应用程序中消耗的内存将比较少,而且速度会快些但是,一个具体的API的序号 在不同的操作系统中可能是不同的例如GetWindowsDirectoryWin95下的序号為432,而在WindowsNT4.0下为338总而言之,如果希望应用程序能够在不同的操作系统下运行那么最好不要使用序号来标识API过程。如果过程不属于API或者應用程序使用的范围很有 限,那么使用序号还是有好处的

  要使用序号来声明DLL过程,Alias子句中的字符串需要包含过程的序号并在序号嘚

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

VisualC++提供的一个实用工具它的使用说明可以参见VC的文档)。利用Dumpbin可以提取出.dll文件中的各种信息,例如DLL中的函数列表它们的序号以及与代码有关的其它信息。

  ()、使用值或引用传递

  茬缺省的情况下VB以引用方式传递所有参数(ByRef)。这意味着并没有传递实际的参 数值VB只传递了数据的32位地址。另外有许多DLL过程要求参数鉯值方式传递(ByVal)这意味着它们需要实际的数据,而不是数据的内存地址如果过程需要一个传值参数,而传递给它的参数是一个指针那么由于得到了错误的数据,该过程将不能正确地工作要使参数以使用值方式传递,在Declare语句中需要在参数声明的前面加上ByVal关键字例洳InvertRect过程要求第一个参数用传值方式传递,而第二个用引用方式传递:

  动态连接库的参数传递是一个复杂的问题也是VB中调用子程序的步骤动态连接库时最容易出现错误的地方。参数类型或传递方式的声明错误都可能导致应用程序出现GPF(通用保护错误)甚至使操作系统崩溃,因此我们将在后面专门详细地讨论这个问题

  ()、灵活的参数类型

  某些DLL过程的同一个参数能够接受多种数据类型。如果需偠传递多种类型的数据可以将参数声明为AsAny,从而取消类型限制例如,下面的声明中的第三个参数(lpptAsAny) 既可以传递一个POINT结构的数组也可以傳递一个RECT结构:

Any子句提供了一定的灵活性,但是由于它不进行任何的类型检查,风险也随之增加因此在使用AsAny子句时,必须仔细检查所囿参数的类型正确的函数声明是在VB中调用子程序的步骤动态连接库的前提,但要想在VB中用对、用好动态库中的 函数仅仅有声明还是远遠不够的。前面已经说过由于VB不能验证应用程序传递到动态连接 库中的参数值是否正确,因此就要求程序员应对参数类型有非常详细的叻解否则很容易引起应用程序发生通用保护错或导致潜在的Bug,降低软件的可靠性下面将参数类型分为简单数据类型、字符串、和用户洎定义类型三种分别进行讨论。

  (1)、简单数据类型:

  简单数据类型是指Numeric数据类型(包括IntegerLongSingleDoubleCurrency类型)、Byte数据类型和Boolean数据类型它們的共同的特点是结构简单,操作系统在处理时不必进行特殊的转换

  简单数据类型参数的传递比较简单。我们知道在VB中传递参数嘚方式有两种:传值(Byval 和传址(ByRef),缺省的方式是传址所谓传值,就是对一个变量的具体值进行传递;而传址则 是传递变量的地址唎如,在VB程序中需要将一个整型变量m=10的值传进动态库如果用传值 方式,那么传进动态库的值就是10而在传址方式下,传入的则是变量m的哋址相当于C/C++ &m的值。需要注意的是以传值方式传进动态连接库的变量,其值在动态库中是不能 被改变的;如果需要在动态连接库中修妀传入参数的值则必须使用传址方式。一般来说在VB 和动态连接库之间传递单个的简单数据类型,只要注意了以上几个方面就可以了當需要将 一个简单数据类型的整个数组传进动态库时,必须将相应参数声明为传址方式然后把数组 的第一个元素作为参数传入,这样在動态连接库中就得到了数组的首地址从而可以对整个数组进行访问。例如声明了一个名为ReadArrayDLL过程,要求传入一个整型数组aArray

在调用子程序的步骤时可以采用如下方式:

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

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

  与简单数据类型相比字符串类型(StringString*n)的参数傳递要复杂得多,这主要是Windows 95 APIVB使用的字符串类型不同的缘故VB使用被称为BSTRString数据类型,它是由自动化(以前被称为OLE Automation)定义的数据类型一個BSTR由头部和字符串组成,头部包含了字符串的长度信息字符串中可以包含嵌入的null值。大部分的BSTR Unicode的即每个字符需要两个字节。BSTR通常以兩字节的两个null字符结束下图表示 了一个BSTR类型的字符串。

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

  另一方面大部分的DLL过程(包括Windows 95 API中的所有过程)使用LPSTR类型字符串,这是指向标准的以null结束的C语言字符串的指针它也被称为ASCIIZ字符串。LPSTR 没有前缀下图显示了一个指向ASCIIZ字符串的LPSTR

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

  如果DLL过程需要一个LPSTR(指向以null结束的字符串的指针)作为参数可以在VB 中将一个字符串以传徝的方式传递给它。因为指向BSTR的指针实际指向以null值结束的字符串的第一个数据字节所以对于DLL过程来说,它就是一个LPSTR这样传入动态连接庫的字符串,DLL过程也可以对它进行修改尽管它是以传值方式传入的。只有当DLL过程需要一个指向LPSTR的指针时才以传址的方式传入字符串,這时DLL过程得到的是一个指向字符串指针的指针(相当于C/C++中的char**)而不是通常所用的字符串的首地址(相当于C/C++中的char*)。

  当需要把一个字苻串数组整个传入动态连接库时情况就变得复杂多了,用传递简单数据类型数组的方式来传递字符串数组是行不通的当我们以传值的方式将一个字符串数组的第一个元素传进动态连接库时,DLL过程得到的实际上是该元素压入堆栈段后的地址而不是数据段中整个数组的首哋址。也就是说这时DLL过程只能得到数组的第一个元素,而无法访问整个数组而以传址方式传入第一个元素时,DLL过程只能得到指向该元素在堆栈段中地址的指针同样无法访问整个数组。这不能不说是VB的一个不足因此,在程序设计中如果确实需要将整个字符串数组传叺动态库,就必须采取其它方法

  我们知道,在VB中有一种Byte数据类型。每个Byte型变量占一个字节不含符号位,因 此所能表示的范围为0255这种数据类型是专门用于存放二进制数据的。为了将整个字符 串数组传进动态库可以用字节数组来保存字符串。由于Byte是一种简单数據类型因此字节 数组的传递是非常简单的。首先需要把一个字符串正确地转变成一个字节数组。这要涉及一 些字符集的知识Windows 95VB使用鈈同的字符集,Windows 95 字符集而VB使用的则是Unicode字符集。所谓ANSI字符集是指每个字符都用一个字节表示, 因此最多只能有28=256个不同的字符这对于英語来说已经足够了,但不能完全支持其它语 DBCS字符集支持很多不同的东亚语言,如汉语、日语和朝鲜语它使用数字0-255表示ASCII 字符,其它大於255或小于0的数字表明该字符属于非拉丁字符集;在DBCSASCII字符的长 度是一个字节,而汉语、日语和其它东亚字符的长度是2个字节而Unicode字符集則完全用 两个字节表示一个字符,因此最多可以表示216=65536个不同字符也就是说,ANSI字符集中 所有的字符都只占一个字节DBCS字符集中ASCII字符占一个芓节,汉字占两个字节Unicode 字符集中每个字符都占两个字节。由于VBWindowsAPI使用的字符集不同因此在进行字符 串到字节数组的转换时,当用Asc函数取得一个字符的字节码后需要判断它是否是一个ASCII 字符;如果是ASCII字符,则在转换后的字节数组中就只占一个字节否则要占两个字节。

  下面给出了转换函数:GetChar Byte得到一个字符的高字节或低字节它的第一个参数 是一个字符的ASCII码,第二个参数是标志取高字节还是低字节;StrToByteDBCSANSI 式将一个字符串转换成一个字节数组第一个参数是待转换的字符串,第二个参数是转换后的一个定长字节数组若该数组长度不足鉯存放整个字符串,则截去超长的部分;ChangeStrAryToByte 利用前两个函数将字符串数组转换成字节数组第一个参数是定长的字符串数组,其中每个元素嘟是一个字符串(各个元素包含的字符数可以不同)第二个参数是一个变长的字节数组, 保存转换后的结果

分别为字符123ASCII码。可見经过转换后,字符串数组中的各个元素按顺序放在了字节数组中相互间以终止符0分隔。

  这样字符串数组就全部转换成了字节數组,然后只要将字节数组的第一个元素以传址的方式传入动态连接库DLL过程就可以正确地访问数组中的所有字符串了。但是使用这种方法,当DLL过程处理结束返回VBVB得到的仍然是字节数组。如果需要在VB中再次得到该字节数组表示的字符串还要把整个字节数组重新以0为汾割符分成多个子数组(每个子数组都对应原来字符串数组中的一个元素),然后使用VB函数StrConv将每个子数组转换成字符串(转换时第二个参數选vbUnicode)就可以显示或进行其它操作了。例如其中一个子数组的名字是SubAry,则函数StrConv(SubAry,vbUnicode)就返回了它所对应的字符串

  总之,VB应用程序和动態库间字符串参数的传递是一个比较复杂的过程使用时要非常谨慎。同时应尽可能避免传递字符串数组类型的参数因为这很容易引起丅标越界、堆栈溢出等严重错误。

  用户自定义类型在VB中是一种重要的数据类型它为编程者提供了很大的灵活性,使开发人员可以根據需要构造自己的数据结构它相当于C/C++中的结构类型(structure)。在VB中允许程序员以传址的方式将自定义数据类型参数传入动态库,DLL过程也可鉯将修改后的参数返回VB程序但是,在VB中仍然不支持以传值的方式传递用户自定义类型参数

  传递用户自定义类型参数时,必须确保VBΦ的数据类型的成员与动态库中的结构成员是一一对应的所占空间也必须严格一致。这里所说的一一对应不仅是指VB 中的所有结构成员茬动态库的结构中都必须有对应的元素,而且它们在数据结构中定义的顺序也必须严格一致这是VB中使用的"数据结构成员对齐方式"决定的。在VB 中数据结构使用双字对齐方式(4-byte alignment),因此在用户自己生成用于VB

  所谓结构成员对齐方式是指一个数据结构内部,其成员的排列方式譬如,在VB中其对齐方式是4字节,这就好象在一个数据结构内部分成了很多个4字节大小的小单元如果相邻 两个或多个数据成员的夶小可以放在一个单元中,那么就放在一起;否则这些小单元中可能 会出现未用的空字节我们来看下面一个数据类型:

  它的三个成員的大小加起来是2+1+4=7。但是由于m1m2的字节总长度是3,小于4 们就存放于一个单元中;但该单元剩下的一个字节不足以放下一个Long型的成员m3,于是m3 就被放在下一个单元中它们之间就有了一个未用的空字节;因此,整个结构所占实际长度是8 字节同理,如果将m3m2的位置交换一丅它所占的尺寸就变成了9字节。可见成员在结构 中的声明顺序也是非常重要的。

  通常当一个用户自定义类型中不包含字符串时,向动态连接库中传递该类型的参数是没有什么问题的如果只传递一个自定义类型变量,则既可以传递该变量名也可以传递该变 量的苐一个成员,它们的效果是一样的都是将该变量的地址传进了动态库;同样,如果要传递一个自定义类型的数组则既可以传递该数组嘚第一个元素,也可以传递第一个元素的第一个成员但是,如果用户自定义类型中包含字符串类型时又该如何与动态连接库传递参数呢?答案是令人遗憾的:在VB中你无法将一个包含字符串成员的用户自定义类型变量或数 组安全、正确地传入动态库中。如果你这样做了即使某次侥幸得到了正确的结果,在其背后也隐藏着许多致命的危险因此,如果一定要在用户自定义类型中包含字符串变量并且该類型的变量又要作为参数传入动态库时,你最好修改类型定义把其中的字符串成员用相应的字节数组类型替换掉(转换方法可参见前文),这样就可以在VB 和动态库间传递这种类型的参数了

  另外,在VB 中还可以把一个函数的指针传递到动态库中方法也并不复杂。但笔鍺强烈建议最好不要这么做因为这样一来VB 应用程序就几乎完全丧失了它所应有的安全性。如果 确实需要传递函数指针的话那么还是编┅个C/C++ 的程序来完成这项工作吧。

总之在VB中调用子程序的步骤DLL过程是一个比较复杂的问题,编程人员必须很好地把握才能达到既提高了程序效率,开拓了程序功能又不降低程序安全性的目的。另外需要特别指出的一点是在本文中提到的所有动态连接库,都是指没有使鼡自动化(OLE Automation)技术的动态库Windows API和大多数用户自编的动态连接库都是这种类型的。对于使用了OLE Automation技术的动态连接库其参数传递的方式有所不哃,读者可以参阅有关OLE 技术的书籍在此不再涉及。

我要回帖

更多关于 调用子程序的步骤 的文章

 

随机推荐