【環境依存の脱却】ScriptFullNameとGetParentFolderNameを用いたポータブルな相対パス解決テクニック
レガシーシステムの暗黙の了解ほど、エンジニアのキャリアを消耗させるものはない。
「Cドライブの直下にフォルダを作れ」「デスクトップに配置しろ」「タスクスケジューラからの実行時はカレントディレクトリが `C:\Windows\System32` に化ける」――。
こうした環境依存の呪縛から脱却し、どこに配置しても自律して動作する「完全ポータブルなVBScript」を構築する。
今回は、FSO(FileSystemObject)の真価を極限まで引き出し、スクリプトの自己位置を基点とした頑健なパス解決アーキテクチャを解説する。
—
1. なぜ「カレントディレクトリ」に頼ってはいけないのか
VBScriptを運用する現場において、最も頻発するバグの温床が `CurDir` や相対パスの安易な使用である。
‘ 【悪手】カレントディレクトリに依存したコード
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set file = fso.OpenTextFile(“settings.ini”, 1) ‘ 実行する瞬間によって参照先がブレる
Windowsのタスクスケジューラ、ショートカットからの起動、あるいはコマンドプロンプトからの直接実行など、スクリプトを起動するトリガーによってカレントディレクトリ(作業ディレクトリ)は動的に変動する。
結果として「ファイルが見つかりません (Error 53)」という、環境依存特有の不可解な障害に直面することになる。
プロフェッショナルな自動化スクリプトに求められるのは、「自身がどこに存在しようとも、自身の絶対位置を正確に把握し、そこからの相対位置でリソースを特定する」という自己完結性である。
—
2. 核心:WScript.ScriptFullName と FSO の連携
この問題を根本から解決するのが、`WScript.ScriptFullName` プロパティと、FileSystemObjectの `GetParentFolderName` メソッドの組み合わせだ。
- `WScript.ScriptFullName`: 実行中のスクリプト自身の「完全なパス(ドライブ名+フォルダパス+ファイル名)」を返す。
- `FSO.GetParentFolderName`: 指定したパスからファイル名を除去し、親フォルダのパスを抽出する。
この2つを組み合わせることで、実行環境のカレントディレクトリがどこであれ、「スクリプトが物理的に置かれているディレクトリの絶対パス」を確実に取得できる。
—
3. 実装パターン:極限まで最適化されたポータブルパス構築コード
以下のコードは、実務の現場でそのまま流用できる堅牢なテンプレートである。
オブジェクトのライフサイクル管理、メモリ最適化、エラーハンドリングの作法を網羅している。
Option Explicit
‘ ==============================================================================
‘ 処理名: ポータブルパス解決および設定ファイル読み込みサンプル
‘ 概要 : スクリプト自身の配置場所を基準に、同一階層のデータに確実にアクセスする
‘ ==============================================================================
Call Main()
Sub Main()
Dim fso, scriptDir, targetFilePath, objFile, fileContent
‘ 1. FileSystemObjectのインスタンス化(リソース消費を最小限に抑えるため単一変数で運用)
Set fso = CreateObject(“Scripting.FileSystemObject”)
On Error Resume Next
‘ 2. 自身の配置ディレクトリ(親フォルダ)の絶対パスを取得
scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
If Err.Number <> 0 Then
WScript.Echo “致命的エラー: スクリプトのパス取得に失敗しました。” & Err.Description
Call ReleaseObjects(fso, Nothing)
Exit Sub
End If
‘ 3. 基準パスをルートとして、目的のリソースへの絶対パスを動的に構築
‘ (FSO.BuildPathを使用することで、スラッシュの重複や欠落を完全に防止する)
targetFilePath = fso.BuildPath(scriptDir, “config\settings.ini”)
‘ 4. リソースの存在確認(フェイルセーフの鉄則)
If Not fso.FileExists(targetFilePath) Then
WScript.Echo “警告: 必須設定ファイルが存在しません。” & vbCrLf & “パス: ” & targetFilePath
Call ReleaseObjects(fso, Nothing)
Exit Sub
End If
‘ 5. ファイルの安全な読み込み
Set objFile = fso.OpenTextFile(targetFilePath, 1, False) ‘ 1 = ForReading
fileContent = objFile.ReadAll
objFile.Close
On Error GoTo 0
‘ 処理結果の出力(検証用)
WScript.Echo “【パス解決成功】” & vbCrLf & _
“実行場所: ” & scriptDir & vbCrLf & _
“対象ファイル: ” & targetFilePath & vbCrLf & _
“— 内容抜粋 —” & vbCrLf & Left(fileContent, 100)
‘ 6. 明示的なオブジェクト解放(メモリリークの根絶)
Call ReleaseObjects(fso, objFile)
End Sub
‘ ==============================================================================
‘ 補助関数: オブジェクトの安全な解放と参照切断
‘ ==============================================================================
Sub ReleaseObjects(ByRef fsoObj, ByRef fileObj)
On Error Resume Next
If Not fileObj Is Nothing Then
fileObj.Close
Set fileObj = Nothing
End If
If Not fsoObj Is Nothing Then
Set fsoObj = Nothing
End If
On Error GoTo 0
End Sub
—
4. チーフアーキテクトが指摘する実装の急所
上記のコードには、レガシーコードの乱立を防ぐための重要な設計思想が組み込まれている。
① `FSO.BuildPath` の絶対的優位性
パスを結合する際、文字列の結合演算子(`scriptDir & “\config\settings.ini”`)を使用すると、環境や変数の末尾スラッシュの有無によってバグを生む。
`fso.BuildPath(path1, path2)` を使用すれば、パス区切り文字の整合性をFileSystemObjectが自動的に担保してくれるため、環境差異によるパス崩壊が原理的に起きなくなる。
② 明示的なオブジェクト解放とスコープ管理
VBScriptのガベージコレクションは完全ではない。特にCOMコンポーネントである `Scripting.FileSystemObject` やテキストストリームは、スクリプトが終了するまでメモリ上に残存しリソースを圧迫する場合がある。
プロシージャの終端では確実に `Set obj = Nothing` を実行し、参照カウントを即座にゼロに戻す設計を徹底すべきである。
③ タスクスケジューラ・ネットワーク越し(UNCパス)への耐性
この手法は、ローカル環境だけでなく、ネットワーク共有フォルダ(`\\server\share\scripts\`)上にスクリプトが配置されている場合でも完全に機能する。
`WScript.ScriptFullName` はUNCパスをそのまま返すため、`GetParentFolderName` は正しくネットワーク上の親ディレクトリを算出し、リモート環境での実行をも完全にポータブル化する。
—
総括
「どこで動かしても絶対に壊れない」。
これが、プロフェッショナルな自動化スクリプトに求められる唯一にして最大の要件である。
環境依存という曖昧模糊とした悪魔を排除し、コードの自律性を高めること。それこそが、VBScriptというレガシー技術の寿命を延命させ、保守性を極限まで高める唯一の王道なのである。
