【VBA】Win32 APIでレジストリを参照しWindowsのUAC(ユーザーアカウント制御)状態を自動判定する

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

【VBA】Win32 APIでレジストリを参照しWindowsのUAC(ユーザーアカウント制御)状態を自動判定する

【背景と目的】

管理者権限が必要なVBA処理で、UACが無効化されておらず権限エラーが発生する問題を防止するため、Win32 APIで安全に状態判定します。(68文字)

【処理フロー図】

graph TD
    A["処理開始"] --> B["レジストリキーのオープン"]
    B --> C{"オープン成功?"}
    C -- Yes --> D["EnableLUA値の取得"]
    C -- No --> E["判定不可・エラー処理"]
    D --> F["レジストリキーのクローズ"]
    F --> G{"EnableLUA = 1?"}
    G -- Yes --> H["UAC有効と判定"]
    G -- No --> I["UAC無効と判定"]
    E --> J["終了"]
    H --> J
    I --> J

※本フローでは、Windowsレジストリ(HKLM)内のキーを取得し、UACの有効状態(EnableLUA)を判定する手順を示しています。

【実装:VBAコード】

Option Explicit

' ==============================================================================
' Win32 API 宣言(64bit / 32bit 両対応)
' ==============================================================================
#If VBA7 Then

    Private Declare PtrSafe Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" ( _
        ByVal hKey As LongPtr, _
        ByVal lpSubKey As String, _
        ByVal ulOptions As Long, _
        ByVal samDesired As Long, _
        ByRef phkResult As LongPtr) As Long

    Private Declare PtrSafe Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" ( _
        ByVal hKey As LongPtr, _
        ByVal lpValueName As String, _
        ByVal lpReserved As LongPtr, _
        ByRef lpType As Long, _
        ByRef lpData As Long, _
        ByRef lpcbData As Long) As Long

    Private Declare PtrSafe Function RegCloseKey Lib "advapi32.dll" ( _
        ByVal hKey As LongPtr) As Long
#Else

    Private Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" ( _
        ByVal hKey As Long, _
        ByVal lpSubKey As String, _
        ByVal ulOptions As Long, _
        ByVal samDesired As Long, _
        ByRef phkResult As Long) As Long

    Private Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" ( _
        ByVal hKey As Long, _
        ByVal lpValueName As String, _
        ByVal lpReserved As Long, _
        ByRef lpType As Long, _
        ByRef lpData As Long, _
        ByRef lpcbData As Long) As Long

    Private Declare Function RegCloseKey Lib "advapi32.dll" ( _
        ByVal hKey As Long) As Long
#End If

' レジストリ定数
Private Const HKEY_LOCAL_MACHINE As Long = &H80000002
Private Const KEY_READ As Long = &H20019
Private Const ERROR_SUCCESS As Long = 0&
Private Const REG_DWORD As Long = 4

' UAC設定のレジストリパス
Private Const UAC_REG_PATH As String = "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
Private Const UAC_VALUE_NAME As String = "EnableLUA"

''' <summary>
''' UAC(ユーザーアカウント制御)が有効かどうかを判定するメインプロシージャ
''' </summary>
Public Sub CheckUACStatus()
    ' 高速化設定
    On Error GoTo ErrorHandler
    Application.ScreenUpdating = False

    Dim isUACEnabled As Boolean
    isUACEnabled = IsUACActive()

    ' 画面更新の再開
    Application.ScreenUpdating = True

    ' 判定結果の表示
    If isUACEnabled Then
        MsgBox "UAC(ユーザーアカウント制御)は【有効】です。", vbInformation, "UAC状態確認"
    Else
        MsgBox "UAC(ユーザーアカウント制御)は【無効】です。", vbExclamation, "UAC状態確認"
    End If

    Exit Sub

ErrorHandler:
    Application.ScreenUpdating = True
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical, "エラー"
End Sub

''' <summary>
''' レジストリを参照してUACの有効状態(EnableLUA)を取得する関数
''' </summary>
''' <returns>True: 有効, False: 無効または取得失敗</returns>
Private Function IsUACActive() As Boolean
    #If VBA7 Then

        Dim hKeyHandle As LongPtr
    #Else

        Dim hKeyHandle As Long
    #End If

    Dim lResult As Long
    Dim dwValue As Long
    Dim cbData As Long
    Dim dwType As Long

    IsUACActive = False
    cbData = 4 ' DWORDのバイトサイズ

    ' レジストリキーを読み込みモードでオープン
    lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, UAC_REG_PATH, 0, KEY_READ, hKeyHandle)

    If lResult = ERROR_SUCCESS Then
        ' EnableLUAの値を取得
        lResult = RegQueryValueEx(hKeyHandle, UAC_VALUE_NAME, 0, dwType, dwValue, cbData)

        If lResult = ERROR_SUCCESS And dwType = REG_DWORD Then
            ' 1 であれば UAC 有効
            If dwValue = 1 Then
                IsUACActive = True
            End If
        End If

        ' 開いたレジストリキーを確実にクローズ
        Call RegCloseKey(hKeyHandle)
    End If
End Function

【技術解説】

  • Win32 APIの活用: WScript.Shell などの外部スクリプトオブジェクトに依存せず、advapi32.dll の標準API(RegOpenKeyExA / RegQueryValueExA)を直接呼び出すことで高速かつ堅牢なレジストリ読み取りを実現しています。

  • 64bit/32bit互換性: #If VBA7 条件分岐と PtrSafe 属性を使用し、ハンドル保持用変数に LongPtr を指定することで、Office 32bit/64bit 環境の双方で安全に動作します。

  • UAC判定ロジック: WindowsにおけるUACの有効状態は HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\SystemEnableLUA (DWORD) に保持されます。値が 1 の場合はUAC有効、0 の場合は無効と判断できます。

【注意点と運用】

  • レジストリハンドルの開放忘れ: RegOpenKeyEx で取得したハンドルは、エラー処理を含めて必ず RegCloseKey でクローズしてください。リソースリークの原因となります。

  • 読み取り権限: KEY_READ 権限でオープンしているため標準ユーザー権限でも取得可能ですが、極端にアクセス制限された環境ではキーオープンに失敗する可能性があります。その場合はデフォルト動作(False扱い等)にフォールバックさせる例外処理が必要です。

  • 再起動のタイムラグ: ユーザーが手動でUAC設定を変更しても、Windowsを再起動するまではレジストリ値が反映されない場合があります。

【まとめ】

  1. PtrSafeとLongPtrを徹底し、64bit Office環境での動作互換性を確保する。

  2. API呼び出し後は必ず RegCloseKey を実行し、リソース解放漏れを防ぐ。

  3. 外部オブジェクト非依存のAPI実装により、権限エラー時の安全なフォールバック処理を構築する。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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