【実務・中級編】【画面構成情報の取得】WMI (Win32_DesktopMonitor) による解像度およびマルチディスプレイ構成判定 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【画面構成情報の取得】WMI (`Win32_DesktopMonitor`) による解像度およびマルチディスプレイ構成判定

開発プロジェクトの現場で、VBScriptを用いた業務自動化ツールやキッティングスクリプトを作成する際、こんな壁にぶつかったことはないだろうか。

「スクリプトからポップアップやHTA(HTML Application)のGUIを表示した際、マルチディスプレイ環境のサブ画面側で変な位置に表示されてしまう」
「フルHD環境で作ったダイアログが、4K環境やモバイルの低解像度環境で盛大にはみ出す、あるいは小さすぎて読めない」

ハードウェアの構成が多様化した現代において、スクリプトの出力するUIの位置やサイズを「固定値」でハードコーディングすることは、もはやプロフェッショナルの仕事とは言えない。実行環境のディスプレイ構成を動的に把握し、ロジカルに最適値を算出する――これこそが、現場のトラブルを防ぐ堅牢なスクリプト設計の第一歩だ。

今回は、WMI(Windows Management Instrumentation)の `Win32_DesktopMonitor` および関連クラスを駆使し、VBScriptで極限まで安定した画面構成情報を取得する手法を伝授する。

なぜ「単純な画面サイズ取得」では実務で破綻するのか?

VBScriptや古いWSHの解説書を見ると、「レジストリから解像度を読む」あるいは「IEオブジェクトの親ウィンドウサイズを借りる」といった姑息なハックが散見される。しかし、これらは実務の現場では使い物にならない。

1. レジストリ直読みの危険性: Windowsのバージョン(Windows 10 / 11、あるいはServerOS)によって解像度情報の格納パスや構造が変わり、OSアップデート一発でスクリプトが沈黙する。
2. マルチディスプレイ(拡張デスクトップ)の無視: 単一のプライマリモニタのサイズしか取れないため、複数画面を駆使するオペレーターのメイン作業領域を無視した奇妙な位置にダイアログがポップアップする。
3. DPIスケーリングの罠: 4Kモニタなどで拡大率(125%や150%など)が適用されている場合、論理ピクセルと物理ピクセルの乖離により、ウィンドウレイアウトが崩壊する。

これらを解決するための唯一無二の正解が、WMIを用いた物理/論理ディスプレイ情報の動的クエリである。

堅牢な画面情報取得のアーキテクチャ

VBScriptからWMIを叩く場合、COMコンポーネントのライフサイクルとエラーハンドリングに細心の注意を払う必要がある。特にWMIクエリは、対象PCのグラフィックドライバの状態やセキュリティソフトの干渉によって稀に例外を投げる。

ここで紹介するプロダクションコードは、以下の要件を満たすよう設計している。

  • フォールバック機構: 単一モニタしか取れない環境やWMIが一部制限されている環境でもスクリプトがクラッシュしない。
  • マルチディスプレイ対応: 接続されている全モニタの解像度を集計し、最大幅・高さを算出。
  • 完全なオブジェクト解放: VBScriptの悪夢であるメモリリークを防ぐため、参照したCOMオブジェクトはスコープごとに明示的に `Nothing` へ代立。

プロダクションコード:マルチディスプレイ対応・画面情報取得ライブラリ

以下のコードを `ScreenInfo.vbs` として保存し、実務のツール群に組み込んでほしい。コピペですぐに動作し、かつ商用利用に耐えうる堅牢性を持たせてある。

‘ ==============================================================================
‘ プロジェクト名: 業務自動化基盤 VBScript標準ライブラリ
‘ モジュール名: ScreenMetrics.vbs
‘ 概要: WMIを使用した画面解像度およびマルチディスプレイ構成の動的取得
‘ 著作権: (c) 202X Enterprise Automation Architecture.
‘ ==============================================================================

Option Explicit

‘ メイン処理実行
Main

Sub Main()
On Error Resume Next

Dim objService, colMonitors, objMonitor
Dim strComputer
Dim primaryWidth, primaryHeight
Dim totalWidth, maxResX, maxResY
Dim monitorCount

strComputer = “.”
maxResX = 0
maxResY = 0
monitorCount = 0

‘ WMIサービス接続の確立 (CIMv2名前空間)
Set objService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)

If Err.Number <> 0 Then
WScript.Echo “致命的エラー: WMIサービスへの接続に失敗しました。Error: ” & Hex(Err.Number)
Exit Sub
End If

‘ Win32_DesktopMonitor クエリの実行
‘ 注意: 物理モニタの検出はドライバ依存度が高いため、Win32_VideoControllerとの併用が定石
Set colMonitors = objService.ExecQuery(“SELECT FROM Win32_DesktopMonitor”)

