'============================================================= ' ファイルコピーツール VBAマクロ ' 使い方: Alt+F11 → 挿入 → 標準モジュール → このコードを貼り付け '============================================================= Option Explicit ' --- 定数 --- Private Const SHEET_NAME As String = "ファイルコピー" Private Const ROW_SRC As Long = 3 ' コピー元パス行 Private Const ROW_DST As Long = 4 ' コピー先パス行 Private Const COL_PATH As Long = 2 ' パス列 (B列) Private Const ROW_FILE_START As Long = 8 ' ファイル名開始行 Private Const COL_NO As Long = 1 ' No.列 (A列) Private Const COL_FILE As Long = 2 ' ファイル名列 (B列) Private Const COL_RESULT As Long = 3 ' 結果列 (C列) '============================================================= ' メインルーチン '============================================================= Public Sub FileCopyMain() Dim ws As Worksheet Dim srcFolder As String Dim dstFolder As String Dim fileNames() As String Dim fileCount As Long Dim i As Long On Error GoTo ErrHandler ' シート取得 Set ws = ThisWorkbook.Worksheets(SHEET_NAME) ' パス取得・整形 srcFolder = Trim(CStr(ws.Cells(ROW_SRC, COL_PATH).Value)) dstFolder = Trim(CStr(ws.Cells(ROW_DST, COL_PATH).Value)) ' 末尾に \ を付与 If Right(srcFolder, 1) <> "\" Then srcFolder = srcFolder & "\" If Right(dstFolder, 1) <> "\" Then dstFolder = dstFolder & "\" ' --- バリデーション --- ' 空チェック If srcFolder = "\" Or dstFolder = "\" Then MsgBox "コピー元フォルダとコピー先フォルダを入力してください。", vbExclamation, "入力エラー" Exit Sub End If ' コピー先がローカルドライブかチェック If Not IsLocalPath(dstFolder) Then MsgBox "コピー先はローカルドライブ(C:\, D:\ 等)を指定してください。" & vbCrLf & _ "ネットワークパス(\\で始まるパス)は指定できません。" & vbCrLf & vbCrLf & _ "現在の指定: " & dstFolder, vbExclamation, "コピー先エラー" Exit Sub End If ' コピー元フォルダ存在チェック If Dir(srcFolder, vbDirectory) = "" Then MsgBox "コピー元フォルダが見つかりません:" & vbCrLf & srcFolder, vbExclamation, "パスエラー" Exit Sub End If ' コピー先フォルダ存在チェック If Dir(dstFolder, vbDirectory) = "" Then Dim ans As VbMsgBoxResult ans = MsgBox("コピー先フォルダが存在しません。作成しますか?" & vbCrLf & dstFolder, _ vbYesNo + vbQuestion, "フォルダ作成") If ans = vbYes Then MkDirRecursive dstFolder Else Exit Sub End If End If ' ファイル名一覧を取得 fileCount = GetFileList(ws, fileNames) If fileCount = 0 Then MsgBox "コピー対象ファイルが1つも入力されていません。", vbExclamation, "入力エラー" Exit Sub End If ' 結果列クリア ClearResults ws ' --- 実行前確認 --- Dim confirmMsg As String confirmMsg = "以下の内容でファイルコピーを実行します。" & vbCrLf & vbCrLf confirmMsg = confirmMsg & "【コピー元】" & vbCrLf & srcFolder & vbCrLf & vbCrLf confirmMsg = confirmMsg & "【コピー先】" & vbCrLf & dstFolder & vbCrLf & vbCrLf confirmMsg = confirmMsg & "【対象ファイル数】" & fileCount & " 件" & vbCrLf & vbCrLf confirmMsg = confirmMsg & "実行してよろしいですか?" If MsgBox(confirmMsg, vbYesNo + vbQuestion, "実行確認") <> vbYes Then Exit Sub End If ' --- コピー元ファイル一覧をキャッシュ(ネットワーク負荷軽減) --- ' ネットワークドライブは Dir() が遅いため、1回だけ列挙してDictionaryに格納 Application.StatusBar = "コピー元フォルダを読み込み中(ネットワークドライブの場合、時間がかかります)..." DoEvents Dim srcFileDict As Object Set srcFileDict = CreateObject("Scripting.Dictionary") srcFileDict.CompareMode = vbTextCompare ' 大文字小文字を無視 Dim dirFile As String dirFile = Dir(srcFolder & "*.*") Do While dirFile <> "" If Not srcFileDict.Exists(LCase(dirFile)) Then srcFileDict.Add LCase(dirFile), dirFile ' Key=小文字, Value=実際の名前 End If dirFile = Dir() Loop Application.StatusBar = False ' --- 既存ファイル上書き確認 --- Dim overwriteList As String Dim overwriteCount As Long overwriteCount = 0 overwriteList = "" For i = 0 To fileCount - 1 Dim actualName As String actualName = FindInCache(srcFileDict, fileNames(i)) If actualName <> "" Then If Dir(dstFolder & actualName) <> "" Then overwriteCount = overwriteCount + 1 overwriteList = overwriteList & " " & actualName & vbCrLf End If End If Next i If overwriteCount > 0 Then Dim owMsg As String owMsg = "コピー先に以下の " & overwriteCount & " ファイルが既に存在します。" & vbCrLf owMsg = owMsg & "上書きしてよろしいですか?" & vbCrLf & vbCrLf owMsg = owMsg & overwriteList If MsgBox(owMsg, vbYesNo + vbExclamation, "上書き確認") <> vbYes Then Exit Sub End If End If ' --- コピー実行 --- Application.StatusBar = "ファイルコピー処理中..." Application.ScreenUpdating = False Dim successCount As Long, failCount As Long, skipCount As Long successCount = 0: failCount = 0: skipCount = 0 For i = 0 To fileCount - 1 Dim rowNum As Long rowNum = ROW_FILE_START + i DoEvents ' UIの応答を維持 Application.StatusBar = "コピー中... (" & (i + 1) & "/" & fileCount & ") " & fileNames(i) ' キャッシュから大文字小文字を無視して検索 actualName = FindInCache(srcFileDict, fileNames(i)) If actualName = "" Then ' ファイルが見つからない SetResult ws, rowNum, "見つかりません", RGB(200, 0, 0) failCount = failCount + 1 Else ' コピー実行 On Error Resume Next FileCopy srcFolder & actualName, dstFolder & actualName If Err.Number = 0 Then SetResult ws, rowNum, "コピー完了", RGB(0, 128, 0) successCount = successCount + 1 Else SetResult ws, rowNum, "エラー: " & Err.Description, RGB(200, 0, 0) failCount = failCount + 1 Err.Clear End If On Error GoTo ErrHandler End If Next i Application.ScreenUpdating = True Application.StatusBar = False ' --- 完了メッセージ --- Dim resultMsg As String resultMsg = "ファイルコピーが完了しました。" & vbCrLf & vbCrLf resultMsg = resultMsg & " 成功: " & successCount & " 件" & vbCrLf resultMsg = resultMsg & " 失敗: " & failCount & " 件" & vbCrLf resultMsg = resultMsg & " 合計: " & fileCount & " 件" If failCount > 0 Then MsgBox resultMsg, vbExclamation, "完了(一部エラーあり)" Else MsgBox resultMsg, vbInformation, "完了" End If Exit Sub ErrHandler: Application.ScreenUpdating = True Application.StatusBar = False MsgBox "予期しないエラーが発生しました。" & vbCrLf & vbCrLf & _ "エラー番号: " & Err.Number & vbCrLf & _ "内容: " & Err.Description, vbCritical, "エラー" End Sub '============================================================= ' ローカルパス判定(ドライブレターで始まるか) '============================================================= Private Function IsLocalPath(ByVal path As String) As Boolean IsLocalPath = False If Len(path) >= 2 Then Dim firstChar As String firstChar = UCase(Left(path, 1)) If firstChar >= "A" And firstChar <= "Z" Then If Mid(path, 2, 1) = ":" Then ' ネットワークドライブのマッピングも除外したい場合は ' 以下のチェックを有効にしてください ' (ただし、マッピングされたドライブはローカル扱いにする場合はコメントアウト) ' ' Dim drv As String ' drv = Left(path, 2) ' Dim fso As Object ' Set fso = CreateObject("Scripting.FileSystemObject") ' If fso.DriveExists(drv) Then ' If fso.GetDrive(drv).DriveType = 3 Then ' Network ' IsLocalPath = False ' Exit Function ' End If ' End If IsLocalPath = True End If End If End If End Function '============================================================= ' キャッシュ済みDictionaryからファイルを検索(大文字小文字無視) ' ネットワークドライブへのDir()は1回だけに抑えるための仕組み '============================================================= Private Function FindInCache(ByVal dict As Object, ByVal targetName As String) As String Dim key As String key = LCase(Trim(targetName)) If dict.Exists(key) Then FindInCache = dict(key) ' 実際のファイル名を返す Else FindInCache = "" End If End Function '============================================================= ' ファイル名一覧を取得 '============================================================= Private Function GetFileList(ByVal ws As Worksheet, ByRef fileNames() As String) As Long Dim count As Long count = 0 Dim row As Long row = ROW_FILE_START ' まず件数をカウント Do While row <= 1000 ' 安全上限 Dim val As Variant val = ws.Cells(row, COL_FILE).Value If IsEmpty(val) Or Trim(CStr(val)) = "" Then Exit Do End If count = count + 1 row = row + 1 Loop If count = 0 Then GetFileList = 0 Exit Function End If ReDim fileNames(0 To count - 1) Dim i As Long For i = 0 To count - 1 fileNames(i) = Trim(CStr(ws.Cells(ROW_FILE_START + i, COL_FILE).Value)) Next i GetFileList = count End Function '============================================================= ' 結果列をクリア '============================================================= Private Sub ClearResults(ByVal ws As Worksheet) Dim row As Long row = ROW_FILE_START Do While row <= 1000 If IsEmpty(ws.Cells(row, COL_FILE).Value) Or Trim(CStr(ws.Cells(row, COL_FILE).Value)) = "" Then Exit Do End If ws.Cells(row, COL_RESULT).Value = "" ws.Cells(row, COL_RESULT).Font.Color = RGB(0, 0, 0) row = row + 1 Loop End Sub '============================================================= ' 結果セルに値と色を設定 '============================================================= Private Sub SetResult(ByVal ws As Worksheet, ByVal row As Long, ByVal text As String, ByVal clr As Long) ws.Cells(row, COL_RESULT).Value = text ws.Cells(row, COL_RESULT).Font.Color = clr End Sub '============================================================= ' フォルダを再帰的に作成 '============================================================= Private Sub MkDirRecursive(ByVal path As String) Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") If Not fso.FolderExists(path) Then ' 親フォルダを先に作成 Dim parentPath As String parentPath = fso.GetParentFolderName(path) If Not fso.FolderExists(parentPath) Then MkDirRecursive parentPath End If fso.CreateFolder path End If End Sub