在vb中调用dll的方法

在vb中调用dll的方法
在vb中调用dll的方法

1 制作好DLL之后,就可以用VB调用它,实现VB调用C程序。VB程序要使用DLL中的函数,首先必须要有特殊的声明,用Declare声明语句在窗体级或模块级或全局模块的代码声明段进行声明,将动态链接库中的函数声明到VB中,供VB程序调用。

语句格式为:Declare Sub 过程名Lib [ Alias " 别名]([ByVal 参数AS类型]),或为Declare Function函数名Lib [Alias " 别名]([ByVal 参数AS类型])AS类型在声明中首先用Declare 关键字表示声明DLL中的函数。在C语言中有的函数类型为VOID,它表示不具有返回值,则必须用关键字Sub将其声明成过程。有的函数具有返回值,则必须用关键字Function将其声明成函数,并且在声明语句的最后要用AS关键字指明函数返回值的类型。

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

Decl are Function ADD Lib “c:\ADD.dll” (ByVal X AS Integer, ByVal Y AS Integer ,ByVal filein asstring)AS Integer

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

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

// 函数名:int __stdcall GetMacNo(int *MacNo)

// 功能:获取卡机的卡机号(单机时)

// 参数: MacNo[0]-被读出的卡机号

// 返回值:0-成功,

// 2-PC接收超时,

// 3-应答错误

dll的文件名是COMM232.dll

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

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

VB里也可以定义指针吗?

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

我是这样声明的对吗?

Public Declare Function GetMacNo Lib "COMM232.dll" (ByVal MacNo As Integer) As Integer

又应该怎么调用呢?要先定义一个指针的变量再传给*MacNo还是要怎么做?

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

dim l as integer

dim m as integer

l=GetMacNo(m)

if l=0 then

label1.caption="卡机号: " & m

elseif l=2 then

msgbox "PC接收超时"

elseif l=3 then

msgbox "应答错误"

end if

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

一般来说,动态连接库都是以DLL为扩展名的文件,如Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多,如VisualC++、BorlandC++、Delphi等,具体方法可以参见相关文档。下面只以Visual C++6.0为例,介绍一下开发应用于VisualBasic6.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方,都以VC为例;所有涉及Visual Basic 的地方都以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中的编译选项"结构成员对齐方式(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关键字,以示区分。下面分别介绍声明语句的各个组成部分。

(一)、指定动态库:

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

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.dll32位ShellAPI库

User32.dll用户接口例程库

V ersion.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函数实际有两种格式:ANSI 和Unicode(关于ANSI和Unicode两种字符集的区别将在后面详细阐述)。因此,在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

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

C.使用序号标识DLL过程

除了使用名称之外,还可以使用序号来标识DLL过程。某些动态连接库中不包含过程的名称,在声明它们包含的过程时必须使用序号。同使用名称标识的DLL过程相比,如果使用序号,在最终的应用程序中消耗的内存将比较少,而且速度会快些。但是,一个具体的API的序号在不同的操作系统中可能是不同的。例如GetWindowsDirectory在Win95下的序号为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.exe是Microsoft 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数据类型(包括Integer、Long、Single、Double、Currency 类型)、Byte数据类型和Boolean数据类型。它们的共同的特点是结构简单,操作系统在处理时不必进行特殊的转换。

简单数据类型参数的传递比较简单。我们知道,在VB中传递参数的方式有两种:传值(Byval)和传址(ByRef),缺省的方式是传址。所谓传值,就是对一个变量的具体值进行

传递;而传址则是传递变量的地址。例如,在VB程序中需要将一个整型变量m=10的值传进动态库,如果用传值方式,那么传进动态库的值就是10,而在传址方式下,传入的则是变量m的地址,相当于C/C++ 中&m的值。需要注意的是,以传值方式传进动态连接库的变量,其值在动态库中是不能被改变的;如果需要在动态连接库中修改传入参数的值,则必须使用传址方式。一般来说,在VB 和动态连接库之间传递单个的简单数据类型,只要注意了以上几个方面就可以了。当需要将一个简单数据类型的整个数组传进动态库时,必须将相应参数声明为传址方式,然后把数组的第一个元素作为参数传入,这样在动态连接库中就得到了数组的首地址,从而可以对整个数组进行访问。例如,声明了一个名为ReadArray的DLL过程,要求传入一个整型数组aArray:

Declare Function ReadArray Lib "mydll.dll" _

(aArray As Integer) As Integer

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

Dim ret,I(5) as Integer

……

ret = ReadArray(I(0)) '

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

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

