VB.NETでOS名を言語に依存せず取得する方法:My.Computer.Info.OSFullNameの代替(ProductName/RtlGetVersion)

VB.NET(.NET Framework)でWindowsのOS名を取得するとき、My.Computer.Info.OSFullNameは手軽ですが、返ってくる文字列がOSの表示言語に合わせて変わります。ログ出力や機能分岐で「言語に依存しない(できれば英語固定の)OS名」が必要な場合に困らないよう、実務で使える取得方法と設計の考え方を整理します。

日程Fit。無料・登録不要。「いつ空いてる?」を、ひとつのリンクで。リンクを送って、○△×でかんたん日程調整。無料で日程を作る。
目次

My.Computer.Info.OSFullName が「言語依存」になる理由

My.Computer.Info.OSFullNameMy.Computer.Info配下のOS情報)は、Windowsが提供する表示用の名称をそのまま返す性質があり、システムの表示言語(UI言語)に合わせてローカライズされた文字列になります。

そのため、たとえばロシア語環境では、OS名が次のようにロシア語として返ることがあります。

Майкрософт Windows 8.1 Профессиональная 6.3.9600.0

この挙動自体は「ユーザーに見せる」用途では正しく便利ですが、次のような用途では不向きになりがちです。

  • ログや監査記録に残して、国や言語をまたいで集計したい
  • OS名の文字列で分岐(例:Windows 10/11で機能切替)したい
  • ライセンス・サポート対象の判定をしたい(ただし「文字列比較」前提は危険)
取得したいもの推奨アプローチ
ユーザー向けの表示名Windows 11 Pro(日本語UIなら日本語表記)OSFullNameなどローカライズ名でOK
言語非依存の識別子(ログ・集計)製品名(英語寄り)+ビルド番号レジストリやバージョンAPIで「生データ」を取る
機能分岐に必要なOS判定10/11、Server/Client、ビルド閾値数値(メジャー/マイナー/ビルド)で判断する

「言語に依存しないOS名」を定義し直すと設計が安定する

ここが実務で一番重要です。「OS名」と言っても、要求が混ざりやすいからです。

  • 表示名(ブランド名):ユーザーが見て分かりやすいが、言語や表記ゆれがある
  • 識別子(判定用の情報):数値やコードで安定するが、人間には分かりにくい

おすすめは、目的別に次のどちらか(または両方)を残すことです。

  • 判定用:メジャー/マイナー/ビルド番号、製品種別(Server/Client)、エディションコード
  • 表示用:必要なら英語固定の製品名(例:ProductName)、またはローカライズ名(OSFullName)

つまり「英語固定のOS名が欲しい」場合でも、英語の文字列そのものを正として扱うのではなく、数値・コードを正として持ち、英語名は“表示ラベル”として扱うと将来の変更に強くなります。

レジストリ ProductName で英語寄りの製品名を取る

質問で挙がっている次のレジストリ値は、現場でよく使われる「製品名」取得手段です。

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName

多くのWindows環境では、このProductName英語表記のまま格納されているケースが多いため、言語非依存(少なくとも「UI言語には引きずられにくい」)な候補になります。実際、ローカライズされた製品名を返す別手段と比べると、ProductNameは英語のまま、というパターンに遭遇しやすいです。

注意点として、これは「必ず英語」と公式に保証されている前提で設計すると危険です。将来のWindowsの仕様変更、特殊なエディション、OEMカスタム、企業イメージ適用などで、想定外の文字列になる可能性はゼロではありません。したがって、文字列一致で厳密判定するのではなく、ログや表示、ゆるい分類に使うのが安全です。

VB.NET(.NET Framework)で ProductName を読むサンプル

64bit OS上で32bitプロセスとして動く場合、レジストリのビュー(32/64)問題が出ます。確実に読むならOpenBaseKeyでビューを指定します。

Imports Microsoft.Win32

Public Module OsNameRegistry

    Public Function TryGetProductName(ByRef productName As String) As Boolean
        productName = Nothing

        Const subKey As String = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
        Const valueName As String = "ProductName"

        ' まず64bitビューを試す(64bit OSで最も期待通りになりやすい)
        If TryReadRegString(RegistryView.Registry64, subKey, valueName, productName) Then
            Return True
        End If

        ' 次に32bitビューを試す(32bit OS / 32bit環境など)
        If TryReadRegString(RegistryView.Registry32, subKey, valueName, productName) Then
            Return True
        End If

        Return False
    End Function

    Private Function TryReadRegString(view As RegistryView,
                                     subKey As String,
                                     valueName As String,
                                     ByRef result As String) As Boolean
        result = Nothing

        Try
            Using baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view)
                Using key = baseKey.OpenSubKey(subKey, writable:=False)
                    If key Is Nothing Then Return False

                    Dim obj = key.GetValue(valueName, Nothing)
                    If obj Is Nothing Then Return False

                    Dim s = TryCast(obj, String)
                    If String.IsNullOrWhiteSpace(s) Then Return False

                    result = s.Trim()
                    Return True
                End Using
            End Using
        Catch
            ' 権限/環境差異などは呼び出し側でフォールバックできるよう握りつぶす
            Return False
        End Try
    End Function

