
Office 的各个组件有各个组件擅长的事情,其实我们可以让 Excel 去叫别的 Office 程序干活。这里做一些常用的示例,后面针对单个组件单独深入一点。
Excel 管数据和计算,Access 管查询和记录集,Word 管正式文档,PowerPoint 管汇报页,Outlook 管邮件提醒。说白了,Excel 可以像一个指挥台,把这些程序串起来跑。
这篇我只讲 Office 应用之间怎么配合。对象库引用、早期写法、后期写法这块,之前已经写过,这里只留一个判断标准:自己电脑长期用,引用对象库写代码更舒服;要发给别人跑,我更喜欢 CreateObject,少受 Office 版本影响。

1. Office 自动化到底在做什么
Office 里的每个程序,都把自己的功能暴露成一套对象。
Excel 里有工作簿、工作表、单元格。Word 里有文档、段落、书签。Access 里有数据库、查询、记录集。PowerPoint 里有演示文稿、幻灯片、形状。Outlook 里有邮件、收件人、附件。
VBA 要做的事也很直白:拿到对象,改属性,调方法,用完释放。
比如 Excel 代码里创建 Word:
1 2 3
Dim wd AsObjectSet wd = CreateObject("Word.Application")wd.Visible = True
在这段代码里,Excel 发命令,Word 被调用。你只要记住“谁发命令,谁拿对象”,概念就够用了。
再补两个常见函数:
1 2
Set wd = CreateObject("Word.Application") '创建一个 Word 应用对象Set doc = GetObject("D:\reports\客户回访纪要.docx") '拿到某个文件对应的对象
我一般这样理解:CreateObject 用来创建应用对象,GetObject 用来接上已有对象,或者拿到某个文件对应的对象。真实项目里,CreateObject 更常见。
2. 案例一:Excel 从 Access 拉出“未关闭工单”
这个案例我换成售后场景。Access 里有一个数据库 售后工单库.accdb,里面保存了一个查询 Q_本周未关闭工单。Excel 只负责把查询结果拉出来,放到“工单看板”工作表。

