Declare Statement

声明一个对外部文件中所实现过程的引用。

语法

[ <attributelist> ] [ accessmodifier ] [ Shadows ] [ Overloads ] _
Declare [ charsetmodifier ] [ Sub ] name Lib "libname" _
[ Alias "aliasname" ] [ ([ parameterlist ]) ]
' -or-
[ <attributelist> ] [ accessmodifier ] [ Shadows ] [ Overloads ] _
Declare [ charsetmodifier ] [ Function ] name Lib "libname" _
[ Alias "aliasname" ] [ ([ parameterlist ]) ] [ As returntype ]

组成部分

术语 定义
attributelist 可选。 请参阅特性列表
accessmodifier 可选。 可以是以下值之一:

- Public
- Protected
- Friend
- 专用
- Protected Friend
- Private Protected

请参阅 Access levels in Visual Basic
Shadows 可选。 请参阅 Shadows
charsetmodifier 可选。 指定字符集和文件搜索信息。 可以是以下值之一:

- Ansi默认值)
- Unicode
- Auto
Sub 可选,但必须显示 SubFunction。 指示外部过程不返回值。
Function 可选,但必须显示 SubFunction。 指示外部过程返回值。
name 必需。 此外部引用的名称。 有关详细信息,请参阅声明的元素名称
Lib 必需。 引入一个 Lib 子句,用于标识包含外部过程的外部文件(DLL 或代码资源)。
libname 必需。 包含所声明过程的文件的名称。
Alias 可选。 指示无法通过 name 中指定的名称在其文件中标识所声明的过程。 在 aliasname 中指定其标识。
aliasname 如果使用 Alias 关键字,则是必需的。 通过以下两种方式之一标识过程的字符串:

过程在其文件中的入口点名称,括在引号 ("") 中

-或-

