VBScriptを掌握する極限の知見:WMIを用いたマルチディスプレイ解像度の完全制圧
レガシーシステムの裏側で、今なおWindowsの自動化を支え続けるVBScript(WSH)。
モダンな言語が持つリッチなランタイム環境とは異なり、開発者はOSの裸の皮膚感覚に近いレイヤーでリソースを管理しなければならない。特に、ダイアログボックスの動的な中央配置や、マルチディスプレイ環境におけるウィンドウの迷子を防ぐための「画面構成情報の取得」は、GUIを伴う自動化スクリプトにおいて避けて通れない関門である。
本稿では、`Win32_DesktopMonitor`をはじめとするWMI(Windows Management Instrumentation)クラスを駆使し、解像度およびマルチディスプレイ構成をミリ秒単位の効率で判別する極限の知見を公開する。
—
1. なぜ `Win32_DesktopMonitor` なのか?
画面情報を取得するアプローチとして、User32.dllの`EnumDisplayMonitors`などのWin32 APIをVBScriptから直接呼び出す手法(`DllCall`の自作やレジストリハック)が脳裏によぎるかもしれない。しかし、VBScript単体で生のポインタや構造体を直接ハンドリングするのは、メモリリークや不正アクセス違反の温床となり、エンタープライズ環境の保守性という観点から悪手である。
そこで我々は、COMオブジェクトとして安全かつ確実にOSの深部へアクセスできる WMI (Windows Management Instrumentation) を採用する。WMIはオーバーヘッドが大きいと誤解されがちだが、クエリを最適化し、オブジェクトのライフサイクルを完全に制御下に入れれば、実用上十分なパフォーマンスを発揮する。
—
2. 実装コード:マルチディスプレイ構成の全容解明
以下のスクリプトは、単一のモニタはもちろん、複雑なマルチディスプレイ環境においても、それぞれの解像度(ピクセル単位)とプライマリモニタの判定を完全に行う実用コードである。
‘ ==============================================================================
‘ Script Name: GetDisplayConfiguration.vbs
‘ Description: WMIを用いた画面解像度およびマルチディスプレイ構成の取得
‘ Architecture: VBScript / WSH (Windows 7 / 10 / 11 対応)
‘ ==============================================================================
Option Explicit
Sub Main()
Dim objWMIService, colMonitors, objMonitor
Dim strComputer
Dim monitorCount
strComputer = “.”
‘ 1. WMIサービスへの接続(接続プールの最適化とセキュリティコンテキストの設定)
On Error Resume Next
Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)
If Err.Number <> 0 Then
WScript.Echo “[CRITICAL] WMIサービスへの接続に失敗しました. Error: 0x” & Hex(Err.Number)
Exit Sub
End If
On Error GoTo 0
‘ 2. Win32_DesktopMonitorのクエリ実行
‘ ※注意: 環境やドライバによっては実際の物理モニタ数と一致しない場合があるため、
‘ Win32_VideoControllerの横幅・縦幅とのフォールバック設計を考慮する。
Set colMonitors = objWMIService.ExecQuery( _
“SELECT DeviceID, MonitorScreenHeight, MonitorScreenWidth, ScreenWidth, ScreenHeight, Caption, IsPrimary ” & _
“FROM Win32_DesktopMonitor”)
monitorCount = 0
WScript.Echo “=== マルチディスプレイ構成診断レポート ===”
For Each objMonitor in colMonitors
monitorCount = monitorCount + 1
‘ プロパティのフォールバック処理(WMIプロバイダの仕様差異吸収)
Dim sWidth, sHeight
sWidth = objMonitor.ScreenWidth
sHeight = objMonitor.ScreenHeight
If IsNull(sWidth) Or sWidth = 0 Then
sWidth = objMonitor.MonitorScreenWidth
End If
If IsNull(sHeight) Or sHeight = 0 Then
sHeight = objMonitor.MonitorScreenHeight
End If
‘ 出力
WScript.Echo “————————————————–”
WScript.Echo “モニタ ID : ” & objMonitor.DeviceID
WScript.Echo “キャプション : ” & objMonitor.Caption
WScript.Echo “解像度 : ” & sWidth & ” x ” & sHeight & ” px”
‘ プライマリ判定(WMIのバージョン・実装依存があるため例外処理を包含)
On Error Resume Next
If objMonitor.IsPrimary Then
WScript.Echo “プライマリ : はい”
Else
WScript.Echo “プライマリ : いいえ”
End If
On Error GoTo 0
Next
WScript.Echo “————————————————–”
WScript.Echo “検出されたモニタオブジェクト数: ” & monitorCount
‘ 3. メモリの明示的解放(極限のライフサイクル管理)
Set objMonitor = Nothing
Set colMonitors = Nothing
Set objWMIService = Nothing
End Sub
‘ 実行
Main()
—
3. チーフアーキテクトが解説する「実装の急所」
① WMIプロバイダの「方言」とフォールバック設計
`Win32_DesktopMonitor` は強力だが、グラフィックボードのドライバや仮想環境(Citrix, Remote Desktopなど)の差異によって、`ScreenWidth` が空(Null)を返すケースが散見される。
上記コードのように、`ScreenWidth` が取得できない場合は `MonitorScreenWidth` を参照する二段構えのフォールバックロジックを組み込むことが、レガシー環境で破綻しないための絶対条件である。
② VBScriptにおける「オブジェクトの明示的解放」の真実
VBScriptのCOMガベージコレクションは頼りにならない。特に複数のWMIクエリをループ内で回すような設計にすると、COMコンポーネントの参照カウントが適切にデクリメントされず、VBScriptプロセス(`wscript.exe` / `cscript.exe`)のメモリフットプリントが肥大化する。
スクリプトの終端、あるいはスコープを抜ける直前には、必ず以下のようにインスタンスを `Nothing` で明示的に破棄せよ。
Set objMonitor = Nothing
Set colMonitors = Nothing
Set objWMIService = Nothing
この一手間が、タスクスケジューラで常時稼働する自動化バッチのメモリリーク死を防ぐ防壁となる。
③GUIウィンドウの動的配置への応用
ここで取得した最大解像度(Primary MonitorのWidth/Height)を基準に、VBScriptから呼び出すHTA(HTML Application)や、SendKeys / UIAutomation を用いた外部ツールのダイアログ座標を動的に計算すれば、「どの解像度の端末で実行されても画面中央にダイアログが出現する」堅牢なシステムが完成する。
—
総括
VBScriptは「古い言語」ではない。OSの深部にダイレクトにタッチできる、極めて軽量かつスパルタンな「システム言語」である。
WMIを用いた画面構成情報の取得という一見地味な処理であっても、背後にあるCOMのライフサイクル、プロバイダの癖、例外のハンドリングを完全に理解していれば、モダンな言語群と同等以上の信頼性をエンタープライズ環境にもたらすことができる。
泥臭く、しかし美しく。真のエンジニアリングを、そのスクリプトの隅々に宿せ。
