【入門編】【上級プロフェッショナル向け】FSOとWMI(Win32_LogicalDisk)を連携させたセクタエラー・書き込み保護状態の事前検知と安全なファイル退避処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

止まらない自動化のために。FSOとWMIを連携させ、ストレージの「不機嫌」を事前に察知するプロの技

こんにちは。自動化の世界へようこそ。

「マクロの記録」や、ネットで見つけた「ファイルをコピーするだけのコード」を動かして、一通りの目的を果たせたとき、あなたはきっと小さな達成感を感じたはずです。でも、もしあなたが「エンジニア」として、そのコードを現場の重要な業務ラインに投入しようとしているのなら、一度立ち止まって考えてみてください。

「もし、書き込み先のディスクが壊れかけていたら?」
「もし、USBメモリが書き込み禁止状態だったら?」

単純なコードは、エラーが起きた瞬間に「無慈悲に停止」します。あるいは、中途半端に書き込みを行ってしまい、ファイルを破損させてしまうことさえあります。

今日は、そんな「初学者が陥る罠」を回避し、プロフェッショナルが実践する「守りの自動化(Defensive Programming)」についてお話しします。FileSystemObject(FSO)という「手」と、WMIという「目」を組み合わせることで、ストレージの状態を事前に監査し、安全にファイルを扱う方法をマスターしましょう。

ここをクリアすれば、あなたのVBScriptは「ただ動くもの」から「信頼されるシステム」へと進化しますよ。

1. 二つの道具の役割を知る:FSO(手)とWMI(目)

高度な自動化を実現するには、役割の異なる二つのオブジェクトを使い分ける必要があります。

FileSystemObject (FSO) :「実行する手」

FSOは、ファイルを作ったり、コピーしたり、フォルダを消したりするための道具です。

  • 得意なこと: ファイルの作成、移動、削除、コピー、存在確認。
  • 弱点: 「その場所が今、物理的に書き込める状態か?」という、OSの深いレベルの健康状態までは知りません。

WMI (Windows Management Instrumentation) :「監視する目」

WMIは、Windows OSそのものの情報を覗き見るための窓口です。

  • 得意なこと: CPUの温度、メモリの使用量、そして「ディスク(ドライブ)の属性や状態」の取得。
  • 役割: ファイルを操作する「前」に、その場所が安全かどうかをチェックします。

「目(WMI)で周囲を確認してから、手(FSO)を動かす」。これがプロの作法です。

2. 実践:ディスクの状態を監査する「安全装置」の設計

では、具体的なコードを見ていきましょう。
今回は、「指定したドライブが書き込み可能か、そして異常な状態(読み取り専用など)ではないか」をチェックしてから、ファイルを安全に退避(コピー)するスクリプトを作成します。

' ==========================================================================
' 【プロフェッショナル仕様】安全なファイル退避スクリプト
' 概要: WMIでドライブの状態を監査し、安全が確認できた場合のみFSOで操作を行う
' ==========================================================================

Option Explicit

' 定数定義(マジックナンバーを避け、可読性を高める)
Const DRIVE_TYPE_FIXED = 3      ' ローカルハードディスク
Const DRIVE_TYPE_REMOVABLE = 2  ' リムーバブルディスク(USBメモリ等)

Dim targetDrive, sourceFile, destFolder
targetDrive = "C:"             ' 監査対象のドライブ
sourceFile  = "C:\Data\重要書類.txt"
destFolder  = "D:\Backup\"      ' 退避先(例としてDドライブ)

' --- メイン処理 ---
If IsDiskSafe(targetDrive) Then
    MsgBox targetDrive & " は安全です。操作を開始します。", vbInformation, "監査完了"
    SafeFileCopy sourceFile, destFolder
Else
    MsgBox targetDrive & " に異常、または書き込み禁止状態を検知しました。処理を中断します。", vbCritical, "警告:安全確保"
End If