If Err.Number <> 0 Then
WScript.Echo “警告: DesktopMonitorの取得に失敗しました。デフォルト値を使用します。”
Err.Clear
Else
For Each objMonitor in colMonitors
‘ ScreenWidth / ScreenHeightプロパティが存在する場合に取得
If Not IsNull(objMonitor.ScreenWidth) And Not IsNull(objMonitor.ScreenHeight) Then
monitorCount = monitorCount + 1
If objMonitor.ScreenWidth > maxResX Then maxResX = objMonitor.ScreenWidth
If objMonitor.ScreenHeight > maxResY Then maxResY = objMonitor.ScreenHeight
End If
Next
End If

‘ Win32_DesktopMonitorだけではマルチ環境や一部仮想環境で取れないケースがあるため
‘ Win32_VideoController (ビデオカード情報) からもフォールバック値を取得
If maxResX = 0 Or maxResY = 0 Then
Dim colVidControllers, objVid
Set colVidControllers = objService.ExecQuery(“SELECT FROM Win32_VideoController”)

For Each objVid in colVidControllers
If Not IsNull(objVid.CurrentHorizontalResolution) And Not IsNull(objVid.CurrentVerticalResolution) Then
maxResX = objVid.CurrentHorizontalResolution
maxResY = objVid.CurrentVerticalResolution
Exit For
End If
Next

‘ オブジェクト解放
Set colVidControllers = Nothing
End If

On Error GoTo 0 ‘ エラー監視を通常に戻す

‘ 結果の判定と出力
WScript.Echo “=== 画面構成情報 診断レポート ===”
If maxResX > 0 And maxResY > 0 Then
WScript.Echo “最高検知解像度 (Width x Height): ” & maxResX & ” x ” & maxResY

‘ 【実務応用】GUIウィンドウの中央配置座標を動的算出するロジック
Dim dialogWidth, dialogHeight, posX, posY
dialogWidth = 600
dialogHeight = 400

posX = (maxResX – dialogWidth) / 2
posY = (maxResY – dialogHeight) / 2

WScript.Echo “推奨ダイアログサイズ: ” & dialogWidth & “x” & dialogHeight
WScript.Echo “画面中央配置時の算出座標 -> X: ” & posX & “, Y: ” & posY
Else
WScript.Echo “画面解像度を正常に取得できませんでした。”
End If

‘ オブジェクトの厳格な解放 (メモリリーク防止)
Set objMonitor = Nothing
Set colMonitors = Nothing
Set objService = Nothing

End Sub

チーフアーキテクトが教える実務適用の極意

このコードを実際の業務システムやキッティングツールに組み込む際、以下の設計思想を必ず頭に入れておいてほしい。

1. `Win32_DesktopMonitor` と `Win32_VideoController` の二段構え

WMIの世界では、ハードウェアベンダーの独自ドライバの作り込みによって `Win32_DesktopMonitor` が正常にモニタの解像度を返さないケース(特にノートPCの外部出力時やリモートデスクトップ接続時)が多々存在する。
そのため、本コードのようにプライマリとしてDesktopMonitorを叩き、値が取れなければVideoControllerへフォールバックする二段構えの構造にすることが、現場で「動かない」と言われないための必須テクニックである。

2. HTAやWScript.Shell Popupとの連携

このスクリプトで算出した `posX` と `posY`、あるいは最大解像度をベースに、HTAウィンドウの `moveTo` メソッドや、VBScriptから操作する外部GUIツールのパラメータに動的に流し込むことで、「どの環境で実行しても美しく中央に配置されるGUI」が完成する。

‘ 例: HTAを画面中央に展開する場合のイメージ(HTML/JS側での処理)
‘ window.moveTo((screen.width – 600) / 2, (screen.height – 400) / 2);

3. 無駄なCOMインスタンス保持の排除

VBScriptのバッチ処理やループ内で何度も `GetObject(“winmgmts:…”)` を呼び出すと、WMIプロバイダのメモリリークやパフォーマンス低下を招く。WMIへの接続はスクリプト全体で極力1回に絞り、用事が済んだら即座に `Set obj = Nothing` で解放する。この鉄則を守るだけで、長時間稼働する監視スクリプトの安定性が劇的に向上する。

総括

VBScriptはレガシーな言語と揶揄されることもあるが、OSの根幹(WMIやCOM)を直接叩けるその機動力は、いまだにWindows環境における自動化の強力な武器である。
環境依存を排除し、ロジカルに画面構成を読み解くコードを組むことで、あなたの作るツールは「ただ動くもの」から「プロフェッショナルが信頼して任せられるシステム」へと昇華する。

現場のクオリティを一段引き上げるこの知見を、ぜひ次の開発プロジェクトから役立ててほしい。

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