End Module

これで、たとえば次のような文字列が取得できることがあります(環境によって異なります)。

  • Windows 10 Pro
  • Windows 11 Pro
  • Windows Server 2022 Standard

ProductName と一緒に取っておくと「実務で強い」関連キー

OS名を「英語固定っぽく」出すだけならProductNameで十分な場合が多いですが、ログや判定の精度を上げるなら、同じ場所にある周辺情報もセットで取るのが強いです。

値名(例)用途よくある例注意点
ProductName製品名(表示用ラベル)Windows 11 Pro文字列は将来変更されうる
EditionIDエディションの分類Professional / EnterpriseUI表示名ではない
InstallationTypeClient/Serverの傾向Client / Server最終判定は別情報と組み合わせ推奨
CurrentBuildNumber / CurrentBuildビルド番号(判定の核)19045 / 22631 など値名が複数あるためフォールバックが必要
UBR更新リビジョン(詳細ログ向け)Windows 10 19045.XXXXDWORDなので型変換に注意
DisplayVersion22H2などの表示22H2古い環境では存在しないことがある

ポイントは、「OS名(文字列)」だけに頼らずビルド番号やエディションIDなど“揺れにくい値”を一緒に取ることです。これだけで、言語差異や表記ゆれへの耐性が大きく上がります。

RtlGetVersion で「言語に依存しないOSバージョン」を確実に取る

OS名そのものよりも「Windowsの系統やバージョン判定」が目的なら、文字列ではなく数値を取るほうが安全です。その代表がntdll.dllRtlGetVersionです。

なぜわざわざRtlGetVersionなのかというと、.NET Frameworkや一般的なAPIでは、アプリのマニフェスト(対応OS宣言)や互換性設定の影響で、OSバージョンが期待通りに取れないケースがあるからです。RtlGetVersionは比較的「実OSに近い値」が得られやすく、ログ用途・判定用途で重宝します。

VB.NET で RtlGetVersion を呼ぶサンプル

HTML中でVB属性の山括弧が壊れないように、以下のコードは<などにエスケープしています(コピペ後はそのままでも動作します)。

Imports System.Runtime.InteropServices

Public Module OsVersionNative

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
    Private Structure OSVERSIONINFOEX
        Public dwOSVersionInfoSize As Integer
        Public dwMajorVersion As Integer
        Public dwMinorVersion As Integer
        Public dwBuildNumber As Integer
        Public dwPlatformId As Integer
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)>
        Public szCSDVersion As String
        Public wServicePackMajor As Short
        Public wServicePackMinor As Short
        Public wSuiteMask As Short
        Public wProductType As Byte
        Public wReserved As Byte
    End Structure

    <DllImport("ntdll.dll", CharSet:=CharSet.Unicode)>
    Private Function RtlGetVersion(ByRef info As OSVERSIONINFOEX) As Integer
    End Function

    Public Function TryGetRealWindowsVersion(ByRef major As Integer,
                                            ByRef minor As Integer,
                                            ByRef build As Integer) As Boolean
        major = 0 : minor = 0 : build = 0

        Try
            Dim info As New OSVERSIONINFOEX()
            info.dwOSVersionInfoSize = Marshal.SizeOf(GetType(OSVERSIONINFOEX))

            Dim status = RtlGetVersion(info)
            If status <> 0 Then Return False

            major = info.dwMajorVersion
            minor = info.dwMinorVersion
            build = info.dwBuildNumber
            Return True
        Catch
            Return False
        End Try
    End Function

End Module

取得したバージョン番号から「英語のOS名」を作る考え方

RtlGetVersionで取れるのは数値です。これをそのままログに残すだけでも言語非依存ですが、運用上「Windows 10/11」くらいは人間が読める形にしたいことも多いです。その場合は、メジャー/マイナー/ビルド番号から自前でラベルを作るのが安全です。

