ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: welcpa

[VBA程序开发] VBAPI函数参考使用手册!

[复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:23 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册

利用API函数实现无TittleBar窗体的移动

Option Explicit Const HTCAPTION = 2 Const WM_NCLBUTTONDOWN = &HA1 Private Declare Function ReleaseCapture Lib "user32" () As Long Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long Sub Form_MouseDown(Button As Integer, Shift As Integer, x As Single, Y As Single) If Button = 1 Then ' Checking for Left Button only Dim ReturnVal As Long x = ReleaseCapture() ReturnVal = SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0) End If

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:24 | 显示全部楼层

未公开API函数揭秘——通用对话框

COMDLG32.DLL 为我们提供了一些很有用的对话框,但是仍然有部分我们可能会用到的系统对话框它却没有提供。如果试图复制这些系统对话框将是一件麻烦的苦差事。幸好shell32.dll为我们提供了这些对话框,本文将带你去发掘一些未公开的API函数,以实现这些对话框。 在使用未公开的API函数之前,你必须知道声明未公开的API函数与声明那些公开的API函数略有不同,那就是,你必须用到未公开API函数的顺序号(ordinal number)。这个顺序号就是未公开函数的别名。也就是说在声明未公开API函数时,必须加上它的别名。例如下面要说到的PickIconDlg函数的顺序号为62,它的别名就是"#62"。如果不这样做,系统会提示你找不到函数的入口点。 选取图标 BOOL WINAPI PickIconDlg( HWND hwndOwner, LPSTR lpstrFile, DWORD nMaxFile, LPDWORD lpdwIconIndex); 该函数的顺序号为62。 hwndOwner拥有该对话框的窗口句柄 lpstrFile指向一个缓冲,包含初始的文件名。函数返回后它就包含新的文件名。nMaxFile指定缓冲的大小,以字符为单位。 lpdwIconIndex指向一个变量其中包含基于零的图标的索引。函数返回后它包含新图标的索引值。 如果用户选择了图标,则返回值为真,如果用户选择取消按钮或是系统菜单的关闭选项则返回值为假。 运行程序对话框 void WINAPI RunFileDlg( HWND hwndOwner, HICON hIcon, LPCSTR lpstrDirectory, LPCSTR lpstrTitle, LPCSTR lpstrDescription, UINT uFlags); 该函数的顺序号为61。在VB中可声明如下: Private Declare Function RunFileDlg Lib "shell32" Alias "#61" (ByVal hwndOwner As Long, ByVal hIcon As Long, ByVal lpstrDirectory As String, ByVal lpstrTitle As String, ByVal lpstrDescription As String, ByVal uFlags As Long) As Long 其中uFlags参数的可选值为 RFF_NOBROWSE 0x01 Removes the browse button. RFF_NODEFAULT 0x02 No default item selected. RFF_CALCDIRECTORY 0x04 Calculates the working directory from the file name. RFF_NOLABEL 0x08 Removes the edit box label. RFF_NOSEPARATEMEM 0x20 Removes the Separate Memory Space check box (Windows NT only).

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:26 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助

在VB中使用API函数 (之一)

  什么是API? API(Advanced Programmers Interface,高级程序员接口)(注:API实际是指Application Programming Interface,应用程序编程接口;此处疑为原文错误,不过在VB中也可以这么说吧!)是一套用来控制Windows的各个部件(从桌面的外观到位一个新进程分配的内存)的外观和行为的一套预先定义的Windows函数.用户的每个动作都会引发一个或几个函数的运行以Windows告诉发生了什么. 这在某种程度上很象Windows的天然代码.其他的语言只是提供一种能自动而且更容易的访问API的方法.VB在这方面作了很多工作.它完全隐藏了API并且提供了在Windows环境下编程的一种完全不同的方法. 这也就是说,你用VB写出的每行代码都会被VB转换为API函数传递给Windows.例如,Form1.Print...VB 将会以一定的参数(你的代码中提供的,或是默认参数)调用TextOut 这个API函数. 同样,当你点击窗体上的一个按钮时,Windows会发送一个消息给窗体(这对于你来说是隐藏的),VB获取这个调用并经过分析后生成一个特定事件(Button_Click). API函数包含在Windows系统目录下的动态连接库文件中(如User32.dll,GDI32.dll,Shell32.dll...). API 声明 正如在"什么是API"中所说,API函数包含在位于系统目录下的DLL文件中.你可以自己输入API函数的声明,但VB提供了一种更简单的方法,即使用API Text Viewer. 要想在你的工程中声明API函数,只需运行API Text Viewer,打开Win32api.txt(或.MDB如果你已经把它转换成了数据库的话,这样可以加快速度.注:微软的这个文件有很多的不足,你可以试一下本站提供下载的api32.txt),选择"声明",找到所需函数,点击"添加(Add)"并"复制(Copy)",然后粘贴(Paste)到你的工程里.使用预定义的常量和类型也是同样的方法. 你将会遇到一些问题: 假设你想在你的窗体模块中声明一个函数.粘贴然后运行,VB会告诉你:编译错误...Declare 语句不允许作为类或对象模块中的 Public 成员...看起来很糟糕,其实你需要做的只是在声明前面添加一个Private(如 Private Declare Function...).--不要忘了,可是这将使该函数只在该窗体模块可用. 在有些情况下,你会得到"不明确的名称"这样的提示,这是因为函数.常量或其他的什么东西共用了一个名称.由于绝大多数的函数(也可能是全部,我没有验证过)都进行了别名化,亦即意味着你可以通过Alias子句使用其它的而不是他们原有的名称,你只需简单地改变一下函数名称而它仍然可以正常运行. 你可以通过查看VB的Declare语句帮助主题来获取有关Alias的详细说明. 消息(Messages) 好了,现在你已经知道什么是API函数了,但你也一定听说过消息(如果你还没有,你很快就会)并且想知道它是什么.消息是Windows告诉你的程序发生了哪些事件或要求执行特定操作的基本方法.例如,当用户点击一个按钮,移动鼠标,或是向文本框中键入文字时,一条消息就会被发送给你的窗体. 所有发送的消息都有四个参数--一个窗口句柄(hwnd),一个消息编号(msg)还有两个32位长度(Long)的参数. hwnd即要接受消息的一个窗口的句柄,msg即消息的标识符(编号).该标识符是指引发消息的动作类型(如移动鼠标),另外两个参数是该消息的附加参数(例如当鼠标移动时光标的当前位置) 但是,当消息发送给你时你为什么看不到呢--就象有人在偷你的信一样?请先别恼火,让我告诉你. 小偷其实是Visual Basic.但它并没有偷走你的信,而是在阅读了之后挑出重要的以一种好的方式告诉你.这种方式就是你代码中的事件(Event). 这样,当用户在你的窗体上移动鼠标时,Windows会发送一条WM_MOUSEMOVE消息给你的窗口,VB得到这条消息以及它的参数并运行你在事件MouseMove中的代码,同时VB会把这条消息的第二个32位数(它包含了x,y坐标,单位为像素(Pixel),每个位16位)转换为两个单精度数,单位为缇(Twip). 现在,如果你需要光标坐标的像素表示,然而VB已经把它转换成了缇,因此你需要重新把它转换为以像素为单位.在这里,Windows给了你所需要的,但VB"好意地"进行了转换而使你不得不重新转换.你可能会问--我难道不能自己接收消息吗?答案是肯定的,你可以使用一种叫做子类处理(Subclass)的方法.但你除非必须否则最好不要使用,因为这与VB的安全程序设计有一点点的违背.(注:子类处理确实有很大的风险,但如果使用得当,是很有用处的.不过有一点一定要注意,即千万不要使用VB的断点调试功能,这可能会导致VB崩溃!) 需要补充说明的是:你可以发送消息给你自己的窗口或其他的窗口,只需调用SendMessage或PostMessage(SendMessage会使接受到消息的窗口立刻处理消息,而PostMessage是把消息发送到一个称为消息队列的队列中去,等候处理(它将会在该消息处理完后返回,例如有些延迟)).你必须制定接受消息的窗口的句柄,欲发送消息的编号(所有的消息的编号均为常量,你可以通过API Text Viewer查得)以及两个32位的参数.

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:27 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助

用注册表保存数据(VB函数篇)

   简介    注册表最开始是出现在Windows 3.x,是用来为OLE保存数据的。那时Windows和其应用程序的设置值大多都保存在各不相同的.INI文件中。例如我们最为熟悉的System.ini和Win.ini就是这样的文件。当软件越安越多,这种文件也越来越多,而且遍布硬盘的各个角落。既不便于管理,也浪费了很多磁盘空间(因为这些文件大多都很小)。    当Windows 95推出的时候,注册表应运而生。所有的设置值,不论是系统的,还是应用程序的,都被保存在一个树型结构的数据库中。这样确实方便了不少,而且对多用户环境提供了更好的支持。而对程序员而言就麻烦了一些,必须了解的是注册表的结构,又因为注册表不再是单一的文本文件,所以必须用专门函数来操作注册表。    注册表(Registry)是保存程序运行参数和用户设置的最佳地方。听过了太多的告诫,编辑注册表对很多人来说似乎是一件神秘而复杂的事,但在VB中,通过四个注册表函数,一切都变得简单起来。    如果你对注册表有一些了解,那么应该知道注册表的结构和我们所熟悉的文件系统的结构是一样的,都是树型结构。用主键来代替目录以划分各种不同的信息。在主键下面还可以有子主键和三种不同类型的键值。每个键值可以是字符串,二进制值或者双字值中的一种。Visual Basic 提供了一个标准的注册位置(主键)以存储创建于 Visual Basic 的应用程序的程序信息:    HKEY_CURRENT_USER\Software\VB and VBA Program Settings\    下面的所有函数所读写的注册表路径都是以这个路径为根的相对位置。    SaveSetting    这个函数用来向注册表数据库写入一些数据    SaveSetting appname, section, key, setting    appname    在向注册表写入数据之前,你必须计划好一个用在整个应用程序中的appname 。可以把注册表的内容比作一台计算机,那么appname就是驱动器。它必须是一个字符串,也可以用数字,不过要用CStr 函数把它转换成字符串格式。事实上它代表的是应用程序或工程的名称。一般来说,appname在整个应用程序中应该保持不便。    section    section可以说是这个模型中的目录,就向一台计算机上的目录一样。它用来把设置分类,可以包含Key或者子section。如果给它传递vbNullChar 这个值的话,就表示要操作的键是在根目录下的,也就是在appname下面的。    key    相当于一个文件名。    setting    它代表文件中的信息,可以是一个数字或者字符串。    执行完后,在注册表的HKEY_CURRENT_USER\Software\VB and VBA Program Settings\appname\section\"主键下面的名为“key”的键将被赋值为“setting”,如果相应的主键或者键名不存在,将会被创建。    GetSetting    这个函数用来从注册表中提取数据。    GetSetting(appname, section, key[, default])    Appname,section和key的含义和上面的一样。    default    如果Visual Basic没有找到指定的键,函数将返回参数default中得值。这个参数是可选的,但是对错误情况的处理很方便。如果没有选用这个参数,而指定的键的名称不存在,函数将返回一个长度为0的字符串。    下面看看例子: SaveSetting "Logon Master", "Users\User0", _ "Name", "John" 注释:这将把数据"John"储存到注册表的 "Logon Master\Users\User0\Name" 注释:不过这是相对路径,相对于一个根 Debug.Print GetSetting("Logon Master", _ "Users\User0", "Name", "Anonymous" 注释:这将显示"John" Debug.Print GetSetting("Logon Master", _ "Users\User1", "Name", "Anonymous" 注释:这次显示的将是"Anonymous",因为在 注释:"Logon Master\Users\User1\Name"没有预先储存值。 GetAllSettings    这个函数用来提取某个目录(主键)下面的所有文件(子键)。    GetAllSettings(appname, section)    参数的含义和其他的函数都是一样的。但这个函数和其他几个比起来更为复杂。他返回的的是一个2维字符串数组,包含了所有的子键名称和乡音的子键值。看看下面这个例子,你很容易的就能搞清楚: 注释: 定义一个Variant类型的变量来保存GetAllSettings返回的二维数组 注释: Interger类型值用来计数 Dim MySettings As Variant, intSettings As Integer 注释:先在注册表中填入一些值 SaveSetting "Logon Master", "Users\User0", _ "Name", "John" SaveSetting "Logon Master", "Users\User0", _ "Priviledges", "Supervisor" 注释:提取预先设定的数据 MySettings = GetAllSettings("Logon Master", _ "Users\User0") 注释:显示出来 For intSettings = LBound(MySettings, 1) _ To UBound(MySettings, 1) 注释:MySettings(x,0)中包含的是键名, 注释:而MySettings(x,1) 中包含的是键值 Debug.Print MySettings(intSettings, 0), _ MySettings(intSettings, 1) Next intSettings    注意:GetAllSettings并不返回目录名,只是返回键名和相应的键值。    DeleteSetting    除了保存和提取数据,我们还要能够删除注册表中的注册项。这就要用到DeleteSetting函数了。    DeleteSetting appname[, section[, key]]    这个函数用来删除主键或者键。如果要删除的是键,要给出它所在的键名。如果要删除的是主键,这它下面所有的键和子主键都会被删除。如果你要删除应用程序所有的注册表信息,就只用指定appname就可以了。    例如: DeleteSetting "Logon Master", "Users\User0", "Name" 注释:这将删除Name键。 DeleteSetting "Logon Master", "Users" 注释:这将删除"Logon Master\Users"主键和其下的所有内容。 DeleteSetting "Logon Master" 注释:这将删除应用程序"Logon Master"下面的所有内容。
[此贴子已经被作者于2005-6-1 0:27:35编辑过]

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:29 | 显示全部楼层

在VB中调用API函数动态改变及恢复屏幕设置

对于Windows平台,显示器的分辩率和颜色数很重要,尤其是对于多媒体应用软件和游戏软件。但许多情况下,用户当前的屏幕设置并不适合软件的运行需要。软件通常的做法是提示用户将屏幕设置到软件要求的分辩率及颜色数,再重新启动软件。这样无疑会增加普通用户操作上的负担和困难,降低了软件的友好性和易用性。 ---- 理想的作法是:在软件开始时,动态的改变屏幕设置来达到软件运行的要求。在软件运行结束后,再自动把屏幕设置改回原来的设置值。这一切过程都在不知不觉中完成。这一做法可以通过在VB中调用API(应用程序接口)函数做到。实现方法如下: ---- 一、打开一个标准的EXE工程。 ---- 二、在“工程”菜单栏下,选取“添加模块”,为工程添加一个模块。 ---- 并在模块中添加如下代码: ‘---------------以下代码用于得到屏幕的设置参数-------------- Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long ‘取指定设备信息API函数 Public Const HORZRES = 8 ‘三个屏幕常量 Public Const VHORZRES = 10 Public Const BITSPIXEL = 12 ‘---------------通过字符COPY进行数据类型转换-------------- Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (lpString1 As Any, lpString2 As Any) As Long ‘------------------以下结构用于屏幕的初始化----------------- Const CCHDEVICENAME = 32 Const CCHFORMNAME = 32 Private Type DEVMODE dmDeviceName As String * CCHDEVICENAME dmSpecVersion As Integer dmDriverVersion As Integer dmSize As Integer dmDriverExtra As Integer dmFields As Long dmOrientation As Integer dmPaperSize As Integer dmPaperLength As Integer dmPaperWidth As Integer dmScale As Integer dmCopies As Integer dmDefaultSource As Integer dmPrintQuality As Integer dmColor As Integer dmDuplex As Integer dmYResolution As Integer dmTTOption As Integer dmCollate As Integer dmFormName As String * CCHFORMNAME dmUnusedPadding As Integer dmBitsPerPel As Integer dmPelsWidth As Long dmPelsHeight As Long dmDisplayFlags As Long dmDisplayFrequency As Long End Type ‘------------------设置屏幕的核心API----------------- Private Declare Function ChangeDisplaySettings Lib "User32" Alias "ChangeDisplaySettingsA" (ByVal lpDevMode As Long, ByVal dwflags As Long) As Long ‘------------------设置屏幕的函数----------------- Public Function SetDispMode(Width As Integer, Height As Integer, Color As Integer) As Long (SetDispMode是自己构造的更改屏幕设置的函数来, 它的三个参数Width、Height和Color分别是屏幕的横向分辨率、 纵向分辨率,颜色位数,其值可为24,16,0等。0为原有颜色设置。) Const DM_PELSWIDTH = &H80000 Const DM_PELSHEIGHT = &H100000 Const DM_BITSPERPEL = &H40000 Dim NewDevMode As DEVMODE Dim pDevmode As Long With NewDevMode .dmSize = 122 If Color = 0 Then ‘如果Color=0则只改变屏幕的分辨率,而不改变色彩。 .dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Else ‘如果Color不等0则改变屏幕的分辨率和色彩。 .dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_BITSPERPEL End If .dmPelsWidth = Width .dmPelsHeight = Height If Color < > 0 Then .dmBitsPerPel = Color End If End With pDevmode = lstrcpy(NewDevMode, NewDevMode) ‘得到一个指向NewDevMode结构的Long型的指针。 ChangeDisplaySettings pDevmode, 0 End Function ---- 三、在工程窗体中,加入两个按钮Command1和Command2,其Caption属性分别为“800x600x16”和“恢复原设置”。 ---- 其程序代码为: ‘窗口的“通用|声明”区 Option Explicit Dim H, V, Color As Long ’声名变量,用于保存最初屏幕设置 Private Sub Form_Load() ‘---------------以下代码用于得到最初的屏幕设备-------------- H = GetDeviceCaps(Form1.hdc, HORZRES) V = GetDeviceCaps(Form1.hdc, VHORZRES) Color = GetDeviceCaps(Form1.hdc, BITSPIXEL) End Sub Private Sub Command1_Click() ‘调用SetDispMode函数改变屏幕设置 SetDispMode 800, 600, 16 End Sub Private Sub Command2_Click() ‘恢复最初屏幕设置 SetDispMode Cint(H), Cint(V), Cint(Color) End Sub ---- 四、将程序编译执行。 ---- 本程序执行后,如果单击Command1,则您的计算机屏幕显示模式将被设置为“800x600x16”的显示模式;如果单击Command2, 则您的计算机屏幕显示模式将被设置为原来的显示模式。此程序稍加修改,即可放置于桌面或任务栏中,直接快捷的修改屏幕设置。

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:29 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助

功能强大的SendMessage函数

Windows API(应用程序接口)是Windows系列软件为程序开发人员提供的火力强大的“武器库”,在这个武器库中,有很多极具威力的武器,SendMessage就是其中之一,它的功能非常丰富,灵活使用这个函数,会给编程工作带来很多便利。本文以Visual Basic为例,结合几个具体的例子介绍该函数的功能。 一、SendMeaasge函数简介 顾名思义,SendMessage函数的功能是“发送消息”,即将一条消息发送到指定对象(操作系统、窗口或控件等)上,以产生特定的动作(如滚屏、修改对象外观等)。 SendMessage函数在VB中的函数说明如下: Declare Function SendMessage Lib "user32" Alias "SendMessageA" (Byval hwnd As Long, Byval wMsg As Long,Byval wParam As Long,lParam As Any) As Long 其中四个自变量的含义和说明如下: hWnd:对象的句柄。希望将消息传送给哪个对象,就把该对象的句柄作为实参传送,在VB中可以简单地用“对象.hWnd”获得某个对象的句柄,如Text1.hWnd和Form1.hWnd分别可以得到Text1和Form1的句柄。 wMsg:被发送的消息。根据具体需求和不同的对象,将不同的消息作为实参传送,以产生预期的动作。 wParam、lParam:附加的消息信息。这两个是可选的参数,用来提供关于wMsg消息更多的信息,不同的wMsg可能使用这两个参数中的0、1或2个,如果不需要哪个附加参数,则将实参赋为NULL(在VB中赋为0)。 在简单了解了SendMessage函数的格式和功能后,让我们以几个例子来看看它的威力。 二、SendMessage函数使用实例 例1 多行TextBox中的快速处理功能在处理多行TextBox时我们经常会碰到以下几种情况: 希望了解多行TextBox中目前共有多少行文字。 想快速返回第N行的文字。 对于上面的情况,如果用VB自身的语句或函数来实现的话,要写不短的代码,而且由于要采用顺序查找的办法来完成,因此代码的执行效率也很低。如果使用SendMessage函数则可以大大减少代码量,并大幅度的提高执行效率。 用SendMessage函数完成上面两个任务的方法非常简单,每个任务只需简单地发送一条消息给多行TextBox即可,两个消息分别为:EM_GETLINECOUNT、EM_GETLINE,其它参数和返回值见附表。 下面用一个简单的实例演示这两个功能: 新建工程,在Form1上添加三个TextBox(名称分别为Text1、txtLineCount、TxtString,将Text1的Multi Line属性置为True)、三个标签和一个命令按钮。为工程添加一个模块Moudle1,在其中写如下声明(其中 SendMessage函数的声明可以从VB的“API浏览器”中复制): 消息常量名 消息值 wParam lParam 返回值 EM_GETLINECOUNT &HBA 未用 未用 行数 EM_GETLINE &HC4 要找的行号 存结果的字节串 结果字节串的字节数 Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long,lParam As Any) As Long Public Const EM_GETLINECOUNT=&HBA Public Const EM_GETLINE=&HC4 在Form1的代码模块中写如下代码: Private Sub Command1_Click() Dim str(256) As Byte str(1)=1 '最大允许存放256个字符 '获取总行数,结果显示在文本框txtLineCount中 txtlineCount=SendMessage(Text1.hwnd,EM_GETLINECOUNT,0,0) '获取第3行的数据放在str中,转换为字符串后显示在文本框txtString中 SendMessage Text1.hwnd,EM_GETLINE,2,str(0) txtString= StrConv(str,vbUnicode) End Sub 之后,按F5运行程序,在多行文本框中随便键入几行文字,然后按下[确定]按钮,出现如图画面,说明程序正确统计出了总行数和第3行的文字。 两点补充说明:在调用SendMessage获取第N行字符串时,lParam需要说明为字节数组,在调用完成后,再将字节数组转换为字符串;另外,调用前必须在lParam的前两个字节指明允许存放的最大长度,其中第一个字节为低位,第二个字节为高位,本例将高位(即str(1))置1.说明最大允许存放256个字符。 例2 程序控制拉下或收起组合框的下拉列来 一般情况下,为了拉下或收起组合框的下拉列表,需要用键盘或鼠标进行操作,而有时我们希望程序运行的某个时刻自动拉出下拉列表(比如在一些演示程序中),为了实现这个目的,我们也只有借助于SendMessage函数,方法是发一个CB_SHOWDROPDOWN(&H14F)消息给组合框。 在发CB_SHOWDROPDOWN消息时,wParam参数决定了是拉下列表(=True时)还是收起列表(=False时),lParam无用(设为0)。 为说明具体的使用方法,下面提供简单的程序片段。首先在代码模块中做如下声明: Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long,ByVal wMsg As Long,ByVal wParam As Long,lParam As Any) As Long Const CB_SHOWDROPDOWN=&H14F 当程序中某处需要拉下组合框Combol的列表时,写如下调用语句: SendMessage Combol.hwnd,CB_SHOWDROPDOWN,True,0 当需要收起组合框Combol的列表时,写如下语句: SendMessage Combol.hwnd,CB_SHOWDROPDOWNN,False,0 例3 在列表框中查找匹配的项目 在Win95风格的帮助系统中一般都有一个“索引”页,索引页含有一个文本框和一个列表框,当用户在文本框中输入文字时,下拉列表会动态地显示与文本框中文字最匹配的项目,为用户提供了最大的方便。这种效果在应用程序的帮助系统中很容易实现(只要按照Win95帮助系统的正常制作过程制作就可以实现),如果想在应用程序的其它地方实现这种特性就需费一番心思了。 而使用SendMessage函数实现上述特性则非常简单,甚至只需一条语句就足够了,那就是在文本框的Change事件中给列表框发一条LB_FINDSTRING(&H18F)消息,该消息告诉列表框在列表中查找匹配的项目。 在发LB_FINDSTRING消息时,wParam参数代表从列表框的哪一个项目后面开始查找,一般情况下该参数可定为-1,表示从List1(0)即第一项开始向后循环查找,lParam则传进欲搜索的字符串(必须采用值传递)。 具体的代码和运行画面与后面的例4合并在一起演示。 例4 为ListBox添加水平滚动条 在VB中,列表框控件仅提供垂直滚动条,没有设置水平滚动条的能力,当某些项目的文本宽度较长时,超出列表框宽度部分的文本就无法显示出来,因此,很有必要为ListBox添加一个水平滚动条来方便操作。 为添加水平滚动条,只需发一条LB_SETHORIZONTALEXTENT(&H194)消息给列表框即可。发送消息时,wParam为滚动条的长度(以像素为单位,可通过计算得出准确的长度,也可随便给一个大于最大文本宽度的数字,如本例的250),lParam无用。下面是例3和例4合并在一起的代码和运行画面 Declare Function SendMessage Lib "user32" Alias "SendMessageA"(ByVal hwnd As Long,ByVal wMsg As Long,ByVal wParam As Long,lParam As Any) As Long Public Const LB_FINDSTRING=&H18F Public Const LB_SETHORIZONTALEXTENT=&H194 Private Sub Form_Load() List1.AddItem "软件" List1.AddItem "电脑游戏" List1.AddItem "电视机" List1.AddItem "电视台" List1.AddItem "电脑" List1.AddItem "电脑游戏软件" '下一句为列表框添加水平滚动条 SendMessage List1.hwnd,LB_SETHORIZONTALEXTENT,250,0 End Sub Private Sub Text1_Change() '注意!当lParam传入的是字符串时,必须用ByVal传递 List1.ListIndex = SendMessage(List1.hwnd,LB_FINDSTRING,-1,ByVal Text1.Text) End Sub 通过上面几个例子,想必您已经对SendMessage函数的强大功能有了初步的了解。事实上利用该函数我们还可以完成更多更好的任务,如控制文本框的自动滚屏、实现文字编辑过程中的Undo功能、操纵应用程序的窗体控制菜单等等,感兴趣的读者请参阅有关Windows API的资料。 本文程序均用Visual Basic 5.0企业版编写,在Pwin95环境下运行正常。

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:30 | 显示全部楼层

