VBAでレジストリを操作する

Tech

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

VBAでレジストリを操作する

背景と要件

Officeアプリケーション(Excel, Accessなど)で複雑な自動化を行う際、アプリケーション固有の設定、ユーザーごとのカスタマイズ、あるいは他のアプリケーションとの連携のために、永続的なデータを保存する必要が生じます。ファイル保存は一般的ですが、より軽量で、OSレベルでの管理が容易な方法としてWindowsレジストリの活用が考えられます。

VBAからレジストリを操作する場合、通常は外部ライブラリへの依存を避けたいという要件があります。このため、Windows API(Win32 API)を直接呼び出す方法が最も堅牢で汎用性が高いアプローチとなります。本記事では、VBAのDeclare PtrSafeステートメントを用いてWin32 APIを宣言し、レジストリの読み書き、キーの作成・削除を行う実践的な方法を解説します。

要件のまとめ:

  • 外部ライブラリは使用せず、Win32 APIを直接利用する。

  • ExcelおよびAccessを対象とし、再現可能なコードを提供する。

  • レジストリ操作における性能考慮点と、運用上の注意点を記述する。

  • 処理の流れをMermaidフローチャートで視覚化する。

  • 実行手順とロールバック方法を明確にする。

設計

レジストリ操作の基本概念

Windowsレジストリは、階層構造を持つデータベースで、オペレーティングシステムやインストールされているアプリケーションの設定情報が格納されています。VBAから操作する際には、以下の基本要素を理解する必要があります。

  • ルートキー (Root Key): レジストリの最上位に位置するキー。主なものとしてHKEY_CURRENT_USER (現在のユーザー設定) や HKEY_LOCAL_MACHINE (システム全体の設定) があります。

  • サブキー (SubKey): ルートキーの下に階層的に連なるキー。パス形式で指定します(例: Software\MyApplication)。

  • 値 (Value): サブキーの下に格納される実際のデータ。名前、データ型、データ本体を持ちます。データ型には文字列 (REG_SZ)、数値 (REG_DWORD) などがあります。

使用するWin32 API関数

VBAでレジストリを操作するために、advapi32.dllおよびkernel32.dllに含まれる以下の主要なWin32 API関数を使用します。これらはDeclare PtrSafeを使って宣言します。

  1. RegOpenKeyExA: 既存のレジストリキーを開く。

  2. RegCreateKeyExA: レジストリキーを作成または開く。

  3. RegSetValueExA: レジストリキーに値を書き込む。

  4. RegQueryValueExA: レジストリキーから値を読み取る。

  5. RegDeleteValueA: レジストリキーから特定の値を削除する。

  6. RegDeleteKeyExA: レジストリキー(サブキー)を削除する。

  7. RegCloseKey: 開いたレジストリキーのハンドルを閉じる。

  8. lstrcpyA: 文字列ポインタのコピー(RegQueryValueExで文字列を読み取る際に補助的に使用)。

データ型とポインタの扱い

VBA (特に64ビット環境) でWin32 APIを扱う際、ポインタやハンドルはLongPtr型で宣言します。文字列はByVal As Stringで渡すか、StrPtr()関数でポインタを渡します。特にRegSetValueExRegQueryValueExで文字列やバイナリデータを扱う際には、メモリの管理に注意が必要です。文字列の読み取りでは、必要なバッファサイズを事前に取得し、VBAの文字列変数に確保してからデータを読み込むのが安全な方法です。

処理フロー

レジストリ操作の一般的な処理フローは以下のようになります。

