[[20211215165504]] 『エクセルからメールを送信する』(中也さん) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『エクセルからメールを送信する』(中也さん)

こんにちは。
エクセルからOutlookメールの送信マクロを作ったのですが、こちらの不備が解消されなのでご教授お願いできないでしょうか。
・メールのTO宛先が入力されない。
・メール本文の途中にエクセルの「A8セルから最終行まで」を載せたいのに最終行しか載らない。

「メールリスト」シートの情報
      A列   B列   D列    C列
1行目・・・No.  名前  アドレス  状況(DONE)
2行目・・・1     中也  xxx@.com
3行目・・・2 太朗  aaa@.com

「メール設定」シートの情報
2行目(メールの件名)・・・リマインドメール
3行目(本文1)・・・サポート切れはこちら(aka)
4行目(本文2)・・・サポート切れ1年以内(kiro)
5行目(本文3)・・・以上です。

7行目・・・■期限切れ ■一年後
8行目・・・A8から下に期限切れのリスト、B8から下に一年後リストがあります。

【完成したメールの本文】
サポート切れはこちら(aka)
★A列の最終行の値だけしか載らない★

サポート切れ1年以内(kiro)
★B列の最終行の値だけしか載らない★

以上です。

Sub メールの送信()

    'プログラム2|変数設定
    Dim i As Long
    Dim status As String, mailaddress As String, ccaddre

    'プログラム3|各シートのデータ取得
    Dim ws1 As Worksheet, ws2 As Worksheet
    Set ws1 = Worksheets("メールリスト")
    Set ws2 = Worksheets("メール設定")
    aka = ws2.Range("A8").End(xlDown).Value
    kiro = ws2.Range("B8").End(xlDown).Value

    'プログラム4|最終行を取得
    Dim cmax As Long
    cmax = ws1.Range("A65536").End(xlUp).Row

    'プログラム5|セル範囲を取得
    Dim myrange1 As Variant
    myrange1 = ws1.Range("A2:D" & cmax)

    'プログラム6|「メール設定」シートのデータ取得
    Dim subject As String, mailbody As String
    subject = ws2.Range("B2").Value '件名
    mailbody = ws2.Range("B3").Value '本文
    'プログラム7|Outlookを起動
    Dim OutlookObj As Outlook.Application
    Set OutlookObj = CreateObject("Outlook.Application")
    Dim myMail As Outlook.MailItem
    'プログラム8|「メールリスト」シートのデータを各行ごとに処理
    For i = LBound(myrange1) To UBound(myrange1)

        'プログラム9|D列が「DONE」でないならば処理を実行
        status = myrange1(i, 4)
        If Not status = "DONE" Then

            'プログラム10|メールアドレスを取得してメール情報を書き換える
            mailaddress = myrange1(i, 4)

            'プログラム11|Outlookメールを作成
            Set myMail = OutlookObj.CreateItem(olMailItem)

            'プログラム12|メール内容を作成
            myMail.BodyFormat = 3
            myMail.To = mailaddress
            myMail.subject = subject
            myMail.Body = mailbody & vbCrLf & aka & vbCrLf & ws2.Range("B4").Value & vbCrLf & kiro & vbCrLf & ws2.Range("B5").Value

            'プログラム14|メール送信
            myMail.Display
            'Application.Wait Now() + TimeValue("00:00:03")
            'myMail.Send

            'プログラム15|オブジェクト解放
            Set myMail = Nothing
        End If
    Next
    Set OutlookObj = Nothing
'プログラム16|プログラム終了
End Sub

メールの宛先が入力されないのは、プログラム6、8、9の何かが間違っている。
メール本文にリストの最終行しか表示されないのはプログラム3が間違っているのだと思います。

長くて申し訳ございません。
宜しくお願い致します。

< 使用 Excel:Office365、使用 OS:Windows10 >


中途半端に変数にいれたりすることでかえって難しくなってないですか?

