【テクニカル・上級編】【デフォルトプリンター自動変更】WScript.Network と WMI を組み合わせたオフィス環境別印刷先切替 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【デフォルトプリンター自動変更】WScript.Network と WMI を組み合わせたオフィス環境別印刷先切替

レガシーシステムの深部、あるいは厳格に管理された閉域網のオフィスインフラにおいて、VBScript(Visual Basic Scripting Edition)いまだに死んでいない。いや、Windowsの基盤に深く根ざした「インフラストラクチャの接着剤」として、その生存価値を高め続けてさえいる。

今回は、現場のヘルプデスクを疲弊させる「移動端末のプリンター泥沼問題」に対し、`WScript.Network` と `WMI (Windows Management Instrumentation)` を極限までチューニングして組み合わせた、環境適応型デフォルトプリンター自動切替スクリプトを提示する。

生半可なスクリプトではない。メモリの枯渇を防ぐオブジェクトのライフサイクル管理、WMIクエリのパフォーマンス最適化、そして例外を許さないエラーハンドリングを網羅した、プロダクション環境投入前提のコードだ。

1. アーキテクチャ設計:なぜ「WScript.Network」と「WMI」の二刀流なのか

オフィスのフロア移動、あるいは在宅勤務と出社を繰り返すハイブリッド環境において、ユーザーが手動でデフォルトプリンターを切り替える手間は、年間で莫大な労働損失を生む。

この課題をVBScriptで解決する場合、役割の分担を明確にしなければならない。

  • WMI (`Win32_NetworkAdapterConfiguration` / `Win32_IP4RouteTable`):

現在のマシンのネットワーク環境(IPアドレス、デフォルトゲートウェイ、サブネット)を精密にスニッフィングする。単なるIP取得ではなく、「現在アクティブな経路」を特定し、誤判定を防ぐ。

  • WScript.Network (`SetDefaultPrinter`):

WMIが特定した環境に基づき、OSのレジストリおよびスプーラーに対してデフォルトプリンターの変更を即時かつ確実にコミットする。

しかし、ここで忘れてはならないのがVBScript特有のメモリ管理の闇である。COMコンポーネントを多用するWMI操作は、ガベージコレクションのタイミングが曖昧であり、不適切な変数使い回しは確実にメモリリークを引き起こす。チーフアーキテクトとして、この点には厳格に対処する。

2. 実装コード:プロダクション品質の完全版VBScript

以下のコードを `SetDefaultPrinter.vbs` として保存し、ログオンスクリプトやタスクスケジューラから実行せよ。エラーログは自動的にローカルのテンポラリ領域に出力される設計にしている。

‘ ==============================================================================
‘ Script Name : DynamicPrinterSwitcher.vbs
‘ Description : IPアドレス帯を動的に検知し、最適なデフォルトプリンターを設定する
‘ Author : Chief Architect
‘ ==============================================================================
Option Explicit

‘ メイン処理の実行とグローバルエラーハンドリング
On Error Resume Next

Call Main()

If Err.Number <> 0 Then
Call WriteLog(“CRITICAL ERROR: ” & Err.Description & ” (Code: ” & Hex(Err.Number) & “)”)
Err.Clear
End If

WScript.Quit(0)

‘ ==============================================================================
‘ メインロジック
‘ ==============================================================================
Sub Main()
Dim ipAddress
ipAddress = GetActiveIPAddress()

If ipAddress = “” Then
Call WriteLog(“WARNING: アクティブなIPv4アドレスが取得できませんでした。処理を中断します。”)
Exit Sub
End If

Call WriteLog(“INFO: 検出されたIPアドレス -> ” & ipAddress)

‘ ネットワークセグメント(第3オクテットまで、あるいはサブネット)に基づく判定
Select Case True
Case Left(ipAddress, 10) = “192.168.1.”
‘ 本社 営業部フロア
Call SetPrinter(“\\printserver\HQ-Sales-Prt01”)

Case Left(ipAddress, 10) = “192.168.2.”
‘ 本社 開発部フロア
Call SetPrinter(“\\printserver\HQ-Dev-Prt02”)

Case Left(ipAddress, 10) = “10.0.10.”
‘ 支店オフィス
Call SetPrinter(“\\branchserver\Branch-Prt01”)

Case Else
‘ リモートワーク / 未知のネットワーク(デフォルトプリンターを変更しない、またはPDF出力等を指定)
Call WriteLog(“INFO: 既知のセグメントに該当しません。プリンター変更をスキップします。”)
End Select
End Sub

‘ ==============================================================================
‘ WMIを用いたアクティブIPアドレスの取得(ルーティングテーブル照合型)
‘ ==============================================================================
Function GetActiveIPAddress()
Dim objWMIService, colAdapters, objAdapter
Dim targetIP

