メール自動作成用のクラスを作る~(2)

先に断っときます。今回はカンバンに偽りあり。

「……クラスを作る」とか題名で言ってますが、クラスは作りません。ただ、前回作ったクラスを活用するためのコードを作るんだから、あながち嘘でもない。そんなわけで、そこんとこヨロシク。

前回作ったクラス「MailData」では、メール作成に必要なデータを4つに分けて取得するようにしていた。すなわち、

  1. メールの基礎データ
  2. メール本文の配列
  3. 添付ファイルのフルパスの配列
  4. 送信者データの配列

の4つだ。

……てことは、まずはこれらのデータをワークシートから取得する処理を書かねばならん、ということだ。

諸データ取得メソッドを作る

基礎データ取得メソッド

基礎データを、構造体の形でまとめる処理を書く。このメソッドは、他には使い道がないと思うので、Privateで呼び出され専用にする。他の3つも同じ。

Private Sub setMailBasicData(ByRef baseCell_ As Range)
  Dim baseRow_ As Integer
  baseRow_ = baseCell_.Row                               '……(1)
  With baseCell_.Parent                                  '……(2)
    '送信相手の基本情報を構造体の各要素にセット
    mldt.mailTo = .Cells(baseRow_, colNum.mailTo).Value  '……(3)
    mldt.CC = .Cells(baseRow_, colNum.CC).Value
    mldt.BCC = .Cells(baseRow_, colNum.BCC).Value
    mldt.mailSubject = .Cells(baseRow_, colNum.mailSubj).Value
    mldt.belongsTo = .Cells(baseRow_, colNum.belongsTo).Value
    mldt.jobTitle = .Cells(baseRow_, colNum.jobTitle).Value
    mldt.personName = .Cells(baseRow_, colNum.personName).Value
    mldt.returnReceipt = .Cells(baseRow_, colNum.returnReceipt).Value
  End With
End Sub

今回のマクロは、メール作成用のデータが「1件につき1行」という形になっていて、「対象データの行のB列を選択した状態でマクロを実行する」というやり方にしている。従って、データを拾い出すときの基準として選択中のセルを渡すことになる。引数の「baseCell_」というのはそういうことです。

さて、コードの解説。

  • ワークシートで選択中のセルの行が、各種データを集める際の基準になるので、(1)で引数として渡されたセルの行番号を変数「baseRow_」に格納している。
  • (2)で、引数として渡されたセルのParentプロパティを取得している。要するにメール作成用データが入力されたシート(「Main」ワークシート)のこと。以下、「End With」まで、「Main」ワークシートに対する操作。
  • (3)からの8行で、構造体「mldt」の各要素にデータを持たせている。
    「Main」ワークシートの列の指定には列挙体を使っているので、何の列を指しているのか分かりやすいと思う。

メール本文の配列取得メソッド

Private Sub setMailBody(ByRef baseCell_ As Range, _
                        ByVal n As Integer)
'※引数「n」は、本文が入力されているセルの数
  Dim baseRow_ As Integer
  baseRow_ = baseCell_.Row
  ReDim mlBody(n)                                            '……(1)
  Dim i As Integer
  With baseCell_.Parent
    For i = 1 To n
      mlBody(i) = .Cells(baseRow_, colNum.p01 + i - 1).Value '……(2)
    Next
  End With
End Sub

このメソッドには、引数として基準セルと本文データの入っているセルの数を渡す。

  • 配列の要素数「n」でmlBody()をReDimする。
  • あとは、Forループを使ってmlBody()の各要素にデータを放り込んでいるだけ。Cellsプロパティの列インデックスが、カウンタ変数「i」がインクリメントするごとに「0,1,2,……」とインクリメントするので、右へ右へと値を取得して配列にセットしていく感じになる。

添付ファイルフルパスの配列取得メソッド

メール本文の取得とほぼ同じやり方なので、コードを載せるだけにする。

Private Sub setAttachmentFiles(ByRef baseCell_ As Range, _
                               ByVal n As Integer)
'※引数「n」は、添付ファイルのフルパスが入力されているセルの数
  Dim baseRow_ As Integer
  baseRow_ = baseCell_.Row
  ReDim mlAttFiles(n)
  Dim i As Integer
  With baseCell_.Parent
    For i = 1 To n
      mlAttFiles(i) = .Cells(baseRow_, colNum.att01 + i - 1).Value
    Next
  End With
End Sub

送信者データの取得メソッド

送信者のデータは、別シートで管理しているので、

f:id:akashi_keirin:20170311114804j:plain

あらかじめこんな風にデータ範囲に名前を付けておく。今回は「UserInformationTable」としている。

そうしたら、

Range("UserInformationTable").Rows.Count