■1
>メールのTO宛先が入力されない。

 myrange1 = ws1.Range("A2:D" & cmax)
 For i = LBound(myrange1) To UBound(myrange1)
    mailaddress = myrange1(i, 4)
    myMail.To = mailaddress
    ↓
 For i =2 To ws1.Cells(Rows.Count,"D").End(xlup).Row
    MyMail.To = ws1.Cells(i,"D").Value

>メールの宛先が入力されないのは、プログラム6、8、9の何かが間違っている。
よく分かりませんが、素直に該当セルを直接見たら如何ですか?

■2
>メール本文の途中にエクセルの「A8セルから最終行まで」を載せたいのに最終行しか載らない。

    myMail.Body = mailbody & vbCrLf _
                & aka & vbCrLf _
                & ws2.Range("B4").Value & vbCrLf _
                & kiro & vbCrLf _
                & ws2.Range("B5").Value

                   ↓変数を展開するとこうなる↓

    myMail.Body = Worksheets("メール設定").Range("B3").Value & vbCrLf _
                & Worksheets("メール設定").Range("A8").End(xlDown).Value & vbCrLf _   ★
                & Worksheets("メール設定").Range("B4").Value & vbCrLf _
                & Worksheets("メール設定").Range("B8").End(xlDown).Value & vbCrLf _
                & Worksheets("メール設定").Range("B5").Value

すなわち、そもそも★の部分ではA8セルから下方向に見たときの最終セルのみ指定しています。

>>メール本文にリストの最終行しか表示されないのはプログラム3が間違っているのだと思います。
したがって、気にするべきはそこではないです。

例えば↓のようにちゃんと「A8セルから最終行まで」を取得しないとダメです。

    Sub 研究用01()
        Dim i As Long
        Dim buf As String

        With Worksheets("メール設定")
            For i = 8 To .Cells(.Rows.Count, "A").End(xlUp).Row
                buf = buf & .Cells(i, "A").Value & vbLf
            Next i

            buf = Left(buf, Len(buf) - 1)

            MsgBox buf
        End With
    End Sub
    '--------------------------------------------------------------------------------
    Sub 研究用02()
        Dim i As Long
        Dim buf As String

        With Worksheets("メール設定")
            i = .Cells(.Rows.Count, "A").End(xlUp).Row
            Select Case True
                Case i < 8
                    MsgBox "データ無し"
                    Exit Sub

                Case i = 8
                    buf = .Cells(i, "A").Value

                Case Else
                    buf = Join(WorksheetFunction.Transpose(.Range("A8:A" & i).Value), vbLf)
            End Select
        End With
        MsgBox buf
    End Sub

(もこな2) 2021/12/15(水) 18:27


もなこ2さん、ありがとうございます。

宛先は素直に直接見ることにしました。

メール本文の研究用1の方ですが、A列の最終行が1番最初にも表示されて2回表示されました。
こちらはどのコードの部分が1回多く表示させているのでしょうか?

【最終行がA11だとこのように表示されました】
A11A8
A9
A10
A11

お手数お掛けしますが、宜しくお願いします。
(中也さん) 2021/12/16(木) 17:37


書き溜めている間にお返事いただきましたので前後しますが。

■3
「■1」の補足になりますが、提示のあった表の【列】標記が正しいとすれば↓ですよね。

 【メールリスト】シート
    ___A__     __B_    ___【C】___     __【D】__
 1  No.     名前    状況(DONE)    アドレス
 2    1    中也                  xxx@.com
 3    2        太朗                  aaa@.com

そうなると「myrange1」には↓のような【二次元配列】が格納されているわけですよね。

         【1】       【2】       【3】       【4】       
 【1】     1    "中也"      ""       "xxx@.com"
 【2】     2        "太朗"      ""       "aaa@.com"

 mailaddress = myrange1(i, 4)
 myMail.To = mailaddress

その上で↑であれば、たとえば「i」が1の場合

 mailaddress = myrange1(i, 4)  → mailaddress = "xxx@.com"
 myMail.To = mailaddress       → myMail.To = "xxx@.com"

