Excel VBAで「PDFを作って自動でメール送信」までやり切ろうとした時、最後の送信でRun‑time error 13(型が一致しません)に悩まされるケースはよくあります。本稿では、実務で頻出する原因を体系的に整理し、確実に解消する実装手順と堅牢なサンプルコードを提示します。添付の型、CDO初期化順序、Gmailの認証、フォルダー作成、文字コード、サイズ上限まで、現場で“落ちない”構成をまとめました。
「型が一致しません」エラーの正体と典型パターン
VBAのType mismatchは、期待される型と渡された値の型が一致しない時に発生します。PDF作成→メール送信の流れでは、以下の箇所で起きがちです。
- CDO.Message のプロパティに Range オブジェクトを渡してしまう(例:
.To = Range("E9")など。.Valueが必要) - .AddAttachment にファイルパス以外(フォルダーや未拡張子のまま)を渡す
- CDO.Configuration の更新前に .Send を呼んでしまい、内部的に不定の型扱いになる
- Date 型を文字列連結する際に暗黙変換が走って想定外の型になる(
Formatで明示整形が必要) - フォルダー未作成で PDF 出力に失敗し、
Attachmentが空文字のまま.AddAttachmentされる
原因と対処の対比表
| 症状 | 主な原因 | 対処 |
|---|---|---|
| .Send 直後にエラー13 | CDO 設定未適用(.Update忘れ) | Configuration.Fields へ設定→.Update→Message.Configuration に割当→最後に .Send |
| .To 設定でエラー13 | Range を直接代入 | Range("E9").Value の文字列を使う |
| .AddAttachment でエラー13 | ファイルパスが未完成/存在しない | フルパス文字列を作る。Dir(Attachment) <> ""で存在検証 |
| PDF 出力で停止 | 出力フォルダー未作成 | Dir(Path, vbDirectory)で確認し、なければMkDir |
| 日本語が文字化け | メッセージの文字コード未指定 | Mail.BodyPart.Charset = "utf-8"(または HTMLBodyPart) |
| Gmail 認証で失敗 | 通常パスワード/古い設定 | 二段階認証+アプリ パスワードを使う |
最短で直すなら:処理フローをこの順番に
- セルから文字列・Date として値を取得(
.Valueまたは.Value2) - 出力フォルダーを必ず作成確認
- ファイル名の禁止文字を除去し、PDFを
ExportAsFixedFormatで出力 CDO.Configuration.Fieldsに SMTP 設定をすべて入れて.UpdateCDO.Messageを生成し、.Configurationに上記を割当- 宛先・件名・本文・添付(いずれも文字列)を設定
- 本文の文字コードを
utf-8に指定(日本語の実務では安定) - 最後に
.Send - 後始末(オブジェクト破棄・画面更新復帰・メッセージ)
動く完全版サンプル(早期バインディング/CDO)
以下は参照設定に「Microsoft CDO for Windows 2000 Library」を加えて使う版です。参照が難しい環境では次章の遅延バインディング版をご利用ください。
Option Explicit
'=== 設定:自社環境に合わせて変更 ===
Private Const ROOT As String = "D:\Members Invoice Records\"
Private Const SMTP_USER As String = "[email protected]"
Private Const SMTP_PASS As String = "(Gmailのアプリ パスワード)"
Public Sub SaveAsPDFandEmail()
On Error GoTo errHandle
Dim ws As Worksheet
Set ws = ActiveSheet
'--- 値の取得(必ず .Value) ---
Dim memID As String
Dim memEAddr As String
Dim memName As String
Dim invDate As Date
memID = CStr(ws.Range("C12").Value)
invDate = CDate(ws.Range("H12").Value)
memEAddr = CStr(ws.Range("E9").Value)
memName = CStr(ws.Range("E10").Value)
'--- パスの構築(フォルダー作成) ---
Dim basePath As String, path As String, file As String, attachment As String
basePath = ROOT & SanitizeFileName(memID) & "\" & Year(invDate) & "\"
EnsureDirExists basePath
file = Format(invDate, "yyyy-mm-dd") & "---" & SanitizeFileName(memID)
attachment = basePath & file & ".pdf"
'--- PDF 出力(印刷範囲に従う) ---
ws.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=attachment, _
Quality:=xlQualityStandard, _
IncludeDocProperties:=True, _
IgnorePrintAreas:=False, _
OpenAfterPublish:=False
If Dir(attachment) = "" Then
Err.Raise 53, , "PDF の出力に失敗しました: " & attachment
End If
'--- CDO 設定 ---
Dim cfg As CDO.Configuration
Set cfg = New CDO.Configuration
With cfg.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.gmail.com"
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 465 'SSL
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = SMTP_USER
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = SMTP_PASS
.Update '★ これを忘れるとエラー13の温床
End With
'--- メール作成・送信 ---
Dim msg As CDO.Message
Set msg = New CDO.Message
Set msg.Configuration = cfg
With msg
.From = SMTP_USER
.To = memEAddr '必ず文字列
.Subject = Format(invDate, "yyyy年m月") & " 請求書"
.TextBody = "Dear " & memName & "," & vbCrLf & vbCrLf & _
"請求書を添付いたします。ご確認をお願いいたします。" & vbCrLf & _
"- " & ThisWorkbook.Name
.BodyPart.Charset = "utf-8" '文字化け防止
.AddAttachment attachment '必ずフルパス文字列
.Send
End With
MsgBox "メール送信が完了しました。", vbInformation
cleanExit:
Set msg = Nothing: Set cfg = Nothing
Exit Sub
errHandle:
MsgBox "エラー(" & Err.Number & "): " & Err.Description, vbExclamation
Resume cleanExit
End Sub
'--- フォルダーを再帰的に作成 ---
Private Sub EnsureDirExists(ByVal targetPath As String)
If Len(targetPath) = 0 Then Exit Sub
If Right$(targetPath, 1) = "\" Then targetPath = Left$(targetPath, Len(targetPath) - 1)
Dim parts() As String: parts = Split(targetPath, "\")
Dim i As Long, p As String: p = ""
For i = LBound(parts) To UBound(parts)
p = p & parts(i) & "\"
If Dir(p, vbDirectory) = "" Then MkDir p
Next
End Sub
'--- ファイル名に使えない文字を安全化 ---
Private Function SanitizeFileName(ByVal s As String) As String
Dim bad As Variant, i As Long
bad = Array("\", "/", ":", "*", "?", """", "<", ">", "|")
For i = LBound(bad) To UBound(bad)
s = Replace$(s, bad(i), "_")
Next
SanitizeFileName = Trim$(s)
End Function
要点:.Fields.Update→Message.Configuration=cfg→宛先・本文・添付→.Send の順、.Toや.AddAttachmentは必ず文字列、PDF の存在検証、フォルダー作成、文字コード指定を徹底します。
参照設定なしで動かす(遅延バインディング版)
CDO ライブラリの参照を追加できない環境では、CreateObject を用いて遅延バインディングにします。型は Object になりますが、配布性が高く、コンパイル時参照の差異に起因するエラーを避けられます。
Option Explicit
Public Sub SendWithCDO_LateBinding(ByVal attachment As String, ByVal toAddr As String, ByVal subject As String, ByVal body As String)
On Error GoTo errHandle
Dim cfg As Object, msg As Object
Set cfg = CreateObject("CDO.Configuration")
With cfg.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.gmail.com"
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 465
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "[email protected]"
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "(アプリ パスワード)"
.Update
End With
Set msg = CreateObject("CDO.Message")
Set msg.Configuration = cfg
msg.From = "[email protected]"
msg.To = CStr(toAddr) '← 文字列にする
msg.Subject = CStr(subject)
msg.TextBody = CStr(body)
msg.BodyPart.Charset = "utf-8"
msg.AddAttachment CStr(attachment)
msg.Send
cleanExit:
Set msg = Nothing: Set cfg = Nothing
Exit Sub
errHandle:
MsgBox "送信失敗: " & Err.Number & " / " & Err.Description, vbExclamation
Resume cleanExit
End Sub
Gmail(smtp.gmail.com)を使う際の実務ポイント
| 項目 | 設定・解説 |
|---|---|
| 認証方式 | 二段階認証を有効化し、アプリ パスワード(16桁)を使う。通常パスワードは不可。 |
| ポート/暗号化 | 465 + smtpusessl=True(SSL/Implicit TLS) 587 + smtpusessl=False + /smtpstarttls=True(STARTTLS) |
| 送信サイズ上限 | 添付含めおおむね25MBが目安。超える場合は圧縮(PDF最適化)や共有リンクの検討。 |
| 文字コード | .BodyPart.Charset = "utf-8"(.HTMLBodyPart.Charset でも可)。 |
| 運用上の注意 | 組織のポリシーで SMTP が制限されるとブロックされることがある。アカウント側のセキュリティ通知メールも確認。 |
STARTTLS(587)を使う例
With cfg.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 587
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = False
.Item("http://schemas.microsoft.com/cdo/configuration/smtpstarttls") = True
.Update
End With
「Range と文字列の混同」を確実に断つ
CDO.Message の .To・.Subject・.AddAttachment・.From はすべて文字列です。Range はオブジェクトであり、未評価のまま渡すとエラー13になります。以下の早見表のとおり、明示変換を徹底しましょう。
| やりがち | 正しい書き方 | 理由 |
|---|---|---|
.To = Range("E9") | .To = CStr(Range("E9").Value) | Range はオブジェクト。.Valueで中身を取り出し、文字列化する。 |
.AddAttachment Path & File | .AddAttachment Path & File & ".pdf" | フルパスのファイルを渡す必要がある。 |
file = invDate & "---" & memID | file = Format(invDate, "yyyy-mm-dd") & "---" & memID | Date を文字列に明示整形してから連結する。 |
PDF の出力品質と安全なファイル名生成
IgnorePrintAreas:=Falseで印刷範囲を活用。- ファイル名に使えない文字(
\ / : * ? " < > |)はSanitizeFileNameで置換。 - 大量出力では、
Application.ScreenUpdatingやDisplayAlertsを一時停止しつつ、エラー時は必ず復帰。
堅牢さを高めるエラーハンドリングとログ
実務では「どこで失敗したか」を素早く突き止める必要があります。以下の工夫が有効です。
- プロシージャ先頭の
On Error GoToと 単一の出口(cleanExit:) - 重要な変数(宛先・添付パス)は
Debug.Printで即時ウィンドウへ出力 - ファイル存在チェック:
If Dir(attachment) = "" Then Err.Raise 53, , "..." - ログ出力を行う場合は
FreeFileとAppendで追記
Private Sub AppendLog(ByVal message As String)
Dim ff As Integer: ff = FreeFile
Open ThisWorkbook.Path & "\mail_log.txt" For Append As #ff
Print #ff, Format(Now, "yyyy-mm-dd HH:NN:SS"); " - "; message
Close #ff
End Sub
CDO を避けたい場合の代替:Outlook オートメーション
組織ポリシーや将来の互換性を考慮して、Outlook を経由する方法も現実的です。Outlook がインストール済みで MAPI が使える環境では、以下のようにできます(送信プロファイルによる差異に留意)。
Option Explicit
Public Sub SendViaOutlook(ByVal attachment As String, ByVal toAddr As String, ByVal subject As String, ByVal body As String)
On Error GoTo errHandle
Dim olApp As Object, mail As Object
Set olApp = CreateObject("Outlook.Application")
Set mail = olApp.CreateItem(0) 'olMailItem
With mail
.To = CStr(toAddr)
.Subject = CStr(subject)
.Body = CStr(body)
.Attachments.Add CStr(attachment)
.Send '表示して確認したいときは .Display
End With
cleanExit:
Set mail = Nothing: Set olApp = Nothing
Exit Sub
errHandle:
MsgBox "Outlook 送信失敗: " & Err.Number & " / " & Err.Description, vbExclamation
Resume cleanExit
End Sub
Outlook 経由は UI 連携や署名適用が容易で、CDO のような SMTP 設定をコードに埋め込む必要がありません。反面、クライアント依存度が上がるため、サーバーサイド実行やロボット環境では CDO/API 連携の方が適します。
メール送信前の自動チェック(チェックリスト)
- 参照・バインディング:CDO を使う場合は参照追加 or 遅延バインディングに統一
- 宛先:空でない・
@を含む・禁止文字なし - 添付:
Dir(attachment) <> ""で存在確認済み - フォルダー:年度フォルダー自動作成、権限OK
- 文字コード:UTF-8 指定で日本語安全
- サイズ:概ね 25MB 以内
- 本番向け:
.Sendの前にテストモードで.Toを自分に差し替えた試験を1通
よくある質問(トラブル別ミニガイド)
| 現象 | 確認ポイント | 解決のヒント |
|---|---|---|
| 型が一致しません(エラー13) | .To/.AddAttachment へ Range を渡していないか | .Value で取り出し CStr で文字列化、.Fields.Update の順序 |
| PDF が出力されない | フォルダー有無/印刷範囲/アクティブシート | EnsureDirExists → IgnorePrintAreas:=False → Export 実行後に Dirで検証 |
| 本文が文字化け | 文字コード指定の有無 | .BodyPart.Charset = "utf-8" を追加。HTML なら .HTMLBodyPart.Charset も。 |
| Gmail がブロックする | 二段階認証/アプリ パスワード/端末の新規ログイン通知 | アプリ パスワード再発行、送信間隔を空ける、アカウントのセキュリティ通知を確認 |
| 複数宛先に送りたい | アドレス区切り | .To = "[email protected],[email protected]" のようにカンマ区切り。BCC は .BCC を使用。 |
追加テクニック:HTMLメール・BCC・複数添付
With msg
.To = "[email protected]"
.BCC = "[email protected]"
.Subject = "ご請求書のご案内"
.HTMLBody = "<p>平素よりお世話になっております。</p><p>請求書を添付いたします。</p>"
.HTMLBodyPart.Charset = "utf-8"
.AddAttachment attachment
.AddAttachment "D:\common\お知らせ.pdf"
.Send
End With
HTMLBody を使う場合は HTMLBodyPart.Charset も指定して統一します。
安全に運用するための設計指針
- 資格情報の分離:ユーザー名・アプリ パスワードはモジュール定数ではなく、暗号化ストレージや別ファイル(権限限定)に退避。
- テスト用スイッチ:開発時は
Const IS_TEST As Boolean = Trueで.Toを自分宛に置換。 - 失敗時の再送:ログにトラッキングID(日時+会員ID)を書き、手動再送に活用。
- 環境差分:32/64 ビットやポリシーに左右されにくい「遅延バインディング」を基本に。
- Mac について:CDO は Windows 専用。Mac Office では Outlook 経由や別APIを検討。
まとめ:まず「型」と「順序」を正す
メール送信時の「型が一致しません」は、CDO 設定の適用順序とRange と文字列の取り扱いを正せば解消します。フォルダー作成と PDF の存在検証、UTF-8 指定、Gmail のアプリ パスワード対応までを一連の“型”としてテンプレ化しておけば、同種の自動メール案件は横展開で安定運用できます。本稿のサンプルをベースに、貴社のテンプレートへ落とし込み、現場で「落ちない」自動化を実現してください。
付録:最小再現コード(原因切り分け用)
原因が掴みにくい時は、以下の最小コードで「送信だけ」を先に通し、次に PDF 生成を足して切り分けると早いです。
Sub Minimal_SendTest()
Dim cfg As Object, msg As Object
Set cfg = CreateObject("CDO.Configuration")
With cfg.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.gmail.com"
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 465
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "[email protected]"
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "(アプリ パスワード)"
.Update
End With
Set msg = CreateObject("CDO.Message")
Set msg.Configuration = cfg
msg.From = "[email protected]"
msg.To = "[email protected]"
msg.Subject = "Send Test"
msg.TextBody = "This is a test."
msg.BodyPart.Charset = "utf-8"
msg.Send
End Sub
このテストが通れば、残る課題は PDF の生成~添付の間に絞り込めます。「.Toは文字列・.Updateは必須・.AddAttachmentはフルパス」の三点を常にチェックしましょう。
付録:メール入力の軽いバリデーション例
厳密な RFC 準拠ではありませんが、業務内の単純なケアレスミス排除には次の程度で十分なことが多いです。
Private Function LooksLikeEmail(ByVal s As String) As Boolean
s = Trim$(s)
LooksLikeEmail = (Len(s) >= 6) And (InStr(1, s, "@") > 1) And (InStrRev(s, ".") > InStr(1, s, "@") + 1)
End Function
LooksLikeEmail が False のときは送信前にメッセージを出して中断すれば、宛先ミスに伴う「型の不一致」や配送失敗をまとめて防げます。
今回の主張(超要点)
- 順序の正規化:
CDO.Configuration.Fields→.Update→Message.Configuration→ 宛先・本文・添付 →.Send - 型の明確化:宛先・添付・件名は常に文字列。
Rangeは必ず.Value経由。 - 運用の安全化:フォルダー作成・PDF存在検証・UTF-8・アプリ パスワード・ログ。
この3点を守れば、Excel VBA での PDF 自動添付メールは安定稼働します。

コメント