これで送信者データが何種類あるか取得できるので。

Private Sub setSenderData(ByVal n As Integer)
'※引数「n」は、送信者データの数
  ReDim mlSenderData(n)
  Dim i As Integer
  For i = 1 To n
    mlSenderData(i) = ThisWorkbook.Worksheets("ユーザ情報").Cells(i, 2).Value
  Next
End Sub

これはもう説明不要だと思う。

メインメソッドを作る

上記4つのメソッドをそれぞれ呼び出してやれば、「MailData」クラスのインスタンスにデータを渡す準備は完了、ということになる。

メインメソッドのコード

Sub voidMain()
  Dim baseCell As Range
  Set baseCell = ActiveCell
  If booleanCheckActiveCell(baseCell) = False Then                              '……(*)
    Call makeUserSick                                                           '……(**)
    Set baseCell = Nothing
    Exit Sub
  End If
  Dim objSh As Worksheet
  Set objSh = baseCell.Parent
  Dim n As Integer  'カウント用変数
  Dim baseRow As Long
  baseRow = baseCell.Row
  '構造体変数mldtに、メールの基本情報をセットする
  Call setMailBasicData(baseCell)                           '……(1)
  Set md = New MailData                                     '……(2)
  'MailBodyクラスのインスタンスにメールの基礎データをセット
  md.getMailBasicData mldt                                  '……(3)
  With md
    Debug.Print "★★★メール基礎データ★★★"              '……(4)
    Debug.Print .mailTo
    Debug.Print .CC
    Debug.Print .BCC
    Debug.Print .mailSubject
    Debug.Print .belongsTo
    Debug.Print .jobTitle
    Debug.Print .personName
    Debug.Print .returnReceipt
  End With
  '本文文字列の入っているセルを数えて変数「n」にセット
  Dim i As Integer
  n = 0                                                     '……(5)
  For i = colNum.p01 To colNum.p10                          '……(6)
    If objSh.Cells(baseRow, i).Value = "" Then              '……(7)
      Exit For                                              '……(8)
    Else
      n = n + 1                                             '……(9)
    End If
  Next
  '本文を配列に格納する。
  Call setMailBody(baseCell, n)                             '……(10)
  'MailDataクラスのインスタンスにメール本文の配列をセット
  md.getMailBodyArray mlBody()                              '……(11)
  Debug.Print "★★★本文の配列★★★"                      '……(12)
  For i = 1 To UBound(mlBody())
    Debug.Print md.mailBody(i)
  Next
  '添付ファイルのフルパスが入っているセルを数えて変数「n」にセット
  n = 0                                                     '……(13)
  For i = colNum.att01 To colNum.att10
    If objSh.Cells(baseRow, i).Value = "" Then
      Exit For
    Else
      n = n + 1
    End If
  Next
  '添付ファイルのフルパスを配列に格納する。
  Call setAttachmentFiles(baseCell, n)
  'MailDataクラスのインスタンスに添付ファイルフルパスの配列をセット
  md.getMailAttFilesArray mlAttFiles()
  Debug.Print "★★★添付ファイルフルパス★★★"
  For i = 1 To UBound(mlAttFiles())
    Debug.Print md.attFiles(i)
  Next
  '送信者データを配列に格納する                             '……(14)
  Call setSenderData(Range("UserInformationTable").Rows.Count)
  'MailDataクラスのインスタンスにユーザー情報の配列をセット
  md.getSenderDataArray mlSenderData()
  Debug.Print "★★★送信者データ★★★"
  For i = 1 To UBound(mlSenderData())
    Debug.Print md.senderData(i)
  Next
End Sub

Private Function booleanCheckActiveCell(ByRef nowSelecting As Range) As Boolean '……(*)
  With nowSelecting
    If nowSelecting.Parent.Name <> MAIN_SHEET_NAME Then
      MsgBox "この痴れ狗めぐぁーーーーーーッ!", vbCritical
      MsgBox "そもそも「" & MAIN_SHEET_NAME & "」ワークシートを選ぶところから出直してこいや!"
      Exit Function
    End If
    'アクティブセルの位置がおかしいときは終了。
    '番号のセルを選んでいないときは、メッセージを表示して終了
    If .Column <> colNum.numOf Then
      MsgBox "番号のセル(B列)を選んで出直してこいや!", vbCritical
      Exit Function
    End If
    '1行目を選んでいるときは、メッセージを表示して終了
    If .Row = 1 Then
      MsgBox "ぼけーーーーーー!", vbCritical
      MsgBox "そこを選んでどうするねん!", vbExclamation
      Exit Function
    End If
    '送信済みフラグがあるときは、メッセージを表示して終了
    If .Cells(nowSelecting.Row, colNum.isSent).Value = "済" Then
      MsgBox "二重に送ってどうするねん!", vbCritical
      Exit Function
    End If
    '宛先がないときは、メッセージを表示して終了
    If .Parent.Cells(nowSelecting.Row, colNum.mailTo).Value = "" Then
      MsgBox "(゚Д゚)ハァ?", vbCritical
      MsgBox "宛先アドレスが空欄なんやけど?", vbExclamation
      Exit Function
    End If

    End If
  End With
  booleanCheckActiveCell = True
