演练:调用 Windows API (Visual Basic)

Windows API 是属于 Windows 操作系统一部分的动态链接库 (DLL)。 难以编写自己的等效过程时,可使用它们执行任务。 例如,Windows 提供名为 FlashWindowEx 的函数,通过它可使应用程序的标题栏在浅阴影与深阴影之间交替显示。

在代码中使用 Windows API 的优点是可以节省开发时间,因为它们包含数十个已编写并等待使用的有用函数。 缺点是 Windows API 可能难以使用,在出现问题时难以恢复。

Windows API 表示特殊的互操作性类别。 Windows API 不使用托管代码,没有内置类型库,并且使用的数据类型与用于 Visual Studio 的数据类型不同。 由于这些差异,并且由于 Windows API 不是 COM 对象,因此与 Windows API 和 .NET Framework 的互操作性使用平台调用(或 PInvoke)来执行。 平台调用是一种服务,使托管代码能够调用 DLL 中实现的非托管函数。 有关详细信息,请参阅使用非托管 DLL 函数。 通过使用 Declare 语句或将 DllImport 属性应用于空过程,可以在 Visual Basic 中使用 PInvoke。

Windows API 调用是过去Visual Basic 编程的重要组成部分,但在 Visual Basic .NET 中很少需要。 应尽可能从 .NET Framework 使用托管代码执行任务,而不是使用 Windows API 调用。 本演练提供有关需要使用 Windows API 的情况的信息。

注意

以下说明中的某些 Visual Studio 用户界面元素在计算机上出现的名称或位置可能会不同。 这些元素取决于你所使用的 Visual Studio 版本和你所使用的设置。 有关详细信息,请参阅个性化设置 IDE

使用 Declare 的 API 调用

调用 Windows API 的最常见方法是使用 Declare 语句。

声明 DLL 过程

  1. 确定要调用的函数的名称及其参数、参数类型和返回值,以及包含它的 DLL 的名称和位置。

    注意

    有关这些 Windows API 的完整信息,请参阅平台 SDK Windows API 中的 Win32 SDK 文档。 有关 Windows API 使用的常量的详细信息,请查看平台 SDK 附带的头文件(如 Windows.h)。

  2. 通过单击“文件”菜单上的“新建”,然后单击“项目”,来打开新的 Windows 应用程序项目。 将显示“新建项目”对话框。

  3. 从 Visual Basic 项目模板列表中选择“Windows 应用程序”。 将显示新项目。

  4. 将以下 Declare 函数添加到要在其中使用 DLL 的类或模块:

    Declare Auto Function MBox Lib "user32.dll" Alias "MessageBox" (
        ByVal hWnd As Integer,
        ByVal txt As String,
        ByVal caption As String,
        ByVal Typ As Integer) As Integer
    

Declare 语句的各个部分

Declare 语句包含以下元素。

Auto 修饰符

Auto 修饰符指示运行时根据公共语言运行时规则,基于方法名称(或指定的别名)转换字符串。

Lib 和 Alias 关键字

跟在 Function 关键字后的名称是程序用于访问导入的函数的名称。 它可以与所调用的函数的真实名称相同,你也可以使用任何有效的过程名称,然后使用 Alias 关键字指定所调用的函数的真实名称。

指定 Lib 关键字,后跟包含所调用的函数的 DLL 的名称和位置。 无需为位于 Windows 系统目录中的文件指定路径。

如果所调用的函数的名称不是有效 Visual Basic 名称,或者与应用程序中其他项的名称冲突,请使用 Alias 关键字。 Alias 指示所调用的函数的真实名称。

参数和数据类型声明

声明参数及其数据类型。 此部分可能很有挑战性,因为 Windows 使用的数据类型与 Visual Studio 数据类型不对应。 Visual Basic 通过将参数转换为兼容的数据类型(一种称为封送处理的过程)来完成大量工作。 可以使用 System.Runtime.InteropServices 命名空间中定义的 MarshalAsAttribute 属性显式控制参数的封送处理方式。

注意

早期版本的 Visual Basic 允许声明参数 As Any,这意味着可以使用任何数据类型的数据。 Visual Basic 要求对所有 Declare 语句都使用特定数据类型。

Windows API 常量

某些参数是常量的组合。 例如,本演练中演示的 MessageBox API 接受名为 Typ 的整数参数,该参数控制消息框的显示方式。 可以通过检查 WinUser.h 文件中的 #define 语句来确定这些常量的数值。 数值通常以十六进制形式显示,因此你可能要使用计算器将它们相加并转换为十进制。 例如,如果要合并感叹号样式 MB_ICONEXCLAMATION 0x00000030 和“是/否”样式 MB_YESNO 0x00000004 的对应常量,可以将这些数字相加并得到结果 0x00000034(即十进制 52)。 尽管可以直接使用十进制结果,但最好在应用程序中将这些值声明为常量,并使用 Or 运算符合并它们。

