精华内容
下载资源
问答
  • VB如何调用DLL

    千次阅读 2014-04-21 16:09:22
    Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、DLL、OCX 等)调用。动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用,而内部函数则只能...
    Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(
    EXEDLLOCX 等)调用。动态连接库包含两种函数:输出(
    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不按装饰名进行输出。

      3VC中的编译选项"结构成员对齐方式(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 类型的表达式

      5VB中进行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关键字,以示区分。下面分别介绍声明语句的各个组成部分。

      ()、指定动态库:

      Declare语句中的Lib子句用来告诉Visual Basic如何找到包含过程的.dll文件。如果引用的过程属于Windows核心库(User32Kernel32GDI32),则可以不包含文件扩展名,如:

       Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long 

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

       Declare Function lzCopy Lib "c:/windows/lzexpand.dll" _

       (ByVal S As Integer, ByVal D As Integer) As Long 

    如果未指定libname的路径,Visual Basic将按照下列顺序查找该文件:

      ①.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.dll32ShellAPI

      User32.dll用户接口例程库

      Version.dll版本库

      Winmm.dllWindows多媒体库

      Winspool.drv后台打印接口,包含后台打印API调用。

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

    ()、使用别名:

    A.函数名是标准的名称

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

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

       Private Declare Function MyFunction Lib "Mydll.dll" _

       Alias "MyFunctionX" ( ) As Long 

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

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

    A.处理使用字符串的系统Windows API过程

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

       WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString); 

      第二个描述将它定义为 SetWindowTextW,尾部的"W" 表明它是一个 Unicode 函数:

       WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString); 

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

    Private Declare Function SetWindowText Lib "user32" _

    Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal _

    lpString As String) As Long

      应当注意,对于VB中使用的系统WindowsAPI函数,应该指定函数的ANSI版本,因为只有WindowsNT才支持Unicode版本,而Windows95不支持这个版本。仅当应用程序只运行在WindowsNT平台上的时候才可以使用Unicode版本。

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

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

    Declare Function lopen Lib "kernel32" Alias "_lopen" _

    (ByVal lpPathName As String, ByVal iReadWrite _

    As Long) As Long

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

      C.使用序号标识DLL过程

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

      要使用序号来声明DLL过程,Alias子句中的字符串需要包含过程的序号,并在序号的前面加一个数字标记字符(#)。例如,Windowskernel中的GetWindowsDirectory函数的序号为432;可以用下面的语句来声明该DLL过程:

    Declare Function GetWindowsDirectory Lib "kernel32" _

    Alias "#432" (ByVal lpBuffer As String, _

    ByVal nSize As Long) As Long

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

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

      ()、使用值或引用传递

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

    Declare Function InvertRect Lib "user32" Alias _

    "InvertRectA" (ByVal hdc As Long, lpRect As RECT) As Long

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

      ()、灵活的参数类型

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

    Declare Function MapWindowPoints Lib "user32" Alias _

    "MapWindowPoints" (ByVal hwndFrom As Long, _

    ByVal hwndTo As Long, lppt As Any, _

    ByVal cPoints As Long) As Long

      As 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

    Declare Function ReadArray Lib "mydll.dll" _

    (aArray As Integer) As Integer

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

    Dim ret,I(5) as Integer

    ret = ReadArray(I(0)) '

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

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

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

      (前缀)aTest/0

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

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

      aTest/0

      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 API使用的是ANSIDBCS 字符集,而VB使用的则是Unicode字符集。所谓ANSI字符集,是指每个字符都用一个字节表示,因此最多只能有28=256个不同的字符,这对于英语来说已经足够了,但不能完全支持其它语言。DBCS字符集支持很多不同的东亚语言,如汉语、日语和朝鲜语,它使用数字0-255表示ASCII 字符,其它大于255或小于0的数字表明该字符属于非拉丁字符集;在DBCS中,ASCII字符的长度是一个字节,而汉语、日语和其它东亚字符的长度是2个字节。而Unicode字符集则完全用两个字节表示一个字符,因此最多可以表示216=65536个不同字符。也就是说,ANSI字符集中所有的字符都只占一个字节,DBCS字符集中ASCII字符占一个字节,汉字占两个字节,Unicode 字符集中每个字符都占两个字节。由于VBWindowsAPI使用的字符集不同,因此在进行字符串到字节数组的转换时,当用Asc函数取得一个字符的字节码后,需要判断它是否是一个ASCII 字符;如果是ASCII字符,则在转换后的字节数组中就只占一个字节,否则要占两个字节。

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

      当转换完成以后,查看字节数组ResultAry,其中包含了21个元素,依次是:178226202212490178226202212505050017822620221251510。其中,[178226]""的字节码,[202112]""的字节码,495051 分别为字符123ASCII码。可见,经过转换后,字符串数组中的各个元素按顺序放在了字节数组中,相互间以终止符0分隔。

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

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

    (3)、用户自定义类型(User-defined Type)参数的传递

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

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

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

    Type TestType

    m1 as Integer

    m2 as Byte

    m3 as Long

    End Type

      它的三个成员的大小加起来是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 技术的书籍,在此不再涉及。

    展开全文
  • 如何在VB中通过相对路径引用标准DLL

    千次阅读 2019-06-25 22:57:28
    很长时间以来,都认为只能通过绝对路径引用标准DLL中的函数。其实,你也可以用相对路径。很简单的,现在就尝试一下吧。 1)绝对路径方法 比如你的DLL文件位于c:\testDLL\debug\testDLL.dll 一般来说,...
    作者:Michael Zhao (yyzhao_sjtu@yahoo.com)
    http://www.cnblogs.com/userinterface/

    很长时间以来,都认为只能通过绝对路径引用标准DLL中的函数。其实,你也可以用相对路径。很简单的,现在就尝试一下吧。

    1)绝对路径方法
    比如你的DLL文件位于c:\testDLL\debug\testDLL.dll
    一般来说,你需要在VB中作如下声明
    Declare Sub mytest Lib "c:\testDLL\dubug\testDLL.dll" (ByVal x As Long)
    另外的一个变通方法是把testDLL.dll放在windows的系统目录下,这样,就可以直接引用文件名了。不过,需要把文件放到windows系统目录下,很是不爽!

    2)相对路径方法
    好了,看看我们如何用相对路径
    假设你的DLL文件位于c:\testDLL\debug\testDLL.dll,VB程序位于目录c:\testDLL\vbClient
    你可以在VB程序中作如下声明
    Declare Sub mytest Lib "../dubug/testDLL.dll" (ByVal x As Long)
    如果直接运行你的VB程序,系统会提示错误:找不到../dubug/testDLL.dll.

    为了使上面的声明起作用,先暂时关闭你的VB工程。然后用一个文本编辑器(notepad,editplus,etc)打开工程文件(就是那个后缀是vbp的家伙)
    通常vbp文件由几个部分组成,比如我的vbp有两部分
    Type=Exe
    Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\WINDOWS\System32\stdole2.tlb#OLE Automation
    Form=Form1.frm
    Module=Module1; Module1.bas
    Startup="Form1"
    ExeName32="Project1.exe"
    Command32=""
    Name="Project1"
    HelpContextID="0"
    CompatibleMode="0"
    MajorVer=1
    MinorVer=0
    RevisionVer=0
    AutoIncrementVer=0
    ServerSupportFiles=0
    VersionCompanyName="American Standard"
    CompilationType=0
    OptimizationType=0
    FavorPentiumPro(tm)=0
    CodeViewDebugInfo=0
    NoAliasing=0
    BoundsCheck=0
    OverflowCheck=0
    FlPointCheck=0
    FDIVCheck=0
    UnroundedFP=0
    StartMode=0
    Unattended=0
    Retained=0
    ThreadPerObject=0
    MaxNumberOfThreads=1

    [MS Transaction Server]
    AutoRefresh=1

    你要做的就是在第一部分MaxNumberofThreads=1后添加一行DebugStartupOption=0。这样,vbp文件就会像下面这样
    。。。(前面的都一样,故省略)
    ThreadPerObject=0
    MaxNumberOfThreads=1
    DebugStartupOption=0

    [MS Transaction Server]
    AutoRefresh=1

    OK.That's all!!重新用VB开发环境打开你的工程然后运行。奇迹发生了吧!

    演示代码

    转载于:https://www.cnblogs.com/userinterface/archive/2005/03/23/124390.html

    展开全文
  • VB制作DLL并引用DLL的实例源码

    热门讨论 2012-08-09 11:22:26
     写exe代码(包含调用DLL代码)  生成usedll.exe,usedll.exe和mydll.DLL要放在一个目录下。    注意:如果引用的时候可能会出现这样的错误:“名称与已存在的工程模块对象冲突”  解决...
  • 1、安装工具文件获得dll文件 默认路径安装“Advantech Common Motion Driver & Utility.exe”,如下图为该安装文件。具体安装步骤,请参考“研华运动控制卡驱动安装与卸载”一文,完整安装“Advantech Common ...

    1、安装工具文件获得dll文件

        默认路径安装“Advantech Common Motion Driver & Utility.exe”,如下图为该安装文件。

    065de9141ce5c00e532a310b4300234e.png

    具体安装步骤,请参考“研华运动控制卡驱动安装与卸载”一文,完整安装“Advantech Common Motion Driver & Utility.exe”安装包。

        安装完成后,在“Public”文件下可以得到如下各种开发语言所需要的开发文件。

    bce27b76e75ba89da5102218dda734f7.png

    2、新建工程及引用dll

    (1)打开“Visual Studio”(以Visual Studio 2015为例),新建“项目”,如下图:

    5a4c13ca543f62fdd029ef1b38035006.png

    (2)选择“Visual Basic”-->“Windows窗体应用程序”-->填写项目名称-->项目存储位置-->“确定”

    746af5caa6cc74452e3b2e70cee1cd90.png

    (3)将“Public”文件夹下面的AdvMotAPI.dll拷贝到工程目录下面的bin文件夹下

    3302fa5f34479134a773535a7639ddea.png

    (4)打开该项目的“解决方案资源管理器”-->右键“引用”-->“添加引用”-->弹出“引用管理器”-->“浏览”-->选择拷贝到工程目录bin下面的AdvMotAPI.dll文件-->选择要引用的窗体“添加”-->引用管理器“确定”

    50e39684d9a91e41b5e25bc977af7595.png

    ae15b1401961caafdcd2f38e4a8b589a.png

    (5)切换到“Form1.cs[设计]”-->窗体空白处点击鼠标右键-->选择“查看代码”-->在“Form1.cs”中添加“Imports Advantech.Motion;”引用命名空间

    97ab7301f84dc11d9412814d2968cfb5.png

    c7401848ea3108a4afb89b2a9040aa4c.png

    (6)至此VB.Net下引用研华运动控制卡dll完成,可在该项目工程下进行研华运动控制卡程序开发。

    展开全文
  • pb调用dll实例

    热门讨论 2013-03-12 14:14:15
    (5)DLL独立于编程语言,大多数WINDOWS编程环境都允许主程序调用DLL中的函数。即可以用VC++、VB、PowerBuilder、Delphi、汇编语言等建立DLL,然后在不同语言编制的应用程序中调用它。下面用一个实例说明通过DLL实现...
  • VB调用DLL

    2015-05-13 13:59:15
    作为一种32位Windows应用程序的开发工具,VB生成的exe文件自然也都是32位的,通常情况下也只能调用32位的动态连接库。但是,并不是所有的32位动态库都能被VB生成的exe 文件正确地识别。 一般来说,自己编写用于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不按装饰名进行输出。

      3VC中的编译选项"结构成员对齐方式(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中调用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关键字,以示区分。下面分别介绍声明语句的各个组成部分。

    ()、指定动态库:

      Declare语句中的Lib子句用来告诉Visual Basic如何找到包含过程的.dll文件。 如果引用的过程属于Windows核心库(User32Kernel32GDI32),则可以不包含文件扩展名,如:

       Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long 

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

       Declare Function lzCopy Lib "c:/windows/lzexpand.dll" _

       (ByVal S As Integer, ByVal D As Integer) As Long 

    如果未指定libname的路径,Visual Basic将按照下列顺序查找该文件:

      ①.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.dll32ShellAPI

      User32.dll用户接口例程库

      Version.dll版本库

      Winmm.dllWindows多媒体库

      Winspool.drv后台打印接口,包含后台打印API调用。

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

    ()、使用别名:

    A.函数名是标准的名称

      Declare语句中的Alias子句是一个可选的部分,用户可以通过它所标识的别名对动态 库中的函数进行引用。例如,在下面的语句中,声明了一个在VB中名为MyFunction的函数,而它在动态库Mydll.dll中最初的名字是MyFunctionX

       Private Declare Function MyFunction Lib "Mydll.dll" _

       Alias "MyFunctionX" ( ) As Long 

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

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

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

       WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString); 

      第二个描述将它定义为 SetWindowTextW 尾部的"W" 表明它是一个 Unicode 函数:

       WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString); 

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

    Private Declare Function SetWindowText Lib "user32" _Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal _lpString As String) As Long

      应当注意,对于VB中使用的系统WindowsAPI函数,应该指定函数的ANSI版本,因为只 WindowsNT才支持Unicode版本,而Windows95不支持这个版本。仅当应用程序只运行 WindowsNT平台上的时候才可以使用Unicode版本。

    ()、使用值或引用传递

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

    Declare Function InvertRect Lib "user32" Alias _

    "InvertRectA" (ByVal hdc As Long, lpRect As RECT) As Long

      动态连接库的参数传递是一个复杂的问题,也是VB中调用动态连接库时最容易出现错误的地方。参数类型或传递方式的声明错误都可能导致应用程序出现GPF(通用保护错误),甚至使操作系统崩溃。



    展开全文
  • visual c++枚举进程和进程对应的文件路径和该进程调用的模块路径.zip
  • 这个问题帖子的解决方案 1.建立一个类库文件 2.类库里面随便写点什么 ...绝对路径,不copy。注意一下签名的地方也是true 7.最关键的是需要修改app.config 8.调试 打包 效果 没有那个dll...
  • 这款音乐播放器基于VB.NET编写,实现了以下功能:播放位置随着滑块的拖动而实时变化,是否静音播放的功能,把跟踪条值赋给MediaPlayer1控件的音量设置值,播放或暂停的功能,打开要播放的媒体文件的功能,实时(每隔...
  • java Jacob调用VB编写的DLL(COM组件)

    千次阅读 2018-11-01 19:42:29
    项目上有调用Visual Basic 6.0编写的DLL文件的需求,在使用JNA调用无果后,在网上找了部分资料,整理如下。 为什么用JNA无法调用VB生成的COM组件,虽然是dll后缀,但事实上,它应该叫做activex dll,是一种com...
  • vb 生成dll ,vbs 调用dll

    千次阅读 2013-04-16 10:38:41
    3.用regsvr32 + DLL的全路径.(注册) 4.打开VBS 定义该DLL对象. "Set test = CreateObject("test.class")"(test是工程名,class是类名) 5.用对象点方法就可以了.   例VB里输入代码: Public Function
  • VBA调用dll实例

    2010-08-12 18:58:37
    VBA调用dll是指定路径的,如果该路径下没有想调的dll怎么办,本文给出了一个解决方法
  • 环境2.dll编译 1.环境 VS2010 win7 sp1 x64 小插曲,第一次用VB的可以看看下面链接和C差不多。迫于工程需要,我也是第一次用。 主要用法。 定义变量 Dim a as String // 定义一个名为 a 的 String 变量。 与java...
  • 开发需求:需要java调用vb生成的dll动态链接库 开发环境:win10 64位、jdk1.8 64位、springboot、以及dll文件 开发步骤:  1)装载dll文件 有两种方式System.load();和System.loadLibrary(); load需要全路径...
  • Dim o As Object ...test.dll是自己封装的dll,封装完后通过regsvr32.exe注册了,调用的时候也引用了这个dll,但调试的时候到llResult = o.rdpany(1, isTrue)这一句就提示这个错误了,不知道是什么原因?
  • 动态链接库(dll)的好处就不多说了,这里就把如何在VB调用VC写的dll函数的方法介绍一下,供有需要的朋友们参考,同时也免得自己以后忘了。 初次接触DLL的用户经常会遇到一个问题:在VC环境下创建的DLL,在VC里...
  • VB怎么调用DLL文件 这个DLL文件是自己用VB写的放在主程序一个文件夹里面的,网上找了很多办法都不行。以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!...
  • 任意C#的DLL不用注册实现被VB6调用

    千次阅读 2019-05-22 22:35:48
    VB6与C#之间的交互,据我所知,有以下几种方式: ...,但是VB操作共享内存麻烦,要调用Windows API(Kernel32.dll中的函数) 3.读写文件/数据库的方式。这种方式无非是轮询,一个程序写请求,另一个...
  • 我们认为没有完整的dll路径VB6找不到它。如果我们用声明函数Declare Function TestTinyDLL Lib "e:\full\path\down\to\TinyDll.dll" Alias "_testdll@0"然后调用工作,但如果没有给出完整路...
  • vb调用c++ dll中注意的问题

    千次阅读 2017-11-06 10:51:38
    目前OLED_V2自动化测试中遇到的问题...在VB调C++的dll时,这个问题就会暴露出来,所以最好是以数组作为VB工程和dll间传参的接口。 2.这个问题比较奇怪,如果有朋友能解释的麻烦说明一下,感谢。  dll本身设置成c++的
  • VB-调用Dll里面的方法

    2019-10-06 17:54:32
    一个简单的调用DLL里面的方法,首先引用DLL进项目,然后根据下面方法进行调用。 Dim objType As System.Type = Nothing Dim objAssembly As System.Reflection.Assembly Dim objMethod As System.Reflection....
  • jacob JACOB就是 JAVA-...jacob对于com组件的访问调用,在方式上已经大大简化,能够像C#一样通过简单的代码调用。现最新版本为1.19,对应jdk1.8 使用 下载jacob,下载地址。 把jacob-1.19-x86.dll和jacob-1.1...
  • vb、vc++、wincc 调用dll相对应的方法

    千次阅读 2011-12-07 10:01:56
    首先我们介绍一下vb调用dll的几种方法 一、在vb工程中引用dll 1.在工程-引用中将asdfg.dll引用过来 2.dim cc as asdfg 'asdfg是类模块的名称 Private Sub Form_Load() dim x Set cc= New asdfg x = cc.zxc(参数1,...
  • VB调用VC++开发的DLL

    千次阅读 2016-02-22 11:17:15
    再谈在VB调用VC++开发的DLL  再谈在VB调用VC++开发的DLL   近日开发一个程序,用到动态链接库,在VB调用时遇到...我查了一些资料,也看了一下CSDN上的文章,感觉这些文章对在VB调用VC++开发的DLL这一
  • DLL的优点 简单的说,dll有以下几个优点: 1) 节省内存。同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内存中。如果使用dll,则只...
  • VB封装DLL实例(三)

    千次阅读 2017-02-11 17:40:46
    进入VBA编辑窗口,点菜单【工具】—【引用】,打开【引用】对话框,点【浏览】按钮,打开【添加引用】对话框,点选要引用的DLL(测试实例为:ClsFindString.dll),点【打开】—点【确定】,我们完成动态链接库的...
  • 如何用VB编写自己的DLL文件,并调用其中的函数?
  • vb调用vc++dll文件

    2011-11-09 17:05:31
    ① 关于DLL的创建与调用  使用VC++的向导即可。具体操作如下:打开菜单“File\New”→选择“Projects\Win32 Dynamic-Link Library”→选择“A simple DLL project”即可。这时系统会自动生成3个文件:*....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,187
精华内容 4,074
关键字:

vb调用dll路径