'標準モジュール
'標準モジュール
Option Explicit
'API(GetDim関数で使用)
Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByVal psa As Long) As Long
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal ptr As Long, ByRef ert As Long)
'メールの保存に成功したグループと失敗したグループをコレクションしておく
Private mSucc As Collection
Private mfail As Collection
'collectionに登録する内容のクラス
'Class1
'Public itm As Object
'Public msgFileName As String
'Public comment As String
'-----------------------------
'msgアイテムのプロパティを格納する構造体
Private Type typMsgPrps
SentOn As Date
ReceivedTime As Date
Subject As String
SenderName As String
ReceiveByName As String
Body As String
attCnt As Long
attfName() As String
TO As String
CC As String
BCC As String
End Type
'ActiveFolder.Items.Item
Public Sub CrrFldrMsgSave()
Dim i As Long
Dim msg As Object
Dim myExplorer As Explorer
Dim strOutFolder As String
Dim strSaveName As String
Dim rpt As New Class1
Dim Fso As New Scripting.FileSystemObject
Dim ttlCnt As Long
On Error Resume Next
Set mSucc = New Collection
Set mfail = New Collection
Set myExplorer = Outlook.ActiveExplorer
strOutFolder = FolderPicker
If strOutFolder = "" Then
Exit Sub
End If
If myExplorer.CurrentFolder.Items.Count = 0 Then
MsgBox "フォルダは空です"
Exit Sub
End If
ttlCnt = myExplorer.CurrentFolder.Items.Count
For i = 1 To ttlCnt
Set msg = myExplorer.CurrentFolder.Items.Item(i)
strSaveName = mkFileName(msg)
Call saveCore(strOutFolder, strSaveName, msg, Fso)
With rpt
Set .Itm = msg
.msgFileName = strSaveName & ".msg"
.comment = ""
End With
If Err.Number = 0 Then
msg.SaveAs strOutFolder & "\" & strSaveName & ".msg"
mSucc.Add rpt
Else
rpt.comment = Err.Number & ":" & Err.Description
mfail.Add rpt
Err.Clear
End If
Debug.Print i & "/" & ttlCnt & "済み"
Next
On Error GoTo 0
Call Report(rpt)
Set mSucc = Nothing
Set mfail = Nothing
Set rpt = Nothing
Set Fso = Nothing
End Sub
'ActiveExplorer.Selection
Public Sub SelectedMsgSave()
Dim i As Long
Dim msg As Object
Dim Itms As Outlook.Selection
Dim strOutFolder As String
Dim strSaveName As String
Dim rpt As New Class1
Dim Fso As New Scripting.FileSystemObject
Dim ttlCnt As Long
On Error Resume Next
Set mSucc = New Collection
Set mfail = New Collection
strOutFolder = FolderPicker
If strOutFolder = "" Then
Exit Sub
End If
Set Itms = Outlook.ActiveExplorer.Selection
ttlCnt = Itms.Count
If ttlCnt = 0 Then
MsgBox "保存するメールを選択されていません"
Exit Sub
End If
For i = 1 To ttlCnt
Set msg = Itms.Item(i)
strSaveName = mkFileName(msg)
Call saveCore(strOutFolder, strSaveName, msg, Fso)
With rpt
Set .Itm = msg
.msgFileName = strSaveName & ".msg"
.comment = ""
End With
If Err.Number = 0 Then
msg.SaveAs strOutFolder & "\" & strSaveName & ".msg"
mSucc.Add rpt
Else
rpt.comment = Err.Number & ":" & Err.Description
mfail.Add rpt
Err.Clear
End If
Debug.Print i & "/" & ttlCnt & "済み"
Next
On Error GoTo 0
Call Report(rpt)
Set mSucc = Nothing
Set mfail = Nothing
Set rpt = Nothing
Set Fso = Nothing
End Sub
Private Sub saveCore( _
strOutFolder As String, _
strSaveName As String, _
msg As Object, _
Fso As Scripting.FileSystemObject)
Dim j As Long
Dim tmpName As String
Dim tmpName2 As String
If Fso.FileExists(strOutFolder & "\" & strSaveName & ".msg") Then
If IsEqualMail(strOutFolder & "\" & strSaveName & ".msg", msg) Then
Err.Raise 514, , "同じメールが既に保存されています"
Else
Stop
j = 2
tmpName = strSaveName
tmpName2 = tmpName
Do _
Until Fso.FileExists( _
strOutFolder & "\" & tmpName2 & ".msg") = False
tmpName2 = tmpName & "(" & j & ")"
j = j + 1
Loop
strSaveName = tmpName2
End If
End If
End Sub
'結果報告
Sub Report(rpt As Class1)
Dim i As Long
'成功
If mSucc.Count > 0 Then
Debug.Print "以下は正常に保存されました。(" & mSucc.Count & " 件)" & vbNewLine & _
"------------------------------------------"
For i = 1 To mSucc.Count
Set rpt = mSucc.Item(i)
Debug.Print rpt.msgFileName
Next
End If
'失敗
If mfail.Count > 0 Then
Debug.Print "以下、保存に失敗しました(" & mfail.Count & " 件)" & vbNewLine & _
"------------------------------------------"
For i = 1 To mfail.Count
Set rpt = mfail.Item(i)
Debug.Print rpt.comment, rpt.msgFileName
Next
End If
End Sub
'アイテムからファイル名を作成
Private Function mkFileName(Itm As Object) As String
Dim sntOn As Date
Dim strSntOn As String
Dim sbj As String
sntOn = Itm.SentOn
sbj = Itm.Subject
sntOn = VBA.DateAdd("s", 30, sntOn)
strSntOn = Format$(sntOn, "yyyymmdd(aaa)hhmm")
sbj = Replace(sbj, " ", "_")
sbj = Replace(sbj, ":", "_")
sbj = Replace(sbj, "/", "_")
sbj = Replace(sbj, "\", "_")
sbj = Replace(sbj, "?", "_")
sbj = Replace(sbj, "<", "_")
sbj = Replace(sbj, ">", "_")
sbj = Replace(sbj, "*", "_")
sbj = Replace(sbj, """", "_")
sbj = Trim$(sbj)
mkFileName = strSntOn & sbj
End Function
'Microsoft Shell Controls And Automation参照設定要
'フォルダーピッカー
Function FolderPicker() As String
Dim Folder As Shell32.Folder
Dim Shell As Shell32.Shell
Const cnsCaption As String = "保存先フォルダを選択して下さい"
Const cnsWndParent = 0
Const cnsUlFlags = 0
Set Shell = New Shell32.Shell
Set Folder = Shell.BrowseForFolder(cnsWndParent, cnsCaption, cnsUlFlags)
If Folder Is Nothing Then
FolderPicker = ""
Else
FolderPicker = Folder.Items.Item.Path
End If
End Function
'タイムスタンプ
Private Function TimeSamp() As String
Dim str As String
str = Format$(Now(), "yyyymmddhhmmss")
TimeSamp = str
End Function
'保存フォルダー内ITEMデータリスト表示
Sub showExcelList()
Dim xlAPP As New Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.worksheet
Dim FoundFiles() As String
Dim i As Long
Dim tgtFldr As String
Dim Fso As New Scripting.FileSystemObject
Const cnsCptXlfdr As String = "アイテムリスト"
Set wb = xlAPP.Workbooks.Add
xlAPP.DisplayAlerts = False
With wb
Do Until .Sheets.Count = 1
.Sheets(.Sheets.Count).Delete
Loop
End With
xlAPP.DisplayAlerts = True
Set ws = wb.Sheets(1)
ws.Name = TimeSamp & "_" & cnsCptXlfdr
tgtFldr = FolderPicker
If tgtFldr = "" Then
Exit Sub
End If
FoundFiles() = GetFiles(Fso.GetFolder(tgtFldr))
If GetDim(FoundFiles) > 0 Then
For i = 0 To UBound(FoundFiles)
Call PutMsgPrps(FoundFiles(i), ws, i + 1)
Next
Else
ws.Cells(1).Value = tgtFldr & " に .msg ファイルは保存されていません"
End If
xlAPP.Visible = True
Set ws = Nothing
Set wb = Nothing
Set xlAPP = Nothing
Set Fso = Nothing
End Sub
'配列の中身を判定する関数
Private Function GetDim(ByVal sa As Variant) As Long
If IsArray(sa) Then
Dim p As Long
GetMem4 VarPtr(sa) + 8, p
If p = 0 Then
GetDim = 0 '初期化されていない配列
Else
GetDim = SafeArrayGetDim(p) '次元数
End If
Else
GetDim = 1 '配列では無い
End If
End Function
'再帰処理なしにサブディレクトリまで検索するコード
'Windows Script Host Object Modelに参照設定
Private Function GetFiles(Filename As String) As String()
Dim tmpPath As String
Dim sCmd As String
Dim ko As Long
Dim ff() As String
tmpPath = Environ$("Temp") & "\Dir.tmp" '作業用ファイル名
sCmd = "DIR """ & Filename & """ /b/s > """ & tmpPath & """" 'Dirコマンド
Dim WSO As New WshShell
With WSO
ko = .Run("CMD /C" & sCmd, 7, True)
End With
Dim io As Integer
Dim buf() As Byte
Dim flg As Boolean
io = FreeFile()
Open tmpPath For Binary As io
If LOF(io) > 0 Then
flg = True
ReDim buf(1 To LOF(io))
Get #io, , buf
End If
Close io
Kill tmpPath
If flg Then
ff() = Split(StrConv(buf, vbUnicode), vbCrLf)
End If
GetFiles = ff()
End Function
'ワークシートにmsgファイル情報を出力
Private Sub PutMsgPrps(msgPth As String, ws As Excel.worksheet, Rw As Long)
Dim msgPrps As typMsgPrps
Dim Fso As New Scripting.FileSystemObject
msgPrps = GetItmPrps(msgPth)
With ws
With .Cells(Rw, 1)
.Value = Fso.GetBaseName(msgPth)
.Hyperlinks.Add .Range(.Cells(Rw, 1)), msgPth
End With
.Cells(Rw, 4).Value = msgPrps.SentOn
.Cells(Rw, 5).Value = msgPrps.ReceivedTime
.Cells(Rw, 6).Value = msgPrps.Subject
.Cells(Rw, 7).Value = msgPrps.SenderName
.Cells(Rw, 8).Value = msgPrps.ReceiveByName
.Cells(Rw, 9).Value = msgPrps.Body
Dim i As Long
.Cells(Rw, 10).Value = msgPrps.attCnt
If msgPrps.attCnt > 0 Then
If msgPrps.attCnt > 1 Then
For i = 1 To msgPrps.attCnt
.Cells(Rw, 10).Value = _
.Cells(Rw, 10).Value & "/" & _
msgPrps.attfName(i)
Next i
Else
.Cells(Rw, 10).Value = _
msgPrps.attfName(i)
End If
End If
.Cells(Rw, 11).Value = msgPrps.TO
.Cells(Rw, 12).Value = msgPrps.CC
.Cells(Rw, 13).Value = msgPrps.BCC
End With
End Sub
'文字列中のUNICODE(でしか表現できない)文字を"□"に置き換えて返す
Function cnvUnicodeRplcRect(str As String) As String
Dim c As String
Dim strRslt As String
Dim i As Long
Const altChr As String = "□"
strRslt = str
For i = 1 To Len(strRslt)
c = Chr(Asc(Mid$(str, i, 1)))
If c = "?" Then
strRslt = VBA.Replace(strRslt, ChrW(AscW(Mid$(str, i, 1))), altChr)
End If
Next
cnvUnicodeRplcRect = strRslt
End Function
'フォルダーに保存された msgファイルパスを渡してアイテムのプロパティを返す
Private Function GetItmPrps(msgPth As String) As typMsgPrps
Dim oNamespace As NameSpace
Dim oItem As Object
Dim oFolder As MAPIFolder
Dim i As Long
Const cnsDiscard = 1 '破棄
'Messaging Application Program Interface
Set oNamespace = Outlook.GetNamespace("MAPI")
'受信トレイ フォルダ
Set oFolder = oNamespace.GetDefaultFolder(6) 'olFolderInbox=6
'msgファイルを開く
If CInt(Left$(Application.Version, 2)) > 11 Then
Set oItem = oNamespace.OpenSharedItem(msgPth) '2007~
Else
Set oItem = Outlook.CreateItemFromTemplate(msgPth)
End If
With GetItmPrps
.attCnt = oItem.Attachments.Count
If .attCnt > 0 Then
ReDim .attfName(1 To .attCnt)
For i = 1 To .attCnt
.attfName(i) = _
oItem.Attachments(i).Filename
Next
Else
ReDim .attfName(1 To 1)
.attfName(1) = ""
End If
.BCC = oItem.BCC
.Body = cleanEndCrLf(oItem.Body)
.CC = oItem.CC
.ReceiveByName = oItem.ReceivedByName
.ReceivedTime = oItem.ReceivedTime
.SenderName = oItem.SenderName
.SentOn = oItem.SentOn
.Subject = oItem.Subject
.TO = oItem.TO
End With
oItem.Close cnsDiscard
Set oItem = Nothing
Set oItem = Nothing
Set oFolder = Nothing
Set oNamespace = Nothing
End Function
'(MailItem|MeetingItem)アイテムの中身を比較
'差異があれば False なければ True
Private Function IsEqualMail( _
sameNameFile As String, crrntObj As Object) As Boolean
Dim prps As typMsgPrps 'ローカルファイルから取得したメールプロパティ
Dim crntPrps As typMsgPrps 'カレントフォルダから取得したメールプロパティ
Dim attnames() As String
Dim crrntAttCnt As Long
Dim i As Long, j As Long
Dim sameFlg As Boolean
prps = GetItmPrps(sameNameFile)
If prps.BCC <> crrntObj.BCC Then
IsEqualMail = False
Exit Function
End If
Stop
' Dim xlAPP As New Excel.Application
' Dim wb As Workbook
' Dim ws As worksheet
' Set wb = xlAPP.Workbooks.Add
' Set ws = wb.Sheets(1)
' ws.Cells(1).Value = prps.Body
' ws.Cells(2).Value = crrntObj.Body
' xlAPP.Visible = True
If prps.Body <> cleanEndCrLf(crrntObj.Body) Then
IsEqualMail = False
Exit Function
End If
If prps.CC <> crrntObj.CC Then
IsEqualMail = False
Exit Function
End If
If prps.CC <> crrntObj.CC Then
IsEqualMail = False
Exit Function
End If
If prps.SenderName <> crrntObj.SenderName Then
IsEqualMail = False
Exit Function
End If
If prps.SentOn <> crrntObj.SentOn Then
IsEqualMail = False
Exit Function
End If
If prps.Subject <> crrntObj.Subject Then
IsEqualMail = False
Exit Function
End If
If prps.TO <> crrntObj.TO Then
IsEqualMail = False
Exit Function
End If
If prps.attCnt <> crrntObj.Attachments.Count Then
IsEqualMail = False
Exit Function
End If
crrntAttCnt = crrntObj.Attachments.Count
If crrntAttCnt > 0 Then
ReDim attnames(1 To crrntAttCnt)
For i = 1 To crrntAttCnt
sameFlg = False
For j = 1 To prps.attCnt
If _
crrntObj.Attachments(i).Filename = _
prps.attfName(j) Then
sameFlg = True
Exit For
End If
If sameFlg = False Then
IsEqualMail = False
Exit Function
End If
Next
Next
End If
IsEqualMail = True
End Function
'Bodyを取得する際の文末に改行が入ったり入らなかったりするので
'文末に改行があれば取り除いて返す
Public Function cleanEndCrLf(str As String) As String
Dim strTmp As String
strTmp = str
Do While Right$(strTmp, 1) = vbCr Or Right$(strTmp, 1) = vbLf
strTmp = Left$(strTmp, Len(strTmp) - 1)
Loop
cleanEndCrLf = strTmp
End Function
yamaV1.02β