ですから「メールのTO宛先が入力されない。」といったことにはならないとおもいます。
一方で、提示のあった表の【列】標記が誤っていて、提示の並び順が正しいとすれば↓ですよね。

 【メールリスト】シート
    ___A__     __B_    ___【C】___    ___【D】__
 1  No.     名前     アドレス   状況(DONE)
 2    1    中也   xxx@.com
 3    2        太朗   aaa@.com

こちらの場合は「myrange1」が↓になるわけですから

         【1】       【2】       【3】       【4】       
 【1】     1    "中也"   "xxx@.com"   ""
 【2】     2        "太朗"   "aaa@.com"     ""

 myMail.To = ""

↑となるのは正しい動作ですよね。
にもかかわらず↓のようにしている(同じ4要素目?を見ている)ので、対象の【列】が間違いではないかと疑っています。

 status = myrange1(i, 4)
 If Not status = "DONE" Then

 mailaddress = myrange1(i, 4)
 myMail.To = mailaddress

配列に入れて処理したほうが有利なケースもあるでしょうし、それぞれの好みもあるので否定はしませんが、エクセルには【セル】という強力な武器があるのですから、そちらを最大限活用したほうが、デバッグしやすいのではないか?というのが「■1」の趣旨でした。
配列に入れることを否定するような書きぶりになってしまったので少々補足させていただきます。

■4
>メールの宛先が入力されないのは、プログラム6、8、9の何かが間違っている。
>メール本文にリストの最終行しか表示されないのはプログラム3が間違っているのだと思います。

なぜ、そのようにおもったのでしょうか?
いずれも【ステップ実行】して追っていけば、原因はそこではないことが容易にわかるかと思いますので、あらためて自己検証してみてください。

※ステップ実行という言葉を聞いたことがなければ↓を読んでみてください。

 【ステップ実行】
https://www.239-programing.com/excel-vba/basic/basic023.html
http://plus1excel.web.fc2.com/learning/l301/t405.html

 【ブレークポイント】
https://www.239-programing.com/excel-vba/basic/basic022.html
https://www.tipsfound.com/vba/01010

また、以下も知っておいて損は無いと思います。

 【イミディエイトウィンドウ】
https://www.239-programing.com/excel-vba/basic/basic024.html
https://excel-ubara.com/excelvba1/EXCELVBA486.html

 【ローカルウィンドウ】
https://excel-ubara.com/excelvba4/EXCEL266.html
http://excelvba.pc-users.net/fol8/8_2.html

■5
>メール本文の研究用1の方ですが、A列の最終行が1番最初にも表示されて2回表示されました。
>こちらはどのコードの部分が1回多く表示させているのでしょうか?
ちょっと状況がわかりませんが、【ステップ実行】して確認のうえ【どのセルを順番にみていたか】結果をおしえてください。

■6
>宛先は素直に直接見ることにしました。
して、方法(どこをどう直せばよいか)はわかっているのですか?
↓でも感じたことですが、言われたことをオウム返しをしているだけで理解できている感じがしないのですが・・・・
[[20211209152742]] 『セルの色毎に一覧に出力する』(中也さん)

■7
最初に提示されたものを見ると、いくつかの【変数】が宣言されていません。
まずは↓を読んで、変数の宣言が強制されるように設定を変えることをお勧めします。
http://officetanaka.net/excel/vba/beginner/06.htm

■8
ちょっと気になったのでお伺いしますが、アーリーバインディングとレイトバインディングどちらでいかれるつもりですか?

 Dim OutlookObj As Outlook.Application
 Set OutlookObj = CreateObject("Outlook.Application")
 Set myMail = OutlookObj.CreateItem(olMailItem)

↑のようになってるので少々気になりました。

(もこな2) 2021/12/16(木) 20:30