graph TD
    A["開始"] --> B{"レジストリ操作の準備"};
    B --> C{"必要なWin32 APIを宣言"};
    C --> D{"定数定義"};
    D --> E{"レジストリキーのパスとアクセス権を設定"};
    E --> F{"キーを開くまたは作成
(RegCreateKeyEx/RegOpenKeyEx)"}; F -- 成功 | HKEY_xxx --> G{"値の読み取り/書き込み/削除"}; G -- 文字列書き込み | RegSetValueEx(REG_SZ) --> H{"処理結果の確認"}; G -- DWORD読み込み | RegQueryValueEx(REG_DWORD) --> H; G -- 値の削除 | RegDeleteValue --> H; H -- 成功 --> I{"レジストリキーを閉じる
(RegCloseKey)"}; F -- 失敗 --> J["エラー処理"]; G -- 失敗 --> J; J --> K["終了"]; I --> K;

実装

以下のコードは、VBAモジュールに貼り付けて使用することを想定しています。ExcelおよびAccessの標準モジュールで動作します。

' /////////////////////////////////////////////////////////////////
' レジストリ操作用モジュール (modRegistry)
' /////////////////////////////////////////////////////////////////

Option Explicit

' --- Win32 API関数の宣言 ---
' PtrSafeは64ビットVBA環境での互換性のために必須
' Alias "..."A" はANSI版APIを明示的に呼び出す (日本語環境では安全)

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, _
    phkResult As LongPtr _
) As Long

Private Declare PtrSafe Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" ( _
    ByVal hKey As LongPtr, _
    ByVal lpSubKey As String, _
    ByVal Reserved As Long, _
    ByVal lpClass As String, _
    ByVal dwOptions As Long, _
    ByVal samDesired As Long, _
    lpSecurityAttributes As Any, _
    phkResult As LongPtr, _
    lpdwDisposition As LongPtr _
) As Long

Private Declare PtrSafe Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" ( _
    ByVal hKey As LongPtr, _
    ByVal lpValueName As String, _
    ByVal Reserved As Long, _
    ByVal dwType As Long, _
    ByVal lpData As LongPtr, _
    ByVal cbData As Long _
) As Long

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

Private Declare PtrSafe Function RegDeleteValue Lib "advapi32.dll" Alias "RegDeleteValueA" ( _
    ByVal hKey As LongPtr, _
    ByVal lpValueName As String _
) As Long

Private Declare PtrSafe Function RegDeleteKeyEx Lib "advapi32.dll" Alias "RegDeleteKeyExA" ( _
    ByVal hKey As LongPtr, _
    ByVal lpSubKey As String, _
    ByVal samDesired As Long, _
    ByVal Reserved As Long _
) As Long

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

Private Declare PtrSafe Function lstrcpy Lib "kernel32" Alias "lstrcpyA" ( _
    ByVal lpDest As LongPtr, _
    ByVal lpSource As LongPtr _
) As Long

' --- レジストリ定数 ---
Public Const HKEY_CURRENT_USER As LongPtr = &H80000001 ' 現在のユーザーの設定
Public Const HKEY_LOCAL_MACHINE As LongPtr = &H80000002 ' ローカルコンピューター全体の設定

' アクセス権
Public Const KEY_READ As Long = &H20019          ' 読み取りアクセス
Public Const KEY_WRITE As Long = &H20006         ' 書き込みアクセス
Public Const KEY_ALL_ACCESS As Long = &HF003F    ' 全てのアクセス権

' 値の型
Public Const REG_SZ As Long = 1                  ' 文字列
Public Const REG_DWORD As Long = 4               ' 32ビット数値 (Long)
Public Const REG_BINARY As Long = 3              ' バイナリデータ (Byte配列)

' 戻り値
Public Const ERROR_SUCCESS As Long = 0           ' 成功

' 64ビットOS上での32ビット/64ビットレジストリビューの指定
' (HKEY_LOCAL_MACHINE\Software の下に 32bitアプリ用 Wow6432Node がある場合など)
Public Const KEY_WOW64_64KEY As Long = &H100     ' 64ビットレジストリビューにアクセス
Public Const KEY_WOW64_32KEY As Long = &H200     ' 32ビットレジストリビューにアクセス

' /////////////////////////////////////////////////////////////////
' --- レジストリ操作ラッパー関数群 ---
' エラー処理やAPI呼び出しの詳細を隠蔽し、使いやすくする
' /////////////////////////////////////////////////////////////////

