Excel2016引用Outlook16对象库无法读取MailItem.Body属性问题
问题产生原因
该故障和Outlook对象库引用版本无直接关联,是Outlook 2013及后续版本的默认机制变更、代码写法缺陷共同导致的:
- 从Outlook 2013开始,微软调整了共享邮箱/共享文件夹的同步策略:对于非本地默认存储的共享邮箱(即代码中访问的
mailbox@company.com这类公共/共享邮箱),默认仅同步邮件元数据(发件人、接收时间、主题等)到本地OST缓存,正文、附件等大体积内容不会自动预下载,仅当用户手动点开邮件时才会拉取完整内容。Outlook 2010无此策略,默认同步全量邮件内容,因此可直接读取Body属性。 - 代码中全局开启了
On Error Resume Next,直接吞掉了Outlook返回的“邮件正文未下载到本地”的MAPI接口错误,没有触发正文拉取逻辑,最终表现为.Body/.HTMLBody/.RTFBody全部返回空值,监视窗口显示<>。 - 遍历
olItms集合时直接访问正文属性的写法,在2016版本中不会像2010版本那样隐式触发正文拉取;而测试时写入Body能成功,是因为写入操作本身会强制Outlook拉取完整邮件项后再执行修改,因此写操作不受该机制影响。
额外代码缺陷:排序行使用了中文全角引号olItms.Sort “Received”,2010版本容错性高可正常执行,2016版本会直接导致排序失效,虽不是正文读取失败的诱因,但会造成邮件遍历顺序异常。
可行解决方案
按落地成本从低到高排序,优先尝试代码层面修改,无需调整客户端配置:
方案1:代码显式触发邮件完整内容加载(最推荐,全版本兼容)
在访问正文属性前,显式触发Outlook拉取完整邮件内容,同时调整错误处理逻辑,不要全局屏蔽错误。
核心修改点:
- 注释掉全局
On Error Resume Next,仅在必要位置做局部错误捕获 - 遍历邮件时先判断项类型,排除会议回执、邀请等非邮件对象
- 访问Body前调用
GetInspector属性强制拉取全量邮件内容,加DoEvents等待加载完成 - 把排序行的全角引号替换为英文半角引号
修改后的遍历段参考代码:
' 修正排序语句的引号问题 olItms.Sort "Received" ' ...保留原有UserForm、日期判断相关代码... For Each olMail In olItms ' 过滤非邮件类型项,43对应MailItem类值,早/晚绑定均可生效 If olMail.Class = 43 Then If LatestDate > CDate(olMail.ReceivedTime) Then If CDate(olMail.ReceivedTime) > EarliestDate Then ' 强制加载完整邮件内容,无需前台弹窗即可生效 Dim insp As Object Set insp = olMail.GetInspector DoEvents ' 等待同步完成 ' 读取正文 mailContents() = Split(olMail.Body, Chr(13)) ' 后续业务逻辑保持不变 End If End If End If Next
方案2:调整Outlook共享邮箱同步设置
如果方案1执行后仍有个别邮件读不到正文,修改同事端Outlook的同步配置:
- 打开Outlook,依次进入「文件」-「账户设置」-「账户设置」
- 双击当前绑定的Exchange账户,点击「更多设置」
- 切换到「高级」选项卡,找到共享文件夹相关设置:要么取消勾选「下载共享文件夹」(直接在线读取全量内容),要么勾选「下载共享文件夹的完整项目」(同步全量内容到本地)
- 重启Outlook等待同步完成后再运行宏即可。
方案3:放开编程访问安全限制
少数域环境下会因安全策略拦截第三方程序读取邮件正文:
- 打开Outlook「文件」-「选项」-「信任中心」-「信任中心设置」
- 切换到「编程访问」页,选择「从不向我发出可疑活动警告」,如果选项为灰选状态,需要联系公司域管理员通过组策略放开VBA访问邮件正文的权限
- 重启Outlook测试即可。
方案4:改用晚绑定写法规避版本差异
如果需要在多版本Excel/Outlook混用的环境下运行宏,不要提前绑定固定版本的Outlook对象库,改用晚绑定写法,避免不同版本对象库的属性调度差异:
- 打开VBA编辑器的「工具-引用」,取消勾选所有Outlook版本的Object Library
- 把开头的Outlook相关对象声明全部改为
Object类型,手动定义用到的Outlook常量,创建对象时用CreateObject:
' 晚绑定写法示例 Dim olApp As Object, olNs As Object, olFldr As Object Dim olItms As Object, olMail As Object Const olMail As Long = 43 Set olApp = CreateObject("Outlook.Application") Set olNs = olApp.GetNamespace("MAPI") ' 后续逻辑不变
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

