相关文章推荐
严肃的围巾  ·  Why I'm getting ...·  5 月前    · 

Excel VBA 实现自动发送邮件

目录

  1. 项目准备
  2. Excel VBA 工具引用
  3. 邮件发送的基础代码
  4. 收件人管理
  5. 附件管理
  6. 邮件正文图片添加的方法
  7. 邮件正文表格添加的方法
  8. 全功能代码

1:项目准备

启用宏的工作簿:新建一个Excel,另存为.xlsm格式

Outlook配置:Outlook中正确配置发信人的邮箱信息,否则无法实现邮件发送

(Outlook根据版本不同,配置的方法有细微不同,建议网络搜索学习)

备注:Outlook 和 Excel 都是Microsoft Office套件里的应用。

2:Excel VBA 工具引用

工具引用:Microsoft Outlook 16.0 Object Library

(根据Outlook版本不同,会有细微差别)

引用OutLook 库

3:邮件发送基础实例

'工具->引用->Microsoft Outlook 16.0 Object Library
'或者  Set Mail = CreateObject("Outlook.Application")
Sub SendEmail()
    Dim Mail As Outlook.Application
    Set Mail = New Outlook.Application
    Dim objMail As Outlook.MailItem
    Set objMail = Mail.CreateItem(olMailItem)
    With objMail
        .Subject = "My Test Mail"  '主题
        .To = "xxxxxx@outlook.com" '收件人
        .CC = "xxxxx@hotmail.com"  '抄送
        .BCC = "xxxxx@sina.cn"     '密送
        .BodyFormat = olFormatHTML
        .HTMLBody = "<h2>My First Mail</h2>" '正文
        .Attachments.Add "D:\RunLog.txt"     '附件
        .Send '执行发送
    End With
End Sub

<如代码里注释说明一样,对于Outlook模块的引用,可以用CreateObject的方式实现,这样不用在菜单里进行引用操作,但该方法会使代码编辑缺少必要的提示>

代码部分与实际邮件部分的对应关系

代码中缺少发件人信息,是因为该程序调用的是Outlook程序,正常使用中的Outlook,是配置有发件人信息的。其它的基本与实际E-mail里的操作或填写内容一致。

Bodyformat:

正文文本格式决定了用于显示消息文本的标准。Microsoft outlook 提供三种正文文本格式选项: 纯文本、富文本(rtf)和 html。当 bodyformat 属性从 rtf 切换到 html 时,所有文本格式都将丢失,反之亦然。

BodyFormat值设置

名称 说明
olFormatHTML 2 HTML 格式
olFormatPlain 1 纯文本格式
olFormatRichText 3 RTF 格式
olFormatUnspecified 0 未指定的格式

对于正文内容和格式较为复杂的Mail,bodyformat建议设置为HTML,对于简单句的Mail,从简单的角度出发,设置为纯文本较为方便些。

4:收件人管理

参见基础代码,收件人,抄送,密送,三类收件人都是代码写死的,这样不利于后期的收件人管理,增加或删除名单都会涉及代码的修改,十分麻烦的。

.To = "xxxxxx@outlook.com" '收件人
.CC = "xxxxx@hotmail.com"  '抄送
.BCC = "xxxxx@sina.cn"     '密送

解决该类问题的通行办法是创建一个配置文件,或者数据库,用代码进行访问调用。

考虑到Excel本身就是一个强大的表格工具,所以对于Excel VBA应用,调用自身表格内容会是一个十分便捷的配置管理方法。

首先制作一个收件人管理表。

收件人管理表制作

首先确定一个原则:收件人信息都是用字符串的方式提供给程序的,有多个收件人时,必须用“;”(英文的分号)隔开。

当我们制作完一个收件人管理表格后,程序的收件人添加问题就变成了表格读取问题和字符串的拼接问题。

VBA中我们可以用function函数来实现功能的模块化,因为function函数是带返回值的

Private Function 收件人(Rng As Range) As String
    收件人 = ""
    Dim Rr As Integer
    Rr = 2
    While Rng.Cells(Rr, 1) <> ""
        收件人 = 收件人 & Rng.Cells(Rr, 1)
        If Rng.Cells(Rr + 1, 1) <> "" Then 收件人 = 收件人 & ";"
        Rr = Rr + 1
End Function

我们将收件人所在的那一列作为参数传入function,函数自动读取收件人,并组成收件人字符串返回给调用者。

基础代码相应部分可被修改为

.To = 收件人(Sheet1.[A:A])     '收件人 在A列
.CC = 收件人(Sheet1.[B:B])     '抄送 在B列
.BCC = 收件人(Sheet1.[C:C])    '密送 在C列

5:附件的管理

我们看基础代码

 .Attachments.Add "D:\RunLog.txt"     '附件  

在基础代码里,我是仅增加了一个附件,当需要添加多个附件时就需要调用多次.add

再实现附件添加模块化之前,我们需要研究如何进行附件的列表确认。对于多变的,需要经常维护的附件列表,手动在代码里进行.Attachments.Add显然是不现实的。

对于这个问题,我们可以用表格的形式,或文件夹遍历的形式进行。具体采用哪种方式取决于具体的需要。

这里我假设需要添加的附件已经写在了表格里。我们就可以用如下函数进行自动装载,整个函数类似收件人管理函数,但无需返回,在传参上略微复杂些

