Office VBA开发经典:中级进阶卷
上QQ阅读APP看书,第一时间看更新

2.1 Shell函数

Shell用于执行一个可执行文件,返回一个Variant (Double)类型的结果,如果成功,代表这个程序的任务ID,若不成功,则会返回0。

Shell的语法格式是:

    Shell(PathName,WindowStyle)

具体的参数说明:

 PathName:是一个字符串,必须包含一个可执行文件的名称,后面还可以加上一些命令参数,各个参数之间用空格隔开。

 WindowStyle:枚举常量,用来规定窗口样式,如表2-1所示。

表2-1 Shell命令的窗口样式常量

需要注意的是,在VBA或VB6编程中,Shell函数是内置函数,不需要添加任何外部引用,也不需要创建对象。

Shell函数的功能可以这样理解,就是代替手工去打开某个应用程序或文档。

实际上,屏幕上面的窗口的根源绝大多数都来源于一个可执行文件(扩展名为.exe)。例如微软的Word,其窗口根本路径是Office安装路径下的WINWORD.EXE文件,当打开一个文本文件时,其根源文件是C:\Windows\System32\notepad.exe。那么Windows系统中的可执行文件,一部分是系统自带的,例如记事本、计算器、注册表编辑器等,它们都处于System32文件夹下,是系统内置可执行文件。而另一部分如QQ、Excel这些应用软件则是后期安装上的,根源文件往往位于用户指定的路径。

为了更好地理解Shell函数的原理,下面使用计算机的“运行”窗口完成一些动作。按下快捷键【Windows + R】,在屏幕左下角弹出“运行”窗口,如图2-1所示。

图2-1 运行窗口

在路径框中输入:C:\windows\System32\notepad.exe C:\temp\new.txt,单击“确定”按钮,就可以看到桌面自动弹出记事本,并能看到其中的内容。

我们仔细分析路径的构成,C:\windows\System32\notepad.exe其实就是系统文件记事本的根源文件,空格后面的路径是命令参数部分,也就是告诉记事本程序,要打开这个文件,而不是其他的文本文件。

针对以上实例,可以使用下面的Shell函数来达到同样目的。只需要把“运行”中的内容传递给Shell函数即可。

上述代码中的vbMaximizedFocus表示打开的记事本窗口取得焦点,并且最大化记事本窗口,如果改为vbHide,则打开的新窗口在屏幕上看不见,在某些场合下这样是很危险的。

对于上述过程,我们要认识到以下几点。

 Shell函数的参数由可执行文件的路径以及命令参数路径构成。

 可执行文件的路径,根据需要可以尽可能缩短简化。

 命令参数不是必需的,如果不带命令参数,则默认只打开该应用程序。

由于上例中的notepad.exe处于系统文件夹中,系统的环境变量中一定有C:\windows\System32这个路径。因此,Shell函数还可以省略所在路径,以及后面的扩展名,从而简化为:Shell "notepad C:\temp\new.txt", vbMaximizedFocus。

如果不打开具体的文件,只打开记事本,则不需带命令参数。进一步简化为:

    Shell "notepad"

根据以上心得,我们尝试用下面的代码打开一个Word文档。

    Shell "WinWord C:\temp\公务员.docx"

代码中的WinWord是Offi ce安装文件夹中Word程序的主文件,如图2-2所示。

图2-2 Word的执行文件

2.1.1 System32中常用的可执行文件

Windows系统中,很多实用的工具都处于System32这个文件夹下,因此可以利用Shell函数调用这些可执行文件,完成一些特定的任务。比较常用的可执行文件如表2-2所示。

表2-2 常用的可执行文件

①这类文件有可能不在System32文件夹中。

2.1.2 执行DOS命令

DOS命令的可执行文件是cmd.exe,用Shell函数调用DOS命令后,会弹出黑屏窗口,当DOS命令执行完毕后,有可能自动退出黑屏,也有可能停留,这取决于cmd的参数。/c表示自动退出,/k表示停留。

下面的代码,首先切换磁盘分区到D盘,其次切换当前目录到Download文件夹,最后用Shell函数调用DOS窗口,用dir命令列举出该文件夹中的内容。

由于cmd的参数使用了/k,因此,当dir命令执行完后,黑屏仍然停留在屏幕上,如图2-3所示。

