![Office VBA开发经典:中级进阶卷](https://wfqqreader-1252317822.image.myqcloud.com/cover/711/26542711/b_26542711.jpg)
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所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/026.jpg?sign=1739267378-Yu28OmRdvXesDwYnthYpIw4ixukac6LC-0-7bc14a6b3ca8180730d16dafc1e6db20)
图3-23 添加外部引用
代码中使用Dim ShellApp As New Shell32.Shell声明了一个新的Shell32的应用程序对象。
后期创建对象的方法如下。
Set ShellApp = CreateObject("Shell.Application")
3.2.2 使用namespace返回文件夹
FSO对象中,使用GetFolder返回一个Folder对象,而Shell32对象中,使用namespace返回一个Folder对象。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/029.jpg?sign=1739267378-4nV4Egebd4sPLzVBGFaZM7ap500WdG4x-0-7e90992e41ab9acfb28dff0bc127e191)
以上过程中,fd是一个文件夹对象变量,本例用它来指代C:\temp这个文件夹。
运行上述程序,在立即窗口打印文件夹的路径、子文件夹和文件的总数,运行结果如图3-24所示。
上面的实例中,文件夹的规定是在namespace的参数中直接指定的,如果要让用户选择一个文件夹,则可以使用BrowseForFolder函数。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/030.jpg?sign=1739267378-JWDJwdk8qzRhDBRV1DuvD8GkgH5lXuq1-0-6f5dad5fc99efe8e56aa47f34456e6f2)
图3-24 运行结果
3.2.3 文件夹选择对话框
BrowseForFolder函数的语法如下。
BrowseForFolder(Hwnd, Title, Options, RootFoler)
返回一个Folder对象。各参数说明如下。
Hwnd:对话框的所属句柄,长整型。设置为0表示在桌面弹出对话框。
Title:对话框显示的提示语。
Options:对话框样式设定参数,十六进制数。
RootFolder:文件夹选择对话框的起始路径,也可以是特殊的文件夹常量。
下面的代码在Excel上面弹出一个文件夹选择对话框,起始目录设置为宏所在工作簿的路径。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/028.jpg?sign=1739267378-MeV8DwoZOUFuCDr1R4wwVibD6iaLsqDl-0-756f0c5d2f63ce90510bc8aaf13e8371)
代码分析:BrowseForFolder函数弹出浏览文件夹对话框,用户选择文件夹并单击“确定”按钮,fd会返回一个Folder对象,如果单击“取消”按钮,那么fd就是Nothing。
运行上述程序,自动弹出一个选择文件夹的对话框,如图3-25所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/033.jpg?sign=1739267378-MArEhkqer5GLRI5RXK1g6TN9MlWfkr4J-0-4892f0f5ca7410667ea39843d2e493c0)
图3-25 浏览文件夹对话框
3.2.4 遍历文件夹中的内容
Shell32中的FolderItem对象是指包含在文件夹中的文件或子文件夹。
假设D:\Download下的文件内容如图3-26所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/031.jpg?sign=1739267378-3VcFhSuXanfGgZKrcAyHkYQfBmiQtsnr-0-39488dd5e96e879b1085623164ef9a46)
图3-26 文件夹中的内容
文件夹中有2个子文件、5个文件。
下面的代码遍历Download文件夹下所有项目的路径、类型。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/032.jpg?sign=1739267378-v5EJVFzldUcKMq58OgUGAyoHCrzp5F6G-0-ee1120f0c7e9dcfbe0cf313171c388ea)
运行上述程序,立即窗口打印出文件夹中的所有内容,如图3-27所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/034.jpg?sign=1739267378-bkc21mWY8z1R6KdNT5QQxa8PLJ5Puq3m-0-9a7abb663de058c56108e333153ca884)
图3-27 遍历文件夹中的内容
可以看出,子文件夹的Type是“文件夹”。
如果要遍历所有子文件夹中的所有内容,需要用下面的递归函数。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/036.jpg?sign=1739267378-9cb03omZFzat3JFAWAlwXClMLWEG1QFC-0-4154dc27049200c3d72cd190802c465d)
运行上面的“遍历所有内容”这个过程,立即窗口打印出Download文件夹下的所有内容(包含递归文件夹),如图3-28所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/035.jpg?sign=1739267378-YI2Wwl7zuc8Hal4Ib4THPeve1P4dgIk5-0-61839ddcd738dc366ccc80362bce89c6)
图3-28 递归遍历文件夹中的所有内容
由于namespace不只限于文件夹,还能把.zip压缩包当成一个文件夹,因此下面讲述遍历.zip压缩包中的内容。
3.2.5 遍历.zip压缩包中的内容
假设D:盘下有一个“东北三省.zip”的压缩包,其内部文件结构如图3-29所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/037.jpg?sign=1739267378-43RszNdWNrSR7OoCWJOYz8xb4dDwEP08-0-d62900a16852c82e413af1d9a8ec0946)
图3-29 压缩包
运行下面的过程,可以把压缩包中的所有文件、路径列举出来。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/039.jpg?sign=1739267378-gu33idXzYwRQ3MyNfUCSSd8Kb5FZAp9I-0-e7f4af574bb7041e441d8c876ce9ff0d)
其中,Recursion是前面讲过的用于递归遍历的函数。
运行上述程序,立即窗口打印出压缩包中的所有内容,如图3-30所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/038.jpg?sign=1739267378-uW1Oal3JEzExEK9YE9YCFhCC5MGJlQBj-0-4d0296dfd680e1599e91f0406f66a240)
图3-30 递归遍历压缩包中的所有内容
3.2.6 遍历Office文档中的内容
Office文档的扩展名不是.zip,理论上用Shell32无法遍历其内部内容,然而,可以先把Office文档更改扩展名为.zip,等遍历完后,再撤销重命名即可。
假设文件夹中有一个幻灯片文件Presentation01.pptx。下面用Shell32遍历该文件的所有内部文件。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/042.jpg?sign=1739267378-R3BOKuU1Svwp5HKOiSenP3iWHRvCgHbL-0-d781aaf18887dddd3a45dc9587cfcc86)
该幻灯片的内部文件比较多,因此只打印出一部分结果,如图3-31所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/040.jpg?sign=1739267378-SbD8rbXHp8godZX9AGa8LmHu4MY6ctgJ-0-2e0d12b7b1b6df35049efa07b7da36b2)
图3-31 递归遍历PPT文件中的所有内容
用WinRAR打开该幻灯片,如图3-32所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/041.jpg?sign=1739267378-5u5nS64g13VazUHU6xwHUEaLsXZhivkQ-0-ecbae0c9d0e659cfd6407c32b35fa081)
图3-32 使用压缩包查看PPT文件的构造
3.2.7 CopyHere方法
Shell32中的Folder对象有CopyHere方法和MoveHere方法,作用是把其他地方的文件(夹)复制或移动到Folder中。
下面的程序把dist路径下的所有文件、子文件夹复制到temp路径下。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/044.jpg?sign=1739267378-PFcLhUPAkRld5CACbEtiqDCJNfkXZgI9-0-2bdea1437c9d3a7cbfa4e38e6522314e)
代码分析: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所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/043.jpg?sign=1739267378-QgDJuIOtaVvs7wR7gcbV2Sa5V01aiBfC-0-cf865c86d60f78f29aed7f4321d789df)
图3-33 手工新建一个空白压缩包
路径C:\temp\datas下有一百多个文本文件,如图3-34所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/045.jpg?sign=1739267378-uQa4IFTLFK8RPB8PD6Dm3j2SUt9p7XLT-0-4990d7410e9f8188a71044a5f88e63de)
图3-34 文件夹中包含多个文本文件
下面的程序把datas文件夹连同其所有子文件压缩到blank.zip中。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/047.jpg?sign=1739267378-zZvntOywHc3uyRt0QDdl6i3w9gGvJ7En-0-bdce3df236adeca136dcce62620ae5db)
执行程序后,使WinRAR软件查看压缩包blank.zip中的内容,如图3-35所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/046.jpg?sign=1739267378-lSOQOKZFu44507dwvaGy63d0geX0ccKy-0-b877a4b09bb5961e8f293e291fd23a1b)
图3-35 自动压缩文件夹
需要注意的是,如果最后一行代码修改为如下形式。
fd.MoveHere data.Items
执行的效果是,datas下面所有的文本文件都直接压缩进去,而没有datas这个文件夹!
如果修改为fd.MoveHere data.Items.Item("39.txt"),则只把一个文本文件移动到压缩包的根目录下。
反过来,CopyHere方法、MoveHere方法也可以从压缩包中释放内容到文件夹中。
下面的程序把上述有内容的Blank.zip中的datas\40.txt文件移动到C:\lib路径下。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/049.jpg?sign=1739267378-rK4WF7TJUCYghobo5LmTAIjQeGVXqS5y-0-5747fae7fc15ede672dd7e2f129cbb68)
代码分析:namespace允许在压缩包路径后追加子路径,因此对象变量fd表示的是压缩包中的datas文件夹。
LIB.MoveHere fd.Items.item("40.txt")把fd中的项目移动到LIB中,一定不要搞错方向。
另外,除了上述讲过的用鼠标右键新建.zip压缩包之外,也可以用代码自动在路径下产生一个空白的.zip压缩包。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/050.jpg?sign=1739267378-Vhbu2uzWSOT30G6KafULOkUS28CnInmp-0-e899aa37fa4e1854a9eb3bb4706c37fa)
运行上述过程,会在C:盘下产生Container.zip,这个压缩包里没有任何内容。
3.2.9 处理文件覆盖
使用CopyHere方法、MoveHere方法进行文件复制、移动时,如果目的地已经存在名称相同的文件,执行程序过程中会弹出是否复制、替换的对话框,如图3-36所示。
如果要默认强制替换已存在文件,屏蔽该对话框,可以在方法之后加一个vOptions参数,并设置为16。例如:
fd.CopyHere vItem:=data.Items.item ("aaa\使 用说明.txt"), vOptions:=16
这样,运行到这行代码时,即使存在同名文件,也强制替换。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/048.jpg?sign=1739267378-emPqTRWxWjSpGAWwNl7Ug26PMJTHs3TO-0-07a038446edfc1e770c25b8f35f5af28)
图3-36 存在同名文件的询问对话框
3.2.10 处理异步问题
使用CopyHere、MoveHere方法移动内容时,不管移动操作是否已完成,VBA代码都会继续向下执行。如果移动的文件容量越大,异步问题越明显。对于一些要求苛刻的程序任务,有必要让程序同步压缩进度。
为了能够让VBA识别到文件移动的进度,需要在CopyHere、MoveHere方法之后加一个Do…Loop循环,循环跳出的条件是目标压缩包中的文件总数达到一个指标。
假设E:\Joker路径下有52张扑克牌图片,使用下面的程序把这52个.jpg格式的图片全部移动到新建的压缩包C:\temp\Package.zip中。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/052.jpg?sign=1739267378-jYeyT4GrSs4MI5P1fetgXOyxxWbxEzGe-0-0afec210f1a6ed793b2243cfd5308838)
代码分析:首先创建一个空白.zip压缩包,该压缩包中项目个数为0。其次使用对象变量fd来指代该压缩包,然后把Joker文件夹下的所有文件移动到压缩包中,移动过程中fd.Items.Count一定小于52,因此根据这个特征可以让VBA代码阻塞在Do循环内。最后,压缩操作结束,跳出Do循环,弹出对话框,如图3-37所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/051.jpg?sign=1739267378-6tLHrtL7NJTnBfFK0FLT76ThhYiZ1qEo-0-ae39a3d2770dde324fc6e0fdcf2ff282)
图3-37 压缩操作完成才弹出对话框
3.2.11 修改Office文档功能区
Office 2007以上版本创建的文档允许自定义功能区。自定义功能区的XML代码存储在文档中CustomUI文件夹下,文件名一般为customUI14.xml。本书源代码中的example02.xlsx文件用WinRAR打开后,可以看到自定义功能区的部分,如图3-38所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/053.jpg?sign=1739267378-pJUDfp7MRS5WSnWJ0q5Rv0VdtCDKJfrG-0-85b64ea0332417b44f7a6bc142f35130)
图3-38 Excel文件的内部构造
双击customUI14.xml,可以看到XML代码,如图3-39所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/054.jpg?sign=1739267378-sEILc1AqD6GiWNsbRfFRbDBANzz82qY5-0-e494643ee48ac8af277cb991426f5fc9)
图3-39 customUI代码
如果在Excel中打开该文件,如图3-40所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/055.jpg?sign=1739267378-eDSARhgydUhk8Fkic1Y5Ampfkzb2IPYl-0-b900788f451f27fe6d6de0b59cf77749)
图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工作簿。具体代码如下。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/056.jpg?sign=1739267378-RDE0W0s7r24yR07Kq50lEqMsj0SyX0g1-0-e35dc3b0e6086dec51ff367f99c70d5c)
代码分析:为了确保压缩操作完成后再重命名,在重命名代码之前加一个Stop语句。
运行上述代码后,在Excel 2013中打开example02.xlsx文件,会看到功能区中的按钮标题和图标都发生了变化,如图3-41所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/057.jpg?sign=1739267378-N3qaVs8M4A60SlOuHeRJLePGzfydpbkN-0-45c6c95fa2dba805645baebb732a514f)
图3-41 使用Shell32修改Excel文件的customUI部分
以上这部分知识是自定义功能区的铺垫,同时表明可以从压缩文件的角度去研究和处理Office文档。
以上内容的源代码文件为“实例文档11.xlsm”。