さらに追加で。
■9
もう一つ気になる点として、この手の処理って大抵の場合相手によって件名や本文の内容(リストとか宛先名など)が変わったりすると思います。
しかし提示のものだと、すべてが固定になっているわけですが問題ないのですか?

問題ないのであれば、現状のようにループの外側で件名にあたる部分や本文にあたる部分を取得すればよいですが、そうでない場合はループ中に取得しないとダメですよね。

(もこな2 ) 2021/12/17(金) 09:26


お世話になります。見直して修正ました。

■3
メールリストの「状況(DONE)」を無くしました。
不要な宛先はシートから削除して送信させない。

■4
ウィンドウ枠の使い方参照させて頂きました。

■5
最初に記載したこちらを消してないのが原因でした。(最終行だけ表示するやつ)

    aka = ws2.Range("A8").End(xlDown).Value
    kiro = ws2.Range("B8").End(xlDown).Value
あと、Nextの位置が悪く、「「メール設定」シートのリストを取得」に戻り、次の宛先分を作成する時に前の「akaとkiro」のリストの続きに追記していたのが問題でした。

■6
宛先を個人宛ではなく、「;」で全員入るように変更しました。
Join関数の方が見やすそうでしたが、「MyMail.To = ws1.Cells(i,"D").Value」の改良の仕方が分からなかったので、&で連結しました。

wkaddress = wkaddress & ws1.Cells(i, "C").Value & " " & ";"

連結したのですが、たまに「表示する名前が多すぎます。名前の文字を追加してもう一度やり直してください」と出るようになりました。。

■7
変数宣言の強制にチェックし、足りない宣言を追加しました。

■8
レイトバインディングの方が分かり易いのですが、実行するまでリスクが大きいそうなのでアーリーバインディングでできればいきたいなと。。。

■9
人によって変更する箇所は予定していないので固定で大丈夫です。
↓↓この時のリスト作成マクロですが、作成前に8行目以降をDeleteするのを忘れていたので最初に初期化を追記しておきます。
[[20211209152742]] 『セルの色毎に一覧に出力する』(中也さん)

■追加なのですが、メールを送信する時にOutlookの誤送信防止ツールが働き、「メール送信最終確認」のポップが表示されます。
ネットにはポップの非表示ばかり挙がっていたのですが、「送信」ボタンを自動で押すことは可能でしょうか?(送信とキャンセルの2択でした)

↓↓こちらが修正後のコードです。
Sub メールの送信()

    'プログラム1|変数設定
    Dim i As Long, j As Long, k As Long
    Dim status As String, mailaddress As String

    'プログラム2|各シートのデータ取得
    Dim ws1 As Worksheet, ws2 As Worksheet
        Set ws1 = Worksheets("メールリスト")
        Set ws2 = Worksheets("メール設定")

    'プログラム3|最終行を取得
    Dim cmax As Long
        cmax = ws1.Range("A65536").End(xlUp).Row

    'プログラム4|「メール設定」シートのデータ取得
    Dim subject As String, mailbody As String
        subject = ws2.Range("B2").Value '件名
        mailbody = ws2.Range("B3").Value '本文
    'プログラム5|Outlookを起動
    Dim OutlookObj As Outlook.Application
        Set OutlookObj = CreateObject("Outlook.Application")
    Dim myMail As Outlook.MailItem
    'プログラム6|メールアドレスを取得
    Dim wkaddress As String
    For i = 2 To ws1.Cells(Rows.Count, "C").End(xlUp).Row
    wkaddress = wkaddress & ws1.Cells(i, "C").Value & " " & ";"
    Next

    'プログラム7|「メール設定」シートのリストを取得
    Dim aka As String, kiro As String
    With Worksheets("メール設定")
        For j = 8 To .Cells(.Rows.Count, "A").End(xlUp).Row
            aka = aka & .Cells(j, "A").Value & vbLf
            Next j
            aka = Left(aka, Len(aka) - 1)

    End With

    With Worksheets("メール設定")
        For k = 8 To .Cells(.Rows.Count, "B").End(xlUp).Row
            kiro = kiro & .Cells(k, "B").Value & vbLf
        Next k
        kiro = Left(kiro, Len(kiro) - 1)
    End With

    'プログラム8|Outlookメールを作成
    Set myMail = OutlookObj.CreateItem(olMailItem)

    'プログラム9|メール内容を作成
    myMail.BodyFormat = 3
    myMail.To = wkaddress
    myMail.subject = subject
    myMail.Body = mailbody & vbCrLf _
                & aka & vbCrLf _
                & ws2.Range("B4").Value & vbCrLf _
                & kiro & vbCrLf _
                & ws2.Range("B5").Value

    'プログラム10|メール送信
    myMail.Display
    Application.Wait Now() + TimeValue("00:00:03")
    myMail.Send '最終確認大矢ログが出現〜考える〜

    'プログラム11|オブジェクト解放
    Set myMail = Nothing
    Set OutlookObj = Nothing
    'プログラム12|プログラム終了
    MsgBox "送信完了"