图2-3 Shell调用DOS命令

DOS命令众多,此处仅举几个典型的例子。

下面的代码用DOS命令自动把D:盘根目录下的VMware10.7z压缩包(该文件大约470MB)复制到D:\Download文件夹中,并试图把复制后的文件重命名。

运行上述过程,当运行到第二行时,弹出如下的运行时错误,如图2-4所示。

结束程序后,可以看到Download文件夹下确实多了一个文件。也就是说,复制操作成功地执行了,但是重命名失败。

导致操作失败的原因是,Shell函数是异步执行的,也就是说,复制文件的宿主程序是cmd文件,并不是VBA代码。因此VBA并不能监测到cmd那边复制操作是否已经完成。换句话说,在VBA中执行一句Shell语句几乎不花时间,而实际上的复制动作可能持续好几秒或好几分钟。由于VBA监测不到复制的状态,就立即运行后续的代码,进行重命名,出现运行时错误也就可以理解了。

图2-4 不可移动或重命名

2.1.3 认识Shell函数的异步

为了提高Shell函数的健壮性,利用Shell函数的返回值,再配合一些API函数,可以让程序在执行Shell函数的时候智能等待。也就是说,只有Shell函数中的动作已完成时,才往下继续执行代码,否则在空循环中等待。

在标准模块中写入如下代码。

代码分析:当文件正在复制时,代码中的ExitCode和常量STILL_ALIVE是相等的,都是非零值,此时跳不出Do循环体。

当复制动作完成时,ExitCode变为0,Do循环的条件不再成立,就跳出并执行后续重命名的代码。

运行上述CopyFile过程,即可进行先复制文件,然后重命名复制的文件,如图2-5所示。

图2-5 处理Shell的异步