' Description: レジストリに文字列値を書き込む
' Parameters:
'   hRootKey: HKEY_CURRENT_USER または HKEY_LOCAL_MACHINE
'   sSubKey: サブキーパス (例: "Software\MyApp")
'   sValueName: 値の名前
'   sData: 書き込む文字列データ
'   lAccess: アクセス権 (通常 KEY_WRITE または KEY_ALL_ACCESS)
' Returns: 成功した場合は True, 失敗した場合は False
Public Function RegWriteString( _
    ByVal hRootKey As LongPtr, _
    ByVal sSubKey As String, _
    ByVal sValueName As String, _
    ByVal sData As String, _
    Optional ByVal lAccess As Long = KEY_WRITE _
) As Boolean
    Dim lRet As Long
    Dim hKey As LongPtr
    Dim lDisposition As LongPtr ' キー作成時の状態 (新規作成か既存か)
    Dim sDataNullTerm As String

    ' キーを開くか作成する (必要であれば作成する)
    ' Windows 64bit環境で32bitアプリのVBAからHKEY_LOCAL_MACHINE\Softwareに書き込む場合はKEY_WOW64_32KEYを含める
    lRet = RegCreateKeyEx(hRootKey, sSubKey, 0, "", 0, lAccess, ByVal 0&, hKey, lDisposition)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegCreateKeyEx failed: " & lRet & " for " & sSubKey
        RegWriteString = False
        Exit Function
    End If

    ' 文字列データにNULL終端を追加(API要件)
    sDataNullTerm = sData & Chr$(0)

    ' 値を書き込む
    lRet = RegSetValueEx(hKey, sValueName, 0, REG_SZ, StrPtr(sDataNullTerm), LenB(sDataNullTerm))
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegSetValueEx failed: " & lRet & " for " & sValueName
        RegWriteString = False
    Else
        RegWriteString = True
    End If

    ' キーを閉じる
    RegCloseKey hKey
End Function

' Description: レジストリから文字列値を読み取る
' Parameters:
'   hRootKey: HKEY_CURRENT_USER または HKEY_LOCAL_MACHINE
'   sSubKey: サブキーパス
'   sValueName: 値の名前
'   sData: 読み取った文字列データを格納する変数 (ByRef)
'   lAccess: アクセス権 (通常 KEY_READ)
' Returns: 成功した場合は True, 失敗した場合は False
Public Function RegReadString( _
    ByVal hRootKey As LongPtr, _
    ByVal sSubKey As String, _
    ByVal sValueName As String, _
    ByRef sData As String, _
    Optional ByVal lAccess As Long = KEY_READ _
) As Boolean
    Dim lRet As Long
    Dim hKey As LongPtr
    Dim lType As Long
    Dim lDataLen As LongPtr
    Dim sBuffer As String

    ' キーを開く
    ' Windows 64bit環境で32bitアプリのVBAからHKEY_LOCAL_MACHINE\Softwareを読み込む場合はKEY_WOW64_32KEYを含める
    lRet = RegOpenKeyEx(hRootKey, sSubKey, 0, lAccess, hKey)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegOpenKeyEx failed: " & lRet & " for " & sSubKey
        RegReadString = False
        Exit Function
    End If

    ' 必要なバッファサイズを取得
    lRet = RegQueryValueEx(hKey, sValueName, ByVal 0&, lType, ByVal 0&, lDataLen)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegQueryValueEx (size) failed: " & lRet & " for " & sValueName
        RegCloseKey hKey
        RegReadString = False
        Exit Function
    End If

    If lType <> REG_SZ Then
        Debug.Print "Registry value type is not REG_SZ: " & lType
        RegCloseKey hKey
        RegReadString = False
        Exit Function
    End If

    ' バッファを準備 (NULL終端と文字数調整)
    ' Win32 APIはバイト長を返すため、文字数に変換し、VBA文字列をNULL文字で埋める
    sBuffer = String$(lDataLen \ 2, Chr$(0)) ' ANSI (2バイト/文字) * NULL終端含む

    ' 値を読み取る
    lRet = RegQueryValueEx(hKey, sValueName, ByVal 0&, lType, StrPtr(sBuffer), lDataLen)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegQueryValueEx (data) failed: " & lRet & " for " & sValueName
        RegReadString = False
    Else
        ' NULL終端を除去してデータをセット
        If InStr(sBuffer, Chr$(0)) > 0 Then
            sData = Left$(sBuffer, InStr(sBuffer, Chr$(0)) - 1)
        Else
            sData = sBuffer
        End If
        RegReadString = True
    End If

    ' キーを閉じる
    RegCloseKey hKey
