日報を送信したり、共有PCの送信専用ファイルとしても使えます。現場でデータを採取して入力→送信といった業務がある場合、メールクライアントを立ち上げずにメールを送信できるので作業者にも簡単に教えることができます。
ファイルもアップするので可能な人はダウンロードしてみて下さい。ダウンロードできない人はコピペでも作れます。最後まで読んでみて下さい。
機能説明
シートをPDFファイルで出力し、メールに添付して送信するマクロです。Outlookを起動せずに処理を完結できます。
メールフォーム作成…ユーザビリティを考慮してメール用のフォームを作成しました。
宛先選択…複数件登録できます。
CC送信(1件のみ)…宛先データを利用します。
添付ファイルを追加…日報に付帯資料を求められたときのために、別ファイルを添付できるようにしました。
テンプレート文編集可能(件名、本文)…普段は登録したテンプレ文をそのまま利用します。一言付け加えたい場合は、登録したテンプレ文はそのままで本文や件名を編集できるようにしました。
フォルダ自動生成…同一フォルダ内にファイルを保存し続けると、整頓が必要になってきます。煩わしいので月別フォルダを自動で生成、その中に保存するようにしました。
ダウンロード
免責事項を一読の上、理解いただける方のみダウンロードして下さい。改変やローカルなコミュニティでの配布は自由に行って下さい。くれぐれも自己責任でお願いします。
尚、本ファイルはxlsmファイル(マクロ機能付き)です。
Sheetモジュール(2個)
Sheetを2つ用意して下さい。設定用シートと日報シートです。シート名は自由に名前を付けて下さい。
オブジェクト名 はそれぞれSheet1をwsData、Sheet2をwsReportとします(画像クリックで拡大します)。
wsDataシートの設定は以下の通りです。1行目はフィールド名とします。自由にフィールド名を設定して下さい。
A,B列はメールアドレス用のフィールドです。A列がコンボボックスに表示される名前、B列がそのアドレスです。件数の制限はありません。
名前の定義が必要です。名前をメールにして、参照範囲を=OFFSET(Sheet1!$A$2,,,COUNTA(Sheet1!$A:$A)-1,2)とします。参照範囲を可変させているので空レコードは禁止です。
C列はメールの件名です。サンプルは=TEXT(Sheet2!A1,”m/d”)&”日報_ひよこ”としています。日報シートのA1に入力する日付を利用して件名にしています。マクロに影響はないので自由に設定して下さい。
D列は本文です。セル内の改行ALT+ENTERを利用して記述します。
E列はPDFファイルを保存するフォルダのパスです。E2セルを=“C:\Users\YOU\Documents\日報\” & TEXT(Sheet2!A1, “yyyymm”)として下さい。パス(赤字)部分は自由に設定して下さい。
日報シートのA1セルに入力した日付の西暦と月を自動でフォルダ名にして生成します。
F列はPDFファイルのフルパスです。F2セルを=E2&”\日報_” & TEXT(Sheet2!A1, “yyyymmdd”) & “.pdf”とします。
フォルダ同様、日付をファイル名に利用しています。赤字部分はマクロには影響しません。自由に設定して下さい。
G列は追加添付ファイル用のTMPセルです。名前を定義して下さい。名前を添付ファイル名に、参照範囲を=OFFSET(Sheet1!$G$2,,,COUNTA(Sheet1!$G:$G)-1)として下さい。
H,I列は添付ファイルを追加するときの「ファイルを開く」ダイアログで選択されるフォルダです。追加添付ファイルが特定のフォルダにある場合はそのパスを指定して下さい。
H列はドライブを、I列はフォルダパスを指定します。サンプルはCドライブのドキュメントフォルダを指定しています。
wsReportは日報シートです。セルA1を日付入力欄として使用します。後は自由にレイアウトして下さい。
標準モジュール
VBEで標準モジュールを挿入し、コードをコピペして下さい。
'///1.定数風関数 保存フォルダパス///
Public Function GetSaveDir() As String
GetSaveDir = wsData.Range("E2")
End Function
'///2.定数風関数 フルパス///
Public Function GetFilePath() As String
GetFilePath = wsData.Range("F2")
End Function
'///3.ファイル上書き確認/// ※1
Public Function OverwriteFile(ByVal file_path As String) As Boolean
Dim rc As Long
If Dir(file_path) <> "" Then
rc = MsgBox("ファイルが既に存在します。" & vbCrLf & "上書き保存しますか?", vbYesNo)
Select Case rc
Case vbYes
Exit Function
Case vbNo
MsgBox "処理を中止しました"
OverwriteFile = True
End Select
End If
End Function
'///4.月別フォルダ自動生成/// ※1
Public Function MakeDirectory(ByVal directory_path As String) As Boolean
Dim rc As Long
If Dir(directory_path, vbDirectory) = "" Then
rc = MsgBox("保存用フォルダ「" & directory_path & "」がありません。作成しますか?", vbYesNo)
Select Case rc
Case 6
MkDir directory_path
MsgBox "フォルダ「" & directory_path & "」を作成しました"
Exit Function
Case 7
MsgBox "処理を中止します"
MakeDirectory = True
End Select
End If
End Function
'///5.PDF出力/// ※2
Public Sub OutputPDF(ByVal pdf_sheetname As Worksheet, ByVal pdf_filename As String)
pdf_sheetname.ExportAsFixedFormat _
Type:=xlTypePDF, _
fileName:=pdf_filename, _
Quality:=xlQualityStandard, _
IncludeDocProperties:=True, _
IgnorePrintAreas:=False, _
OpenAfterPublish:=True
End Sub
'///6.フルパスからファイル名を切り出し///
Public Sub GetAttachment(file_path As String)
Dim pos As String
pos = InStrRev(file_path, "\") 'filePathの文字列を後方から検索し、初めに見つかった「\」の位置を返す
UserForm1.TextBox_Attachments.Value = Mid(file_path, pos + 1) 'pos以降の文字列を全て返す
End Sub
'///7.メール送信///
Public Sub SendMail(ByVal arg_mailaddress As String, ByVal arg_mailaddresscc As Variant, ByVal arg_subject As Variant, ByVal arg_body As Variant, ByVal arg_attachmentsname As Variant, ByVal arg_attachmentspath As Variant)
Dim myOLApp As Object
Dim myData As Object
Dim r As Range
Dim rc As String
If IsNull(arg_mailaddresscc) Then arg_mailaddresscc = ""
If wsData.Range("G2") = "" Then
rc = MsgBox("件名:" & arg_subject & vbCrLf & "添付ファイル:" & arg_attachmentsname & vbCrLf & "でよろしいですか?", vbYesNo)
Else
rc = MsgBox("件名:" & arg_subject & vbCrLf & "添付ファイル:" & arg_attachmentsname & vbCrLf & "追加添付ファイル 有" & vbCrLf & "でよろしいですか?", vbYesNo)
End If
Select Case rc
Case vbYes
Set myOLApp = CreateObject("Outlook.Application") '※3
Set myData = myOLApp.CreateItem(0) '0 = MailItemオブジェクト
With myData
.BodyFormat = 1 '1 = olFormatPlain:テキスト形式
.To = arg_mailaddress
.CC = arg_mailaddresscc
.Subject = arg_subject
.Body = arg_body
.attachments.Add arg_attachmentspath 'デフォルト添付ファイル
On Error Resume Next
For Each r In Range("添付ファイル名") '追加添付ファイル
.attachments.Add r.Value
Next
.Send
End With
Set myData = Nothing
Set myOLApp = Nothing
MsgBox "メールを送信しました"
Case vbNo
MsgBox "送信を中止しました"
End Select
End Sub
Bookモジュール
ThisWorkBookモジュール にコピペして下さい。
Private Sub Workbook_Open()
If ActiveSheet.CodeName = "wsReport" Then
UserForm1.Show vbModeless
Range("A1").Activate
End If
Application.EnableEvents = False '※1
wsReport.Range("A1").Clear '※2
Application.EnableEvents = True
On Error Resume Next
Range("添付ファイル名").Clear '※3
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
If Sh.CodeName <> "wsData" Then
UserForm1.Show vbModeless
Range("A1").Activate
AppActivate Application.Caption '※4
End If
End Sub
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
If Sh.CodeName = "wsReport" Then UserForm1.Hide
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range) '※5
If Sh.CodeName = "wsReport" Then
If Intersect(Target, wsReport.Range("A1")) Is Nothing Then
Exit Sub
Else
UserForm1.TextBox_Subject = wsData.Range("C2")
Call GetAttachment(GetFilePath)
End If
End If
End Sub
- 7行目…Bookオープン時はWorkbook_SheetChangeイベントを走らせたくないので無効にしています。
- 8行目…入力データのクリアです。前回使用した日付と追加添付ファイルのパスをクリアします。
- 12行目…Range(“添付ファイル名“)が空だとエラーになるので回避しています。
- 19行目…フォーカスをユーザーフォーム から外してシートに移す処理です。フォーカスがユーザーフォーム に残ったままだとセル入力にワンクリック必要でイラっとするのでその対策です。
- 27行目~36行目…日報シートのセルA1(日付入力)を変更すると、件名と添付ファイル名を日付に合わせて更新する処理です。
19行目にSetFocus関数を使わなかった理由
以前、ユーザーフォーム からフォーカスを外すのにWindowsAPI のSetFocus関数 を使用したことがあります。
'API関数はDeclareステートメントで呼び出しグローバルで宣言する
Public Declare PtrSafe Function SetFocus Lib “user32” (ByVal hwnd As Long) As Long 'PtrSafe…64ビット対応
Sub demo1()
SetFocus Application.hwnd
End Sub
今回もこれで、と思ったもののエラーが返されました。「オブジェクトモジュール (ThisWorkBook やSheetモジュール )では使えません」との事。他に方法がないか探していたところ、MicroSoftのコミュニティでヒットしました。
モードレスなUserFormから下記のようにするのはどうでしょうか?(試していないので上手くいくかどうか分かりませんが・・・)
ActiveSheet.Range(“C5”).Select
AppActivate Application.Caption
https://answers.microsoft.com/ja-jp/msoffice/forum/all/excel/d3888bc5-70b8-4b4f-8eb4-ee4b07a358ae
力業感が否めません(誉め言葉)が、狙った通りの動きになりました。
Formモジュール デザイン
ユーザーフォーム を挿入し、ツールボックスを使ってレイアウトします。各コントロールの名称と配置は画像を参照して下さい。
コピペ用に各コントロールのオブジェクト名 を列挙します。
ComboBox_To、ComboBox_CC、TextBox_Subject、TextBox_Attachments、TextBox_AttachAdd、TextBox_LetterBody、CommandButton_ClipFile、CommandButton_Trash、CommandButton_SendMail
ComboBox_CCですが、下に「必要があれば追加して下さい」のラベル と白地のラベル を重ねています。ComboBox_CCが一番上になるように書式で順序を設定して下さい。「必要があれば追加して下さい」のラベル は少し小さめにすると位置の調整が楽になります。
Formモジュール コード
コードはこれをコピペして下さい。
'///1.初期化/// ※1
Private Sub UserForm_Initialize()
With ComboBox_To
.RowSource = "メール"
.BoundColumn = 2
.ListIndex = 0 'プロパティウィンドウで設定不可
End With
With ComboBox_CC
.RowSource = "メール"
.BoundColumn = 2
.BackStyle = fmBackStyleTransparent
End With
With TextBox_LetterBody
.MultiLine = True
.EnterKeyBehavior = True
.Value = wsData.Range("D2").Value 'プロパティウィンドウで設定不可
End With
TextBox_Attachments.Locked = True
TextBox_AttachAdd.Locked = True
End Sub
'///2.CCコンボボックスA/// ※2
Private Sub ComboBox_CC_Enter()
ComboBox_CC.BackStyle = fmBackStyleOpaque 'フォーカスを受け取った時は不透明にする
End Sub
'///3.CCコンボボックスB/// ※2
Private Sub ComboBox_CC_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If ComboBox_CC.Text = "" Then 'フォーカスが外れた時にコンボボックスに値がなければ
ComboBox_CC.BackStyle = fmBackStyleTransparent 'コンボボックスを透明にする=下のテキストボックスが見える
End If
End Sub
'///4.追加添付ファイル///
Private Sub CommandButton_ClipFile_Click()
Dim clipFile As Variant
Dim f As Variant
ChDrive wsData.Range("H2")
ChDir wsData.Range("I2") '※3
clipFile = Application.GetOpenFilename(MultiSelect:=True)
If IsArray(clipFile) Then '※4
For Each f In clipFile
With wsData.Cells(Rows.Count, 7).End(xlUp)
If .Row = 2 And .Value = "" Then
.Value = f
Else
.Offset(1, 0).Value = f
End If
If TextBox_AttachAdd.Text = "" Then
TextBox_AttachAdd = Dir(f)
Else
TextBox_AttachAdd = TextBox_AttachAdd.Text & " , " & Dir(f)
End If
End With
Next f
End If
End Sub
'///5.追加添付ファイル削除///
Private Sub CommandButton_Trash_Click()
TextBox_AttachAdd.Value = ""
On Error Resume Next 'Range("添付ファイル名")が空だとエラーになるので回避
Range("添付ファイル名").Clear
End Sub
'///6.メール送信///
Private Sub CommandButton_SendMail_Click()
Dim mailSubject As String, mailBody As String
If wsReport.Range("A1") = "" Then
MsgBox "日付を入力して下さい"
Exit Sub
ElseIf ComboBox_To.ListIndex = -1 Then
MsgBox "宛先を選択して下さい"
Exit Sub
End If
If MakeDirectory(GetSaveDir) Then Exit Sub 'フォルダ作成
If OverwriteFile(GetFilePath) Then Exit Sub 'ファイル上書き
Call OutputPDF(ActiveSheet, GetFilePath) 'PDF出力
mailSubject = TextBox_Subject
mailBody = TextBox_LetterBody
Call GetAttachment(GetFilePath) '添付ファイル名取得
Call SendMail(ComboBox_To, ComboBox_CC, mailSubject, mailBody, TextBox_Attachments, GetFilePath)
End Sub
- 2行目~23行目…大半のプロパティ がVBEのプロパティウィンドウ でも設定できます。私はサイズ、フォント、位置等をプロパティウィンドウ で設定し、その他はコードで書くようにしています。共有しないコードであれば、自分が分かりやすい方法で記述しましょう。可読性で考えるならコード>プロパティウィンドウ です
- 25行目~34行目…ComboBox_CCは未選択の状態だとグレー文字で「必要があれば追加して下さい」が表示されます。方法は、ラベル(必要があれば~が入力されたもの)の上にコンボボックス を重ね、コンボボックス を透明にします(初期化でプロパティ 設定済です)。コンボボックス がフォーカスされると透明をいったん解除し(不透明)、フォーカスが外れた時に値のあり/なしで透明/不透明の2択処理をします。アプリ等の入力フォームでよくみるアレを再現している訳です。
- 42行目…ファイルを開くダイアログを表示する処理は、wsDataシートで設定したフォルダが表示されます。表示したいフォルダがローカルPCではなく、ネットワークドライブにある場合はChDirステートメント は使えません。CurrentDirectoryプロパティ を使用して下さい。
- 45行目…If IsArray(clipFile) Thenはダイアログでファイルが選択された時にTrueになり、キャンセルされたときにFalseになります。Application.GetOpenFilenameでググれば丁寧な解説がヒットします。
以上です。とりあえずコピペで動く状態にはなっているので、後はご自身の環境に合わせてカスタムして下さい。ユーザーフォーム の挙動は、まだ詰められる要素があると思います。
テストはご自身のメールアドレスを宛先にして行うのが吉です。
コピペで一から作ると30分~1時間はかかると思います。結構なボリュームですが、興味ある方は是非マクロを動かしてみて下さい。可能ならダウンロード推奨です。