End Function

Private Sub makeUserSick()                                                      '……(**)
  MsgBox "     _________" & vbCrLf & _
         " /          \ " & vbCrLf & _
         "/ /・\  /・\    \" & vbCrLf & _
         "|   ̄ ̄    ̄     | ち~んw" & vbCrLf & _
         "|    (_人_)    |" & vbCrLf & _
         "|     \     |          |" & vbCrLf & _
         "\      \_|     /"
End Sub

コードの解説。

  • (*)のところは、起動時のチェック。マクロ実行時に「Main」ワークシートのB列を選択しているかどうかとか、そういう最低限のチェックをしている。下の方の(*)を付けたメソッドでチェックしている。
  • チェックを通らなかったら、(**)でユーザを煽る仕様w
  • (1)。選択中のセルを引数として渡してsetMailBasicDataメソッドを実行。これで、構造体変数「mldt」の各要素にデータがセットされる。
  • (2)でMailDataクラスのインスタンスを生成。
  • (3)でMailDataクラスのgetMailBasicDataメソッドに引数「mldt」を渡してプロパティに各データをセット。
  • (4)から8行は、各データがちゃんとセットされているかどうかを確認するためのDebug.Print。
  • 今度は本文データの取得とセット。(5)でカウント用変数「n」を初期化。
  • (6)から7行のForループで、本文用の10個のセルのうち、文字列が入っているセルを数える。
    列挙体で開始列番号と終了列番号を指定しているので、読みやすいと思う。
  • (7)で空白セルかどうかを判定。空白なら(8)でループを抜ける。空白でなかったらカウント変数「n」をインクリメントする。
  • Forループが終わると、「n」には本文の入ったセルの数、すなわち配列の要素数が入っていることになるので、(10)でsetMailBodyメソッドを引数として基準セルと配列の要素数「n」を渡して実行する。
  • (10)の実行後は、配列「mlBody()」に本文データが格納されているので、(11)で、配列「mlBody」を引数として渡してMailDataクラスのgetMailBodyArrayメソッドを実行する。
  • (12)は、メール本文のデータがちゃんとMailDataクラスのプロパティにセットされているかどうかをチェックするためのDebug.Print。
  • (13)以降は、添付ファイルフルパスのセットと確認用のDebug.Print。本文データとほとんど同じ処理なので説明は省略。
  • (14)以降は、送信者データのセットと確認用のDebug.Print。これも説明は省略。

とりあえず、ここまで。もし、(13)とか(14)のところの意味が分からなかったら、(5)~(12)までをよーく解読してみてほしい。まあ、あまりうまく説明できているとも思えないので、質問とか寄せてくださったらありがたい。

実行

f:id:akashi_keirin:20170311171025j:plain

「Main」ワークシートのB列を選択して実行すると……、

f:id:akashi_keirin:20170311154303j:plain

ほれ、この通り、イミディエイト・ウインドウに、無事全てのデータが表示されている。

これで、このワークブックから、必要な全てのデータが取得できた。あとは、LotusNotesとかThunderbirdとか、メーラーに合わせてメールを作成するメソッドを書けば良い。たぶん、OutLookにも対応できるだろう。

ちなみに、選択すべきセルを選択せずに実行すると、

f:id:akashi_keirin:20170311154319j:plain

f:id:akashi_keirin:20170311154325j:plain

f:id:akashi_keirin:20170311154330j:plain

こんな具合に煽られますwww

※「痴れ狗めぐぁーーーーーーッ!」ってのは、マンガ『蒼天航路』の董卓のセリフが元ネタです。

ひとりごと

それぞれのメーラーに合わせたメール作成用メソッドなんだけど、メーラーごとに別々のクラスを作って、同じメソッド名(たとえば「createMail」とか)にした方がいいんだろうか?

VBAでもInterfaceが使えるらしいんだけど、Interface型の変数が使えるんだったら、ポリモーフィズムができるということなんだろうか? まあ、今のやり方だと「ポリモーフィズムが使えたとして何がうれしいんだよ!?」ということなんですけど……。

まだまだ勉強が必要ですね。そこら辺、詳しい人がいたらヒントをくれください。