End Function

' Description: レジストリにDWORD値を書き込む
' Parameters:
'   hRootKey: HKEY_CURRENT_USER または HKEY_LOCAL_MACHINE
'   sSubKey: サブキーパス
'   sValueName: 値の名前
'   lData: 書き込むLong型データ
'   lAccess: アクセス権
' Returns: 成功した場合は True, 失敗した場合は False
Public Function RegWriteDWord( _
    ByVal hRootKey As LongPtr, _
    ByVal sSubKey As String, _
    ByVal sValueName As String, _
    ByVal lData As Long, _
    Optional ByVal lAccess As Long = KEY_WRITE _
) As Boolean
    Dim lRet As Long
    Dim hKey As LongPtr
    Dim lDisposition As LongPtr

    lRet = RegCreateKeyEx(hRootKey, sSubKey, 0, "", 0, lAccess, ByVal 0&, hKey, lDisposition)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegCreateKeyEx failed: " & lRet & " for " & sSubKey
        RegWriteDWord = False
        Exit Function
    End If

    ' 値を書き込む (lDataのメモリアドレスを直接渡す)
    lRet = RegSetValueEx(hKey, sValueName, 0, REG_DWORD, VarPtr(lData), LenB(lData))
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegSetValueEx failed: " & lRet & " for " & sValueName
        RegWriteDWord = False
    Else
        RegWriteDWord = True
    End If

    RegCloseKey hKey
End Function

' Description: レジストリからDWORD値を読み取る
' Parameters:
'   hRootKey: HKEY_CURRENT_USER または HKEY_LOCAL_MACHINE
'   sSubKey: サブキーパス
'   sValueName: 値の名前
'   lData: 読み取ったLong型データを格納する変数 (ByRef)
'   lAccess: アクセス権
' Returns: 成功した場合は True, 失敗した場合は False
Public Function RegReadDWord( _
    ByVal hRootKey As LongPtr, _
    ByVal sSubKey As String, _
    ByVal sValueName As String, _
    ByRef lData As Long, _
    Optional ByVal lAccess As Long = KEY_READ _
) As Boolean
    Dim lRet As Long
    Dim hKey As LongPtr
    Dim lType As Long
    Dim lDataLen As LongPtr

    lRet = RegOpenKeyEx(hRootKey, sSubKey, 0, lAccess, hKey)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegOpenKeyEx failed: " & lRet & " for " & sSubKey
        RegReadDWord = False
        Exit Function
    End If

    lDataLen = LenB(lData) ' DWORDは4バイト
    lRet = RegQueryValueEx(hKey, sValueName, ByVal 0&, lType, VarPtr(lData), lDataLen)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegQueryValueEx failed: " & lRet & " for " & sValueName
        RegReadDWord = False
    ElseIf lType <> REG_DWORD Then
        Debug.Print "Registry value type is not REG_DWORD: " & lType
        RegReadDWord = False
    Else
        RegReadDWord = True
    End If

    RegCloseKey hKey
End Function

