VB.NETからWMIのWin32_BIOSを使ってBIOSバージョンを取得する際、SMBIOSPresentがFalseのPCや仮想環境に遭遇すると、SMBIOSBIOSVersionが空になり取得処理が落ちることがあります。本記事では例外回避のチェック順序と、BIOSVersionを使った堅牢なフォールバック実装を整理します。
結論:SMBIOSPresent を見てから SMBIOSBIOSVersion を読む
Win32_BIOS クラスで「SMBIOS 由来の情報(SMBIOSBIOSVersion など)」を使いたい場合は、SMBIOSPresent が True であることを先に確認する運用が最も安全です。SMBIOSPresent が False(または取得できない)環境では、SMBIOSBIOSVersion が空(null)になったり、機種固有のWMI実装の癖で想定外の値が返ったりすることがあります。
- SMBIOSPresent = True:SMBIOSBIOSVersion を優先して取得する
- SMBIOSPresent = False:SMBIOSBIOSVersion には依存せず、BIOSVersion / Version などでフォールバックする
- SMBIOSPresent が Nothing/未取得:安全側に倒してフォールバック(または例外を握りつぶさずログに残す)
一方、BIOSVersion プロパティは SMBIOS に依存しないケースが多いため、まずは BIOSVersion を使う実装に寄せておくと、現場ではトラブルが減ります(ただし機種や仮想環境によっては BIOSVersion も空になることがあるため、複数候補で組み立てるのが現実的です)。
Win32_BIOS でよく使うプロパティの違い
「どのプロパティを見れば“BIOSバージョン”として扱えるか」は環境差が出やすいポイントです。Win32_BIOS の代表的なプロパティを、依存関係と戻り値のクセに着目して整理します。
| プロパティ | 主な型 | SMBIOS依存 | 特徴 | 実運用の注意 |
|---|---|---|---|---|
| SMBIOSPresent | Boolean | - | SMBIOS情報が提供されているかを示す | Nothing の場合もあるため、取得可否を加味して扱う |
| SMBIOSBIOSVersion | String | 高い | SMBIOSが提供する「BIOSバージョン文字列」 | SMBIOSPresent=False では null/空文字になりやすい。まずSMBIOSPresentを確認 |
| BIOSVersion | String または String[] | 低い(環境差あり) | ベンダー実装次第で配列で返ることがある | 配列→結合が必要。改行や余計な空白が混ざることがある |
| Version | String | 低い | 「バージョン」らしい文字列が入ることがある | SMBIOSBIOSVersion/BIOSVersion が取れない場合の保険として扱う |
| Name | String | 低い | 製品名に近い文字列(バージョンが混ざる場合あり) | 「BIOSバージョン」としては過剰情報になりやすい |
| Manufacturer | String | 低い | BIOSベンダー名 | 表示用途には有用。バージョン識別には単独では不足 |
| ReleaseDate | String(WMI日時形式) | 低い | リリース日 | 表示するならWMI日時→DateTime変換を行う |
SMBIOSPresent が False になる典型パターン
「SMBIOSが無いなんてあるの?」と思いがちですが、現場では意外と遭遇します。原因は大きく分けて次のようなものです。
| パターン | 例 | 起こりやすい症状 | 実務での対処 |
|---|---|---|---|
| 古い/特殊なファームウェア | 古いPC、産業機器、組み込み機器 | SMBIOSPresent=False、SMBIOSBIOSVersion が null | BIOSVersion / Version を優先し、それも無い場合は別経路(レジストリ等)を検討 |
| 仮想環境の実装差 | 一部のハイパーバイザー、特殊なクラウド環境 | 取得値が固定文字列、または空 | 複数プロパティで候補を作り、空なら「取得不可」として扱う |
| WMI提供側の問題 | WMIリポジトリ破損、権限/サービス不調 | ManagementException、Get() 自体が失敗 | 例外を捕捉し、診断用ログ(例外メッセージ)を残す |
| セキュリティ/制限 | 最小構成OS、制限された環境 | 一部プロパティだけ取得できない | 「取得できない前提」でフォールバック設計 |
SMBIOSBIOSVersion を参照すると例外が起きるのか
結論から言うと、「SMBIOSPresent=False だから即WMI例外が起きる」とは限りません。多くの環境では、SMBIOSBIOSVersion の値が Nothing(null)や空文字になり、.ToString() を呼んだタイミングで NullReferenceException(VBなら「Null 参照」系の例外)が発生します。
ただし、以下のようなケースでは WMI 側の例外(ManagementException)が起きる可能性もあります。
- WMIサービスやプロバイダーが不調で、プロパティ参照時に内部エラーになる
- クエリの書き方や管理スコープが特殊で、取得できるプロパティが制限されている
- 取得した ManagementObject が破棄されている、または接続が切れている(稀)
そのため実装としては、SMBIOSPresent のチェック+値が Nothing/空文字でないことのチェック+必要に応じて例外捕捉の3点セットで考えると、予期せぬ停止を防ぎやすくなります。
おすすめの判定フロー(チェック順序)
「SMBIOSBIOSVersion が取れるならそれを使いたい」「無理なら別の方法でBIOSバージョンを返したい」という要求を満たす、現実的なフローを表にまとめます。
| 優先度 | 条件 | 取得対象 | 採用理由 |
|---|---|---|---|
| 高 | SMBIOSPresent=True かつ SMBIOSBIOSVersion が空でない | SMBIOSBIOSVersion | SMBIOS由来の「それっぽい」バージョンが得られやすい |
| 中 | BIOSVersion が空でない | BIOSVersion(配列なら結合) | SMBIOSが無い/壊れている環境でも取れることがある |
| 中 | Version が空でない | Version | 最後の砦として使えることがある |
| 低 | 上記が全て空 | 取得不可として継続 | 無理に推測して誤判定するより、明示的に「不明」を返す方が安全 |
VB.NET 実装例:最小の安全対策
まずは質問に近い形で、「SMBIOSPresent を見てから SMBIOSBIOSVersion を読む」最小実装です。Nullチェックを入れても、ToString() 前提のままだと抜け漏れが出るため、空文字の可能性も合わせて処理します。
Imports System.Management
Dim search As New ManagementObjectSearcher(
New SelectQuery("SELECT SMBIOSPresent, SMBIOSBIOSVersion, BIOSVersion, Version FROM Win32_BIOS")
)
For Each info As ManagementObject In search.Get()
Dim smbiosPresent As Boolean = False
If info("SMBIOSPresent") IsNot Nothing Then
' WMI は Boolean が Object で返るため、文字列化して TryParse するのが安全
Boolean.TryParse(info("SMBIOSPresent").ToString(), smbiosPresent)
End If
If smbiosPresent Then
Dim s As String = ""
If info("SMBIOSBIOSVersion") IsNot Nothing Then
s = info("SMBIOSBIOSVersion").ToString()
End If
If Not String.IsNullOrWhiteSpace(s) Then
MsgBox(s.Trim())
Continue For
End If
End If
' フォールバック:BIOSVersion(配列になることがある)
If info("BIOSVersion") IsNot Nothing Then
Dim vObj = info("BIOSVersion")
Dim v As String = ""
If TypeOf vObj Is String() Then
Dim arr = DirectCast(vObj, String())
v = String.Join(" ", arr)
Else
v = vObj.ToString()
End If
If Not String.IsNullOrWhiteSpace(v) Then
MsgBox(v.Trim())
Continue For
End If
End If
' さらにフォールバック:Version
If info("Version") IsNot Nothing Then
Dim v2 = info("Version").ToString()
If Not String.IsNullOrWhiteSpace(v2) Then
MsgBox(v2.Trim())
Continue For
End If
End If
MsgBox("BIOS バージョン情報を取得できませんでした。")
Next
このレベルでも「SMBIOSが無い環境での停止」はかなり減ります。ただし、配列結合の結果に空要素が混ざる、例外が飛ぶ、複数インスタンスが返るなど、現場の“汚れたデータ”に耐えたいなら次の実装が有効です。
VB.NET 実装例:実運用向けの堅牢版(関数化+例外処理)
業務ツールや資産管理のように「絶対に落としたくない」場合は、取得処理を関数化して、取得できた最初の妥当値を返す方式にすると扱いやすくなります。例外は握りつぶすのではなく、ログに残せる設計にしておくと調査が楽です。
Imports System.Management
Imports System.Text
Public Module BiosReader
Public Function GetBiosVersionString() As String
Dim query As String =
"SELECT SMBIOSPresent, SMBIOSBIOSVersion, BIOSVersion, Version, Manufacturer, ReleaseDate " &
"FROM Win32_BIOS"
Try
Using searcher As New ManagementObjectSearcher(query)
For Each mo As ManagementObject In searcher.Get()
' 1) SMBIOSPresent を安全に読む
Dim smbiosPresent As Boolean = TryGetBoolean(mo, "SMBIOSPresent")
' 2) SMBIOS があるなら SMBIOSBIOSVersion を最優先
If smbiosPresent Then
Dim s As String = TryGetString(mo, "SMBIOSBIOSVersion")
If Not String.IsNullOrWhiteSpace(s) Then
Return NormalizeVersionString(s)
End If
End If
' 3) BIOSVersion(配列対策)
Dim biosV As String = TryGetBiosVersion(mo)
If Not String.IsNullOrWhiteSpace(biosV) Then
Return NormalizeVersionString(biosV)
End If
' 4) 最後の砦:Version
Dim v As String = TryGetString(mo, "Version")
If Not String.IsNullOrWhiteSpace(v) Then
Return NormalizeVersionString(v)
End If
' ここまで来たら、そのインスタンスでは取得できない
Next
End Using
Catch ex As ManagementException
' TODO: ログに ex.Message を出すなど(例:イベントログ、ファイル、ETW)
Return ""
Catch ex As UnauthorizedAccessException
Return ""
Catch
Return ""
End Try
Return ""
End Function
Private Function TryGetString(mo As ManagementBaseObject, propName As String) As String
Try
Dim v = mo(propName)
If v Is Nothing Then Return ""
Return v.ToString()
Catch
Return ""
End Try
End Function
Private Function TryGetBoolean(mo As ManagementBaseObject, propName As String) As Boolean
Dim s As String = TryGetString(mo, propName)
Dim b As Boolean = False
If String.IsNullOrWhiteSpace(s) Then Return False
Boolean.TryParse(s, b)
Return b
End Function
Private Function TryGetBiosVersion(mo As ManagementBaseObject) As String
Try
Dim vObj = mo("BIOSVersion")
If vObj Is Nothing Then Return ""
If TypeOf vObj Is String() Then
Dim arr = DirectCast(vObj, String())
Dim sb As New StringBuilder()
For Each item In arr
If Not String.IsNullOrWhiteSpace(item) Then
If sb.Length > 0 Then sb.Append(" ")
sb.Append(item.Trim())
End If
Next
Return sb.ToString()
End If
Return vObj.ToString()
Catch
Return ""
End Try
End Function
Private Function NormalizeVersionString(s As String) As String
' 余計な改行・タブをスペースに寄せ、前後の空白を落とす
Dim t = s.Replace(vbCr, " ").Replace(vbLf, " ").Replace(vbTab, " ")
Do While t.Contains(" ")
t = t.Replace(" ", " ")
Loop
Return t.Trim()
End Function
End Module
ポイントは次の通りです。
- クエリで必要なプロパティだけを SELECT する(取得負荷と意図が明確になる)
- SMBIOSPresent の値は Boolean として返ることもあれば、文字列変換が必要なこともあるため TryParse で吸収する
- BIOSVersion は配列で返ることがあるので、空要素を除外して結合する
- 返却前に正規化(改行や余計なスペースを潰す)して、比較・保存に耐える形にする
さらに実務で効く改善:複数候補を「一つの表示文字列」にまとめる
資産管理やサポート窓口では、単一の「バージョン」だけでなく、メーカーやリリース日も合わせて表示できると問い合わせ対応が速くなります。Win32_BIOS の情報をまとめて1行にする例を紹介します。
Imports System.Management
Imports System.Globalization
Imports System.Text
Public Function GetBiosSummary() As String
Dim query As String =
"SELECT SMBIOSPresent, SMBIOSBIOSVersion, BIOSVersion, Version, Manufacturer, ReleaseDate " &
"FROM Win32_BIOS"
Using searcher As New ManagementObjectSearcher(query)
For Each mo As ManagementObject In searcher.Get()
' バージョン候補を同じ ManagementObject から組み立てる
Dim version As String = ""
Dim smbiosPresent As Boolean = False
If mo("SMBIOSPresent") IsNot Nothing Then
Boolean.TryParse(mo("SMBIOSPresent").ToString(), smbiosPresent)
End If
If smbiosPresent AndAlso mo("SMBIOSBIOSVersion") IsNot Nothing Then
version = mo("SMBIOSBIOSVersion").ToString()
End If
If String.IsNullOrWhiteSpace(version) AndAlso mo("BIOSVersion") IsNot Nothing Then
Dim vObj = mo("BIOSVersion")
If TypeOf vObj Is String() Then
version = String.Join(" ", DirectCast(vObj, String()))
Else
version = vObj.ToString()
End If
End If
If String.IsNullOrWhiteSpace(version) AndAlso mo("Version") IsNot Nothing Then
version = mo("Version").ToString()
End If
If String.IsNullOrWhiteSpace(version) Then version = "(unknown)"
version = version.Replace(vbCr, " ").Replace(vbLf, " ").Replace(vbTab, " ").Trim()
Dim maker As String = ""
If mo("Manufacturer") IsNot Nothing Then maker = mo("Manufacturer").ToString().Trim()
Dim release As String = ""
If mo("ReleaseDate") IsNot Nothing Then
Try
Dim dt = ManagementDateTimeConverter.ToDateTime(mo("ReleaseDate").ToString())
release = dt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
Catch
release = ""
End Try
End If
Dim sb As New StringBuilder()
If Not String.IsNullOrWhiteSpace(maker) Then
sb.Append(maker).Append(" / ")
End If
sb.Append(version)
If Not String.IsNullOrWhiteSpace(release) Then
sb.Append(" / ").Append(release)
End If
Return sb.ToString()
Next
End Using
Return ""
End Function
このように「メーカー / バージョン / 日付」を併記しておくと、同じバージョン文字列でもベンダー違いの取り違えを防げます。ログに残す場合も読みやすくなります。
PowerShell での事前確認(現場での切り分けが速くなる)
コードを直す前に、対象端末がどのプロパティを返しているかを素早く確認すると、原因切り分けが楽になります。管理者権限が不要なことも多いので、まずは次のコマンドで出力を見てください。
Get-CimInstance -ClassName Win32_BIOS |
Select-Object SMBIOSPresent, SMBIOSBIOSVersion, BIOSVersion, Version, Manufacturer, ReleaseDate
もし SMBIOSPresent が False で SMBIOSBIOSVersion が空なら、VB.NET 側で SMBIOSBIOSVersion を前提にしない設計が必要です。逆に SMBIOSPresent が True なのに SMBIOSBIOSVersion が空なら、ファームウェア実装やWMIの癖を疑い、BIOSVersion/Version も合わせて採用する方針が妥当です。
トラブルシューティング:よくある症状と対策
Win32_BIOS は便利ですが、環境差の影響を強く受けます。ありがちなハマりどころを、症状ベースでまとめます。
| 症状 | よくある原因 | 対策 |
|---|---|---|
| SMBIOSBIOSVersion が Nothing で ToString() で落ちる | SMBIOSPresent=False、または値が未設定 | SMBIOSPresent を先に確認+IsNot Nothing/空文字チェックを入れる |
| BIOSVersion が String() で、表示すると System.String[] になる | 配列を ToString() している | TypeOf で配列判定し、String.Join で結合する |
| 同じ端末でも取得文字列に改行や余計な空白が混ざる | ベンダーの実装差 | 正規化(改行→スペース、連続スペース削除)して保存・比較する |
| ManagementException が発生し、Get() 自体が失敗する | WMIサービス不調、WMIリポジトリ破損、アクセス制限 | 例外を捕捉して処理継続。端末側はWMIの健全性確認を実施(運用手順に従う) |
| 取得値が固定文字列(例:To Be Filled By O.E.M.)になる | 仮想環境やテンプレート、未設定のSMBIOS情報 | 固定値は「不明」とみなし、別情報(メーカー/モデル/シリアル等)と合わせて扱う |
設計の考え方:BIOSバージョン取得は「正解が1つ」とは限らない
BIOSバージョンは本来ファームウェアが持つ情報ですが、アプリが参照する経路は複数あります。Win32_BIOS はその一つに過ぎず、環境によっては値が欠落します。そのため実装では、次のような“現場前提”を置くと失敗しにくくなります。
- 最優先は「落ちないこと」:取得に失敗してもアプリが停止しない設計(例外捕捉+空文字返却)
- 値が取れたら勝ち、ではなく妥当性を確認:空文字、固定文言、異常に長い文字列は弾く
- 比較用途なら正規化が必須:改行・連続スペース・前後空白を統一し、ログやDBで比較しやすくする
- 運用で必要な粒度を決める:画面表示なら多少の揺れは許容、資産管理なら厳密に揺れを抑える
よくある質問
SMBIOSPresent を見ずに SMBIOSBIOSVersion の Nothing チェックだけではだめ?
最小限の安全策としては動くことが多いですが、SMBIOSPresent=False の環境では「そもそも SMBIOSBIOSVersion を信頼できない」ため、設計としては先に SMBIOSPresent を見て分岐させた方が、意図が明確で保守しやすいです。特に将来、取得結果をログ集計したり、端末の世代判定に使ったりする場合に差が出ます。
SMBIOSPresent が Nothing のときは True とみなしていい?
おすすめしません。Nothing は「取得できない」可能性を含むため、安全側(False扱い)に倒してフォールバックした方が、例外や誤判定を減らせます。必要ならログに「SMBIOSPresent を取得できなかった」という情報を残すと、後で端末差を追いやすくなります。
BIOSVersion と SMBIOSBIOSVersion のどちらが“正しい”の?
どちらが正しいかは環境次第です。SMBIOSBIOSVersion は SMBIOS 由来の文字列で、ベンダーが想定する「表示向け」になっていることが多い一方、BIOSVersion は配列で返ったり、表記が揺れたりします。実務では「SMBIOSがあるなら SMBIOSBIOSVersion を優先し、無ければ BIOSVersion/Version で補う」というハイブリッドが安定します。
最後に:チェック順序を固定し、フォールバックで吸収する
Win32_BIOS で BIOS バージョンを取得するなら、SMBIOSPresent → SMBIOSBIOSVersion → BIOSVersion → Versionの順に候補を評価し、空や例外を前提にフォールバックするのが実運用で強いアプローチです。端末差が避けられない領域だからこそ、チェック順序と正規化をテンプレ化しておくと、後々の保守コストが下がります。

コメント