因此,只要Shell函数中的新窗口不关闭,就不会跳出Do循环,读者可以把上述代码中的Pid = Shell("cmd.exe /c copy D:\VMware10.7z C:\lib\", vbNormalFocus),更改为Pid =Shell("notepad.exe", vbNormalFocus),再试一次,可以发现,弹出的记事本窗口如果不手工关闭,程序就阻滞在Do循环体中。

2.1.4 处理Shell函数中的空格

计算机的文件夹或文件名中经常包含空格,如果把包含空格的路径作为参数传递给Shell函数,该函数会以空格作为分隔符,把一个路径理解为多个参数的连接。

例如,试图用Word 2013打开C:\temp文件夹下的“公务员.docx”,如果写成如下形式。

         Shell "C:\Program Files\Microsoft Office\Office15\WINWORD.EXE C:\temp\公 务 员.docx",
    vbNormalFocus

图2-6 文件或路径中包含空格

执行时,并不能打开名称或路径带空格的文件,而是弹出如图2-6所示错误对话框。

这是因为不仅Word的路径包含空格,而且计划打开的文档名称也有空格,Shell函数无法解释这些参数的含义,因此不能打开。

如果为Shell函数中各路径事先用双引号包起来,就不会有上述麻烦。

自定义函数AddQuote的作用是给路径两侧加上双引号,并且添补一个空格,以防止连在一起。

运行上面的“处理空格”过程,该文档可以正常地在Word中打开。

2.1.5 自动打开控制面板

控制面板的主文件是位于System32文件夹下的control.exe,因此只需要执行:

    Shell "control.exe", vbNormalFocus

就可以打开控制面板的主页,如图2-7所示。

图2-7 自动打开控制面板

如果要直接打开控制面板中特定的一项,需要添加命令参数。

    Shell "control.exe appwiz.cpl", vbNormalFocus

上述代码表示直接打开控制面板中的“卸载程序”,命令参数appwiz.cpl是Application Wizard的缩写,意思是程序向导,如图2-8所示。

因此,只需要把Shell函数命令参数中的cpl文件更改一下,就可以打开控制面板中的其他各项。常用的有如下这些。

卸载程序:Shell "Control.exe " & "appwiz.cpl", vbMaximizedFocus

显示属性:Shell "Control.exe " & "desk.cpl", vbMaximizedFocus

浏览器属性:Shell "Control.exe " & "inetcpl.cpl", vbMaximizedFocus

图2-8 控制面板中的项目

区域和语言:Shell "Control.exe " & "intl.cpl", vbMaximizedFocus

声音和音频:Shell "Control.exe " & "mmsys.cpl", vbMaximizedFocus

网络连接:Shell "Control.exe " & "ncpa.cpl", vbMaximizedFocus

用户账户:Shell "Control.exe " & "nusrmgr.cpl", vbMaximizedFocus

电源选项:Shell "Control.exe " & "powercfg.cpl", vbMaximizedFocus

计算机属性:Shell "Control.exe " & "sysdm.cpl", vbMaximizedFocus

日期和时间:Shell "Control.exe " & "timedate.cpl", vbMaximizedFocus

安全中心:Shell "Control.exe " & "wscui.cpl", vbMaximizedFocus

2.1.6 打开资源管理器

资源管理器是以树状结构显示计算机中的文件系统的窗口,在Windows XP系统中双击“我的电脑”,在Windows 7系统中双击“计算机”,或者按下快捷键【Windows + E】,都可以打开资源管理器。

资源管理器的主文件为explorer.exe。

在实际编程中,经常用到自动打开某个文件夹,或者自动选中文件、文件夹的操作。如下面的代码。

    Shell "explorer.exe C:\temp\成绩表.pdf", vbNormalFocus

上述代码表示用系统默认程序打开指定的PDF文件,这相当于用鼠标双击了计算机中的“成绩表.pdf”文件,如果计算机中安装的是Adobe Acrobat,那么用该软件打开PDF文件。对于其他扩展名的文件也是如此,因此这个代码实用价值很高。

    Shell "explorer.exe C:\temp\", vbNormalFocus

上述代码表示打开temp文件夹,相当于用鼠标双击了该文件夹,进入该文件夹内部。

针对以上两句代码,如果添加参数“/select,”,那么不是打开文件或文件夹,而是选中。

    Shell "explorer.exe /select, C:\temp\成绩表.pdf", vbNormalFocus

上述代码表示显示资源管理器,并自动选中文件,如图2-9所示。

图2-9 在资源管理器中自动选中文件

    Shell "explorer.exe /select, C:\temp\", vbNormalFocus

上述代码表示自动选中temp文件夹,而不打开。

此外,利用explorer还可以自动打开指定url的网页。

    Shell "explorer.exe http://vba.mahoupao.net/forum.php", vbNormalFocus

上述代码会用计算机默认的网页浏览器打开一个论坛。

2.1.7 注册ocx文件和dll文件

ocx文件是指对象类别扩充组件。计算机中扩展名为.ocx的文件,不能直接双击执行,一般要把ocx控件插入到窗体中使用。例如在VBA的UserForm中,可以插入CommonDialog、DataGrid这些ActiveX控件,如图2-10所示。

图2-10 用户窗体中使用ocx控件

这些ocx文件大多数是微软公司开发的性能优良的控件,插入到窗体后,可以让编程更加简单,界面更加专业、美观。

此外,用户根据需要也可以自己制作专用的ocx控件。但是,ocx的移植,也就是说从一台计算机把ocx控件复制到另一台计算机,是不能直接使用的,必须在目标计算机上注册。因为未注册的ocx控件不会显示在“附加控件”对话框中,也就无法装入窗体。所谓注册,就是为该控件分配一个GUID,并保存于注册表中。反注册,就是从注册表中移除该控件的有关信息,使其无法在编程环境中访问。

注册和反注册文件,可以用Shell函数调用System32文件夹下的regsvr32.exe文件实现。

注册ocx文件的语法格式如下。

    Shell "regsvr32.exe ocx文件的路径"

下面尝试注册一款笔者开发的TreeviewExplorer.ocx控件,并在VBA中运行。

    Shell "regsvr32.exe E:\TreeviewExplorer\TreeviewExplorer.ocx"

执行上述过程会弹出注册成功的提示框,如图2-11所示。

如果要屏蔽注册成功的对话框,可以在regsvr32.exe后面添加/s参数,也就是改为如下形式。

图2-11 成功注册ocx控件

         Shell "regsvr32.exe /s E:\TreeviewExplorer\
    TreeviewExplorer.ocx"

那么,注册成功后,到底引起了哪些变化呢?

运行Shell "regedt32.exe" ,vbNormalFocus,自动打开注册表编辑器,在查找对话框中输入关键字,可以快速找到该控件的注册信息,如图2-12所示。

图2-12 ocx控件的信息写入注册表

可以看出注册信息位于:HKEY_CLASSES_ROOT\TreeviewExplorer.UC。

注册成功的控件,更重要的变化在于能够在各种窗体中使用该控件。下面是VBA的用户窗体中的“附加控件”对话框,在该项前面勾选,即可把自定义控件添加到“控件工具箱”中,从而可以插入窗体中,如图2-13所示。

图2-13 使用自定义控件

控件的反注册也很简单,只需要在上述注册的代码中插入一个/u参数,就是反注册。

    Shell "regsvr32.exe /u E:\TreeviewExplorer\TreeviewExplorer.ocx"

如果不弹出提示对话框,更改为如下代码。

    Shell "regsvr32.exe /s /u E:\TreeviewExplorer\TreeviewExplorer.ocx"

控件被反注册以后,注册表中会删除该控件的信息,在“附加控件”对话框中也找不到该控件。

dll文件,即动态链接库文件(Dynamic Link Library),也可以称为类库,与ocx文件一样,不能直接运行。dll文件中主要封装了一些函数和代码。Office的COM加载项就是一种扩展名为.dll的文件。

在VBA编程中,可以向VBA工程的引用中添加dll文件,从而使用dll文件中的功能和函数。

dll文件的注册、反注册方法和ocx的代码是一模一样的,只需要把ocx控件的路径更改为dll文件的路径即可。

OfficeDll是笔者开发的一款功能丰富的动态链接库,把该文件复制到目标计算机后,运行下面的代码进行注册。

    Shell "regsvr32.exe E:\OfficeDll\OfficeDll.dll"

注册成功后,单击VBA编辑器的菜单【工具/引用】,在“可使用的引用”列表中可以看到该动态链接库,如图2-14所示。

添加引用成功后,编写代码,可以看到该类库的成员列表,如图2-15所示。

注意,如果同一台计算机中存在多个同名ocx或dll文件,以最近注册的为准。

注册和反注册的方法总结如下。

Shell "regsvr32.exe文件"表示注册一个文件,并弹出提示对话框。

Shell "regsvr32.exe /s文件"表示注册一个文件,不弹出提示对话框。

Shell "regsvr32.exe /u文件"表示取消注册一个文件,并弹出提示对话框。

Shell "regsvr32.exe /s /u文件"表示取消注册一个文件,不弹出提示对话框。

图2-14 动态链接库的注册和使用

图2-15 VBA中使用动态链接库

关于ocx控件和动态链接库的开发和应用,本书暂不讨论。

2.1.8 结束进程

在Windows系统中按下快捷键【Ctrl+Shift+Esc】可以弹出Windows任务管理器,通过Windows任务管理器可以强行结束一个进程,如图2-16所示。

图2-16 Windows任务管理器

假定现在屏幕上打开了计算器,进程列表中一定有calc.exe进程,然后在VBA中运行如下代码。

    Shell "taskkill /f /im calc.exe", vbHide

可以看到计算器自动被终止。taskkill.exe也是System32文件夹下的一个可执行文件,专门用来终止进程。

2.1.9 自动关机

shutdown.exe是一个用于关机的系统文件,用Shell函数调用shutdown可以实现自动关机、取消关机、自动重启等操作。

运行上述Test1或Test2,会弹出提示框,如图2-17所示。

如果又不想关机或重启,那么需要运行Test3取消计划。计划被取消时,屏幕右下角弹出提示,如图2-18所示。

图2-17 计划关机

图2-18 取消计划

在日常办公中,一台计算机经常会连续工作好几天,此时,可以用Excel VBA设置一个计划,在未来某一天的某一时刻定时关机。

Application对象的OnTime方法可以在某一时刻准时执行某个过程,因此,运行下面的MySchedule过程,计算机不会发生任何变化,但是到了指定的时刻,会准时调用AutoShutdown过程,从而实现自动关机。

注意 运行MySchedule过程后,Excel要一直保持打开状态,如果退出Excel,计划无效。

以上内容的源代码文件为“实例文档06.xlsm”。