为 Windows API 调用声明常量
  1. 请参阅所调用的 Windows 函数的文档。 确定它使用的常量的名称以及包含这些常量的数值的 .h 文件的名称。

  2. 使用文本编辑器(如记事本)查看头文件 (.h) 的内容,并查找与所使用的常量关联的值。 例如,MessageBox API 使用常量 MB_ICONQUESTION 在消息框中显示问号。 MB_ICONQUESTION 的定义在 WinUser.h 中,如下所示:

    #define MB_ICONQUESTION 0x00000020L

  3. 向类或模块添加等效 Const 语句,使这些常量可供应用程序使用。 例如:

    Const MB_ICONQUESTION As Integer = &H20
    Const MB_YESNO As Integer = &H4
    Const IDYES As Integer = 6
    Const IDNO As Integer = 7
    
调用 DLL 过程
  1. 将名为 Button1 的按钮添加到项目的启动窗体,然后双击它以查看其代码。 会显示该按钮的事件处理程序。

  2. 将代码添加到所添加按钮的 Click 事件处理程序,以调用过程并提供相应的参数:

    Private Sub Button1_Click(ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles Button1.Click
    
        ' Stores the return value.
        Dim RetVal As Integer
        RetVal = MBox(0, "Declare DLL Test", "Windows API MessageBox",
            MB_ICONQUESTION Or MB_YESNO)
    
        ' Check the return value.
        If RetVal = IDYES Then
            MsgBox("You chose Yes")
        Else
            MsgBox("You chose No")
        End If
    End Sub
    
  3. 按 F5 运行项目。 会显示消息框,其中包含“Yes”和“No”响应按钮。 单击任一个按钮。

数据封送处理

Visual Basic 会为 Windows API 调用自动转换参数和返回值的数据类型,不过你可以使用 MarshalAs 属性显式指定 API 所需的非托管数据类型。 有关互操作封送处理的详细信息,请参阅互操作封送处理

在 API 调用中使用 Declare 和 MarshalAs
  1. 确定要调用的函数的名称及其参数、数据类型和返回值。

  2. 若要简化对 MarshalAs 属性的访问,请向类或模块的代码顶部添加 Imports 语句,如以下示例所示:

    Imports System.Runtime.InteropServices
    
  3. 将导入函数的函数原型添加到所使用的类或模块,并将 MarshalAs 属性应用于参数或返回值。 在下面的示例中,需要 void* 类型的 API 调用会封送为 AsAny

    Declare Sub SetData Lib "..\LIB\UnmgdLib.dll" (
        ByVal x As Short,
        <MarshalAsAttribute(UnmanagedType.AsAny)>
            ByVal o As Object)
    

使用 DllImport 的 API 调用

DllImport 属性提供了另一种方法来调用 DLL 中的函数而不使用类型库。 DllImport 大致等效于使用 Declare 语句,但通过它可进一控制函数的调用方式。

只要调用引用共享(有时称为静态)方法,便可以将 DllImport 与大多数 Windows API 调用结合使用。 不能使用需要类的实例的方法。 与 Declare 语句不同,DllImport 调用不能使用 MarshalAs 属性。

使用 DllImport 属性调用 Windows API

  1. 通过单击“文件”菜单上的“新建”,然后单击“项目”,来打开新的 Windows 应用程序项目。 将显示“新建项目”对话框。

  2. 从 Visual Basic 项目模板列表中选择“Windows 应用程序”。 将显示新项目。

  3. 将名为 Button2 的按钮添加到启动窗体。

  4. 双击 Button2 以打开窗体的代码视图。

  5. 若要简化对 DllImport 的访问,请将 Imports 语句添加到启动窗体类的代码顶部:

    Imports System.Runtime.InteropServices
    
  6. 在窗体的 End Class 语句前面声明一个空函数,并将该函数命名为 MoveFile

  7. PublicShared 修饰符应用于函数声明,并基于 Windows API 函数使用的自变量设置 MoveFile 的参数:

    Public Shared Function MoveFile(
        ByVal src As String,
        ByVal dst As String) As Boolean
        ' Leave the body of the function empty.
    End Function
    

    函数可以具有任何有效的过程名称;DllImport 属性指定 DLL 中的名称。 它还处理参数和返回值的互操作性封送处理,因此可以选择与 API 使用的数据类型类似的 Visual Studio 数据类型。

  8. DllImport 属性应用于空函数。 第一个参数是包含所调用函数的 DLL 的名称和位置。 无需为位于 Windows 系统目录中的文件指定路径。 第二个参数是命名自变量,用于指定 Windows API 中的函数名称。 在此示例中,DllImport 属性强制将 MoveFile 调用转发到 KERNEL32.DLL 中的 MoveFileWMoveFileW 方法从将文件从路径 src 复制到路径 dst

    <DllImport("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
        ' Leave the body of the function empty.
    End Function
    
  9. Button2_Click 事件处理程序添加代码以调用函数:

    Private Sub Button2_Click(ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles Button2.Click
    
        Dim RetVal As Boolean = MoveFile("c:\tmp\Test.txt", "c:\Test.txt")
        If RetVal = True Then
            MsgBox("The file was moved successfully.")
        Else
            MsgBox("The file could not be moved.")
        End If
    End Sub
    
  10. 创建名为 Test.txt 的文件,并将它置于硬盘上的 C:\Tmp 目录中。 如有必要,请创建 Tmp 目录。

  11. 按 F5 键启动该应用程序。 主窗体会出现。

  12. 单击“Button2”。 如果可以移动文件,则会显示消息“The file was moved successfully”。

另请参阅