SENDMESSAGE函数巧应用

在这一期的SendMessage函数应用中,我将向大家介绍如何利用消息函数来扩展树型列表(TreeView)控件的功能相信对于树型列表控件大家十分的熟悉,典型的应用就是Windows资源管理器中的目录列表。在VB中,树型列表控件包含在Microsoft Windows Common Control 6.0(也可能是5.0,视你的VB或者系统版本而定)中。在Windows API中,有一系列的以TVM_ 开头的消息值,这些消息就是扩展树型列表控件所特定的消息值,下面向大家介绍其中的一些应用。 1、设置树型列表控件的背景颜色 首先做如下的定义: Private Declare Function SendMessage Lib "user32" Alias "Send MessageA" (ByVal hwnd As Long,ByVal wMsg As Long, ByVal wParam As Long,ByVal lParam As Long) As Long Const TV-FIRST = &H1100 Const TVM-SETBKCOLOR = TV_FIRST + 29 然后再作如下调用: Call SendMessage(TreeView1.hwnd, TVM-SETBKCOLOR, 0, RGB(255, 0, 0)) 上面的SendMessage调用将TreeView1的背景颜色设置为红色。 大家可能注意到了。在上面的Sendmessage函数定义中,我们将lParam定义为 ByVal lParam As Long,而不是象前面的那些范例那样定义为Any或者String类型,关于这个问题,我会在最后的一章中做介绍。 2、设置树型列表控件标题行高度 利用TVM_SETITEMHEIGHT消息可以设定控件的标题行的高度,该消息的定义及调用方法如下: 定义: Const TV_FIRST = &H1100 Const TVM-SETITEMHEIGHT = TV_FIRST + 27 Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long,ByVal wMsg As Long,ByVal wParam As Long,lParam As Any) As Long 调用: CallSendMessage(TreeView1.hwnd, TVM-SETITEMHEIGHT, 60, 0) 上面的代码将TreeView1的标题行高度设置到60像素高 3、为树型列表控件中不同的标题行设置不同的提示 在第一期的ListBox控件介绍中,我向大家介绍了如何为列表中的每一个标题行设置不同的提示(ToolTips),在这里为要向大家介绍如何为树型列表控件中的每一个标题设置不同的提示。 同ListBox控件不通,树型列表控件中并没有根据光标位置获得标题行索引的消息,我们需要另外想办法。在TVM类消息中有一个TVM_HITTEST消息,发送该消息可以检测控件表面上的某一点,如果该点位于一个标题上,则返回该标题的句柄。而利用TVM_GETITEM消息,则可以根据标题句柄返回该标题行的文本。所以结合利用这两个消息可以获取光标所在标题行的标题文本。具体的范例代码如下: Option Explicit Private Type TPoint x As Long y As Long End Type Private Type TVHITTESTINFO pt As TPoint flags As Long hItem As Long End Type Private Type TVITEM mask As Long HTreeItem As Long state As Long stateMask As Long pszText As Long cchTextMax As Long iImage As Long iSelectedImage As Long cChildren As Long lParam As Long End Type Const TV-FIRST = &H1100 Const TVM_HITTEST = TV-FIRST + 17 Const TVM_GETITEM = TV-FIRST + 12 Const TVHT-ONITEMLABEL = &H4 Const TVIF-TEXT = &H1 Const GMEM-FIXED = &H0 Private Declare Function Send MessageRef Lib"user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long,ByVal wParam As Long,lParam As Any) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As String,ByVal Source As Long,ByVal Length As Long) Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long Dim hItemPrv As Long Private Sub Form_Load() Dim ndX As Node `加入若干Item Set ndX = TreeView1.Nodes.Add(, , "R", "Root") Set ndX = TreeView1.Nodes.Add("R", tvwChild, "Key1", "Node1") Set ndX = TreeView1.Nodes.Add("Key1", tvwChild, "SubKey1", "SubNode1") Set ndX = TreeView1.Nodes.Add("SubKey1", tvwChild, "SubKeys1", "SubNode1") Set ndX = TreeView1.Nodes.Add("Key1", tvwChild, "SubKey2", "SubNode2") Set ndX = TreeView1.Nodes.Add("Key1", tvwChild, "SubKey3", "SubNode3") Set ndX = TreeView1.Nodes.Add("Key1", tvwChild, "SubKey4", "SubNode4") End Sub Private Sub TreeView1_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single) Dim ptA As TPoint Dim tf As TVHITTESTINFO Dim tv As TVITEM Dim hStr As Long Dim hItem As Long Dim astr As String * 1024 Dim bstr On Error GoTo errLab `获得当前光标所在的位置坐标 ptA.x = Int(x / Screen.TwipsPerPixelX) ptA.y = Int(y / Screen.TwipsPerPixelY) tf.pt = ptA tf.flags = TVHT_ONITEMLABEL `获得光标所在的Item的句柄 hItem = SendMessageRef(TreeView1.hwnd, TVM_HITTEST, 0, tf) `如果未获得句柄或者同上一次是同一个Item的句柄则退出 If ((hItem <= 0) Or (hItem = hItemPrv)) Then Exit Sub hItemPrv = hItem `分配一定的内存空间用以存储Item的标题 hStr = GlobalAlloc(GMEM-FIXED, 1024) If hStr > 0 Then tv.mask = TVIF_TEXT `获取标题文本 tv.HTreeItem = hItem `Item句柄 tv.pszText = hStr tv.cchTextMax = 1023 `发送TVM_GETITEM获得标题文本 CallSendMessageRef(TreeView1.hwnd, TVM-GETITEM, 0, tv) `将标题文本拷贝到字符串astr中 CopyMemory astr, hStr, 1024 bstr = Left$(astr, (InStr(astr, Chr(0)) - 1)) TreeView1.ToolTipText = bstr `释放分配的内存空间 GlobalFree hStr End If Exit Sub errLab: Resume Next End Sub 运行上面的程序,当光标在TreeView1上面移动时,TreeView1的ToolTips就会根据光标所在的不同标题行而变动。 以上程序在Win98、Win2000,VB6下运行通过

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:32 | 显示全部楼层