' Description: レジストリキーから値を削除する
' Parameters:
'   hRootKey: HKEY_CURRENT_USER または HKEY_LOCAL_MACHINE
'   sSubKey: サブキーパス
'   sValueName: 削除する値の名前
'   lAccess: アクセス権
' Returns: 成功した場合は True, 失敗した場合は False
Public Function RegDeleteRegistryValue( _
    ByVal hRootKey As LongPtr, _
    ByVal sSubKey As String, _
    ByVal sValueName As String, _
    Optional ByVal lAccess As Long = KEY_WRITE _
) As Boolean
    Dim lRet As Long
    Dim hKey As LongPtr

    lRet = RegOpenKeyEx(hRootKey, sSubKey, 0, lAccess, hKey)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegOpenKeyEx failed (for delete value): " & lRet & " for " & sSubKey
        RegDeleteRegistryValue = False
        Exit Function
    End If

    lRet = RegDeleteValue(hKey, sValueName)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegDeleteValue failed: " & lRet & " for " & sValueName & "\" & sValueName
        RegDeleteRegistryValue = False
    Else
        RegDeleteRegistryValue = True
    End If

    RegCloseKey hKey
End Function

' Description: レジストリキーを削除する (サブキーを持つ場合は失敗する)
' Parameters:
'   hRootKey: HKEY_CURRENT_USER または HKEY_LOCAL_MACHINE
'   sSubKey: 削除するサブキーパス
'   lAccess: アクセス権
' Returns: 成功した場合は True, 失敗した場合は False
Public Function RegDeleteRegistryKey( _
    ByVal hRootKey As LongPtr, _
    ByVal sSubKey As String, _
    Optional ByVal lAccess As Long = KEY_WRITE _
) As Boolean
    Dim lRet As Long

    ' RegDeleteKeyEx は開いたキーハンドルではなく、ルートキーとサブキーパスを直接取る
    ' Windows 64bit環境で32bitアプリのVBAからHKEY_LOCAL_MACHINE\Softwareを削除する場合はKEY_WOW64_32KEYを含める
    lRet = RegDeleteKeyEx(hRootKey, sSubKey, lAccess, 0)
    If lRet <> ERROR_SUCCESS Then
        Debug.Print "RegDeleteKeyEx failed: " & lRet & " for " & sSubKey
        RegDeleteRegistryKey = False
    Else
        RegDeleteRegistryKey = True
    End If
End Function

' /////////////////////////////////////////////////////////////////
' --- 実装例 ---
' /////////////////////////////////////////////////////////////////

' 例1: Excelアプリケーション設定の保存と読み込み (HKEY_CURRENT_USER)
' 目的: 最後に保存したパスや、処理のリトライ回数などの設定を保持
Sub ExcelApplicationSettingsExample()
    Const ROOT_KEY As LongPtr = HKEY_CURRENT_USER
    Const SUB_KEY As String = "Software\MyExcelApp\Settings"
    Const VALUE_LAST_PATH As String = "LastSavedPath"
    Const VALUE_MAX_RETRIES As String = "MaxRetries"

    Dim sPath As String
    Dim lRetries As Long
    Dim bResult As Boolean

    Debug.Print "--- Excelアプリケーション設定 例 ---"

    ' 1. 設定を書き込む
    sPath = "C:\Users\Public\Documents\MyReport.xlsx"
    lRetries = 3

    Debug.Print "設定書き込み中..."
    bResult = RegWriteString(ROOT_KEY, SUB_KEY, VALUE_LAST_PATH, sPath)
    Debug.Print "LastSavedPath 書き込み: " & bResult

    bResult = RegWriteDWord(ROOT_KEY, SUB_KEY, VALUE_MAX_RETRIES, lRetries)
    Debug.Print "MaxRetries 書き込み: " & bResult

    ' 2. 設定を読み込む
    sPath = "" ' 初期化
    lRetries = 0 ' 初期化

    Debug.Print "設定読み込み中..."
    bResult = RegReadString(ROOT_KEY, SUB_KEY, VALUE_LAST_PATH, sPath)
    Debug.Print "LastSavedPath 読み込み: " & bResult & ", 値: " & sPath

    bResult = RegReadDWord(ROOT_KEY, SUB_KEY, VALUE_MAX_RETRIES, lRetries)
    Debug.Print "MaxRetries 読み込み: " & bResult & ", 値: " & lRetries

    ' 3. 設定を削除する (クリーンアップ)
    Debug.Print "設定クリーンアップ中..."
    bResult = RegDeleteRegistryValue(ROOT_KEY, SUB_KEY, VALUE_LAST_PATH)
    Debug.Print "LastSavedPath 値削除: " & bResult
    bResult = RegDeleteRegistryValue(ROOT_KEY, SUB_KEY, VALUE_MAX_RETRIES)
    Debug.Print "MaxRetries 値削除: " & bResult
    bResult = RegDeleteRegistryKey(ROOT_KEY, SUB_KEY)
    Debug.Print "サブキー削除: " & bResult

