![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=1739239151-4OnzFqbSbcm663K695HBUVexEG2hw0hF-0-83be0f7b0f5230e5aa2c6cb19b52785f)
图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=1739239151-lObIPXyzQBCbKen2v4HSMRKsG8JxbXyY-0-c5ea53d66087046be3c4a04833feac1a)
以上过程中,fd是一个文件夹对象变量,本例用它来指代C:\temp这个文件夹。
运行上述程序,在立即窗口打印文件夹的路径、子文件夹和文件的总数,运行结果如图3-24所示。
上面的实例中,文件夹的规定是在namespace的参数中直接指定的,如果要让用户选择一个文件夹,则可以使用BrowseForFolder函数。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/030.jpg?sign=1739239151-PrItxQEEyXC538ZD8bVLgv2bM7hwKJMV-0-b4447548d853ad8203173d6149ea72a0)
图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=1739239151-iazemcMVfZMarsQpswkBAaOc8N09QGTh-0-e2463ec086f3542f29e496dcf32aa6e9)
代码分析:BrowseForFolder函数弹出浏览文件夹对话框,用户选择文件夹并单击“确定”按钮,fd会返回一个Folder对象,如果单击“取消”按钮,那么fd就是Nothing。
运行上述程序,自动弹出一个选择文件夹的对话框,如图3-25所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/033.jpg?sign=1739239151-VGbcApOpmkx2mr93CPj8G7GLfNspYsfX-0-5beb1efb9087a7bc23ce617a3f58e266)
图3-25 浏览文件夹对话框
3.2.4 遍历文件夹中的内容
Shell32中的FolderItem对象是指包含在文件夹中的文件或子文件夹。
假设D:\Download下的文件内容如图3-26所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/031.jpg?sign=1739239151-3Ob9DjD6RmNiRje7buhq1cvzXQal44Do-0-e9e1c1adf9cd0f296f544019e59c51c2)
图3-26 文件夹中的内容
文件夹中有2个子文件、5个文件。
下面的代码遍历Download文件夹下所有项目的路径、类型。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/032.jpg?sign=1739239151-KoT8Aiy2hHrRkFuJAe4hMgCXIVTh2AZQ-0-118d4b8d86e26506fd6494963c99fcdb)
运行上述程序,立即窗口打印出文件夹中的所有内容,如图3-27所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/034.jpg?sign=1739239151-nXAKLcgDBgCQ3tpiKjTqZDnSmkS0HiS6-0-60c6f11c5a9fc07096653421b526ef55)
图3-27 遍历文件夹中的内容
可以看出,子文件夹的Type是“文件夹”。
如果要遍历所有子文件夹中的所有内容,需要用下面的递归函数。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/036.jpg?sign=1739239151-Aolz4YfMZGFeuRxbUfW53M0YFBtF7PaT-0-1a3f846793004febcc82813ee9e16305)
运行上面的“遍历所有内容”这个过程,立即窗口打印出Download文件夹下的所有内容(包含递归文件夹),如图3-28所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/035.jpg?sign=1739239151-tBCrR3Cq5WXnNl2LBvDTc0rE5i5szoEi-0-c6a004c11e4dac1b589c25aaf77ab678)
图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=1739239151-Yz7cLoiQ2rX7YUMzaUwDJdAc3kv2CgU0-0-313d3245965b8a1a969e2b4c71ee760e)
图3-29 压缩包
运行下面的过程,可以把压缩包中的所有文件、路径列举出来。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/039.jpg?sign=1739239151-Vw9t0DH7BbE8PIcF9H5XWZnB5LN1enyh-0-41c1c46451e30663fd6dbee3285082bc)
其中,Recursion是前面讲过的用于递归遍历的函数。
运行上述程序,立即窗口打印出压缩包中的所有内容,如图3-30所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/038.jpg?sign=1739239151-sevsYGkOq1utdNgLdHTOqvWNtkxqHGB2-0-7cc680e039bdb6df7a498c2a84a1612b)
图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=1739239151-103oL1zAuWLrU6kvA025CTMhNKm0Gq1w-0-61528da9386b388087e8e65b2ffc0d62)
该幻灯片的内部文件比较多,因此只打印出一部分结果,如图3-31所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/040.jpg?sign=1739239151-ZUMN9SEICoNsRnYJV9k4s3lCkcMB3p00-0-a38024027649f1febdb7898384a63ade)
图3-31 递归遍历PPT文件中的所有内容
用WinRAR打开该幻灯片,如图3-32所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/041.jpg?sign=1739239151-4qj5QSvqEt2X0AkNSFmWGQQF3bxXzSuK-0-93b6897686f2e20f3b641375be8b0ae9)
图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=1739239151-MNkk56ki2xmlJJdgGt1nEVJQHO4zoxZG-0-fa16cab0635afe90437f4ead97438f2e)
代码分析: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=1739239151-mUr7YYCweOLII5nc6geqPjVD0vgAZ1Pe-0-9edf2b180ca0a210d1520cbb5e28af42)
图3-33 手工新建一个空白压缩包
路径C:\temp\datas下有一百多个文本文件,如图3-34所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/045.jpg?sign=1739239151-tBYtjaw7t3IZOuLpvM8kyadPgo0TY4Ib-0-eaf87d7a40c21fb8c262c62f5650ebc1)
图3-34 文件夹中包含多个文本文件
下面的程序把datas文件夹连同其所有子文件压缩到blank.zip中。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/047.jpg?sign=1739239151-lgDqhcF4KcOthOShEHdQvPuwNWrgSRKK-0-588126280f8de66d67ae97e30e74ba68)
执行程序后,使WinRAR软件查看压缩包blank.zip中的内容,如图3-35所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/046.jpg?sign=1739239151-Mxl8zNlWeJbhwovqJgBHaMmAeOUJhdso-0-f287bf3533b5d46804cc7468222fe1b5)
图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=1739239151-akXhrFOEQFWnCvS4F5BFkhAemMmdUt7Z-0-9542dd570d36a794b3f1d66645f4dad9)
代码分析: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=1739239151-TuodZGH0QtKprZnGH2My7QYLMX9JeJou-0-26f81c197eafa3d4aa2a5b9ea6fbdeee)
运行上述过程,会在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=1739239151-migZsQPUVP8GdP8314dc5epFHHId7PCt-0-d2871883b6cdb77616ea6db99e9d173a)
图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=1739239151-RfRSvoXtNOgtDP1zoYTgZmCIIAu8Oiht-0-aec32e15b584890225a4af0ff61ac50d)
代码分析:首先创建一个空白.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=1739239151-Od3vDQRD3dFhB4ZwkMjTW0lxUXQTFhgr-0-32e1cf6ce8df9071c8c732fe2d818bad)
图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=1739239151-Zan63kRi8Lj8hp48GyDKVKgT2b9yavaA-0-b6b0f4edb060132011ecaf49be07df12)
图3-38 Excel文件的内部构造
双击customUI14.xml,可以看到XML代码,如图3-39所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/054.jpg?sign=1739239151-JQ1WiAch0OlZ2tXQzBnrj2N3fAtmAtmD-0-c1138138aae0a340899e89277e762791)
图3-39 customUI代码
如果在Excel中打开该文件,如图3-40所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/055.jpg?sign=1739239151-67Fv5pGgjsfee5jqm11VRQd5GNQWEG14-0-54fb8afdd97b9cab61c99063376c58a5)
图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=1739239151-SAQ1prXJz1aiMfQMrGHSgRGFJqS8IlpK-0-43ec9ec1ff30c41c7545e58f80c57b95)
代码分析:为了确保压缩操作完成后再重命名,在重命名代码之前加一个Stop语句。
运行上述代码后,在Excel 2013中打开example02.xlsx文件,会看到功能区中的按钮标题和图标都发生了变化,如图3-41所示。
![](https://epubservercos.yuewen.com/F986E7/15056702504171006/epubprivate/OEBPS/Images/057.jpg?sign=1739239151-vCoQG0pBX9L4Dpeoy6If0HL5ZKDGW1ub-0-87a3ef21ac7d33cd54aa2bece1f63e4b)
图3-41 使用Shell32修改Excel文件的customUI部分
以上这部分知识是自定义功能区的铺垫,同时表明可以从压缩文件的角度去研究和处理Office文档。
以上内容的源代码文件为“实例文档11.xlsm”。