一个数字符号 (#),后跟用于指定过程在其文件中的入口点的序号的整数
parameterlist 如果过程采用参数,则是必需的。 请参阅参数列表
returntype 如果指定了 FunctionOption StrictOn,则是必需的。 过程返回的值的数据类型。

注解

有时,需要调用在项目外部的文件(如 DLL 或代码资源)中定义的过程。 执行此操作时,Visual Basic 编译器无法访问正确调用过程所需的信息,例如过程所在的位置、其标识方式、其调用序列和返回类型以及它使用的字符串字符集。 Declare 语句可创建对外部过程的引用,并提供这些必要信息。

只能在模块级别使用 Declare。 这意味着外部引用的声明上下文必须是类、结构或模块,不能是源文件、命名空间、接口、过程或块。 有关详细信息,请参阅声明上下文和默认访问级别

外部引用默认为具有 Public 访问权限。 可以使用访问修饰符调整其访问级别。

规则

  • 特性。 可以将属性应用于外部引用。 应用的任何属性都只在项目中有效果,在外部文件中无效果。

  • 修饰符。 外部过程是隐式共享。 声明外部引用时不能使用 Shared 关键字,并且不能更改其共享状态。

    外部过程不能参与重写、实现接口成员或处理事件。 因此,不能在 Declare 语句中使用 OverridesOverridableNotOverridableMustOverrideImplementsHandles 关键字。

  • 外部过程名称。 为此外部引用提供的名称(在 name 中)不必与其外部文件 (aliasname) 中的过程入口点名称相同。 可以使用 Alias 子句指定入口点名称。 如果外部过程的名称与 Visual Basic 保留修饰符或变量、过程或是同一范围中的任何其他编程元素相同,则这可能十分有用。

    备注

    大多数 DLL 中的入口点名称区分大小写。

  • 外部过程编号。 或者,可以使用 Alias 子句指定外部文件导出表中的入口点序号。 为此,请将 aliasname 以数字符号 (#) 开头。 如果外部过程名称中的任何字符不允许在 Visual Basic 中使用,或是外部文件导出不带名称的过程,则这可能会非常有用。

数据类型规则

  • 参数数据类型。 如果 Option StrictOn,则必须在 parameterlist 中指定每个参数的数据类型。 这可以是任何数据类型或是枚举、结构、类或接口的名称。 在 parameterlist 中,使用 As 子句指定要传递到每个参数的自变量的数据类型。

    备注

    如果未为 .NET Framework 编写外部过程,则必须注意数据类型是否对应。 例如,如果使用 Integer 参数(在 Visual Basic 6.0 中为 16 位)声明对 Visual Basic 6.0 过程的外部引用,则必须在 Declare 语句中将对应的自变量标识为 Short,因为这是 Visual Basic 中的 16 位整数类型。 同样,Long 在 Visual Basic 6.0 中具有不同的数据宽度,并且 Date 以不同方式实现。

  • 返回数据类型。 如果外部过程是 FunctionOption StrictOn,则必须指定返回到调用代码的值的数据类型。 这可以是任何数据类型或是枚举、结构、类或接口的名称。

    备注

    Visual Basic 编译器不会验证数据类型是否与外部过程的数据类型兼容。 如果存在不匹配,则公共语言运行时会在运行时生成 MarshalDirectiveException 异常。

  • 默认数据类型。 如果 Option StrictOff,并且未在 parameterlist 中指定参数的数据类型,则 Visual Basic 编译器会将对应自变量转换为 数据类型。 同样,如果未指定 returntype,则编译器会将返回数据类型设为 Object

    备注

    由于在处理可能已在不同平台上编写的外部过程,因此对数据类型进行任何假设或允许它们采用默认值是危险的。 指定每个参数和返回值(如果有)的数据类型要安全得多。 这还可提高代码的可读性。

行为

  • 作用域。 外部引用的作用域是其整个类、结构或模块。

  • 生存期。 外部引用的生存期与声明它的类、结构或模块相同。

  • 调用外部过程。 调用外部过程的方式与调用 FunctionSub 过程的方式相同 — 在表达式中使用(如果返回值),或者在 Call 语句中指定(如果不返回值)。

    完全按照 Declare 语句中 parameterlist 的指定,将参数传递到外部过程。 无需考虑参数最初在外部文件中的声明方式。 同样,如果存在返回值,请完全按照 Declare 语句中 returntype 的指定进行使用。

  • 字符集。 可以在 charsetmodifier 中指定 Visual Basic 在调用外部过程时应如何封送字符串。 Ansi 修饰符指示 Visual Basic 将所有字符串封送为 ANSI 值,Unicode 修饰符指示它将所有字符串封送为 Unicode 值。 Auto 修饰符指示 Visual Basic 基于外部引用 namealiasname(如果指定),根据 .NET Framework 规则封锁字符串。 默认值为 Ansi

    charsetmodifier 还指定 Visual Basic 应如何在外部文件中查找外部过程。 AnsiUnicode 都指示 Visual Basic 进行查找,不会在搜索过程中修改其名称。 Auto 指示 Visual Basic 确定运行时平台的基本字符集,并可能会修改外部过程名称,如下所示:

    • 在 Unicode 平台(如 Windows)上,首先查找外部过程,不进行任何名称修改。 如果该操作失败,请将“W”追加到外部过程名称的末尾,并再次查找。

    • 在 ANSI 平台上,首先查找外部过程,不进行任何名称修改。 如果该操作失败,请将“A”追加到外部过程名称的末尾,并再次查找。

  • 机制。 Visual Basic 使用 .NET Framework 平台调用 (PInvoke) 机制解析和访问外部过程。 Declare 语句和 DllImportAttribute 类都自动使用此机制,你无需对 PInvoke 有任何了解。 有关详细信息,请参阅演练:调用 Windows API

重要

如果外部过程在公共语言运行时 (CLR) 外部运行,则它是非托管代码。 调用此类过程(例如 Windows API 函数或 COM 方法)时,可能会使应用程序面临安全风险。 有关详细信息,请参阅非托管代码的安全编码指南

示例 1

下面的示例声明对返回当前用户名的 Function 过程的外部引用。 随后它会在 getUser 过程中调用外部过程 GetUserNameA

Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (
    ByVal lpBuffer As String, ByRef nSize As Integer) As Integer
Sub GetUser()
    Dim buffer As String = New String(CChar(" "), 25)
    Dim retVal As Integer = GetUserName(buffer, 25)
    Dim userName As String = Strings.Left(buffer, InStr(buffer, Chr(0)) - 1)
    MsgBox(userName)
End Sub

示例 2

DllImportAttribute 提供了一种在非托管代码中使用函数的替代方法。 下面的示例声明一个导入的函数,而未使用 Declare 语句。

' Add an Imports statement at the top of the class, structure, or
' module that uses the DllImport attribute.
Imports System.Runtime.InteropServices
<DllImportAttribute("kernel32.dll", EntryPoint:="MoveFileW",
    SetLastError:=True, CharSet:=CharSet.Unicode,
    ExactSpelling:=True,
    CallingConvention:=CallingConvention.StdCall)>
Public Shared Function MoveFile(ByVal src As String,
  ByVal dst As String) As Boolean
    ' This function copies a file from the path src to the path dst.
    ' Leave this function empty. The DLLImport attribute forces calls
    ' to MoveFile to be forwarded to MoveFileW in KERNEL32.DLL.
End Function

另请参阅