Office 95 API 的企业开发者指南

摘要 这篇技术文章探讨如何编写Microsoft Office解决方案代码,以便使用16位和32位版本的Office产品顺利进行应用编程接口(API)调用。特别是,本文适用于Microsoft Access 、Visual Basic 、Microsoft Word 、Microsoft Excel 和Microsoft Project 。实现这种API调用的方法有三类:使用REGISTER、使用Declare语句和使用一个类型库。本文分别对这三种API调用方法进行了探讨和举例。在本文中,假设读者已对我先前的一篇技术文章“将你基于16位Office的解决方案移植到32位Office ”较为熟悉,并且假设读者是一位经验丰富的Office 开发者,他需要移植或编写解决方案,以满足16位到32位互操作性的新要求。 引言 对于编写一个面向多个平台多个版本Microsoft Office 产品的解决方案问题,它是大多数编译语言开发者所不熟悉的。编译语言应用程序开发者喜欢在编辑程序开发中选择一个时间段,将解决方案加到一个执行文件中,而很少需要解决由产品开发引起的问题。一个Office 解决方案应具有一个可在所有Office 产品和所有操作系统中正确执行的解决方案文件(一个.XLS、.MDB、.MPP或.DOT文件)。Office 开发者必须编写具有对前五年内发布的版本的向后兼容性和对将发布的下一版本的向前兼容性。例如,一个在Microsoft Excel 4.0和Microsoft Excel 5.0上运行的费用报告应能在Microsoft Excel NT、Microsoft Excel 95和Microsoft Excel 2001(假定存在)上运行。 Office 95将32位Office 产品引入到主流企业环境中,它们将与16位Office 产品共存许多年。在我以前的一篇技术文章“将你基于16位Office的解决方案移植到32位Office ”中,对不能从一个32位应用程序调用16位API的问题以及不能从一个16位应用程序调用32位API的问题进行了阐述,在那篇文章中列出的16位/32位互操作性解决方案将在本文中作进一步的探讨。 注:根据经验,我把Windows 3.1中的API调用转换为Win32 应用编程接口(API)调用称之为32A规则,即:“如果在任何调用参数中使用了名称字符串,给DLL名添加一个32,给函数名添加一个A(对于ANSI)”。这个经验对于绝大多数API调用是可行的,但对于某些函数,这个经验可能行不通,因此它并不是绝对正确的解决方案。 16位/32位互操作性解决方案比16位API调用移植到相当的32位API调用(换句话说,使用32A规则)的要求更多;它需要解决方案在16位或32位Office 产品中执行时能选择适当的API。 术语 为了避免用语使用的混淆,下列表中定义了在本技术文章中使用的术语: 术语 定义 Office 产品 不将代码编译为一个可执行程序的产品。下列产品之一:Microsoft Project 、Microsoft Access、Word 和Microsoft Excel 。 编译语言 将代码编译为一个可执行程序的产品,例如Visual Basic 、FORTRAN、PowerStation 、Visual FoxPro 和Visual C++。 解决方案 由第三方或开发者使用某个Office 产品及其格式(如Microsoft Excel (.XLS)、Word (.DOT)、Microsoft Project (.MPP)或者Microsoft Access(.MDB))开发的应用程序。 解决方案代码 编写有解决方案的代码(XLM或Basic或者结合使用) 平台 Microsoft Windows和Apple Macintosh 操作系统。一些Office 产品适用于所有Windows和Macintosh操作系统。通过将解决方案从一个操作系统复制到另一操作系统, Microsoft Excel 可在Macintosh和Windows PC中执行同一应用程序(在一定的限制条件下)。   API调用方法 解决方案代码可采用下列三种方法之一进行编写--使用Microsoft Excel 宏(XLM)、Basic(Access Basic、Visual Basic 、VBA和WordBasic)或者混合使用XLM和Basic。XLM使用REGISTER和CALL命令来进行API调用,Basic使用Declare语句、类型库或者这两者的结合使用来进行API调用。经一个开发者传给另一开发者的Microsoft Excel 解决方案可以使用所有这些方法进行API调用。这三种方法对于最新版本的Microsoft Excel 都适用。 表1 API调用方法描述 方法 语言 进行API调用的方法 REGISTER XLM宏 从宏中使用REGISTER和CALL函数 Declare Access Basic、 Visual Basic 、 VBA、WordBasic 使用Declare语句 类型库 仅VBA 从对象浏览器上选择类型库方法 这些方法不会产生某个特定版本的解决方案,它取决于开发者编写代码的引用或解决方案所必须运行的产品范围。例如,为了使解决方案在Microsoft Excel 4.0中也有效,开发者可以在Microsoft Excel 95的XLM中编写一个解决方案。表2中列出了各种API调用可在哪些Microsoft 产品中使用。 表2 Microsoft 产品的API调用方法 产品版本 REGISTER方法 Declare方法 类型库方法 Microsoft Excel 3.0,4.0 X     Microsoft Excel 5.0,5.0NT,95 X X X Word 2.0,6.0,95   X   Visual Basic 1.0,2.0,3.0   X   Visual Basic 4.0   X X Project 4.0,95   X   Microsoft Access1.0,1.1,2.0   X   Microsoft Access95   X X FoxPro 2.5   X   FoxPro 3.0   X X 我们在讨论16位和32位API调用时,都将使用同一个简单的API调用示例--GetTickCount来进行论述。 REGISTER方法 对于在编写Office 解决方案前先在Visual Basic 中编写过代码的开发者,他们并不熟悉使用REGISTER方法的API调用。旧版本Microsoft Excel 的开发者从XLM中进行API调用,并避免使用VBA。我们不能把XLM看作是过时的技术--虽然从Microsoft Excel 4.0开始未对其更改。如果性能问题变得很重要时,XLM仍是最好的选择。Microsoft Excel 5.0开发者工具包中陈述了直接从XLM中进行API调用的优点。 由于C API最易于在Microsoft Excel 宏和工作表上使用,因此对于Visual Basic 所用的外部函数的编写,它并不是非常好(虽然VB 和C API可被融入混合解决方案中)。 为了从XLM进行API调用,要求使用REGISTER函数来引用动态连接库(DLL)以及使用CALL函数来执行它。REGISTER函数与VBA中的Declare语句的作用是一样的。推荐的REGISTER句法如下: REGISTER(module_text,procedure,type_text,function_text,argument_text,macro_type,category,shortcut_text) 要返回到所给出的API调用示例,我们可以在XLM的一些代码行中采用GetTickCount。 这个代码在16位Microsoft Excel 中产生下列输出: 如果我们在32位的Microsoft Excel 中运行这个代码,结果是失败的。API调用返回#VALUE!,表明传递给DLL或从DLL中传递变量是失败的: 我们必须将这个代码转换为一个32位的API调用,因此,我们使用32A规则,在DLL名上添加一个32,如果是字符串,在其上添加一个A。采用32A规则的代码如下所示。 在调用TestGetTickCount32A时,我们会遇到另一种错误。返回#NAME?变量,这意味着在DLL中不存在这个函数。它可能是错误的DLL或错误的函数名(记住,函数名是区分大小写的)。这必定是32A规则的一个例外(我承认,我是有意选用这个函数的)。 在MSDN库中,使用关键字索引在PlatformSDK(平台软件开发包)中查找GetTickCount。在主题的顶端弹出的快速信息(Quick Info)会告诉你该函数在KERNEL32库中(在Window 3.x中,该函数在USER库中)。下列代码列出修改后的32位Microsoft Excel 宏。 在32位Microsoft Excel 中运行此代码,我们得到正确的结果: 为说明在16位和32位Microsoft Excel 中的API调用方式,参阅表3,表中显示了这些宏的结果。使用32A规则的宏(“32A位”)在16位和32位解决方案中产生的结果相同。它说明了REGISTER函数从16位或者32位解决方案中定位(或定位失败)16位和32位DLL中的函数名的能力。返回#VALUE!表明在传递或接收参数中出现了问题。 表3:从16位和32位Microsoft Excel 进行调用的结果 Microsoft Excel 4.0(16位版) Microsoft Excel 95(32位版) 我们能够编写宏以进行16位API调用或32位API调用,但是,我们必须编写可进行16位API调用和32位API调用的宏,这取决于Office 产品的版本。解决方案代码必须在16位Microsoft Excel 和32位Microsoft Excel 上均能运行。在我以前的文章(“将你基于16位Office的解决方案移植到32位Office ”)中描述的VBA函数Engine32不能在Microsoft Excel 4.0(Microsoft Excel 4.0不包含VBA)中正常工作。Engine32可在Microsoft Excel 5.0及更高版本上正常工作。一个与Microsoft Excel 4.0兼容的Engine32函数必须使用宏代码。 XLM的Engine32函数 对于XLM,解决方案与VBA解决方案类似。如果Microsoft Excel 是一个32位版,函数信息(“osversion(操作系统版本)”)将包含32。如果Microsoft Excel 是一个32位版,下列所示的Engine32宏返回TRUE;如果Microsoft Excel 是16位版,该宏返回FALSE。 REGISTER方法解决方案的示例 由于Enging32已定义,GetTickCount的宏代码很简单: Engine32将在所有版本的Microsoft Excel 中运行正常。GetTickCount函数与在Windows3.1中的API调用是一样的。 注:如果对性能关心,你应在装载这个解决方案时注册所有API调用。 REGISTER方法解决方案的步骤 如果你正将Microsoft Excel 解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤: 创建一个名为APICALLS的新宏; 在APICALLS宏中创建Engine32函数; 定位解决方案中的所有REGISTER函数,并将它们移到APICALLS中,一个一栏; 使用上面的REGISTER解决方案示例中的宏代码作为一个模板,在你的宏表中为每个API创建函数; 添加所需的ARGUMENT行; 添加所需的RESULT行; 添加16位API REGISTER行,在函数名后加上16; 添加32位API REGISTER行,在函数名后加上32; 进行任何数据处理以进行API调用; 添加一个IF行以调用相应的API; RETURE返回变量(如果有); 测试函数; 为Microsoft Excel 定义函数。 这个过程可使宏表中现有的API调用保持原样。一旦你创建了宏表APICALLS(并进行了测试),你可以把它加到其他解决方案中,重复使用这个宏,这样可节省转换时间。该宏表的内容独立于解决方案(它仅包含Windows API调用)并可在其他解决方案中重复使用。这个宏表APICALLS成为将来开发16位/32位解决方案的罗塞达石碑。 Declare方法 VBA的引入给Microsoft Excel 开发者提供了XLM的替代方法。Visual Basic 、WordBasic和Microsoft Access开发者也能毫无困难地编写Microsoft Excel 和Microsoft Project解决方案。Basic代码可在广泛的产品中进行交换。年轻一代的Office 开发者将在VBA中编写代码,而很少使用XLM。 从VBA中调用API需要使用API Declare语句。一个经声明的API可在Basic代码(或在一个宏中)的任何地方被调用。编写Declare语句有两种方法,如下所示: Word Declare语句 'Word是首选的编写Declare 语句的方法 '它的格式是固定的,具有向后兼容性。 Declare Sub SubName Lib LibName$ [(ArgumentList)] [ Alias Routine$] Declare Function FunctionName[$] Lib LibName$ [( ArgumentList)] [Alias Routine$] As Type VBA、Basic和Microsoft Access Declare语句 Declare Sub globalname Lib "libname" [Alias "aliasname" ][([ argumentlist])] Declare Function globalname Lib libname [Alias aliasname ] [([ argumentlist])] [As type] 因为我已在“将你基于16位Office的解决方案移植到32位Office ”中进行了这方面的一些讨论,并得出了上面的分类,我将简要地利用一个函数示例来说明Declare解决方案。 Basic Engine32函数 如果32位API调用是正常的(16位API调用失败),Engine32函数返回True;如果16位API调用是正常的(32位API调用失败),该函数返回False。在“将你基于16位Office的解决方案移植到32位Office ”一文中给出的Engine32函数被设计成可说明它们在每个Office 产品中的差异的示例。下面给出的是经修改后的函数,它具有更好的性能并能在更多的版本上运行。 利用在第一个函数中初始化静态变量可改善性能。随后的所有调用使用这些静态变量,而不是重复进行额外的函数调用。改善性能的另一方法是初始化一个全局或公有变量;然而,它存在一个缺陷,在一些产品中发生Reset(重置)时,在全局变量得到重新初始化前,随后的所有API调用可能失败。 Microsoft Excel 5或更高版本和Project 4或更高版本 如果Microsoft Excel 和Microsoft Project 是32位的产品,它们中的Application.OperatingSystem属性通常包含32。由于Microsoft Excel 5.0既有16位产品又有32位产品,因此产品版本号是不充分的。 Function Engine32%() Static sEngine32%,SEval% 'Statics用于性能的改善。 If SEval% Then Engine32%=sEngine32%: Exit Function If instr(Application.OperatingSystem,"32") then sEngine32%=True Seval%=True Engine32%=sEngine32% End Function Microsoft Access 1.1或更高版 Microsoft Access不具有既有16位又有32位产品的版本。通过调用SysCmd检查版本号,确定正在使用的Microsoft Access的版本。Microsoft Access 1.1在构造时没有加入一个版本号常量,因此,我们通常使用7,以确保代码可在Microsoft Access中正常工作。这种方法可用来查看你的解决方案代码进行的是一个16位还是一个32位API调用。 Function Engine32% () Static sEngine32%,SEval% If SEval% Then Engine32%=sEngine32%: Exit Function If SysCmd(7) > 2 Then sEngine32% = True Seval%=True End Function Word for Wndows 2.0或更高版 由于Word不支持静态变量,Word 必须每次对Engine32函数赋值。首先,我们检查产品版本号是否表明其是一个32位版本,然后我们检查操作系统的版本,查看它是否是一个32位的操作系统。这两个步骤是需要的,因为Word 6.0以前的版本不能使用GetSystemInfo,并且Word 6.0既具有16位版又具有32位版。 Function Engine32 Engine32 = 0 If Val(AppInfo$(2)) > 5 Then OS$ = GetSystemInfo$(23) If Val(OS$) > 6.3 Or Len(OS$) = 0 Then Engine32 = - 1 End If End Function Visual Basic 虽然Visual Basic 不使用解决方案代码,但Basic代码通常要与包含上述的产品之间进行交换。Visual Basic 4.0没有 Application.OperatingSystem 属性(它不是VBA所专有的一部分),而是使用条件编译#IF和 #ELSE。如果你打算将你的代码与其他Microsoft 产品共享,你应创建下列函数(不要在别处使用条件编译): Function Engine32%() '这仅针对 VB4。 #IF WIN16 Engine32% = False #ELSE Engine32% = True #ENDIF End Function 对于更早版本的Visual Basic ,使用下列函数: Function Engine32%() '这是用于 VB1 - VB3的; 不支持#IF。 Engine32% = False End Function

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:33 | 显示全部楼层
Declare方法解决方案的示例 下列代码演示了Declare方法解决方案(不包括Word ): Declare Function GetTickCount32 Lib "Kernel32" Alias "GetTickCount" () As Long Declare Function GetTickCount16 Lib "USER" Alias "GetTickCount" () As Long Function GetTickCount() As Long If Engine32() Then GetTickCount = GetTickCount32() Else GetTickCount = GetTickCount16() End If End Function Engine32函数可用来确定进行的API调用。Declare语句指示实际的API函数名的Alias(别名),以避免偶尔的大小写变化(32位API调用是区分大小写的),然后在函数后添加16或32以表明API函数的位数(是16位还是32位)。 这个代码可在除Word 外的所有Office 产品中复制和粘贴。WordBasic在其他Office 产品使用Basic之前已经存在,并与之不同。我们将在后面探讨Word 解决方案。 Declare方法解决方案的步骤 如果你正将解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤: 创建一个名为APICalls的新模块; 在APICalls宏中创建Engine32函数; 定位解决方案中的所有Declare函数,并将它们移到APICalls中; 使用“Declare方法解决方案的示例”中的宏代码作为一个模板,为每个API创建函数; 自变量应与Windows 3.1 版API调用匹配; 结果应是Win32 API调用的结果(若需要,Visual Basic 将自动转换为Windows 3.1版); 添加16位API Declare行,在函数名后加上16; 添加32位API Declare行,在函数名后加上32; 进行任何数据处理; 添加一个IF行以调用相应的API; RETURE返回变量(如果有); 测试函数。 这个过程使得在其他模块中现有的调用保持原样。一旦开发者定义并测试了APICalls模块,她或他可以将这个模块加入到其他解决方案中并可重复使用,这样可节省开发时间。这个模块的内容独立于解决方案,并且开发者可以重复使用这个模块。模块APICalls成为将来开发VBA中16位/32位解决方案的一个组成部分。 API包装的替代解决方案 我通常在API调用上加一层包装,而不是将API调用暴露在代码中。例如,我会将代码包装在GetProfileString上,以创建一个名为vbGetWinIni的函数,该函数采用相同的自变量,但返回的是字符串。我在“创建有用的内在Visual Basic和Microsoft Access函数”一文中论述了公用API包装到DLL的转换问题。 如果你以这种方式编写代码,你可能想要修改API包装函数以调用相应的API,而不是额外创建函数。 Word Declare方法解决方案示例 Word具有不同的Declare格式和句法。Word解决方案比较复杂,这是因为你不能将16位和32位Declare语句都放在相同的宏中。 解决方案将创建三个宏库:APICALL16、APICALL32(它们包含针对每个操作环境的Declare语句)以及一个具有16位/32位互操作性的宏APICALLS。这可能听起来很混乱,那么让我们一步一步讨论。 首先,我们创建一个名为APICALL16的宏库。这个宏包含所有16位API Decalre语句。 '这是APICALL16 -- 所有16位Declare语句都包含在此处。 Declare Function GetTickCount16 Lib "USER" Alias "GetTickCount"() As Long Function GetTickCount GetTickCount = GetTickCount16 End Function 其次,我们创建一个名为APICALL32的宏库。这个宏包含所有32位API Declare语句。 '这是 APICALL32 -- 所有32位 Declare 语句都包含在此处。 Declare Function GetTickCount32 Lib "KERNEL32"() Alias "GetTickCount" As Long Function GetTickCount GetTickCount = GetTickCount32 End Function 第三步,我们创建一个名为APICALLS的宏库。这个宏包含Engine32和你的解决方案代码所要调用的过程。 '这是APICALLS -- 这个宏中不包含任何Declare语句。 Function Engine32 Engine32 = 0 If Val(AppInfo$(2)) > 5 Then OS$ = GetSystemInfo$(23) If Val(OS$) > 6.3 Or Len(OS$) = 0 Then Engine32 = - 1 End If End Function Function GetTickCount If Engine32 Then GetTickCount = APICall32.GetTickCount Else GetTickCount = APICall16.GetTickCount End If End Function '其他API函数调用包含在此处。 你现在可以从你的解决方案代码中调用这个函数。你必须以APICALLS开始你的调用,例如: Sub MAIN MsgBox Str$(APICalls.GetTickCount) End Sub Word Declare方法解决方案的步骤 如果你正将Word解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤: 创建一个名为APICALLS的新模块; 在APICALLS中创建Engine32函数; 创建一个名为APICALL16的模块; 定位解决方案中的所有16位Declare语句,并将它们移到APICALL16中; 创建一个名为APICALL32的新模块; 创建相当的32位Declare语句,并将它们置于APICALL32中。 使用上述模板,为三个宏库中的每个API创建函数; 在调用所有API前,在你的解决方案代码中添加APICALLS; 测试每个函数。 这个过程使得在其他模块中现有的调用保持原样。一旦开发者定义和测试了这些宏,她或他可将这些宏添加到NORMAL.DOTMO模板中,并可在其他解决方案中重复使用这些宏,以节省时间。 类型库方法 进行API调用的类型库方法对于大多数开发者来说都是较新的内容。在Bruce McKinney的一本将由Microsoft出版的书《Visual Basic核心》中,包含了16位API调用(WIN16.TLB)的Windows API Functions函数类型库以及一个与32位API调用(WIN32.TLB)匹配的类型库。一旦这些类型库被注册,对于16位或32位版本的Office产品,相应的类型库将被装载。 一个类型库可提供Microsoft Excel 5.0或更高版、Microsoft Project 4.0或更高版、Visual Basic 4.0或更高版以及Microsoft Access 95或更高版产品的API调用的简易途径。所有这些Windows API调用变成了内在函数。(参阅我的“创建有用的内在Visual Basic和Microsoft Access函数”一文中的相关材料,它们仅适用于早期版本的Visual Basic和Microsoft Access)。由于对于我的读者来说,类型库方法是一种新的方法,我会详细逐步进行分析。 类型库的注册 下列步骤可完成在你注册中添加一个调用Windows API函数的类型库。这样,该类型库对于所有使用VBA的产品都可用,而不仅仅限于你进行该函数注册的产品。 打开任何VBA产品(例如,Microsoft Excel 5.0)。 为了创建一个模块,从“插入”菜单中选择“宏模块”。 从“工具”中选择“引用”。“引用”对话框的显示如图1。 图1:“引用”对话框 假设你未注册Windows API函数类型库,单击“浏览”按钮并定位到Win32.TLB,然后单击“确定”。对于WIN16.TLB,重复进行这个步骤。这样WIN16.TLB和WIN32.TLB都被注册。参见图2。 图2:选择类型库 在“浏览器”对话框关闭后,滚动到“引用”对话框的底部,你可以在列表中看到Windows API函数类型库(图3)。 图3:Windows API 函数的注册 关闭“引用”对话框。 从“视图”菜单中选择“对象浏览器”。 在“对象浏览”对话框上,在库/工作表下拉列表框中选择Win(Windows API 函数类型库)。所有可用的函数将显示在对象/模块和方法/属性列表框中(如图4)。 图4:Microsoft Excel 5.0“对象浏览器”显示Bruce McKinney的Windows API函数类型库中可用的对象和方法 在对象/模块列表框中,选择“Kernel”;然后在方法/属性列表框中选择“GetTickCount”。 单击“粘贴”按钮,GetTickCount出现在模块中。 下面示例列出的代码可实现在消息框中显示GetTickCount API调用的返回值。 Sub Demo() MsgBox Str$(GetTickCount) End Sub 不需要REGISTER命令和Declare语句。上面的代码就是你所需要的全部代码。 类型库方法解决方案的步骤 如果你正将解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤--假设Windows API函数类型库已注册: 假设你仅使用标准的16位API调用,删除你的所有Declare语句; 检查“引用”对话框中的Wondows API函数检查框。 这就是全部步骤,你已经完成了移植。 类型库问题 类型库是一项发展中的技术,商业上可用的类型库还非常少。Bruce先生在使用Windows 3.1 API调用名设计16位和32位产品的类型库方面,作出了优异的工作。尽管如此,类型库仍存在一些问题留待解决。 Windows API函数类型库在VBA中添加了超过1000个的新保留词。所有包含在类型库中API调用变成了语言的保留词。如果你已经有一个名为ordShell的函数,你必须更改它的名称,这样才不会发生与类型库中定义的ordShell函数的冲突。 Windows API函数类型库不包括需要用户定义的类型(UDT)的API调用。这可能在下一版本中得到改善。 Windows API函数类型库方法在Microsoft Excel的speadsheet(电子数据表)中是不可用的。 这种技术是非常有希望的,并将在开发Office解决方案中简化API调用的使用。