End Sub

' 例2: Accessデータベース接続設定の保存と読み込み (HKEY_LOCAL_MACHINE - 管理者権限が必要な場合あり)
' 目的: DB接続文字列など、アプリケーション全体で共有する設定を保持
Sub AccessDBConfigurationExample()
    Const ROOT_KEY As LongPtr = HKEY_LOCAL_MACHINE ' システム全体設定 (管理者権限が必要な場合あり)
    Const SUB_KEY As String = "Software\MyAccessDB\Configuration"
    Const VALUE_CONN_STRING As String = "ConnectionString"
    Const VALUE_TIMEOUT As String = "QueryTimeoutSeconds" ' 32bitアプリからのアクセスを想定

    Dim sConnString As String
    Dim lTimeout As Long
    Dim bResult As Boolean

    Debug.Print "--- Access DB設定 例 ---"

    ' RegCreateKeyEx/RegOpenKeyExでKEY_WOW64_32KEYを含めることで、
    ' 64ビットOS上の32ビットVBAアプリがHKEY_LOCAL_MACHINE\Software\Wow6432Node\... にアクセスできる
    ' RegCreateKeyEx(HKEY_LOCAL_MACHINE, SUB_KEY, 0, "", 0, KEY_WRITE Or KEY_WOW64_32KEY, ByVal 0&, hKey, lDisposition)
    ' 上記はRegWriteString/RegWriteDWord内部で扱わないとコードが複雑になるため、今回は省略。
    ' HKLMへの書き込みは通常インストーラーが行うため、VBAからはHKCU推奨。
    ' 以下は管理者権限でVBAを実行しているか、UACが無効な環境を前提とする。

    ' 1. 設定を書き込む
    sConnString = "Provider=SQLNCLI11;Server=MyServer;Database=MyDB;Uid=user;Pwd=password;"
    lTimeout = 60

    Debug.Print "設定書き込み中..."
    bResult = RegWriteString(ROOT_KEY, SUB_KEY, VALUE_CONN_STRING, sConnString)
    Debug.Print "ConnectionString 書き込み: " & bResult

    bResult = RegWriteDWord(ROOT_KEY, SUB_KEY, VALUE_TIMEOUT, lTimeout)
    Debug.Print "QueryTimeoutSeconds 書き込み: " & bResult

    ' 2. 設定を読み込む
    sConnString = ""
    lTimeout = 0

    Debug.Print "設定読み込み中..."
    bResult = RegReadString(ROOT_KEY, SUB_KEY, VALUE_CONN_STRING, sConnString)
    Debug.Print "ConnectionString 読み込み: " & bResult & ", 値: " & sConnString

    bResult = RegReadDWord(ROOT_KEY, SUB_KEY, VALUE_TIMEOUT, lTimeout)
    Debug.Print "QueryTimeoutSeconds 読み込み: " & bResult & ", 値: " & lTimeout

    ' 3. 設定を削除する (クリーンアップ)
    Debug.Print "設定クリーンアップ中..."
    bResult = RegDeleteRegistryValue(ROOT_KEY, SUB_KEY, VALUE_CONN_STRING)
    Debug.Print "ConnectionString 値削除: " & bResult
    bResult = RegDeleteRegistryValue(ROOT_KEY, SUB_KEY, VALUE_TIMEOUT)
    Debug.Print "QueryTimeoutSeconds 値削除: " & bResult
    bResult = RegDeleteRegistryKey(ROOT_KEY, SUB_KEY)
    Debug.Print "サブキー削除: " & bResult

