【テクニカル・上級編】【アプリインベントリ化】レジストリ Uninstall キー全走査による端末内インストール済みソフトウェア一括抽出 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

レガシーを制する者だけが辿り着く「Windowsインベントリ管理の深淵」

現場で長年戦っているエンジニアなら知っているはずだ。「最新のツールを入れる」ことよりも、「今そこにある混沌をいかに正確に読み解くか」の方が、組織にとっては遥かに価値があるという事実を。

Windows環境のアプリインベントリ化。PowerShellでワンライナーを書けば済む話だと考えるのは甘い。我々が対峙するのは、権限昇格の壁、32bit/64bitの混在、そしてレジストリに残された「墓標」のようなゴミデータだ。

今日は、VBScriptとWSHを用いて、この混沌を極限まで効率的に掃討するアーキテクチャを伝授する。

1. なぜ今、VBScriptなのか

モダンな環境であればPowerShellを使うべきだ。だが、クライアントのセキュリティポリシーでスクリプト実行が制限されていたり、レガシーなWindows Server 2008/2012環境での運用を求められたりする場面は未だに多い。

VBScriptは「枯れた技術」ではない。「OSの基幹に最も近い場所で、余計なオーバーヘッドなしに動作する超軽量エンジン」だ。メモリ効率を極め、Windows APIの深淵を覗くための最高のツールになり得る。

2. アーキテクチャの要諦:32bitと64bitの「二重の壁」

レジストリの `Uninstall` キーには、以下の2つの深淵が存在する。

  • `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`
  • `HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall`

64bitOS上では、32bitアプリの情報は後者に格納される。これを走査するには、`winmgmts`のRegProvを利用するのが定石だが、今回はパフォーマンスとメモリ使用量を考慮し、WMIを直接叩くのではなく、レジストリ操作に特化した設計を行う。

3. 実装:メモリ効率を最大化したインベントリ収集エンジン

以下のコードは、オブジェクトのライフサイクルを厳密に管理し、メモリリークを許さない構造になっている。

‘ インベントリ抽出スクリプト
Option Explicit

Dim objFSO, objFile, strLogPath
Dim strHeader

‘ 実行環境のセットアップ(UTF-8出力はVBS単体では困難なため、CSVのBOM書き込みを考慮)
strLogPath = “C:\Temp\AppInventory.csv”
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objFile = objFSO.CreateTextFile(strLogPath, True)

‘ CSVヘッダー書き込み
objFile.WriteLine “DisplayName,DisplayVersion,Publisher”

‘ 走査対象のレジストリパス(64bit/32bit)
Dim arrPaths
arrPaths = Array( _
“HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\”, _
“HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\” _
)

Dim objShell
Set objShell = CreateObject(“WScript.Shell”)

Dim strPath, i
For i = 0 To UBound(arrPaths)
Call ScanRegistryKeys(arrPaths(i), objShell, objFile)
Next

‘ 資源の解放(アーキテクトの矜持)
objFile.Close
Set objFile = Nothing
Set objFSO = Nothing
Set objShell = Nothing

‘ レジストリ走査の核心部分
Sub ScanRegistryKeys(basePath, objShell, objFile)
On Error Resume Next
Dim objRegistry, arrSubKeys, strKey
Set objRegistry = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv”)

‘ サブキー列挙
objRegistry.EnumKey &H80000002, Replace(basePath, “HKLM\”, “”), arrSubKeys

If IsArray(arrSubKeys) Then
For Each strKey In arrSubKeys
Dim name, version, publisher
objRegistry.GetStringValue &H80000002, Replace(basePath, “HKLM\”, “”) & strKey, “DisplayName”, name
objRegistry.GetStringValue &H80000002, Replace(basePath, “HKLM\”, “”) & strKey, “DisplayVersion”, version
objRegistry.GetStringValue &H80000002, Replace(basePath, “HKLM\”, “”) & strKey, “Publisher”, publisher

‘ 空データを除外し、メモリに乗せる前にフィルタリング
If Not IsNull(name) And name <> “” Then
objFile.WriteLine “””” & name & “””,””” & version & “””,””” & publisher & “”””
End If
Next
End If

Set objRegistry = Nothing
End Sub

4. チーフアーキテクトからの助言

オブジェクトのライフサイクル管理

`Set objRegistry = Nothing` をループ内で行うのは非効率に見えるかもしれないが、WMI経由のプロバイダ接続は肥大化しやすい。大規模なキー走査を行う場合、明示的な解放はメモリ不足(Out of Memory)を防ぐための生命線だ。

レジストリの「ゴミ」との向き合い方

実際のインベントリ抽出では、`DisplayName` が空のキーや、単なるパッチ情報が大量に混入する。これらをCSV上で除外するのではなく、スクリプト実行中にフィルタリングを行うことで、後続のExcel集計作業の負荷を劇的に軽減できる。

システム間連携への展開

このスクリプトをタスクスケジューラに登録し、出力先をネットワーク共有フォルダに指定すれば、自動インベントリ収集システムが完成する。さらに、出力したCSVを `ADODB.Connection` でSQLのように操作すれば、特定のバージョン以下のアプリを抽出してアラートを出す、といった自動化が即座に実装可能だ。

技術の洗練とは、新しいものを詰め込むことではない。「何を残し、何を削ぎ落とすか」

このスクリプトは、単なるコードではない。あなたのシステム管理における「目」となるものだ。使いこなしてほしい。

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