与简单数据类型相比,字符串类型(String、String*n)的参数传递要复杂得多,这主要是Windows 95 API和VB使用的字符串类型不同的缘故。VB使用被称为BSTR的String数据类型,它是由自动化(以前被称为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型变量占一个字节,不含符号位,因此所能表示的范围为0到255。这种数据类型是专门用于存放二进制数据的。为了将整个字符串数组传进动态库,可以用字节数组来保存字符串。由于Byte是一种简单数据类型,因此字节数组的传递是非常简单的。首先,需要把一个字符串正确地转变成一个字节数组。这要涉及一些字符集的知识。Windows 95和VB使用不同的字符集,Windows 95 API使用的是ANSI或DBCS 字符集,而VB使用的则是Unicode字符集。所谓ANSI字符集,是指每个字符都用一个字节表示,因此最多只能有28=256个不同的字符,这对于英语来说已经足够了,但不能完全支持其它语言。DBCS字符集支持很多不同的东亚语言,如汉语、日语和朝鲜语,它使用数字0-255表示ASCII 字符,其它大于255或小于0的数字表明该字符属于非拉丁字符集;在DBCS中,ASCII字符的长度是一个字节,而汉语、日语和其它东亚字符的长度是2个字节。而Unicode字符集则完全用两个字节表示一个字符,因此最多可以表示216=65536个不同字符。也就是说,ANSI字符集中所有的字符都只占一个字节,DBCS字符集中ASCII字符占一个字节,汉字占两个字节,Unicode 字符集中每个字符都占两个字节。由于VB与WindowsAPI使用的字符集不同,因此在进行字符串到字节数组的转换时,当用Asc函数取得一个字符的字节码后,需要判断它是否是一个ASCII 字符;如果是ASCII字符,则在转换后的字节数组中就只占一个字节,否则要占两个字节。

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

当转换完成以后,查看字节数组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-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。但是,由于m1和m2的字节总长度是3,小于4,它们就存放于一个单元中;但该单元剩下的一个字节不足以放下一个Long型的成员m3,于是m3 就被放在下一个单元中,它们之间就有了一个未用的空字节;因此,整个结构所占实际长度是8 字节。同理,如果将m3和m2的位置交换一下,它所占的尺寸就变成了9字节。可见,成员在结构中的声明顺序也是非常重要的。

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

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

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

二、FORTRAN编译成.dll的问题

1、.for 可以编译成.dll

有几个步骤链接到Visual Basic 程序FORTRAN 中编写的dll。这些步骤简要如下所述,本文中稍后介绍的详细说明。

首先,创建或修改FORTRAN 过程。使用程序员工作台可以创建或修改FORTRAN 过程。您可以使用子例程和函数的过程。该过程必须遵循下列规则:

使用默认FORTRAN 或Pascal 调用约定。

?使用Visual Basic 可以使用的数据类型。请参阅本文中的"兼容数据类型中FORTRAN 和Visual Basic"一节。

接下来,创建模块定义(.DEF) 文件。此文件Segmented 可执行文件链接器提供特定于Windows 的信息。此信息以后由Windows 用来动态地链接到Visual Basic 应用程序的库。

现在,您可以编译源代码以生成对象文件,然后链接它实际创建DLL。注意Visual Basic 期望采取远指针通过引用传递的值的所有DLL 过程。Windows 还都有上dll 的一些限制。您可以创建DLL 通用对象文件使用编译器开关,但需要将有适当的静态链接(.LIB) 库来创建DLL。

接下来,编写Visual Basic 过程声明,每个过程。这些信息将告诉Visual Basic 几件事:

?名称和类型的每个过程中。

?名称和DLL 的位置。

?顺序和其正式参数的类型。

?数据类型函数将返回。

最后,编写过程调用。过程调用将调用vba 过程所做的相同。

回到顶端

分步示例

1.请确保您有适当的库。请检查该FORTRAN\LIB 文件LDLLFEW.LIB 的目录。如果

您没有此文件,您将需要创建从原始FORTRAN 程序盘。如果您已经有此文件跳到步骤

2 ;否则,请按照下列步骤操作:

a.要确保LIB.EXE 程序当前PATH 中目录中。

b.插入磁盘1 然后运行SETUP.EXE。

c.按ENTER 键转到主菜单。

d.从主菜单中选择"加载或生成附加库",然后按ENTER 键。

e.请按照该屏幕说明如果您需要更改的组件库的源。然后按ENTER 键。

f.指定为库文件的目标位置。这应该是组合的库已有的目录安装(可能是

C:\FORTRAN\LIB)。然后按ENTER 键。

g.突出显示"MS-DOS 和Windows"目标操作系统中列表,然后按空格键将其选中。

取消选择任何其他项目,然后按ENTER 键。

h.突出显示"是"构建FORTRAN Windows DLL 库,然后按ENTER 键。

i.突出显示"是"以生成组合的库,然后按ENTER 键。

j.突出显示"仿真程序数学"Math 选项列表中的,然后按空格键将其选中。取消选择任何其他项目,然后按ENTER 键。

k.请确保内存模型中突出显示"大内存模型"列表,然后按ENTER 键。

l.您可以选择"是"否"在组合库中包括GRAPHICS.LIB。然后按ENTER 键。

m.您可以选择"是"否"在组合库中包含错误说明。然后按ENTER 键。

n.选择否安装C 的从属库?提出的问题,并按ENTER 键。

o.选择"是"以删除组件库完成,然后按ENTER 键。

p.您可能要重命名默认库的任何选择;此步骤不会影响LDLLFEW.LIB 文件的创建。

q.确认您的答复显示准确地,"否更改"项,以开始生成库按ENTER 键。

r.按ENTER 键来开始生成在提示符组合的库。

s.它完成时,请退出安装程序。

2.创建FORTRAN 的过程。

.使用程序员工作台或其他的文本编辑器创建称为FDLL.FOR,文件和列7 处键入下面的代码开头:

a. SUBROUTINE fsub (x [REFERENCE])

b. INTEGER*4 x

c. x = x + 1

d. END

e.

f. FUNCTION ffunc (y [VALUE])

g. INTEGER*2 y

h. ffunc = y + 1

i. END

j.

k. SUBROUTINE fstring (fstr [REFERENCE])

l. CHARACTER*20 fstr

m. fstr = 'Jack Be Nimble'

n. END

o.保存该文件,但不退出编辑器。

注意:此源代码定义了三个过程。第一个fsub,通过引用将长时间(4 字节)整数。它然后递增 1 传递给它的最长的整数。第二个过程ffunc,按值,短(2 字节)整数并返回短整数的整数按 1 递增的值相等。最后一个fstring,通过引用,采用一个字符串值,并分配"插座娉Nimble"到字符串传递给它的字符串。

3.创建模块定义(.DEF) 文件。

.使用文本编辑器创建一个新的文件,在下面的行中称为FDLL.DEF 和类型:

a. ; FDLL.DEF - Module Definition file for

FDLL.FOR

b. ;

c. LIBRARY FDLL

d. DESCRIPTION 'Sample FORTRAN DLL'

e. EXETYPE WINDOWS 3.0

f. CODE PRELOAD MOVEABLE

DISCARDABLE

g. DATA PRELOAD MOVEABLE SINGLE

h. HEAPSIZE 1024

i. EXPORTS fsub

j. ffunc

k. fstring

l. WEP

m.保存该文件并退出编辑器。

注意:这是相当标准FORTRAN 模块定义文件的DLL。Microsoft FORTRAN 版本 5.1 高级主题手册的136 页上可以找到类似的一个。您将需要更改为您创建的未来dll 最重要部分是

LIBRARY、说明,和导出条目。

4.编译和链接FORTRAN 项目。

.在MS-DOS 提示符键入:

a. fl /c /Aw /Gw fdll.for

注意:该/c 选项创建对象文件FDLL.OBJ,而没有链接。创建Windows dll 需

要/Aw 和/Gw 选项。

b.在MS-DOS 提示符键入:

c. link /NOE /NOD fdll.obj, fdll.dll, ,

ldllfew.lib, fdll.def;

注意:的/NOE 和/NOD 选项防止加载默认FORTRAN 静态链接库。完成

LINK 应该有一个名FDLL.DLL 的文件。

d.请确保您没有称为FDLL.DLL WINDOWS\SYSTEM 目录中的一个文件。重

命名或将其移动到另一个目录中,如果这样做。将刚创建的FDLL.DLL 复制到该

WINDOWS\SYSTEM 目录。

5.编写Visual Basic 过程声明。

.对于Windows 启动Visual Basic。form1 将加载新的项目显示。

a.在窗体的常规声明部分中添加以下代码:

b. Declare Sub fsub Lib "fdll.dll" (d As Long)

c. Declare Function ffunc Lib "fdll.dll"

(ByVal n As Integer) As Integer

d. Declare Sub fstring Lib "fdll.dll" (ByVal

fstr As String)

6.将两个命令按钮(Command1 和Command2)和两个文本在Form1 上控制(文本

1 和文本2)。排列它们,这样Command1 已接近Text1,Command

2 是Text2 附

近。下面的代码添加到每个命令按钮:

7. Sub Command1_Click ()

8. Dim lngTest as Long

9. lngTest = CLng(Val(Text1.Text))

10. Call fsub(lngTest)

11. Text1.Text = CStr(lngTest)

12. End Sub

13.

14. Sub Command2_Click ()

15. Dim intCheck as Integer

16. intCheck = CInt(Val(Text2.Text))

17. intCheck = ffunc(intCheck)

18. Text2.Text = CStr(intCheck)

19. End Sub

20.添加第三个命令按钮(Command3),并将下面的代码添加到其Click() 事件:

21. Sub Command3_Click()

22. Dim fstr As String

23. fstr = Space$(20) ' Pad string before making

call.

24. Call fstring(fstr)

25. MsgBox fstr ' Display the results.

26. End Sub

27.运行程序。单击Command1 或Command2 按钮时,您将看到递增的值显示在每个按

钮相关联的文本框中。

单击Command3 按钮时您将看到"插座娉Nimble"显示的消息。

回到顶端

其他说明

DLL 的过程和库的名称不区分Visual Basic。此外,Declare 语句可以在项目中的任何模块中。如果它们是在.BAS 文件,然后他们将您的项目的全局。如果它们是在.FRM 文件,DLL 函数只能调用从该窗体中。

您的DLL 中调用过程时,您可能会得到一个一般保护(GP) 错误。最可能的原因是您Declare 不能精确匹配FORTRAN 过程。检查这些方面:

?确保用于声明Sub 子例程,并声明函数的函数过程,并确保所有参数均为正确的类型和正确匹配。

?要确保Declare 语句中使用ByVal 关键字用于FORTRAN 过程中有[VALUE] 属性的任何参数。榛樿鎯呭喌涓嬶,FORTRAN 过程预期要通过引用,传递的所有参数和默认,

则Visual Basic 将通过引用传递所有参数。

始终确保您使用的最近创建的DLL。如果在另一个目录中创建DLL,您必须将其复制到WINDOWS\SYSTEM 目录或Declare 语句中指定DLL 的完整路径。

回到顶端

在FORTRAN 和Visual Basic 中的兼容数据类型

下表显示了一些FORTRAN 数据类型与Visual Basic 数据类型之间的转换信息: FORTRAN data types Visual Basic data types

---------------------- -------------------------------- INTEGER*2 Integer

INTEGER*4 Long

REAL Single

REAL*4 Single

DOUBLE PRECISION Double

REAL*8 Double

LOGICAL*2 Integer

LOGICAL*4 Long

CHARACTER*n String*n (Passed ByVal)

调用dll 的详细信息,请参阅第24 章"呼叫过程在dll"Microsoft Visual Basic 版本 3.0,对于Windows 的程序员指南。

3、Fortran源程序:

SUBROUTINE ARRAYTEST (n, arr)

! $ ATTRIBUTES DLLEXPORT :: ARRAYTEST

INTEGER n, i, j

REAL (4) arr (3, 3)

DO i= 1, 3

DO j= 1, 3

arr ( i, j) = 10.1*i+j

END DO

END DO

n= n+ 10

END SUBROUTINE

VB源程序(一个窗体,两个文本框,一个命令控件):

模块里为:Private Declare Sub ARRAYTEST Lib

"C:\xinjian\exam\Debug\exam.dll" Alias "_ARRAYTEST@8" (X As Long, Y As Single)

Private Sub Command1_Click()

Dim a As Long

Dim ab(1 To 3, 1 To 3) As Single

a = 10

Call ARRAYTEST(a, ab(1, 1))

Text1.Text = Str(a)

Text2.Text = Str(ab(3, 3))

End Sub

这是我的DLL源码:

recursive integer function ans(n)

!DEC$ ATTRIBUTES DLLEXPORT::ans

implicit none

integer n

if(n==1) then

ans=1

return

end if

ans=n+ans(n-1)

return

end

编译后用VB调用:

Private Declare Function ANS Lib "usedll.dll" (m As Integer) As Integer

Private Sub Command1_Click()

Dim m As Integer

Dim n As Integer

m = Val(Text1.Text)

n = ANS(m)

Text2.Text = Str(n)

End Sub

程序意图很简单,就是求1+2+。。n的值!

但只要执行就会立即关闭,程序应该没有错吧?

调用类的方法

语法如下: 语法 [访问修饰符] 返回值的类型方法名([参数列表]){ //方法体 }

(1)访问修饰符 已经讲述过类的访问修饰符,其实同理,这里的方法的访问修饰符功能也是一样,public 表示公共的,private 表示私有的。 在程序中,如果将变量或者方法声明为public,就表示其他类可以访问,如果声明为private,

(2)方法的返回类型。 方法是供别人调用的,调用后可以返回一个值,这个返回值的数据类型就是方法的返回类型,可以是int、float、double、bool、string 等。如果方法不返回任何值,就使用void。

语法 return 表达式; 如果方法没有返回值,则返回类型应该使用void(空虚;空的),用于说明无返回值。 如:public void Singing() //无返回值 { Console.Write(“在唱歌。。。”); } return 语句做两件事情:表示已经完成,现在要离开这个方法;如果方法产生一个值,这个值放置在return 后面,即<表达式>部分。意思就是“离开该方法,并且将<表达式>的值返回给调用其的程序”。

注意:在编写程序的时候,一定要注意方法声明中返回值的类型和方法体中真正的返 回的值的类型是否匹配,如果不匹配,后果很严重。比如在下面这个ToString()方法中,返 回类型是String 类型,因此在方法体中必须用return 返回一个字符串,否则编译器将报错。

(3)方法名 定义一个方法都要有一个名称 注意:方法名主要用于调用这个方法时用,命名方法就像命名变量、类一样,要遵守一定的规则,如必须以字母、下划线“_”或“$”开头,绝对不能以数字开头。

vb调用vc 的DLL

4.1 新建工程 打开在VC++6.0,new一个Win32-Dynamic-Link Library工程dlltest。 4.2 在工程中添加代码 4.2.1添加lib.h文件: #ifndef _LIB_H_ #define _LIB_H_ extern “C” int __stdcall add(int,int) ; extern “C” int __stdcall sub(int,int) ; /* __stdcall 约定 如果通过VC++ 编写的DLL 欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall 方式, WINAPI 都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall 方式与__cdecl 对函数名 最终生成符号的方式不同 */ #endif 4.2.2添加lib.cpp文件: #include “lib.h” int __stdcall add(int a,int b) { return a+b ; } int __stdcall sub(int a,int b) { return a-b ; } 4.2.3添加lib.def文件: 右击SOURCE FILE目录,添加一个文本文件(不能是word文档,一般是txt 格式的),将文档重命名为lib.def,点击打开该文档。 在文档中输入代码: LILBRARY dlltest EXPORTS add @ 1 sub @ 2 说明:DLL函数的导出有两种方式。

一种是显式导出:在.h文件中在函数声明前加入如下语句: __declspec(dllexport)。使用该方法是不用添加.def文档的。该方法导出的函数,只能被C或C++调用,不能被其他语言调用。 一种是隐式导出:就是在SOURCE FILE目录中添加一个.def文档,在该文档中写入要导出函数的代码。 .def语法规定注释用分号(;),且注释不能与代码共享一行,代码说明如下:LIBRARY dlltest ;该语句说明了相应的DLL工程。 EXPORTS add @ 1 sub @ 2 ;该语句声明了将DLL工程中导出的函数,只要写入 ;函数名称即可,不用说明返回值以及函数参数。其实@ 1 ;和@ 2是为函数编号所使用的 在添加所有的工程文件后,编译生成dll文件后 4.3 用vc调用DLL 在vc中new一个Win32 Console Application 工程test 代码如下: #include #include typedef int (__stdcall *padd)(int,int) ; typedef int (__stdcall *psub)(int,int) ; int main() { HINSTANCE hd ; hd = LoadLibrary(“填入要调用DLL的路径”) ; if(!hd) { printf(“Load DLL err\n”) ; return 0 ; } padd add = (padd)GetProcAddress(hd,“add”) ; if(!add) { printf(“Get add err\n”) ; FreeLibrary(hd) ; return 0 ; }

过程和子过程的定义和调用方法(精)

实习六 过程和子过程的定义和调用方法 地点:计算机实验室。每名学生一台计算机。 实习时间:90分钟 指导教师:任课教师,实习指导教师 学生分组:每三人为一组。每名教师负责七个组的指导。 实习目的: 1、 掌握自定义函数过程和子过程的定义和调用方法. 2、 掌握形参和实参的对应关系. 3、 掌握值传递和地址的传递方式. 4、 掌握变量、函娄和过程的作用域. 5、 掌握递归概念和使用方法. 熟悉程序设计中的常用算法. 实验6.1 编一子过程ProcMin,求一维数组a 中的最不值,子过程的形参自己确定. 主调程序随机产生10个-300~-400之间的整数,调用ProcMin 子过程,显示 最小值. 实验6.2 编一函数Max(a( )),求一维数组a 中的最大值.主调程序随机产生10个 100~200之间的数,调用Max 函数,显示最大值. 实验6.3 编一个函数过程MySin(x),求 MySin(x)=()()!121...! 7!5!31121753--++-+---n x x x x x n n 当第n 项的精度小于510-时结束计算,x 为弧度.主要程序同时调用MySin 和内

部函数Sin,进行验证. 【提示】 关键是找部分级数和的通项,通项表示如下: ))2()1/((12+?+???-=+i i x x T T i i ......7,5,3,1=i 实验6.4编一个函数过程HtoD(H),将十六进制字符串H 转换成十进制数.主调程序 调用HorD 函数,再调用内部函数Hex(number)进行逆向验证. 实验6.5 参阅教篇例6.2,编一个子过程DeleStr(s1,s2),将字符串s1中出现s2子字 符串删去,结果还是存放在s1中. 例如:s1=”12345678AAABBDFG12345” s2=”234” 结果:s1=”15678AAABBDFG15” 【提示】 解决此题的方法有以下要点: (1) 在s1字符串中找s2的子字符串,可利用lnStr()函数,要考虑到s1中可能存在多个或不存在s2字符串,用Do While Instr(s1,s2)﹥0循环结构来实现. 若在s1中找到s2的子字符串,首先要确定s1字符串的长度,因s1字符串在进行多次删除时,长度在变化..然后通过Left() 、Mid() 或Rigth()函数的调用达到删除s1中存在的s2字符串. 实验6.6 编一函数过程MaxLength(s),在已知的字符串s 中,找出最长的单词.假定 字符s 内只含有字母和空格,空格发隔不同的单词.程序运行界面如图2.6.1 所示

JAVA里面方法调用时传对象参数的陷阱

JAVA里面方法调用时传对象参数的陷阱 类似的问题,又被人问到了几次,决定还是简单总结一下吧。这个问题,一些老手已经很清楚了,但有时也会很不小心的被XX了一把。 其实问题的核心,就是参数虽然是传的引用,但参数就是参数,他自身是一个本地的局部引用而已,设为首页只不过在这个时刻和调用者指向了同一个对象。但并不代表这个局部引用在整个方法调用期间内能始终和调用者保持一致。 下面是2个测试,分别测试可修改的Object和不可修改的 /** * JA V A里面对象参数的陷阱 */ public class Test { public static void main(String[] args) { TestV alue tv = new TestV alue(); tv.first(); TestInteger ti = new TestInteger(); ti.first(); } } class TestV alue { class V alue { public int i = 15; } // 初始化 V alue v = new V alue(); public void first() { // 当然是15 System.out.println(v.i); // 第一次调用 second(v); System.out.println(v.i); third(v); System.out.println(v.i); } public void second(V alue v) { // 此时这里的v是一个局部变量 // 和类属性的v相等 System.out.println(v == this.v); v.i = 20; } public void third(V alue v) { // 重新设置一个对象 v = new V alue();

VB调用动态链接库(DLL)

VB调用动态链接库(DLL) 作为一种简单易用的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

12.4.3 包含实例变量的方法调用[共2页]

COBOL I AM WORKING I AM PLAYING I AM LEARNING COBOL 该段客户程序首先创建了一个基于HUMANCLS类的实例。并且,该程序使用数据部中定义的数据M来引用该实例。其后,该程序通过数据M实现了HUMANCLS类的实例中3个方法的调用。关于HUMANCLS类的原型,同前面章节中的一致。 12.4.3 包含实例变量的方法调用 上一小节仅是使用客户程序实现了最基本的方法调用。在以上所调用的方法中,只包含了一条输出语句,并未涉及到实例中的变量。本节将在此基础上,讨论如何通过客户程序实现包含有实例变量的方法调用。 例如,在HUMANCLS类中有3个实例变量,可分别表示姓名、年龄和心情状态。此处要求通过客户程序,分别调用包含有以上3个实例变量的方法。其中所要实现的功能是能够对实例状态进行设置,并得到该状态的输出信息。 为实现以上功能,首先需要重写HUMANCLS类。不妨将重写后的HUMANCLS类命名为HUMANCLS2。重写后的HUMANCLS2类需要包含6个方法。设置姓名、年龄和心情状态为其中的3个方法。输出姓名、年龄和心情状态信息为另外的3种方法。HUMANCLS2类的代码如下。 IDENTIFICATION DIVISION. CLASS-ID. HUMANCLS2 INHERITS BASECLS. * ENVIRONMENT DIVISION. CONFIGURATION SECTION. REPOSITORY. CLASS BASECLS IS 'BASECLS' CLASS HUMANCLS IS 'HUMANCLS'. * IDENTIFICATION DIVISION. OBJECT. DATA DIVISION. WORKING-STORAGE SECTION. 01 NAME PIC X(10). 01 AGE PIC 999. 01 MOOD PIC X(5). PROCEDURE DIVISION. * IDENTIFICATION DIVISION. METHOD-ID. SETNAME. DATA DIVISION. LINKAGE SECTION. 01 PASS-NAME PIC X(10). PROCEDURE DIVISION USING PASS-NAME. MOVE PASS-NAME TO NAME. END METHOD SETNAME. * IDENTIFICATION DIVISION. METHOD-ID. SETAGE. DATA DIVISION. 25312.4 COBOL中的客户程序

VB如何调用dll函数

VB如何调用dll函数 2008-01-10 17:17 開始習慣孤單 | 分类:VB| 浏览13089次 假如我有个DLL,名为 asdfg.dll 它里面有一个函数 zxc(参数1,参数2) 我要用这个函数,该怎么写? 请高手帮我写出脚本,有时间的话请再加上注释 谢谢!! 2008-01-10 19:50 提问者采纳 1.在工程-引用中将asdfg.dll引用过来 2.dim AAA as asdfg 'asdfg是类模块的名称 Private Sub Form_Load() dim x Set AAA = New asdfg x = AAA.zxc(参数1,参数2)'参数1,2自己写 End Sub 建议你了解一下下面dll的制作方法,理解就更透彻了。下面内容来自百度。 1.新建一个ActiveX Dll,工程名字为vbmytestdll,类模块

名字为mytestdll 2.类模块内容只有一个函数,主要返回DLL的HELLO WORLD Public Function dlltest1() As String dlltest1 = "HELLO WORLD" End Function 3.保存,生成DLL,名字为 vbmytestdll.dll 4.新建一个EXE工程,在菜单: 工程---引用---浏览里找到vbmytestdll.dll,把它引用进来 5.EXE工程代码如下: Option Explicit Dim testdll As mytestdll'类模块名字 Private Sub Form_Load() Set testdll = New mytestdll 'DLL的一个新实例 Me.Caption = testdll.dlltest1 '我的标题=返回DLL的HELLO WORLD

java方法的调用基础入门

1 方法 /* 方法:完成特定功能的代码块。 注意:在很多语言里面有函数的定义,而在Java中函数被称为方法。 方法格式: 修饰符返回值类型方法名(参数类型参数名1,参数类型参数名2...) { 方法体语句; return 返回值; } 详细解释: 修饰符:目前就用public static。后面我们再详细的讲解其他的修饰符。 返回值类型:就是功能结果的数据类型。 方法名:符合命名规则即可。方便我们的调用。 参数: 实际参数:就是实际参与运算的。 形式参数;就是方法定义上的,用于接收实际参数的。 参数类型:就是参数的数据类型 参数名:就是变量名 方法体语句:就是完成功能的代码。 return:结束方法的。 返回值:就是功能的结果,由return带给调用者。 要想写好一个方法,就必须明确两个东西:

A:返回值类型 结果的数据类型 B:参数列表 你要传递几个参数,以及每个参数的数据类型 需求:求两个数据之和的案例 方法的执行特点: 不调用,不执行。 如何调用呢?(有明确返回值的调用) A:单独调用,一般来说没有意义,所以不推荐。 B:输出调用,但是不够好。因为我们可能需要针对结果进行进一步的操作。 C:赋值调用,推荐方案。 */ class FunctionDemo { public static void main(String[] args) { int x = 10; int y = 20; //方式1:单独调用 //sum(x,y); //方式2:输出调用 //System.out.println(sum(x,y));

//System.out.println(30); //方式3:赋值调用 int result = sum(x,y); //result在这里可以进行操作 System.out.println(result); } /* 需求:求两个数据之和的案例 两个明确: 返回值类型:int 参数列表:2个,都是int类型。 */ public static int sum(int a,int b) { //如何实现呢? //int c = a + b; //return c; //c就是a+b,所以,我可以直接返回a+b return a + b; } } 2 方法注意事项

一步一步教用VC和VB调用C++ DLL

一步一步教你用VC和VB调用C++ DLL 从VC++应用程序调用C++ DLL的函数 Visual Studio 6 使创建包含函数或类的动态连接库(DLL) 变得非常容易.第一步 打开Visual Studio 然后选择File | New菜单项: 选择Win32 Dynamic Link Library, 输入工程名, 敲OK.

选择A DLL that exports some symbols 并单击Finish.在File View里你会看到如下的工程文件: 第二步 在Test.cpp里,你将看到如下代码: // Test.cpp : Defines the entry point for the DLL application.//#include "stdafx.h"#include "Test.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_ call, LPVOID lpReserved)

{ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } // This is an example of an exported variable TEST_API int nTest=0; // This is an example of an exported function. TEST_API int fnTest(void) { return 42; } // This is the constructor of a class that has been exported.// see Test.h for the class definition CTest::CTest() { return; } Test.cpp 包含了fnTest 和 CTest::CTest.如果你现在编译Test.dll, 你将会得到一个可以被其他VC++应用程序直接调用的DLL. 允许其他VC++程序调用的关键机制?( key mechanism)就包含在Test.h中: // The following ifdef block is the standard way of creating macros// which make exporting from a DLL simpler. All files within this DLL// are compiled with the TEST_EXPORTS symbol defined on the command line.// This symbol should not be defined on any project that uses this DLL.// This way any other project whose source files include this file see // TEST_API

远程调用的几种方式

远程调用的几种方式 在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB 和JMS 等,这些名词之间到底是些什么关系呢,它们背后到底是基于什么原理实现的呢,了解这些是实现分布式服务框架的基础知识,而如果在性能上有高的要求的话,那深入了解这些技术背后的机制就是必须的了,在这篇blog中我们将来一探究竟,抛砖引玉,欢迎大家提供更多的实现远程通讯的技术和原理的介绍。 基本原理 要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO 来实现,其中传输协议有tcp、udp等等,tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。 应用级协议 远程服务通讯,需要达到的目标是在一台计算机发起请求,另外一台机器在接收到请求后进行相应的处理并将结果返回给请求端,这其中又会有诸如one way request、同步请求、异步请求等等请求方式,按照网络通信原理,需要实现这个需要做的就是将请求转换成流,通过传输协议传输至远端,远端计算机在接收到请求的流后进行处理,处理完毕后将结果转化为流,并通过传输协议返回给调用端。 原理是这样的,但为了应用的方便,业界推出了很多基于此原理之上的应用级的协议,使得大家可以不用去直接操作这么底层的东西,通常应用级的远程通信协议会提供: 1. 为了避免直接做流操作这么麻烦,提供一种更加易用或贴合语言的标准传输格式; 2. 网络通信机制的实现,就是替你完成了将传输格式转化为流,通过某种传输协议传输至远端计算机,远端计算机在接收到流后转化为传输格式,并进行存储或以某种方式通知远端计算机。 所以在学习应用级的远程通信协议时,我们可以带着这几个问题进行学习: 1. 传输的标准格式是什么? 2. 怎么样将请求转化为传输的流? 3. 怎么接收和处理流? 4. 传输协议是? 不过应用级的远程通信协议并不会在传输协议上做什么多大的改进,主要是在流操作方面,让应用层生成流和处理流的这个过程更加的贴合所使用的语言或标准,至于传输协议则通常都是可选的,在java领域中知名的有:RMI、XML-RPC、Binary-RPC、SOAP、CORBA、JMS,来具体的看看这些远程通信的应用级协议:

vb编写DLL学习

怎样用VB编写DLL文件??? 你先打开VB,然后先中“ActiveX Dll”工程,然后开始编辑。编完后,保存一般是以“.cls”和“.vbw”保存。这时你选择“文件”菜单,弹出下拉框,选中“生成.dll”选项,它就会在上面弹出一个进度条“正在生成.dll文件”。生成以后是一个有好象齿轮状的图标。 如果你要引用,就开一个“标准EXE”工程,然后在“工程”选项中,单击“引用”,找到你刚才的“.dll”的文件名,在前面的复选框中打勾,就可以引用你刚才在那里面写的方法等。 在vb6里建新工程时选用建立dll 做好后用vb的引用就可以了 VB中创建的DLL只是COM组件,无法作为输出函数的DLL,其实这只是个错误的说法。其实MS 非常狡猾,如果你是个VB疯狂发烧友的话,应该早就狂试出这种可以创建输出函数的DLL的方法。 VB编译文件实际上采取了两次编译的方法,首先是调用C2.exe产生*.OBJ文件,然后调用Link.EXE连接。如果在LINK的时候添加EXPORT选项,实际上是可以输出函数的。但是,在VB的工程选项中将这些屏蔽了。而且过分的是:VB在Build完成后会将OBJ文件删除,这样就无法手动通过Link来创建我们需要的DLL了。不过我找到一个比较龌鹾的变通的方法,就是先创建一个Exe工程,在Form_Load事件里面写下面的语句: Sub Main If MsgBox("哈哈", vbOKCancel) = vbOK Then Shell "link2.exe " & Command$ End If End Sub 然后编译为LinkTemp.EXE,接下来将LINK.EXE改名为Link2.exe,将LinkTemp.EXE改名为Link.EXE。这样在VB调用Link.EXE时会弹出对话框,处理就会中断。这时就可以有机会将OBJ文件拷贝出来了。然后我创建了一个ActiveX DLL工程,在这个工程里面添加一个Module并创建一个Public函数mathadd: Public Function mathadd(ByVal a As Long, ByVal b As Long) As Long mathadd = a + b End Function 编译这个工程,在Link的时候就会中断。然后把创建的Class1.obj、Module1.obj、Project1.obj备份出来。然后就可以调用Link2.exe连接OBJ到DLL了,我的连接代码是: Link2.exe "e:\vbdll\Class1.obj" "e:\vbdll\Module1.obj" "e:\vbdll\Project1.obj" "E:\Program Files\Microsoft Visual Studio\VB98\VBAEXE6.LIB" /ENTRY:__vbaS /EXPORT:mathadd /OUT:"e:\vbdll\ProjectOK.dll" /BASE:0x11000000 /SUBSYSTEM:WINDOWS,4.0 /VERS 注意里面的/ENTRY和/EXPORT开关,/EXPORT开关声明了输出函数mathadd。这样就大功告成了,可以被其他语言引入,例如在VB中,只需要: Private Declare Function mathadd Lib "e:\vbdll\ProjectOK.dll" (ByVal a As Long, ByVal b As Long) As Long

【IT专家】js函数常见的写法以及调用方法

本文由我司收集整编,推荐下载,如有疑问,请与我司联系 js函数常见的写法以及调用方法 2017/04/01 0 写在前面:本文详细的介绍了5中js函数常见的写法以及调用的方法,平时看别人代码的时候总是看到各种不同风的js函数的写法。不明不白的,找了点资料,做了个总结,需要的小伙伴可以看看,做个参考。1.常规写法(最常见的那种) ?//函数的写法function run () { alert(‘常规写法’);//这里是你函数的内容} // 调用run();2.匿名函数写法(可以想成给变量赋一个函数) ?// 匿名函数的写法var run = function(){ alert(‘这是一种声明函数的方式,左边是一个变量,右边是一个函数的表达式,意思就是把一个匿名的函数表达式赋给了变量myfun,只是声明了一个变量指向了一个函数对象。’);//这里是你函数的内容} // 调用run();3.将方法作为一个对象 ?// 作为对象方法,函数写法,这里创建了两个函数外面用{}包裹起来var Test = {run1:function(){ alert(‘这个必须放在一个对象内部,放在外边会出错!’);//这里是你函数的内容},run2:function(){ alert(‘这个必须放在一个对象内部,放在外边会出错!’);//这里是你函数的内容}}//调用Test.run1();//调用第1个函数Test.run2();//调用第2个函数4.构造函数中给对象添加方法javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。 ?// 给对象添加方法var funName = function(){}; funName.prototype.way = function(){ alert(‘这是在funName函数上的原始对象上加了一个way方法,构造函数中用到’); } // 调用var funname = new text();// 创建对象funname.way();//调用对象属性5.自执行函数js自执行函数查到了几种不同写法,放上来给大家看看 ?//方法1:最前最后加括号(function(){alert(1);}()); /*这是jslint推荐的写法,好处是,能提醒阅读代码的人,这段代码是一个整体。例如,在有语法高亮匹配功能的编辑器里,光标在第一个左括号后时,最后一个右括号也会高亮,看代码的人一就可以看到这个整体。*///方法2:function外面加括号(function(){alert(1);})(); //这种做法比方法1少了一个代码整体性的好处。//方法3:function前面加运算符,常见

在VB中调用DLL的方法

1制作好DLL之后,就可以用VB调用它,实现VB调用C程序。VB程序要使用DLL中的函数,首先必须要有特殊的声明,用Declare声明语句在窗体级或模块级或全局模块的代码声明段进行声明,将动态链接库中的函数声明到VB中,供VB程序调用。 语句格式为:Declare Sub过程名Lib[Alias"别名]([ByVal参数AS类型]),或为Declare Function函数名Lib[Alias"别名]([ByVal参数AS类型])AS类型在声明中首先用Declare 关键字表示声明DLL中的函数。在C语言中有的函数类型为VOID,它表示不具有返回值,则必须用关键字Sub将其声明成过程。有的函数具有返回值,则必须用关键字Function将其声明成函数,并且在声明语句的最后要用AS关键字指明函数返回值的类型。 例如上面的ADD.DLL在VB中就可以声明为: Declare Function ADD Lib“c:\ADD.dll”(ByVal X AS Integer,ByVal Y AS Integer,ByVal filein asstring)AS Integer 通过此声明语句将函数ADD声明到VB中,便可直接调用。 2、dll文件中的函数好像是C语言写的, //函数名:int__stdcall GetMacNo(int*MacNo) //功能:获取卡机的卡机号(单机时) //参数:MacNo[0]-被读出的卡机号 //返回值:0-成功, //2-PC接收超时, //3-应答错误 dll的文件名是COMM232.dll 函数的形参int*MacNo是指针吗? 在VB中应该怎么声明和调用该函数? VB里也可以定义指针吗? 问题补充:vb调用dll文件中的函数我是会的,但这儿的形参有一个星号才不知是怎么一回事, 我是这样声明的对吗? Public Declare Function GetMacNo Lib"COMM232.dll"(ByVal MacNo As Integer)As Integer 又应该怎么调用呢?要先定义一个指针的变量再传给*MacNo还是要怎么做? 都说了MacNo是被读出的卡机号,那么就是传址的了。 dim l as integer dim m as integer l=GetMacNo(m) if l=0then label1.caption="卡机号:"&m elseif l=2then msgbox"PC接收超时" elseif l=3then msgbox"应答错误" end if

函数调用有哪几种方式

函数调用有哪几种方式 我们知道在进行函数调用时,有几种调用方法,主要分为C式,Pascal式.在C和C++中 C式调用是缺省的,类的成员函数缺省调用为_stdcall。二者是有区别的,下面我们用 实例说明一下:(还有thiscall和fastcall) 1. __cdecl :C和C++缺省调用方式 C 调用约定(即用__cdecl 关键字说明)按从右至左的顺序压参数入栈,由调用者把 参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数 的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl 是C 和C++ 程序缺省的调用方式。每一个调用它的函数都包含清空堆栈的 代码,所以产生的可执行文件大小会比调用_stdcall 函数的大。函数采用从右到左的 压栈方式。VC 将函数编译后会在函数名前面加上下划线前缀。它是MFC 缺省调用约 定。 例子: void Input( int &m,int &n); 以下是相应的汇编代码: 00401068 lea eax,[ebp-8] ;取[ebp-8]地址(ebp-8),存到eax 0040106B push eax ;然后压栈 0040106C lea ecx,[ebp-4] ;取[ebp-4]地址(ebp-4),存到ecx 0040106F push ecx ;然后压栈 00401070 call @ILT+5(Input) (0040100a);然后调用Input函数 00401075 add esp,8 ;恢复栈 从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然后压栈ebp-4,然后调用函数Input,最后Input函数调用结束后,利用esp+8恢复栈。由 此可见,在C语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复 堆栈。 下面看一下:地址ebp-8和ebp-4是什么? 在VC的VIEW下选debug windows,然后选Registers,显示寄存器变量值,然后在选debug windows下面的Memory,输入ebp-8的值和ebp-4的值(或直接输入ebp-8和-4), 看一下这两个地址实际存储的是什么值,实际上是变量n 的地址(ebp-8),m的地址(ebp-4),由此可以看出:在主调用函数中进行实参的压栈并且顺序是从右到左。另外,

JAVA有哪些方法调用语句

abstract (关键字 ) 抽象 [.bstr.kt] access vt.访问,存取 [.kses](n.入口,使用权) algorithm n.算法 [.lg.riem] annotation [java]代码注释 [.n.utei..n] anonymous adj.匿名的[.n.nim.s](反义:directly adv.直接地,立即[direktli, dairektli]) apply v.应用,适用 [.plai] application n.应用,应用程序 [,.plikei..n] (application crash 程序崩溃) arbitrary a.任意的 [ɑ:bitr.ri] argument n.参数;争论,论据 [ɑ:gjum.nt](缩写 args) assert (关键字) 断言 [.s.:t] (java 1.4 之后成为关键字 ) associate n.关联(同伴,伙伴) [.s.u.ieit] attribute n.属性(品质,特征) [.tribju:t] boolean (关键字) 逻辑的 , 布尔型 call n.v.调用; 呼叫; [k.:l] circumstance n.事件(环境,状况) [s.:k.mst.ns] crash n.崩溃,破碎 [kr..] cohesion内聚,黏聚,结合 [k.uhi:..n] (a class is designed with a single, well-focoused purpose. 应该不止这点) [k.ments] const (保留字) constant n. 常量, 常数, 恒量 [k.nst.nt] continue (关键字) coupling耦合,联结 [k.pli.] making sure that classes know about other classes only through their apis. declare [java]声明 [dikl..] default (关键字) 默认值 ; 缺省值 [dif.:lt] delimiter定义符; 定界符

VB封装DLL实例讲解(三)

4 1 2点选DLL 3点打开按钮

5 2 3点选DLL 4点打开按钮

我们可以将注册语句放在窗体的加载事件,自动完成DLL的注册,具体可以参看实例。但如果我们有多个DLL需要批量注册时,可以考虑通过软件打包发布工具来完成DLL的注册工作;也可以事先编写BA T文件,让打包发布时将该BA T文件一并打包发布,安装时运行该BA T文件,来完成N个DLL的批量注册,在些就不多着笔墨,大家可以参看实例包中的BA T文件实例。 (二)DLL自动引用方法 2.1 通过References对象的AddFromFile方法实现自动引用 Dim ref As Reference '申明引用类对象 On Error Resume Next '避免因重复引用造成的错误提示 '实例化引用对象,完成DLL的引用 Set ref = References.AddFromFile(CurrentProject.Path & "\ClsFindString.dll") 为了避免因重复引用出现的错误,我们可以如上代码中加入Error语句,我们还可以在应用程序退出时,通过对References对象的Remove方法释放DLL或反引用。 Dim ref As Reference '申明引用类对象 '实例化反引用对象 Set ref = References("ClsFindString") '移除引用指定类库 References.Remove ref 说明:根据本人实践,我个人倾向于使用Error语句,因为如果应用程序非正常退出,引用对象没有反引用成功,启动时就难免出现重复引用的错误问题。 2.2 通过DLL唯一标识号实现自动引用 Dim ref As Reference'申明引用类对象 On Error Resume Next '避免因重复引用造成的错误提示 '唯一标识号完成注册,需要DLL标识号,主版本号,次版本 Set ref = References.AddFromGuid("{C5E340E2-C557-4852-AE83-5A0578B6863B}", 1, 0) DLL的标识号是编译生成时就确定了的,这个标识号就是DLL的终生制身份证号,我们可以通过这个唯一标识号来完成DLL自动引用。但此种方法必须具备两个条件,一是DLL已经成功注册,二是我们知道了该DLL的标识号、主版本号、次版本号。 2.2.1获取DLL标识号、主版本号、次版本号方法 Dim ref As Reference '申明引用类对象 '实例化引用类库对象 Set ref = References.AddFromFile(CurrentProject.Path & "\ClsFindString.dll") Debug.Print ref.GUID '获得DLL唯一标识号 Debug.Print ref.Major '获得主版本号 Debug.Print ref.Minor '获得次版本号

vb调用vc的dll

一般来说,VB和VC共同编程有3种方式:一种是VC生成DLL,在VB中调用DLL;一种是VC 生成ActiveX控件(.ocx),在VB中插入;还有一种是在VC中生成ActiveX Automation服务器,在VB中调用。相对而言,第一种方法对VC编程者的要求最低,但要求你的伙伴进行配合,我推荐这种方法。 先说说VC++的编程。首先在VC++中生成Win32 DLL工程。在这个工程中添加几个函数供VB 用户调用。一个DLL中的函数要想被VB调用,必须满足两个条件:一是调用方式为stdcall,另一个是必须是export的。要做到第一条,只须在函数声明前加上__stdcall关键字。如: short __stdcall sample(short nLen, short *buffer) 要做到第二条,需要在*.def文件中加上如下的几行: EXPORTS sample @1 这里的sample是你要在VB中调用的函数名,@1表示该函数在DLL中的编号,每个函数都不一样。注意这里的函数名是区分大小写的。至于你说的需要传递大量数据,可以这样做,在VB中用一个数组存放数据,然后将该数组的大小和地址传给VC(至于如何在VB中编程我会在下面介绍)。就象上面的例子,nLen是数组大小,buffer是数组地址,有了这两条,你可以象使用VC的数组一样进行处理了。至于输出图形,可以生成WMF或BMP格式,让VB调用。不过,我认为也可以直接输出到视窗,只要VB将窗口的句柄hWnd和hDC以及视窗的绘图位置(VB和VC采用的坐标系必须一致才行)传给VC就行了。而VB的AutoRedraw属性必须为False,在Paint事件中调用VC的绘图程序。 再谈谈VB的编程。VB调用DLL的方法和调用Windows API的方法是一样的,一般在VB的书中有介绍。对于上面一个例子,先要声明VC函数: Declare Function sample Lib "mydll.dll" (ByVal nLen As Integer, buffer As Integer) As Integer 这里mydll.dll是你的dll的名字。你可能已经注意到了两个参数的声明有所不同,第一个参数加上了ByVal。规则是这样的:如果在VC中某个参数声明为指针和数组,就不加ByVal,否则都要加上ByVal。在VB中调用这个函数采用这样的语法: sample 10, a(0) 这里的a()数组是用来存放数据的,10为数组长度,这里的第二个参数不能是a(),而必须是要传递的数据中的第一个。这是VB编程的关键。 下面在说几个可能遇到的问题。一个问题是VB可能报告找不到dll,你可以把dll放到system目

相关文档
最新文档