判定材料ラベル例(英語)コメント
Major=10, Minor=0, Build >= 2200010.0.22631Windows 11Windows 11は「10.0」のままなのでBuildで分ける
Major=10, Minor=0, Build < 2200010.0.19045Windows 1022H2などの細分は別途DisplayVersion等で補う
Major=6, Minor=36.3.9600Windows 8.1旧世代
Major=6, Minor=16.1.7601Windows 7サポート切れ環境の検知にも使える

この方法の良い点は、ローカライズされる余地がないことです。さらに、将来のWindowsでも「未知のビルド番号」が出た場合に、文字列の判定よりもフォールバックが作りやすいです(例:Windows (NT 10.0, Build 26000)のように残せる)。

GetProductInfo でエディションを「言語に依存しないコード」として取得する

「Pro/Enterprise/Education などのエディション」まで言語非依存で判定したいなら、Windows APIのGetProductInfoを使う手があります。これは製品タイプを数値コードとして返すため、文字列よりも安定します(表示名ではありませんが、判定やマッピングに向きます)。

注意点として、GetProductInfoは「Windowsのメジャー/マイナー」などを引数に取るため、取得したバージョン(例:RtlGetVersion)と組み合わせて呼ぶのが実務的です。

Imports System.Runtime.InteropServices

Public Module WindowsEdition

    <DllImport("kernel32.dll", SetLastError:=True)>
    Private Function GetProductInfo(dwOSMajorVersion As Integer,
                                    dwOSMinorVersion As Integer,
                                    dwSpMajorVersion As Integer,
                                    dwSpMinorVersion As Integer,
                                    ByRef pdwReturnedProductType As Integer) As Boolean
    End Function

    Public Function TryGetProductType(major As Integer, minor As Integer, ByRef productType As Integer) As Boolean
        productType = 0
        Try
            ' Service Packは0でOK(必要ならOSVERSIONINFOEXから渡す)
            Return GetProductInfo(major, minor, 0, 0, productType)
        Catch
            Return False
        End Try
    End Function

End Module

返ってくるproductTypeは、たとえば「Professional」「Enterprise」などに対応する定数です。全定数をここで網羅するより、実務では次のように扱うと運用が楽です。

  • ログには数値コード(例:productType=48)も一緒に残す
  • 主要なものだけテーブル化して英語ラベルに変換する
  • 未知の値は UnknownEdition(XXXX) としてフォールバックする
方式言語依存判定の安定性人間の読みやすさ
ProductName(レジストリ文字列)低い傾向(英語のことが多い)中(表記ゆれの可能性)高い
GetProductInfo(数値コード)なし高い低い(変換が必要)

実務向け:英語固定っぽい「OS名」を組み立てる実装パターン

「言語に依存しない」「なるべく英語固定」「将来のOSでも壊れにくい」を同時に満たすには、次のようなフォールバック付きの合成が強いです。

優先取得元得られるもの使いどころ
1レジストリ ProductName英語寄りの製品名(例:Windows 11 Pro)表示・ログ(人間が読む)
2RtlGetVersionMajor/Minor/Build判定・ログ(言語非依存の核)
3レジストリ(Build/UBR/DisplayVersion 等)詳細なバージョン文字列トラブルシュート・サポート連携
4GetProductInfoエディションコードエディション判定が必要な場合

合成結果の例

  • 人間向けラベルWindows 11 Pro
  • 判定向け識別子NT 10.0 (Build 22631, UBR 2861)
  • 将来の未知環境Windows (NT 10.0, Build 26000) / ProductName=Unknown

VB.NET:OS情報をまとめて返すサンプル(フォールバック付き)

以下は「言語非依存の核(数値)」と「英語寄りの製品名(文字列)」を両方返す例です。あくまで雛形なので、ログ形式や例外処理は現場の方針に合わせてください。

Public Class WindowsOsInfo
    Public Property ProductName As String
    Public Property Major As Integer
    Public Property Minor As Integer
    Public Property Build As Integer
    Public Property DisplayLabelEnglish As String
    Public Property VersionLabel As String
End Class