这类自动化的关键点有 4 个:打开数据库,打开查询,拿到记录集,把记录集一次写进 Excel。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Sub PullOpenTickets()Const DB_NAME AsString = "售后工单库.accdb"Const QUERY_NAME AsString = "Q_本周未关闭工单"Dim dbEngine AsObjectDim db AsObjectDim rs AsObjectDim ws As WorksheetDim i AsLongSet dbEngine = CreateObject("DAO.DBEngine.120")Set db = dbEngine.OpenDatabase(ThisWorkbook.Path & "\" & DB_NAME)Set rs = db.OpenRecordset(QUERY_NAME)Set ws = ThisWorkbook.Worksheets("工单看板")ws.Range("A1:Z5000").ClearContentsFor i = 0To rs.Fields.Count - 1ws.Cells(1, i + 1).Value = rs.Fields(i).NameNext iws.Range("A2").CopyFromRecordset rsrs.Closedb.CloseSet rs = NothingSet db = NothingSet dbEngine = NothingEndSub
我喜欢这个写法的原因很简单:Access 查询负责筛数据,Excel 负责展示。数据量上来以后,比手动复制粘贴省很多事。
这里有个小提醒:DAO.DBEngine.120 跟电脑上的 Access Database Engine 有关。你的电脑如果提示创建失败,就改成自己环境能用的版本,或者走对象库引用写法。
3. 案例二:Excel 触发 Access 宏,做月结准备
有些 Access 数据库里已经写好了宏。比如财务同事有一个库 财务月结.accdb,里面有一个宏 M_生成本月费用汇总。Excel 只要打开这个库,跑一下宏,再关掉 Access。
1 2 3 4 5 6 7 8 9 10 11
Sub RunMonthEndMacro()Dim ac AsObjectSet ac = CreateObject("Access.Application")ac.OpenCurrentDatabase ThisWorkbook.Path & "\财务月结.accdb"ac.DoCmd.RunMacro "M_生成本月费用汇总"ac.QuitSet ac = NothingEndSub
我觉得这种写法适合“Access 里已经有老系统,Excel 只是入口”的情况。按钮放在 Excel 里,使用者不用打开 Access,也能触发里面的固定动作。
4. 案例三:把 Excel 表格塞进 Word 模板
Word 自动化最常见的场景,是把 Excel 里的数据放进一份格式已经设计好的文档。我的习惯是先做 Word 模板,在要插数据的位置放一个书签,比如 VisitTable。

Excel 代码只做三件事:复制区域,打开 Word 模板,找到书签位置再粘贴。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Sub BuildVisitMemo()Dim wd AsObjectDim doc AsObjectDim rng AsObjectDim src As RangeDim templatePath AsStringDim outPath AsStringSet src = ThisWorkbook.Worksheets("回访明细").Range("A1:D12")templatePath = ThisWorkbook.Path & "\客户回访纪要模板.docx"outPath = ThisWorkbook.Path & "\输出\客户回访纪要_" & Format(Date, "yyyymmdd") & ".docx"Set wd = CreateObject("Word.Application")wd.Visible = TrueSet doc = wd.Documents.Open(templatePath)src.CopySet rng = doc.Bookmarks("VisitTable").Rangerng.PasteExcelTable False, False, Falsedoc.SaveAs2 outPathdoc.Closewd.QuitSet rng = NothingSet doc = NothingSet wd = NothingEndSub
这个案例里,Word 模板负责排版,Excel 负责准备数据。以后领导想改文档标题、页眉、说明文字,我只改 Word 模板,代码基本不用动。
对了,Word 书签被内容替换是常见现象。你要是希望书签一直保留,可以粘贴后再补一个同名书签。
5. 案例四:把 Excel 图表放进 PPT 汇报页
PowerPoint 自动化适合做周会页、月报页。比如 Excel 里有一个图表 Cht完成率,PPT 里有一个模板 周会模板.pptx,我想把图表贴到第 2 页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Sub BuildWeeklySlide()Dim pp AsObjectDim pres AsObjectDim sld AsObjectDim cht As ChartObjectDim outPath AsStringSet cht = ThisWorkbook.Worksheets("周报看板").ChartObjects("Cht完成率")cht.Chart.ChartArea.CopySet pp = CreateObject("PowerPoint.Application")pp.Visible = TrueSet pres = pp.Presentations.Open(ThisWorkbook.Path & "\周会模板.pptx")Set sld = pres.Slides(2)sld.Shapes.PasteSpecial 2'2 表示增强型图元文件outPath = ThisWorkbook.Path & "\输出\周会_" & Format(Date, "yyyymmdd") & ".pptx"pres.SaveAs outPathpres.Closepp.QuitSet sld = NothingSet pres = NothingSet pp = NothingEndSub
我更喜欢把图表贴成增强型图元文件。这样放大后边缘更清晰,也不容易因为源数据被误改影响展示。
6. 案例五:Outlook 只做提醒,别上来就群发
Outlook 自动化很有用,也要更谨慎。我一般用 Excel 生成邮件正文,让 Outlook 打开草稿窗口,人确认后再发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Sub ShowTodoMail()Dim ol AsObjectDim mail AsObjectDim ws As WorksheetDim bodyText AsStringSet ws = ThisWorkbook.Worksheets("今日待办")bodyText = "大家好,今天需要关注:" & vbCrLf & _"1. "&ws.Range("A2").Value&vbCrLf& _"2. "&ws.Range("A3").Value&vbCrLf& _"3. "&ws.Range("A4").ValueSet ol = CreateObject("Outlook.Application")Set mail = ol.CreateItem(0)With mail.To = "ops@example.com".Subject = "今日待办提醒 - " & Format(Date, "yyyy-mm-dd").Body = bodyText.DisplayEndWithSet mail = NothingSet ol = NothingEndSub
Display 会把邮件窗口打开。确认收件人、正文、附件都没问题,再手动点击发送。我不太建议一开始就用 .Send,发错群真的很麻烦。
如果要加附件,可以这样写:
1
mail.Attachments.Add ThisWorkbook.Path & "\输出\周会_" & Format(Date, "yyyymmdd") & ".pptx"7. Excel 也能启动别的程序
Office 自动化不只局限在 Office 内部。比如我想打开日志文件、调用本地脚本、启动一个小工具,都可以用 Shell。
1 2 3 4 5 6
Sub OpenRunLog()Dim logPath AsStringlogPath = ThisWorkbook.Path & "\输出\run_log.txt"Shell "notepad.exe " & Chr(34) & logPath & Chr(34), vbNormalFocusEndSub
这里我会特别注意路径。少写死 D:\xxx\xxx 这种路径,多用 ThisWorkbook.Path,这样文件夹整体搬走后,代码还能继续跑。
8. 我会盯住这些坑

看得见。 调试时把 Visible 设成 True,能看到 Word、PPT、Access 到底打开了什么。等代码能跑了,再考虑隐藏窗口。
关得掉。 创建出来的应用对象,最好用完就 Quit,再 Set xxx = Nothing。不然后台可能残留一堆 Word、Excel、Access 进程。
路径清楚。 模板、数据库、输出文件,尽量跟工作簿放在同一套目录里。用 ThisWorkbook.Path 拼路径,少依赖某台电脑的盘符。
少盲发。 Outlook 相关代码我更愿意用 Display。邮件是对外动作,多看一眼很值。
一次倒表。 Access 查询结果进 Excel 时,CopyFromRecordset 很好用。字段名自己写一行,数据从 A2 开始倒进去,结构清楚。
失败能收尾。 真实工作里要加错误处理。哪怕中途报错,也要尽量把打开的文件和应用关掉。
9. 我更推荐的一键周报组合
把前面的几个案例合起来,就能做一个很实用的“一键周报”:
1 2 3 4 5 6
Sub BuildWeeklyPackage()Call PullOpenTickets '从 Access 拉未关闭工单Call BuildVisitMemo '生成 Word 回访纪要Call BuildWeeklySlide '生成 PPT 汇报页Call ShowTodoMail '展示 Outlook 待办邮件EndSub
我看来,Office 自动化最有价值的地方,重点不在炫技。它真正解决的是这些重复动作:查一次库,刷新一次表,复制一段内容,做一页 PPT,写一封邮件。
把这些动作交给代码,人就少做机械活,把时间留给判断和沟通。
夜雨聆风