使用 .NET 框架类替代 API 调用
升级到 Microsoft .NET
Ken Getz
MCW Technologies
2002 年 2 月
摘要:通过学习 Microsoft .NET 框架中某些特定而有用的类,可以减少您对 Win32 API 调用的依赖。本文讨论的每个类都可以代替一个或多个 Win32 API 调用,而在 Microsoft Visual Basic 6.0 中,您必须调用一个或多个 Win32 API 才能完成相同的任务。
目标
- 查找现有 Win32 API 调用的特定替代品。
- 了解 Registry 类。
- 使用 FileVersionInfo 类。
- 使用环境信息和系统信息。
目录
- 避免使用 Win32 API
- 使用注册表
- 使用常用对话框
- 检索文件版本信息
- 检索环境信息
- 总结
避免使用 Win32 API
如果您是一位 Microsoft Visual Basic? 6.0 开发人员,您就无法避免调用 Win32 API。开发人员有太多的任务需要完成,而 Visual Basic 却不能提供任何实现方法。例如,在 Visual Basic 6.0 中,您很难完成以下任务:
- 确定文件版本信息。
- 在注册表的任何位置进行读取和写入操作。
- 确定用户的特定文件夹,例如 Microsoft Windows? 收藏夹或个人文件夹。
- 检索所有可用驱动器的列表。
- 查找用户的登录名或计算机名。
- 检索所有打开窗口的列表。
如果仅使用 Visual Basic 6.0 中提供的工具,您不可能解决上述任何问题。对于每个问题,开发人员都需要使用 Windows API。许多开发人员使用 Windows API 已经找到了完成这些(以及许多其他)任务的方法。
Windows API 存在什么问题?
为什么不继续在 .NET 环境中使用 Windows API 呢?如果使用 .NET 平台调用服务(称为“P/Invoke”),您当然可以这样做。从 Visual Basic 开发人员的角度来说,调用 Windows API 并不比使用他们所熟悉的 Declare 语句困难。不过,在 .NET 环境中使用 Windows API 存在一些比较严重的缺陷,您可能需要考虑采取任何可行的措施来避免这些问题。例如:
- .NET 公共语言运行时不会受平台影响。当您使用 Windows API 调用时,您将代码绑定到编写代码的特定平台上(即,相对于其他操作系统的某个特定 Windows 版本或 Windows 本身)。必要时,您需要将代码转换到另一个平台上,而这样做就需要修改使用 API 调用的每行代码。
- 从 .NET 中调用 Windows API(或 DLL 中的任何非托管代码)不像在 Visual Basic 6.0 中那样简单。例如,对结构的工作方式的限制使得很难将结构传递给 API 调用。此外,由于数据类型的更改以及更严格的类型转换,Visual Basic 6.0 的 API 声明也需要进行更改。
- 根据语言的不同,使用 Windows API(以及通常情况下使用的外部代码)的技巧也不尽相同。如果您打算在多 .NET 语言环境中工作,则需要掌握各种语言的不同技巧。
- 调用 Windows API 的代码要求调用这些代码的用户具有执行此操作的权限。这将影响应用程序的安全保护方案,您需要对此要求提前做出安排。
这个问题很简单:尽管您可以在 Visual Basic .NET 应用程序中继续使用 Windows API,但通常情况下,您应当尽可能寻找由 .NET 框架提供的替代品。虽然 .NET 框架的目的并不是要阻止您直接使用 Windows 的功能,但框架的确提供了大量的类,可以帮助您放弃对 Windows API 调用的依赖。
如果能够给出一个完整列表,列出 Win32 API 调用以及在 .NET 框架中完成相同任务的相应方法(如果有),可能会很方便,不过本文不涉及此任务。在本文中,您将了解到一些由 .NET 框架提供的特定且非常有用的类,它们可以解决您的问题。在每个示例中,本文所讨论的类都可以用来替代一个或多个 Win32 API 调用,而在 Microsoft Visual Basic 6.0 中,您必须调用一个或多个 Win32 API 才能完成相同的任务。
使用注册表
如果您与大多数 Visual Basic 6.0 开发人员一样,您会发现 Microsoft Visual Basic for Applications (VBA) 中内置的 SaveSetting、GetSetting、GetAllSettings 和 DeleteSetting 方法有点儿用处,但却很可能被它们的局限性弄得精疲力尽。所有这些方法都只能在注册表的 HKEY_CURRENT_USER\Software\VB
和 VBA Program Settings 下的项中使用。如果您要在注册表的其他地方读取或写入注册表项或注册表值,则必须使用复杂的 API 调用,或依靠别人的代码来处理此问题。
.NET 框架在 Microsoft.Win32 名称空间中提供了一对功能强大的类(Registry 和 RegistryKey),从而简化了注册表的使用,即不再需要 API 调用!
作为演示,请在示例项目的主窗体上单击 Work with the Registry(使用注册表)按钮。此窗体提供了 SOFTWARE\Microsoft\Windows\CurrentVersion\Run 项的 HKEY_LOCAL_MACHINE 配置单元中所有注册表值的列表。您可以右键单击列表中的任何项,然后选择插入新项,或者编辑或删除选定项,如图 1 所示。
提示:示例窗体也已经过设计,在列表框中按下 Enter 键时,可以编辑当前选定的项。按下 Delete 键可以删除选定项,按下 Insert 键可以添加一个新值。这些项对应于列表框的上下文菜单中的项。
图 1:使用 Registry 和 RegistryKey 类轻松检索和修改 Windows 注册表中的信息
.NET 框架提供了两个非常有用的类,使您可以轻松使用 Windows 注册表。第一个类是 Registry,它提供的字段与标准 Registry 配置单元的各字段相对应:
- ClassesRoot (HKEY_CLASSES_ROOT)
- CurrentConfig (HKEY_CURRENT_CONFIG)
- CurrentUser (HKEY_CURRENT_USER)
- DynData (HKEY_DYN_DATA)
- LocalMachine (HKEY_LOCAL_MACHINE)
- PerformanceData (HKEY_PERFORMANCE_DATA)
- Users (HKEY_USERS)
要使用 Registry 类,只需检索所需配置单元的引用。示例窗体的 LoadList 过程中包含如下代码,以便使用注册表中的 HKEY_LOCAL_MACHINE 配置单元:
Imports Microsoft.Win32
Dim reg As RegistryKey = Registry.LocalMachine
另一个类是 RegistryKey,它可以完成所有工作。它提供了一组使用 Registry 的方法。表 1 列出了 RegistryKey 类的所有有用方法。
表 1:RegistryKey 类方法
方法 | 说明 |
---|---|
CreateSubKey | 创建新子项或打开现有子项 |
DeleteSubKey | 删除指定子项。 |
DeleteSubKeyTree | 以递归方式删除子项和该子项的所有子项。 |
DeleteValue | 从该项中删除指定项值。 |
GetSubKeyNames | 检索包含所有子项名称的字符串数组。 |
GetValue | 检索指定的值。 |
GetValueNames | 检索包含与此项相关联的所有项值名称的字符串数组。 |
OpenSubKey | 检索指定子项,具有可选的写入权限。 |
SetValue | 设置指定的值。 |
RegistryKey 类还提供以下三个属性:
- Name:检索项的名称。
- SubkeyCount:检索与该项相关联的子项的数量。
- ValueCount:检索与该项相关联的项值的数量。
示例窗体的 ListLoad 过程将检索所请求项中的所有值,并将检索到的值添加到窗体的列表框中:
Private Const conRegKey As String = _
"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Private Structure RegData
Public Value As String
Public Data As String
Public Overrides _
Function ToString() As String
Return Me.Value
End Function
End Structure
Private Sub ListLoad()
Dim reg As RegistryKey = Registry.LocalMachine
Dim astrValues() As String
Dim strValue As String
Dim rd As RegData
' 清除列表框中的现有项。
lstItems.BeginUpdate()
lstItems.Items.Clear()
' 打开注册表项,然后使用
' 该项的值加载列表框。
reg = reg.OpenSubKey(conRegKey)
astrValues = reg.GetValueNames()
For Each strValue In astrValues
rd.Value = strValue.ToString
rd.Data = reg.GetValue(strValue)
lstItems.Items.Add(rd)
Next
lstItems.EndUpdate()
End Sub
要编辑示例窗体中的值或添加新值,需要运行以下代码:
Private Sub AddOrEdit( _
ByVal rd As RegData, _
ByVal Mode As frmAddValue.AccessMode)
Dim reg As RegistryKey = Registry.LocalMachine
Dim frm As New frmAddValue(Mode)
frm.KeyName = rd.Value
frm.KeyData = rd.Data
If frm.ShowDialog() = DialogResult.OK Then
If frm.KeyName <> String.Empty Then
reg = reg.OpenSubKey(conRegKey, True)
reg.SetValue(frm.KeyName, frm.KeyData)
ListLoad()
End If
End If
End Sub
此代码将再次打开注册表项,这次将请求写入项值的权限(此请求由 OpenSubKey 的第二个参数发出)。然后,代码将调用 SetValue 方法,传递图 1 所示的对话框窗体中的项名和项值。为简化工作,可以使用 SetValue 方法添加新值或修改现有值。如果项值不存在,SetValue 方法将添加一个项值。
要删除项值,示例窗体将调用以下代码:
Private Sub DeleteKey(ByVal rd As RegData)
Dim strText As String
Dim reg As RegistryKey = Registry.LocalMachine
If lstItems.SelectedIndex = -1 Then
Exit Sub
End If
' 删除选定的项。
strText = String.Format( _
"Are you sure you want to delete ""{0}""?", _
rd.Value)
If MessageBox.Show(strText, _
"Delete Registry Value", _
MessageBoxButtons.YesNo, _
MessageBoxIcon.Question) = DialogResult.Yes Then
' 打开项,允许写入。
reg = reg.OpenSubKey(conRegKey, True)
reg.DeleteValue(rd.Value)
' 重新加载列表框。
ListLoad()
End If
End Sub
此代码将打开项并请求对其执行写入操作,然后将调用 DeleteValue 方法删除选定的值。
有了示例窗体提供的信息和 .NET 框架附带的文档,便可以轻松地完成与注册表相关的任何任务,而不必使用 Windows API。这是一个简单的对象模型,但它提供的功能比 Visual Basic 6.0 开发人员先前所拥有的功能更强大。
提示:如果具有必要的权限,您还可以使用远程计算机上的注册表。您可以调用 RegistryKey.OpenRemoteBaseKey 方法检索其他计算机上的基本项,而不是简单地使用其中一个 Registry 类属性,来代表您自己计算机上的 Registry 配置单元。
使用常用对话框
Windows 提供了一组常用对话框,使开发人员可以方便地请求用户信息。您肯定见过并且使用过打开和保存文件、颜色、打印机和字体设置等常用对话框。Visual Basic 6.0 开发人员在使用这些对话框时有两种选择。他们可以:
- 使用一个 Microsoft ActiveX? 控件,该控件提供了一个包含常用对话框的简单对象模型,但是由于控件有多个不同的版本且底层 DLL 不同,因此该控件存在严重的部署问题。(对于很多 Visual Basic 6.0 开发人员来说,这个问题是最致命的 DLL 问题。)
- 直接使用 Windows API 发送消息并提供回叫功能,以管理各个常用对话框。
两个解决方案都不是完美无缺的,而且由于对 .NET 框架进行了增补,这两个解决方案当前都不是必需的。查看 System.Windows.Forms 名称空间时,您会发现 ColorDialog、FileDialog、FontDialog 和 PrintDialog 类。这些类都集成在框架中(也就是说,既不需要使用 API 调用,也不需要使用 ActiveX 控件),这样可以方便地将这些标准功能合并到您的应用程序中。
每个类都提供了一系列属性,您可以使用类的 ShowDialog 方法在显示对话框之前设置这些属性。本文将不对每个类进行详细讨论,但在示例项目中使用了 FileDialog 类,使您可以选择文件。如果您在主窗体上选择了 File Version Info(文件版本信息)按钮,则可以使用演示窗体上的 Select a File(选择文件)按钮显示 File Open(打开文件)对话框,如图 2 所示。
图 2:使用 FileDialog 类显示 Windows 常用对话框
虽然 FileDialog 类不像直接使用 Windows API 那样灵活,但它提供了大量的属性,您可以使用这些属性控制对话框的操作。您可以决定文件的来源,选择一个或多个文件,还可以检索选定的文件名。表 2 列出了可能会用到的 FileDialog 类的部分属性和方法。
表 2:FileDialog 对象的属性和方法
属性/方法 | 说明 |
---|---|
AddExtension | 指示当用户省略扩展名时,对话框是否自动为文件添加一个扩展名。 |
CheckFileExists | 指示当用户指定的文件名不存在时,对话框是否显示警告信息。 |
CheckPathExists | 指示当用户指定的路径不存在时,对话框是否显示警告信息。 |
DefaultExt | 默认文件扩展名。 |
DereferenceLinks | 指示对话框是否返回快捷方式所引用的文件的位置,或者是否返回快捷方式 (.lnk) 所在的位置。 |
FileName | 在文件对话框中选定的文件名。 |
FileNames | (只读)对话框中所有选定文件的文件名。 |
Filter | 当前文件名筛选字符串,确定对话框的“Save as file type”(另存为文件类型)或“Files of type”(文件类型)框中显示的选项。 |
FilterIndex | 文件对话框中当前选定的筛选器的索引。 |
InitialDirectory | 文件对话框显示的初始目录。 |
RestoreDirectory | 指示对话框在关闭之前是否还原当前目录。 |
ShowHelp | 指示文件对话框中是否显示 Help(帮助)按钮。 |
Title | 文件对话框标题。 |
ValidateNames | 指示对话框是否只接受有效文件名。 |
Reset(Method) | 将所有属性重置为默认值。 |
ShowDialog(Method) | 显示文件对话框。如果用户按 OK(确定),将返回 DialogResult.OK,否则返回 DialogResult.Cancel。 |
单击 Select a File(选择文件)时,示例窗体 frmFileVersionInfo 将调用以下代码:
Private Sub btnSelectFile_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnSelectFile.Click
Dim ofd As OpenFileDialog = New OpenFileDialog()
ofd.Filter = _
"Executable files (*.exe;*.dll;*.ocx)|" & _
"*.exe;*.dll;*.ocx|" & _
"Drivers (*.sys;*.drv;*.fnt)|" & _
"*.sys;*.drv;*.fnt|" & _
"All files (*.*)|*.*"
ofd.FilterIndex = 1
ofd.ShowReadOnly = False
ofd.RestoreDirectory = True
If ofd.ShowDialog() = DialogResult.OK Then
If ofd.FileName.Length > 0 Then
' 显示文件版本信息。
DisplayFileInfo(ofd.FileName)
End If
End If
End Sub
提示:如同使用 Visual Basic 6.0 CommonDialog ActiveX 控件一样,您可以将 FileDialog 类的 Filter 属性设置为一个字符串,在其中包含一对以竖线分隔的值,如下所示:“Description|FileSpec”。 在上面的示例中,一旦您选择了一个文件名,示例窗体就会在窗体的 ListView 控件中显示有关该文件的信息。下一节将讨论 FileVersionInfo 类,它可以实现上述操作。
检索文件版本信息
开发人员和编译人员可以将版本信息嵌入到可执行文件、DLL 文件和驱动程序文件中。您可能需要检索部分或全部版本信息以用作应用程序的一部分,在 Visual Basic 6.0 中执行此操作需要大量的 API 调用。您需要调用 GetVersionInfoSize、VerQueryValue 和 GetFileVersionInfo Win32 API 函数,在 Visual Basic 中使用上述任何函数都不容易。
这种情况再一次显示出 .NET 框架的强大:使用 FileVersionInfo 对象使得这些工作变得非常简单。您只需调用 FileVersionInfo 对象的共享 GetVersionInfo 方法,传递一个文件名,一切问题就都迎刃而解了。示例窗体使用了以下代码:
Dim fvi As FileVersionInfo = _
FileVersionInfo.GetVersionInfo(strFile)
完成以上操作之后,检索 FileVersionInfo 对象的属性就是一件非常简单的事情了。示例窗体使用下面所示的一小段程序,将每个属性名称和值都添加到窗体的 ListView 控件中:
Private Sub AddItem( _
ByVal strProperty As String, _
ByVal strValue As String)
With lvwInfo.Items.Add(strProperty)
.SubItems.Add(strValue)
End With
End Sub
真正起作用的是代码,而代码只需反复调用 AddItem,每个属性调用一次即可:
AddItem("Comments", fvi.Comments)
AddItem("CompanyName", fvi.CompanyName)
AddItem("Language", fvi.Language)
' 此处删除了一些行...
AddItem("Product", fvi.ProductName)
AddItem("ProductPrivatePart", _
fvi.ProductPrivatePart.ToString())
AddItem("ProductVersion", fvi.ProductVersion)
AddItem("SpecialBuild", fvi.SpecialBuild)
图 3 所示的结果窗体显示了可通过编程使用的所有版本信息。
图 3:使用 FileVersionInfo 类检索文件版本信息
检索环境信息
Win32 API 提供了一系列函数,使您可以确定用户的环境设置。GetSystemMetrics 和 SystemParametersInfo 是其中的两个函数。您可以继续从 .NET 应用程序中调用这些 API 函数,但很可能您并不需要这样做。Environment 类(位于 System 名称空间)和 SystemInformation 类(位于 System.Windows.Forms 名称空间)提供了许多与 API 函数相同的信息。在本节中,我们将通过示例来演示这两个类的功能。
警告:不要浪费时间从这些类中寻找设置用户环境设置的方法。此处显示的所有信息均为只读。如果您仍然希望修改环境设置,则需要寻找其他方法。
使用 System.Environment 类
System.Environment 类提供了若干不同的信息,如果没有这些信息,就需要进行多次 Windows API 调用。使用 System.Environment 可以检索:
- 有关可用驱动器的信息(GetLogicalDrives 方法)
- Windows 启动后的毫秒数(TickCount 属性)
- 一般环境设置(由 CurrentDirectory、MachineName、OSVersion、SystemDirectory、UserDomainName、UserInteractive、UserName 和 WorkingSet 属性提供)
- 特定文件夹列表(使用 GetFolderPath 方法提供)
如果您使用过 Windows API,您将知道选择方法和属性将代替很多 API 调用,包括 GetTickCount、GetLogicalDrives、GetSystemDirectory、GetComputerName、GetUserName 以及 GetVersionEx 等等。
在图 4(单击主窗体上的 Environment Info [环境信息])所示的示例窗体中,在靠近顶部的列表框中显示了执行 GetLogicalDrives 方法的结果,其中包括所有特定文件夹的列表(使用 GetFolderPath 方法检索)。在窗体下部的列表框中,显示了该类的许多属性的运行结果。
图 4:显示 System.Environment 类所使用的属性和方法的窗体
另外,要测试 TickCount 属性,请单击 Test ClickCount(测试 ClickCount),以显示使用示例窗体中定义的 StopWatch 类的结果,并请使用以下代码:
Public Class StopWatch
Private mintStart As Integer
Public Sub Start()
mintStart = Environment.TickCount
End Sub
Public Function Elapsed() As Integer
Return Environment.TickCount - mintStart
End Function
Public Overrides Function ToString() As String
Return String.Format( _
"Started: {0}, Elapsed: {1}", _
mintStart, Me.Elapsed)
End Function
End Class
此处显示的 FillProperties 方法使用先前显示过的 AddItem 方法的一个副本,将属性名称和结果填入 ListView 控件,如下所示:
Private Sub FillProperties()
AddItem("CurrentDirectory", _
Environment.CurrentDirectory)
AddItem("MachineName", Environment.MachineName)
AddItem("OSVersion.Platform", _
Environment.OSVersion.Platform.ToString)
AddItem("OSVersion.Version", _
Environment.OSVersion.Version.ToString)
AddItem("SystemDirectory", _
Environment.SystemDirectory)
AddItem("UserDomainName", Environment.UserDomainName)
AddItem("UserInteractive", _
Environment.UserInteractive)
AddItem("UserName", Environment.UserName)
AddItem("WorkingSet", Environment.WorkingSet)
End Sub
除了使用 Environment 类之外,FillFolderList 实际上还包含一些有趣的代码。这个过程的目的是循环使用 Environment 类提供的 SpecialFolder 枚举中的所有成员。(此枚举包含物理文件夹 [如收藏夹和历史记录等] 的逻辑名称。)该过程将每个枚举值的名称添加到窗体上的 ListView 控件中,同时将传递枚举值的结果添加到 Environment 对象的 GetFolderPath 方法中。下面所示的过程能够完成所有工作:
Private Sub FillFolderList()
Dim strName As String
Dim astrNames() As String
Dim aintValues As Array
Dim i As Integer
' 使用 SpecialFolder 枚举中的名称
' 填写 ListView 控件的第一列。
astrNames = System.Enum.GetNames( _
GetType(Environment.SpecialFolder))
aintValues = System.Enum.GetValues( _
GetType(Environment.SpecialFolder))
For i = 0 To astrNames.Length - 1
With lvwFolders.Items.Add(astrNames(i))
.SubItems.Add( _
Environment.GetFolderPath(aintValues(i)))
End With
Next
End Sub
此示例没有对发送到 Environment 对象的参数进行硬编码,而是使用了 Enum 类的共享 GetNames 和 GetValues 方法。通过将调用 Visual Basic .NET GetType 函数的结果传递给 GetNames(传递指定的枚举类型),您可以检索使用所有枚举成员的名称填写的数组。重复包含 GetValues 方法的过程将返回一个 Array 对象,其中包含所有枚举值。
astrNames = System.Enum.GetNames( _
GetType(Environment.SpecialFolder))
aintValues = System.Enum.GetValues( _
GetType(Environment.SpecialFolder))
给出这两个数组后,程序的其余部分将在这两个数组中循环运行,将 astrNames 中的值添加到 ListView,然后调用 Environment 类的 GetFolderPath 方法来检索相应的路径:
For i = 0 To astrNames.Length - 1
With lvwFolders.Items.Add(astrNames(i))
.SubItems.Add( _
Environment.GetFolderPath(aintValues(i)))
End With
Next
图 4 中的上部 ListView 控件包含这些代码的输出内容。
提示:Enum 为您提供了一些 Visual Basic 6.0 中没有的技巧,例如示例中所示的 GetNames 和 GetValues 方法。有关利用 Enum 类的功能的详细信息,请参阅 .NET 框架文档。
使用 Windows.Forms.SystemInformation 类
在提供精心设计的用户界面时,您常常需要确定当前的 Windows 设置,例如图标的高度和宽度,或者滚动条的宽度。在 Visual Basic 6.0 中,您可以使用 GetSystemMetrics 和 SystemParametersInfo Windows API 函数来确定许多此类设置。在 .NET 框架中,您可以使用 Windows.Forms 名称空间提供的 SystemInformation 类。
尽管图 5 中 frmSystemInformation 使用的代码不是很有趣,但它确实显示了该类提供的所有属性。(单击主窗体上的 SystemInformation Info [系统信息] 可以测试此示例窗体。)示例窗体对 SystemInformation 类的所有属性进行操作,将每个属性的名称和当前值显示在窗体的 ListView 控件中。
图 5:显示 Windows.Forms.SystemInformation 类的所有属性的示例窗体
示例窗体使用了前面介绍过的 AddItem 方法,在 SystemInformation 类所有 60 多个属性中简单地循环运行并显示输出内容:
AddItem("ArrangeDirection", _
SystemInformation.ArrangeDirection)
AddItem("StartingPosition", _
SystemInformation.ArrangeStartingPosition)
AddItem("BootMode", SystemInformation.BootMode)
' 等等...
总结
虽然您可以使用 Visual Basic .NET 中的平台调用服务 (P/Invoke) 来处理非托管代码,然后直接调用 Windows API,但在创建 Visual Basic .NET 应用程序时,您还是应该寻找其他替代方法。不过,您不用担心 P/Invoke 的细节问题,因为 Declare 语句会为您处理这些细节。
- .NET 框架并不封装所有的 Windows API 功能,但是您在 Visual Basic 6.0 中可能要使用 API 调用才能实现的操作在 .NET 框架中也可以实现。
- Registry 和 RegistryKey 类简化了对 Windows 注册表的操作,可以帮助开发人员避免大量的 API 调用。
- FileDialog、ColorDialog、FontDialog 和 PrinterDialog 类使您可以很方便地使用 Windows 中的常用对话框。您不需要直接调用 Windows API,也不需要使用可怕的 CommonDialog ActiveX 控件。
- 您可以使用 FileVersionInfo 类检索内置在可执行文件、驱动程序文件和 DLL 文件中的所有文件版本信息。此类可以替代 Visual Basic 6.0 中所需的某些复杂的 Windows API 代码。
- Environment 和 SystemInformation 类使您可以方便地检索系统设置,如果没有这些类,则需要调用许多不同的 API 函数。
关于作者
Ken Getz 是 MCW Technologies 的资深顾问,他的工作涉及编程、著书和培训。他精通用 Microsoft Access、Visual Basic 以及 Office 和 BackOffice 套件编写的工具和应用程序。Ken 还与其他人一起编写了许多书籍,包括:与 Paul Litwin 和 Mike Gilbert 合著的《Access 97 Developer's Handbook》;与 Paul Litwin 和 Mike Gilbert 合著的《Access 2000 Developer's Handbooks》;与 Paul Litwin 和 Mike Gunderloy 合著的《Access 2002 Developer's Handbooks》;与 Mike Gilbert 合著的《Visual Basic Language Developer's Handbook》;以及与 Mike Gilbert (Sybex) 合著的《VBA Developer's Handbook》。他还参与了 AppDev 培训资料的编写工作,并从事这方面的教学工作。Ken 经常在技术会议上发言,自 1994 年以来,每一届的 Microsoft Tech*Ed 会议上他都会发表演讲。Ken 是 Access/VB/SQL Advisor 杂志的技术编辑和 Informant Communication Group 属下的《Microsoft Office Solutions》杂志的电子文档撰稿人。
关于 Informant Communications Group
Informant Communications Group, Inc. (www.informant.com) 是一家专注于信息技术行业的多媒体公司。ICG 成立于 1990 年,致力于与软件开发有关的出版物、会议、目录发布和 Web 站点等领域。ICG 在美国和英国均设有办事处,目前已成为享有盛誉的媒体和营销内容集成商,并以高质量的技术信息满足 IT 人员不断增长的需求。
© 2002 Informant Communications Group 和 Microsoft Corporation 版权所有。
技术编辑:KNG Consulting