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

3.2 使用Shell32对象

Shell32对象中的Folder对象与FSO文件系统对象中的Folder用法非常相似,都能代表计算机中的一个文件夹。但是,Shell32.Folder还可以表示一个扩展名为.zip的压缩文件,而FSO不能操作到压缩文件的内部。

因此,本节介绍一下用Shell32对象处理计算机中的文件夹和文件以及.zip压缩包中的内容。

3.2.1 引入Shell32对象

前期绑定方式:在VBA工程中添加外部引用“Microsoft Shell Controls And Automation”,如图3-23所示。

图3-23 添加外部引用

代码中使用Dim ShellApp As New Shell32.Shell声明了一个新的Shell32的应用程序对象。

后期创建对象的方法如下。

    Set ShellApp = CreateObject("Shell.Application")

3.2.2 使用namespace返回文件夹

FSO对象中,使用GetFolder返回一个Folder对象,而Shell32对象中,使用namespace返回一个Folder对象。

以上过程中,fd是一个文件夹对象变量,本例用它来指代C:\temp这个文件夹。

运行上述程序,在立即窗口打印文件夹的路径、子文件夹和文件的总数,运行结果如图3-24所示。

上面的实例中,文件夹的规定是在namespace的参数中直接指定的,如果要让用户选择一个文件夹,则可以使用BrowseForFolder函数。

图3-24 运行结果

3.2.3 文件夹选择对话框

BrowseForFolder函数的语法如下。

    BrowseForFolder(Hwnd, Title, Options, RootFoler)

返回一个Folder对象。各参数说明如下。

 Hwnd:对话框的所属句柄,长整型。设置为0表示在桌面弹出对话框。

 Title:对话框显示的提示语。

 Options:对话框样式设定参数,十六进制数。

 RootFolder:文件夹选择对话框的起始路径,也可以是特殊的文件夹常量。

下面的代码在Excel上面弹出一个文件夹选择对话框,起始目录设置为宏所在工作簿的路径。

代码分析:BrowseForFolder函数弹出浏览文件夹对话框,用户选择文件夹并单击“确定”按钮,fd会返回一个Folder对象,如果单击“取消”按钮,那么fd就是Nothing。

运行上述程序,自动弹出一个选择文件夹的对话框,如图3-25所示。

图3-25 浏览文件夹对话框

3.2.4 遍历文件夹中的内容

Shell32中的FolderItem对象是指包含在文件夹中的文件或子文件夹。

假设D:\Download下的文件内容如图3-26所示。

图3-26 文件夹中的内容

文件夹中有2个子文件、5个文件。

下面的代码遍历Download文件夹下所有项目的路径、类型。

运行上述程序,立即窗口打印出文件夹中的所有内容,如图3-27所示。

图3-27 遍历文件夹中的内容

可以看出,子文件夹的Type是“文件夹”。

如果要遍历所有子文件夹中的所有内容,需要用下面的递归函数。

运行上面的“遍历所有内容”这个过程,立即窗口打印出Download文件夹下的所有内容(包含递归文件夹),如图3-28所示。

图3-28 递归遍历文件夹中的所有内容

由于namespace不只限于文件夹,还能把.zip压缩包当成一个文件夹,因此下面讲述遍历.zip压缩包中的内容。

3.2.5 遍历.zip压缩包中的内容

假设D:盘下有一个“东北三省.zip”的压缩包,其内部文件结构如图3-29所示。

图3-29 压缩包

运行下面的过程,可以把压缩包中的所有文件、路径列举出来。

其中,Recursion是前面讲过的用于递归遍历的函数。

运行上述程序,立即窗口打印出压缩包中的所有内容,如图3-30所示。

图3-30 递归遍历压缩包中的所有内容

3.2.6 遍历Office文档中的内容

Office文档的扩展名不是.zip,理论上用Shell32无法遍历其内部内容,然而,可以先把Office文档更改扩展名为.zip,等遍历完后,再撤销重命名即可。

假设文件夹中有一个幻灯片文件Presentation01.pptx。下面用Shell32遍历该文件的所有内部文件。

该幻灯片的内部文件比较多,因此只打印出一部分结果,如图3-31所示。

图3-31 递归遍历PPT文件中的所有内容

用WinRAR打开该幻灯片,如图3-32所示。

图3-32 使用压缩包查看PPT文件的构造

3.2.7 CopyHere方法

Shell32中的Folder对象有CopyHere方法和MoveHere方法,作用是把其他地方的文件(夹)复制或移动到Folder中。

下面的程序把dist路径下的所有文件、子文件夹复制到temp路径下。

代码分析:data.Items表示该命名空间(路径)下的所有项目,包括文件、子文件夹。

如果要复制个别的项目,需要用Item属性来约定。例如把上面最后一行代码修改为如下形式。

    fd.CopyHere data.Items.item("aaa\使用说明.txt")

上述代码的含义是把C:\dist\aaa\使用说明.txt这个文件直接复制到temp文件夹下。其中,aaa是dist路径下的一个文件夹。

3.2.8 MoveHere方法