End Sub

実行手順

  1. VBAエディタを開く: ExcelまたはAccessでAlt + F11を押してVBAエディタを開きます。

  2. 新しいモジュールの挿入: 挿入 > 標準モジュール を選択します。

  3. コードの貼り付け: 上記の「レジストリ操作用モジュール」および「実装例」のすべてのコードを新しいモジュールに貼り付けます。

  4. 実行: いずれかの例 (ExcelApplicationSettingsExample または AccessDBConfigurationExample) の中にカーソルを置き、F5キーを押して実行します。結果はVBAエディタのイミディエイトウィンドウ(Ctrl + Gで表示)に出力されます。

ロールバック方法

各実装例の最後にRegDeleteRegistryValueおよびRegDeleteRegistryKeyを呼び出すコードが含まれており、実行すると作成されたレジストリエントリが自動的に削除されます。これにより、テスト実行後のレジストリへの影響を最小限に抑えることができます。

もし途中でエラーが発生し、クリーンアップが実行されなかった場合は、以下の手順で手動で削除できます。

  1. レジストリエディタを開く: Win + Rを押し、regeditと入力してEnterを押します。

  2. キーを検索:

    • HKEY_CURRENT_USER\Software\MyExcelApp (Excelの例の場合)

    • HKEY_LOCAL_MACHINE\Software\MyAccessDB (Accessの例の場合、管理者権限が必要)

  3. 削除: 該当するキーを右クリックし、削除を選択します。

検証

コードの実行結果は、VBAエディタのイミディエイトウィンドウで確認できます。各操作がTrue(成功)を返しているか、読み込んだ値が期待通りかを確認します。

また、手動でレジストリエディタ (regedit.exe) を開いて、以下の点を確認します。

  • 書き込み時: HKEY_CURRENT_USER\Software\MyExcelApp\SettingsHKEY_LOCAL_MACHINE\Software\MyAccessDB\Configuration のキーが作成されているか。

  • : LastSavedPath (REG_SZ) や MaxRetries (REG_DWORD) などの値が正しいデータ型と内容で格納されているか。

  • 削除後: 上記のキーと値が削除されているか。

性能チューニング

レジストリ操作の性能チューニングは、一般的にデータ処理やUI操作のようなVBAの他の領域とは性質が異なります。レジストリ操作はI/O操作であり、CPU処理がボトルネックになることは稀です。

  1. RegOpenKeyEx / RegCloseKeyの最小化: 複数のレジストリ値を読み書きする場合、値ごとにキーを開いて閉じるのではなく、一度キーを開き、必要なすべての操作を行った後で一度だけ閉じる方が効率的です。

    • 非効率な例 (概念): 10個の値を読み書きするのに RegOpenKeyExRegSetValueExRegCloseKey を10回繰り返すと、約 10ms (1ms/ペア と仮定) かかります。

    • 効率的な例 (概念): RegOpenKeyEx → (10回の RegSetValueEx) → RegCloseKey なら、約 1.5ms (開閉1ms + 各SetValue 0.05ms) で済みます。

    • : 上記の数値はあくまで概念的なものであり、実際の環境やレジストリの負荷によって大きく変動します。しかし、I/O操作のオーバーヘッドは存在するため、この原則は重要です。

  2. 適切なデータ型の選択: REG_DWORDは固定長でシンプルなため、文字列 (REG_SZ) よりもわずかに高速に処理されます。数値データを保存する際はREG_DWORDを優先しましょう。文字列はバッファの管理やNULL終端処理などのオーバーヘッドが生じます。

  3. 不必要なアクセス権の回避: KEY_ALL_ACCESSではなく、KEY_READKEY_WRITEなど、必要な最小限のアクセス権でキーを開くことで、セキュリティとパフォーマンスの両面で有利です(ただし、性能差は微々たるものです)。