TA的精华主题

TA的得分主题

 楼主| 发表于 2005-6-1 00:34 | 显示全部楼层

SHELL语句用法心得

一. 调用系统“创建快捷方式”向导   是否为VB不支持创建快捷方式而于着急呢?虽然您可以调用vb5stkit.dll中的fCreateShellLink函数,但它是为安装程序设计的,快捷方式的默认路径总是从当前用户的“\Start Menu\Programs”开始,也就是说,如果您的Windows95装在C盘上,您无法通过 fCreateShellLink 函数把快捷方式创建到D盘上去。   现在,给大家介绍一种极为方便、巧妙的方法: 用Shell语句调用系统“创建快捷方式”向导。   新建一个项目,在窗体上放一个按钮,双击此按钮,加入以下代码: Private Sub Command1_Click()   Open App.Path & "\temp.lnk" For Output As #1   Close #1 '以上两句在程序所在目录建立一个临时文件   Shell "Rundll32.exe AppWiz.Cpl,NewLinkHere "& App.Path & "\temp.lnk" End Sub   (注意:Shell语句中NewLinkHere后面跟着一个空格才是引号,否则将出错。)   运行程序,按一下命令按钮,怎么样?“创建快捷方式”向导出现了,如果创建成功,快捷方式将取 代临时文件temp.lnk的位置,如果选取消,temp.lnk 也会自动消失。当然,您可以在硬盘的任意位置建立 temp.lnk。好,现在又可以为您的程序增添一项新功能了。Enjoy! 二. Rundll32.exe的用途   我们知道,用Shell语句只能调用可执行文件,即 exe、com、bat 和 pif 文件,有时我们想要调用其他一 些系统功能该怎么办呢?此时,Windows提供的 Rundll32.exe可大显身手了。下面我们来认识一下这些用法,也许会给您带来一点惊喜。   1.要打开设置系统时间的控制面板文件 (Timedate.cpl),只需运行如下代码:   Shell "Rundll32.exe   Shell32.dll,Control_RunDLL Timedate.cpl"   至于打开其他控制面板文件,相信您一定能够举一反三,尝试一下,换个文件名就成了。   2.要运行某一快捷方式(*.lnk)则可以用以下代码:   She11 "Rundll32.exe url.dll, FileProtocolHandler X"   (X代表要运行的文件,包括路径,下同。)   3. 也可以这样写来打开ActiveMovie控制:   Shell "RUNDLL32.EXE amovie.ocx,Rundll",1   而用Shell "RUNDLL32.EXE amovie.ocx,Rundll /play X",1 将直接播放媒体文件。   4. Shell "rundll32.exe desk.cpl,InstallScreenSaver X”当然是安装屏幕保护啦,如果你写了一个屏幕保护程序,那么可以在安装程序中写上它,而不一定要装到system目录下。顺便提一下,VB不是自捞一个“Application Setup Wizard”么?它的VB源代码都在安装目录下的 “\setupkit\setup1”中放着呢,好好把它研究一下。 你完全能做出富有个性的安装程序来。   5.按住shift键,右击某一文件,菜单中会出现 “打开方式”选项,这也许已不是什么秘密。但现在, 用shell "rundll32.exe shell32.dll OpenAs_RunDLL X" 便能直接调用“打开方式”框。   6. 甚至能用这样一句来打印文件(包括HTML所 支持的所有文本与图像格式):   Shell "rundll32.exe MSHTML.DLL,PrintHTML X”, 1   怎么样?是不是小有收获呢?这下,您一定会对 Rundll32.exe这个平时不起眼的文件另眼相待了,它可是系统运行必不可少的部件呢!其实 Shell+Rundll32 还能调用其他许多系统功能,比如关于NetMeeting和Telnet方面的。如果您有兴趣,可以 到往册表的“我的电脑\HKEY_CLASSES_ROOT\”或“我的电脑\HKEY_LOCAL_MACHINE\Software\CLASSES”下 去看看,本文的内容大都是从那里“抄”来的。只要有足够的决心、信心、耐心,您一定能发现更多有价值的东西。Good luck!
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-24 01:57 , Processed in 0.046774 second(s), 5 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表