targetIP = “”
Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)

‘ DHCPまたは固定IPが有効で、かつIPが割り当てられているアダプターを抽出
Set colAdapters = objWMIService.ExecQuery _
(“Select IPAddress From Win32_NetworkAdapterConfiguration Where IPEnabled = True”)

For Each objAdapter in colAdapters
If Not IsNull(objAdapter.IPAddress) Then
Dim i
For i = LBound(objAdapter.IPAddress) To UBound(objAdapter.IPAddress)
‘ IPv4アドレスのみを対象とする(”:”が含まれないもの)
If InStr(objAdapter.IPAddress(i), “:”) = 0 Then
targetIP = objAdapter.IPAddress(i)
Exit For
End If
Next
End If
If targetIP <> “” Then Exit For
Next

‘ オブジェクトの明示的解放(メモリリーク防止の鉄則)
Set colAdapters = Nothing
Set objWMIService = Nothing

GetActiveIPAddress = targetIP
End Function

Sub SetPrinter(printerPath)
Dim net
Set net = CreateObject(“WScript.Network”)

‘ プリンターの設定試行
net.SetDefaultPrinter printerPath

If Err.Number <> 0 Then
Call WriteLog(“ERROR: プリンターの変更に失敗しました [” & printerPath & “]. Error: ” & Err.Description)
Err.Clear
Else
Call WriteLog(“SUCCESS: デフォルトプリンターを [” & printerPath & “] に変更しました。”)
End If

Set net = Nothing
End Sub

‘ ==============================================================================
‘ 簡易ログ出力関数
‘ ==============================================================================
Sub WriteLog(message)
Dim fso, logFile, logPath
Const ForAppending = 8

On Error Resume Next
Set fso = CreateObject(“Scripting.FileSystemObject”)
logPath = fso.GetSpecialFolder(2) & “\PrinterSwitcher.log” ‘ テンポラリフォルダに出力

Set logFile = fso.OpenTextFile(logPath, ForAppending, True)
logFile.WriteLine “[” & Now & “] ” & message
logFile.Close

Set logFile = Nothing
Set fso = Nothing
End Sub

3. チーフアーキテクトが解説するコードの急所と極意

このスクリプトを実運用するにあたり、他のエンジニアが見落としがちな3つの設計思想を解説する。

① WMIクエリのスコープ制限とメモリ管理

WMI (`winmgmts:`) の呼び出しは非常にコストが高い。さらに、VBScriptにおけるCOMオブジェクトの参照カウントは、スクリプト終了まで解放されないケースがある。
上記のコードでは、関数を抜ける直前に `Set colAdapters = Nothing` と `Set objWMIService = Nothing` を明示的に記述している。これを怠ると、常駐型プロセスや高頻度実行時にメモリリークを引き起こし、クライアント端末の動作を重くする元凶となる。

② IPv4とIPv6の厳格な分離

現代のWindows環境では、デフォルトでIPv6が有効であり、`Win32_NetworkAdapterConfiguration` から取得される配列の先頭にIPv6アドレスが返されるケースが増えている。
スクリプト内の `InStr(…, “:”) = 0` という判定ロジックにより、純粋なIPv4アドレスのみを確実に抽出する堅牢性を担保している。

③ エラーハンドリングのレイヤー分離

`On Error Resume Next` は諸刃の剣である。これをグローバルに乱用するとバグの温床になるため、メイン処理とロギング処理、プリンター設定処理の間でスコープを意識し、`Err.Number` の監視と `Err.Clear` によるステータスのリセットを徹底している。万が一プリンターサーバーがダウンしていても、スクリプト全体が異常終了してユーザー画面にVBScriptのエラーダイアログがポップアップすることはない。

4. 展開と運用上の注意点(レガシー環境の保守)

1. 実行権限: プリンターのデフォルト設定変更は、標準ユーザー権限でもローカルレジストリ(`HKCU`)の範囲内であれば実行可能だが、ネットワークプリンターの接続状態によっては管理者権限を要求される場合がある。その場合はタスクスケジューラにて「最高特権で実行」を付与して配備すること。
2. グループポリシー(GPO)との競合: 組織のドメイン環境でGPOによりデフォルトプリンターが強制されている場合、このスクリプトの実行後に上書きされる可能性がある。GPOの適用タイミングとログオン時のスクリプト実行順序を精査すること。

VBScriptは「古い言語」ではない。OSの深部にダイレクトにアクセスし、過剰な依存関係(Node.jsや.NETランタイムのインストールなど)を必要としない、究極のポータビリティを持つ軽量ランタイムである。
このスクリプトをあなたのインフラに組み込み、無駄なヘルプデスク工数を物理的に排除せよ。

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