End Sub

(中也さん) 2021/12/17(金) 16:46


■10
>メールリストの「状況(DONE)」を無くしました。
その発想はなかったです。
まぁ本人がよければいいんじゃないですか。

■11
>ウィンドウ枠の使い方参照させて頂きました。
仰ってることがよくわかりません。
【ステップ実行】についてお話したつもりですが・・・・

■12
>最初に記載したこちらを消してないのが原因でした。(最終行だけ表示するやつ)
わかりました。解決したなら深堀しません。(仰ってることが私には理解できないので)

■13
>宛先を個人宛ではなく、「;」で全員入るように変更しました。
それでよかったのですか?まぁ困らないなら好きにしてください。
なお、「" "」は要らないと思います。

■14
>変数宣言の強制にチェックし、足りない宣言を追加しました。
チェックするだけではダメなんですが・・・・まぁ見る限り全部宣言されているようなのでいいですかね。

■15
>アーリーバインディングでできればいきたいなと。。。
わかりました。

■16
>人によって変更する箇所は予定していないので固定で大丈夫です。
わかりました。

■17
>追加なのですが〜〜
ちょっとそのような挙動は見たことがないのでわかりません。
ただ、いきなり「Send」してよいなら「Display」する必要はないのでは?

■18
ということを踏まえて、私が整理するならこんな感じですかね(■10と■13は考慮してません)
ステップ実行して研究の上、改造の参考にしてみてください。

    Sub メールの送信の整理()
        Dim i As Long
        Dim 本文 As String, aka As String, kiro As String
        Dim OutlookObj As Outlook.Application
        Dim myMail  As Object
        Set OutlookObj = New Outlook.Application

        'メール本文を取得する
        With Worksheets("メール設定")
            For i = 8 To .Cells(.Rows.Count, "A").End(xlUp).Row Step 1
                aka = aka & .Cells(i, "A").Value & vbLf
            Next i

            For i = 8 To .Cells(.Rows.Count, "B").End(xlUp).Row Step 1
                kiro = kiro & .Cells(i, "B").Value & vbLf
            Next i

            本文 = .Range("B3").Value & vbCrLf & aka & .Range("B4").Value & vbCrLf & kiro & .Range("B5").Value
        End With

        'メールを生成する
        With Worksheets("メールリスト")
            For i = 2 To .Cells(.Rows.Count, "C").End(xlUp).Row
                If Cells(i, "D").Value <> "DONE" Then
                    Set myMail = OutlookObj.CreateItem(olMailItem)
                    myMail.BodyFormat = olFormatRichText
                    myMail.To = .Cells(i, "C").Value
                    myMail.subject = Worksheets("メール設定").Range("B2").Value
                    myMail.Body = 本文
                    myMail.Display '送信せず表示するだけ
                End If
            Next i
        End With

        MsgBox "内容を確認して送信してください"
    End Sub

(もこな2 ) 2021/12/17(金) 22:11


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.