ScreenUpdatingApplication.CalculationDAO/ADO最適化といったExcel/Access固有のチューニング手法は、レジストリ操作自体には直接影響しません。

運用

32ビットVBAと64ビットVBAの互換性

Declare PtrSafeキーワードを使用しているため、32ビット版および64ビット版のOffice VBA環境の両方で動作します。

レジストリリダイレクト (Wow6432Node)

64ビット版Windowsで32ビットアプリケーション(多くのOffice VBAもこれに該当)がHKEY_LOCAL_MACHINE\Softwareにアクセスしようとすると、OSによって自動的にHKEY_LOCAL_MACHINE\Software\Wow6432Nodeにリダイレクトされます。 特定のレジストリビュー(32ビットまたは64ビット)に明示的にアクセスしたい場合は、RegOpenKeyExRegCreateKeyExsamDesired引数にKEY_WOW64_32KEYまたはKEY_WOW64_64KEYフラグを追加します。本記事のラッパー関数ではデフォルトではこれらを考慮していませんが、必要に応じて修正してください。

アクセス権とセキュリティ

HKEY_LOCAL_MACHINE配下のキーを操作するには、管理者権限が必要となる場合があります。VBAコードが管理者権限なしで実行されている場合、書き込み操作が失敗したり、アクセス拒否のエラーが発生したりすることがあります。 ユーザー固有の設定はHKEY_CURRENT_USER配下に保存するのが一般的かつ安全です。

落とし穴

  1. ハンドルを閉じ忘れる: RegOpenKeyExRegCreateKeyExで開いたレジストリキーのハンドル (hKey) は、必ずRegCloseKeyで閉じる必要があります。閉じ忘れるとメモリリークやリソースの枯渇につながる可能性があります。

  2. エラー処理の不足: API呼び出しは常に成功するとは限りません。戻り値 (ERROR_SUCCESSかどうか) を確認し、適切なエラー処理を実装することが重要です。Debug.Printだけでなく、ユーザーへのメッセージ表示やログ記録も考慮しましょう。

  3. データ型の不一致: REG_SZ (文字列) と REG_DWORD (数値) の間で読み書きを間違えると、データが壊れたり、意図しない値が読み込まれたりします。特にRegQueryValueExで型をチェックしないと問題につながります。

  4. 文字列のNULL終端: Win32 APIの多くの文字列関数はNULL終端文字列(Cスタイルの文字列)を期待します。VBAの文字列は内部的にはNULL終端されませんが、APIに渡す際はChr$(0)を追加してNULL終端する必要があります(RegWriteStringで対応済み)。また、読み取り後にはNULL終端を除去する処理が必要です(RegReadStringで対応済み)。

  5. レジストリの乱用: レジストリはアプリケーション設定の保存に適していますが、大量のデータや頻繁に更新されるデータを保存する場所ではありません。そのような場合は、データベースやファイルを使用すべきです。

まとめ

、VBAからWin32 APIを直接利用してレジストリを操作する実践的な方法を解説しました。Declare PtrSafeを使ったAPI関数の宣言、主要なレジストリ操作(読み書き、作成、削除)のラッパー関数の実装、そしてExcelとAccessでの具体的な利用例を示しました。

レジストリ操作は、Officeアプリケーションの高度なカスタマイズやシステムレベルの設定管理において強力な手段となりますが、その一方で、アクセス権、データ型、ハンドルの管理、エラー処理といった多くの注意点を伴います。これらの落とし穴を理解し、堅牢なコードを記述することで、VBAアプリケーションの機能を大きく拡張できるでしょう。

レジストリはシステムの重要な部分であるため、不用意な操作はシステムの不安定化を招く可能性があります。本記事のコードを参考に、十分にテストを行い、慎重に運用してください。

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

コメント

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