Private Sub 附件添加(附件 As Outlook.Attachments, Rng As Range)
    Dim Rr As Integer
    Rr = 2
    While Rng.Cells(Rr, 1) <> ""
        附件.Add Rng.Cells(Rr, 1).Text
        Rr = Rr + 1
End Sub

这样基础代码中的附件添加可以改写为

   附件添加 .Attachments, Sheet1.[D:D]

6:邮件正文图片添加

如果邮件的正文仅仅就是一句话,那么基础代码已经足够了。

但我们的mail,往往是图文并茂的,不仅有带格式的文字,还有漂亮表格,甚至夹插着图片。

Html可以提供这样的需求,正如常见的网页那样。

需要注意的是,Mail里的html和网页里的html是有所区别的,主要区别用“阉割版”这样的说法可能更容易理解。但它不影响表格,图片,文字的完美呈现,所以它也是足够受用的。

Mail正文里添加图片

.BodyFormat = olFormatHTML
.HTMLBody = "<img src='F:\图片汇总\PHOTO.png'>" '正文
.Display

修改过的基础代码正文编辑部分。Html中我写入了一个img标签,并且加载了本地F盘里的一个图片。

这里需要特别注意的是:在用html设置好.HTMLBody后,我启用了一个.Display方法。该方法会让Outlook在屏幕上一闪而过,它是模拟的Outlook手动编辑的过程,可以将Html中引用的本地图片加载并修正到网络地址。否则对方收到Mail时,是无法正确显示图片的。

还有一个十分诡异的事情,如果img标签里设置了width和height参数,接收方往往是不能正确显示图片的,其中的原因不得而知。

如果想得到能控制尺寸的正文图片,需要用如下方法进行

.Attachments.Add "F:\图片汇总\PHOTO.png"
.BodyFormat = olFormatHTML
.HTMLBody = "<img src='cid:PHOTO.png' width='100' height='100'>" 
.Display

即,先将图片作为附件添加,然后img的src属性以cid:的方式进行引用。和直接引用图片地址相比,该方法必须提供width和height尺寸信息,否则也是不能正确显示的。

7:邮件正文表格添加

邮件正文添加表格,是通过HTML来实现的,当我们将自动邮件与Excel相结合时,通常希望能发送和Excel表格里一样的内容。如果我们将Excel里每一个关键属性都对应到Html属性里,通过编码的方式是可以实现的,单纯从技术的实现上,是不错的逻辑训练过程,但的确辛苦。

可喜的是,Excel里自带有表格区域向html的转换方法,它通过先将特定的区域保存为htm格式,然后再以文件读取的方式加载到.HTMLBODY中。

文件读取,我们首先需要到工具里引用“Microsoft Scripting Runtime”库,该库提供了一个强大的文件管理功能,可以用它轻松整体读取一个文件。

我们同样可以将这样的方法进行封装

 Function Range_to_Html(Rng As Range) As String
    Dim PO As PublishObject
    Set PO = ThisWorkbook.PublishObjects.Add(xlSourceRange, "D:\Result.htm", Rng.Parent.Name, Rng.Address, xlHtmlStatic)
    PO.Publish True
    PO.Delete
    Dim FS As FileSystemObject
    Set FS = New FileSystemObject
    Dim TS As TextStream
    Set TS = FS.OpenTextFile("D:\Result.htm", ForReading, True, TristateUseDefault)
    Range_to_Html = TS.ReadAll
End Function

该函数,要求一个期望加载到邮件正文的区域,其返回的就是代表那个表区域的HTML代码.

.HTMLBody = Range_to_Html(Sheet2.[A1:O63]) 

8 全功能代码

'工具->引用->Microsoft Outlook 16.0 Object Library
'或者  Set Mail = CreateObject("Outlook.Application")
Sub SendEmail()
    Dim mail As Outlook.Application
    Set mail = New Outlook.Application
    Dim objMail As Outlook.MailItem
    Set objMail = mail.CreateItem(olMailItem)
    With objMail
        .Subject = "My Test Mail"  '主题
        .To = 收件人(Sheet1.[A:A]) '收件人
        .CC = 收件人(Sheet1.[B:B])  '抄送
        .BCC = 收件人(Sheet1.[C:C])    '密送
         附件添加 .Attachments, Sheet1.[D:D] '添加附件
        .BodyFormat = olFormatHTML
        .HTMLBody = Range_to_Html(Sheet2.[A1:O63]) '正文
        .Display
        .Send '执行发送
    End With
End Sub
Private Sub 附件添加(附件 As Outlook.Attachments, Rng As Range)
    Dim Rr As Integer
    Rr = 2
    While Rng.Cells(Rr, 1) <> ""
        附件.Add Rng.Cells(Rr, 1).Text
        Rr = Rr + 1
End Sub
Private Function 收件人(Rng As Range) As String
    收件人 = ""
    Dim Rr As Integer
    Rr = 2
    While Rng.Cells(Rr, 1) <> ""
        收件人 = 收件人 & Rng.Cells(Rr, 1)
        If Rng.Cells(Rr + 1, 1) <> "" Then 收件人 = 收件人 & ";"
        Rr = Rr + 1
End Function
'工具->引用-> Microsoft Scripting Runtime
Function Range_to_Html(Rng As Range) As String
    Dim PO As PublishObject
    Set PO = ThisWorkbook.PublishObjects.Add(xlSourceRange, "D:\Result.htm", Rng.Parent.Name, Rng.Address, xlHtmlStatic)
    PO.Publish True
    PO.Delete