MoveHere方法与Copy方法几乎是一样的语法,唯一不同的是,使用MoveHere方法是把文件或文件夹移动到Folder中,也就相当于文件的移动、剪切。

下面举一个把文件夹中的文件移动到压缩包中的实例。首先在桌面或者任意文件夹中新建一个“WinRAR ZIP archive”空白压缩包,并把该压缩包重命名为blank.zip,如图3-33所示。

图3-33 手工新建一个空白压缩包

路径C:\temp\datas下有一百多个文本文件,如图3-34所示。

图3-34 文件夹中包含多个文本文件

下面的程序把datas文件夹连同其所有子文件压缩到blank.zip中。

执行程序后,使WinRAR软件查看压缩包blank.zip中的内容,如图3-35所示。

图3-35 自动压缩文件夹

需要注意的是,如果最后一行代码修改为如下形式。

    fd.MoveHere data.Items

执行的效果是,datas下面所有的文本文件都直接压缩进去,而没有datas这个文件夹!

如果修改为fd.MoveHere data.Items.Item("39.txt"),则只把一个文本文件移动到压缩包的根目录下。

反过来,CopyHere方法、MoveHere方法也可以从压缩包中释放内容到文件夹中。

下面的程序把上述有内容的Blank.zip中的datas\40.txt文件移动到C:\lib路径下。

代码分析:namespace允许在压缩包路径后追加子路径,因此对象变量fd表示的是压缩包中的datas文件夹。

LIB.MoveHere fd.Items.item("40.txt")把fd中的项目移动到LIB中,一定不要搞错方向。

另外,除了上述讲过的用鼠标右键新建.zip压缩包之外,也可以用代码自动在路径下产生一个空白的.zip压缩包。

运行上述过程,会在C:盘下产生Container.zip,这个压缩包里没有任何内容。

3.2.9 处理文件覆盖

使用CopyHere方法、MoveHere方法进行文件复制、移动时,如果目的地已经存在名称相同的文件,执行程序过程中会弹出是否复制、替换的对话框,如图3-36所示。

如果要默认强制替换已存在文件,屏蔽该对话框,可以在方法之后加一个vOptions参数,并设置为16。例如:

         fd.CopyHere vItem:=data.Items.item ("aaa\使
    用说明.txt"), vOptions:=16

这样,运行到这行代码时,即使存在同名文件,也强制替换。

图3-36 存在同名文件的询问对话框

3.2.10 处理异步问题

使用CopyHere、MoveHere方法移动内容时,不管移动操作是否已完成,VBA代码都会继续向下执行。如果移动的文件容量越大,异步问题越明显。对于一些要求苛刻的程序任务,有必要让程序同步压缩进度。

为了能够让VBA识别到文件移动的进度,需要在CopyHere、MoveHere方法之后加一个Do…Loop循环,循环跳出的条件是目标压缩包中的文件总数达到一个指标。

假设E:\Joker路径下有52张扑克牌图片,使用下面的程序把这52个.jpg格式的图片全部移动到新建的压缩包C:\temp\Package.zip中。

代码分析:首先创建一个空白.zip压缩包,该压缩包中项目个数为0。其次使用对象变量fd来指代该压缩包,然后把Joker文件夹下的所有文件移动到压缩包中,移动过程中fd.Items.Count一定小于52,因此根据这个特征可以让VBA代码阻塞在Do循环内。最后,压缩操作结束,跳出Do循环,弹出对话框,如图3-37所示。

图3-37 压缩操作完成才弹出对话框

3.2.11 修改Office文档功能区

Office 2007以上版本创建的文档允许自定义功能区。自定义功能区的XML代码存储在文档中CustomUI文件夹下,文件名一般为customUI14.xml。本书源代码中的example02.xlsx文件用WinRAR打开后,可以看到自定义功能区的部分,如图3-38所示。

图3-38 Excel文件的内部构造

双击customUI14.xml,可以看到XML代码,如图3-39所示。

图3-39 customUI代码

如果在Excel中打开该文件,如图3-40所示。

图3-40 包含customUI部分的Excel文件

下面通过Shell32的MoveHere方法更改XMl代码,从而更改功能区的外观显示。

具体实现原理和步骤如下。

(1)在工作簿关闭的前提下,后面追加.zip扩展名,以便Shell32访问。

(2)使用MoveHere方法把customUI14.xml文件移动到某个文件夹中。

(3)使用XML外部对象自动修改文件夹中的customUI14.xml文件。

(4)用MoveHere方法把文件夹中的customUI14.xml文件逆向移动回到压缩包中的customUI文件夹下。

(5)删掉工作簿后面的.zip,恢复为正常的Excel工作簿。具体代码如下。

代码分析:为了确保压缩操作完成后再重命名,在重命名代码之前加一个Stop语句。

运行上述代码后,在Excel 2013中打开example02.xlsx文件,会看到功能区中的按钮标题和图标都发生了变化,如图3-41所示。

图3-41 使用Shell32修改Excel文件的customUI部分

以上这部分知识是自定义功能区的铺垫,同时表明可以从压缩文件的角度去研究和处理Office文档。

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