Public Module WindowsOsInfoProvider

    Public Function GetWindowsOsInfo() As WindowsOsInfo
        Dim info As New WindowsOsInfo()

        ' 1) ProductName(英語寄りの可能性が高い)
        Dim pn As String = Nothing
        If OsNameRegistry.TryGetProductName(pn) Then
            info.ProductName = pn
        Else
            info.ProductName = ""
        End If

        ' 2) 実OSに近いバージョン(言語非依存)
        Dim major As Integer, minor As Integer, build As Integer
        If OsVersionNative.TryGetRealWindowsVersion(major, minor, build) Then
            info.Major = major : info.Minor = minor : info.Build = build
            info.VersionLabel = $"NT {major}.{minor} (Build {build})"
        Else
            info.VersionLabel = "NT (Unknown)"
        End If

        ' 3) 表示用ラベル(英語固定っぽく)
        '    ProductNameが取れればそれを優先、取れなければバージョンから作る
        If Not String.IsNullOrWhiteSpace(info.ProductName) Then
            info.DisplayLabelEnglish = info.ProductName
        Else
            info.DisplayLabelEnglish = GuessWindowsFamilyLabel(major, minor, build)
        End If

        Return info
    End Function

    Private Function GuessWindowsFamilyLabel(major As Integer, minor As Integer, build As Integer) As String
        If major = 10 AndAlso minor = 0 Then
            If build >= 22000 Then
                Return "Windows 11"
            Else
                Return "Windows 10"
            End If
        ElseIf major = 6 AndAlso minor = 3 Then
            Return "Windows 8.1"
        ElseIf major = 6 AndAlso minor = 2 Then
            Return "Windows 8"
        ElseIf major = 6 AndAlso minor = 1 Then
            Return "Windows 7"
        ElseIf major = 6 AndAlso minor = 0 Then
            Return "Windows Vista"
        ElseIf major = 5 AndAlso minor = 1 Then
            Return "Windows XP"
        End If

        Return $"Windows (NT {major}.{minor}, Build {build})"
    End Function

End Module

この形にしておくと、UI表示にはDisplayLabelEnglish、判定やサポート用にはMajor/Minor/BuildVersionLabelを使えるため、要件が途中で増えても破綻しにくくなります。

「ProductNameは常に英語なのか?」への現実的な答え

結論としては、多くの環境では英語のまま入っているケースが多い一方で、「必ず英語」と断言できる前提にはしないのが安全です。

実務では、次のように扱うのがおすすめです。

  • 表示用ラベルとしてはProductNameはかなり有力(英語で揃えやすい)
  • 判定の正は、RtlGetVersionの数値・ビルド番号など(言語に依存しない)
  • ProductNameが想定外でも壊れないよう、フォールバック(Unknown扱い)を入れる
  • ログには「取得できた生の値」を残しておき、後からマッピングを追加できるようにする

「英語名で揃えたい」という要求はよくありますが、英語文字列そのものをキーにすると、将来の表記ゆれに負けやすいのが落とし穴です。英語名は“見せる”ため、判定は“数値・コード”で、という役割分担が最も事故が少ないです。

よくある落とし穴と対策

落とし穴起きる問題対策
OSFullNameの文字列で判定してしまう言語や表記ゆれで分岐が壊れる判定はRtlGetVersionの数値・ビルド番号で行う
32bit/64bitのレジストリビュー32bitアプリが別ビューを読んで値が取れないRegistryView.Registry64/Registry32を順に試す
Windows 11はバージョンが10.0のままMajor/Minorだけでは10/11が区別できないBuild閾値(例:22000以上)を併用する
将来のWindowsで未知の値が来るマッピングがないと表示が崩れる未知値はUnknownとしてログに残し、後から追加
レジストリ値の存在差DisplayVersionなどが古いOSで存在しない存在チェック+複数キーのフォールバックを用意

用途別のおすすめ(結局どれを使うべきか)

最後に「何を使うべきか」を用途別に整理します。ここを押さえると、要件が増えても迷いません。

用途必要な情報おすすめ手段理由
ログ出力(運用・監査)人が読める名称+詳細ProductNameRtlGetVersion(Build)英語寄り名称と数値を両方残せる
機能分岐(Windows 10/11など)判定に必要な最小情報RtlGetVersion(Build閾値)文字列比較より壊れにくい
エディション判定(Pro/Enterprise等)エディションの安定判定GetProductInfo(コード)+必要なら表示ラベル言語に依存しないコードで扱える
ユーザー向け画面表示現地語で自然な表示My.Computer.Info.OSFullNameローカライズされているのがむしろ利点

まとめ

My.Computer.Info.OSFullNameは「表示用のローカライズ名」を返すため、言語に依存しない識別子が必要な用途(ログの集計、機能分岐、判定)には不向きです。言語非依存でいくなら、レジストリのProductNameで英語寄りの製品名を取りつつ判定の正はRtlGetVersionの数値(特にBuild)に寄せるのが安定します。さらにエディションが必要ならGetProductInfoのコードを併用し、未知の値に備えたフォールバックを入れておけば、将来のWindowsでも壊れにくい実装になります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次