' ==========================================================================
' 関数: IsDiskSafe
' 目的: WMIを使用して、指定されたドライブが書き込み可能かチェックする
' 戻り値: Boolean (True: 安全, False: 危険)
' ==========================================================================
Function IsDiskSafe(driveLetter)
    Dim objWMIService, colDisks, objDisk
    Dim isSafe
    isSafe = False ' デフォルトは「危険」とみなす(Fail-safe設計)

    On Error Resume Next ' WMI接続エラーに備える
    
    ' WMIサービスへの接続
    Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
    
    If Err.Number <> 0 Then
        WScript.Echo "WMI接続エラー: " & Err.Description
        IsDiskSafe = False
        Exit Function
    End If

    ' Win32_LogicalDisk クラスから情報を取得
    ' DriveLetter プロパティで対象を指定
    Set colDisks = objWMIService.ExecQuery _
        ("Select * from Win32_LogicalDisk Where DeviceID = '" & driveLetter & "'")

    For Each objDisk In colDisks
        ' 1. ドライブが存在するか?
        ' 2. 書き込み禁止(ReadOnly)ではないか?
        ' 3. ドライブタイプが適切か? (ここでは固定ディスクまたはリムーバブルを許可)
        If Not objDisk.IsReadOnly And _
           (objDisk.DriveType = DRIVE_TYPE_FIXED Or objDisk.DriveType = DRIVE_TYPE_REMOVABLE) Then
            isSafe = True
        End If
    Next

    On Error GoTo 0 ' エラーハンドリングを元に戻す
    IsDiskSafe = isSafe
End Function


' ==========================================================================
' 関数: SafeFileCopy
' 目的: FSOを使用して安全にファイルをコピーする
' ==========================================================================
Sub SafeFileCopy(src, dest)
    Dim objFSO
    Set objFSO = CreateObject("Scripting.FileSystemObject")

    On Error Resume Next ' ファイル操作中の予期せぬエラー(セクタエラー等)を捕捉

    ' コピー実行
    objFSO.CopyFile src, dest, True ' Trueは上書き許可

    If Err.Number <> 0 Then
        ' ここでエラーが起きた場合、物理的な書き込み失敗(セクタエラー等)の可能性がある
        WScript.Echo "致命的なエラーが発生しました: " & Err.Description, vbCritical
    Else
        WScript.Echo "ファイルの退避に成功しました。", vbInformation
    End If

    On Error GoTo 0
    Set objFSO = Nothing
End Sub

3. コードの深層:なぜこの書き方をするのか?

このコードには、初学者が卒業すべき「3つのプロの設計思想」が組み込まれています。

① Fail-safe(フェイルセーフ)設計

`isSafe = False` を関数の最初に宣言しています。これは、「もしチェック中に何らかの理由でエラーが起きても、デフォルトでは『危険』と判断する」という考え方です。
「確認に失敗した=安全」と判断してしまうと、壊れたディスクに書き込みに行ってしまうかもしれません。プロは「不明なものは、危険とみなす」のです。

② WMIによる「属性」の監査

`objDisk.IsReadOnly` をチェックしています。
USBメモリの物理スイッチが「Lock」になっていたり、ネットワークドライブが読み取り専用でマウントされていたりする場合、FSOでいきなり書き込もうとすると、プログラムはエラーで止まります。事前にWMIでこれを知ることで、スマートなエラーハンドリングが可能になります。

③ エラーオブジェクトの適切なハンドリング

`On Error Resume Next` は、使い方を誤ると「エラーを無視して進む恐ろしい魔法」になります。
しかし、このコードのように「特定の危険な操作(WMI接続やファイルコピー)の直前」に使い、その直後に `If Err.Number <> 0` でエラーを判定することで、「エラーを無視するのではなく、エラーを捕まえて適切に処理する」という強力な武器に変わります。

4. 陥りやすいエラーと、さらに高みを目指すヒント

この仕組みを運用する上で、以下の点に注意してください。

  • 権限の問題: WMIの操作には、実行ユーザーにある程度の権限が必要です。管理者権限が必要な環境では、スクリプトを「管理者として実行」する必要があります。
  • パフォーマンスの重み: WMIのクエリ(`ExecQuery`)は、FSOの操作に比べて少し「重い」処理です。ループの中で何万回も呼び出すような使い方は避けましょう。「操作の前に一度だけチェックする」のがベストプラクティスです。
  • セクタエラーの検知について: 今回のコードでは「論理的な書き込み禁止」を検知していますが、物理的な「不良セクタ」そのものを完全に予知するのは、実はOSのカーネルレベルの非常に高度な領域です。しかし、`SafeFileCopy` 内でエラーを捕捉することで、「書き込みに失敗した瞬間に、被害を最小限に抑えて停止する」という、実務上極めて重要な防御ができています。

最後に

VBScriptは古い言語だと言われることもあります。しかし、その仕組みを深く理解し、今回のように「目(WMI)」と「手(FSO)」を組み合わせる知恵を持てば、現代のシステムにおいても極めて堅牢な自動化ツールになります。

「ただ動けばいい」から、「止まらない、壊さない」へ。
この一歩が、あなたを単なるスクリプターから、信頼されるエンジニアへと変えてくれます。

応援していますよ。頑張ってくださいね!

タイトルとURLをコピーしました