.net框架
使用 .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.Win32Dim 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 FunctionEnd StructurePrivate 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.ToStringrd.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 Thenreg = reg.OpenSubKey(conRegKey, True)
reg.SetValue(frm.KeyName, frm.KeyData)
ListLoad() End If End IfEnd 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 IfEnd 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 IfEnd 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 WithEnd 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 FunctionEnd 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 NextEnd 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 WithNext
图 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
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索windows
, 文件
, 对话框
, 方法
, 窗体
, environment
, vba win32 api
, ebay additem 的api
, api调用示例
, API示例
对话框示例代码
ci框架调用api接口、spring替代框架、ajax 替代iframe框架、ssh框架 替代、api gateway开源框架,以便于您获取更多的相关知识。