アドインやテンプレートにユーザー設定を保存したい場合、どこに保存するのかが問題になります。VBAアセットでは、「VA公用文」(Word VBA)の開発過程から得られた経験を踏まえ、カスタムドキュメントプロパティを利用することをそのスタイルとしています。
設定保存方法の変遷
アドインとして使用することを想定した「VA公用文」の開発では、ユーザー設定の保存方法について改善を重ねてきました。
第一段階:アドインファイルのドキュメント自体に保存(Ver.7D1以前)
初期バージョンでは、設定値をアドインファイル(.dotm)内のWordドキュメント本体に、次のようにして直接書き込んでいました。

この方法で実際に問題が生じたことはなかったのですが、ユーザーが予期しない内容に設定値を書き換えてしまったり、Wordの自動保存や復元機能で予期しない不具合を発生させたりするのではないかという不安がありました。
第二段階:外部設定ファイル(.ini)への移行(Ver.800~840)
Ver.8では、プログラムの安定性を高めるため、設定値を外部ファイル「VA公用文.ini」に分離し、次のようにして保存するように変更しました。
' ファイルパスを生成
filePath = Application.StartupPath & Application.PathSeparator & strSettingsFileName
--- 以下省略 ---
この外部ファイル方式により、ドキュメント本体への書き込みによる不安が解消され、Windows環境では安定して動作することが確認できていました。ところが、Mac環境では動作しなくなってしまったのです。
その主な原因は、権限の欠如でした。macOSでは、Officeアプリケーションの起動フォルダ(StartupPath)はシステムに近い保護された領域に位置しており、マクロからファイルを書き込む権限が与えられていないのです。さらに、代替策としてユーザーのホームディレクトリに保存しようとしても、Mac環境では Environ("USERPROFILE") や Environ("HOME") が空文字列を返す場合があり、パスの特定自体が困難でした。
最終解決策:カスタムドキュメントプロパティの利用(Ver850以降)
このような状況が生じていることをAI(Gemini)に入力して、解決策を検討させたところ、回答として得られたのが、Office製品に標準搭載されているカスタムドキュメントプロパティの利用でした。
カスタムドキュメントプロパティであれば、設定値がOfficeファイル形式の内部にメタデータとして格納されるため、OSのファイルシステムやパス構造に一切依存しないようです。また、VBAコードが格納されているアドインファイル自体に対しては、アプリケーションが常に読み書きの権限を持っているため、外部ファイルへの書き込み制限を気にする必要がないようです。
カスタムドキュメントプロパティの利用法
保存処理(Word VBA)
'==============================================================================
' 機能: キーと値の配列の内容を、アドインファイル(ThisDocument)の
' カスタムドキュメントプロパティとして保存します。
' 保存後、アドインファイルを明示的に保存します。
'------------------------------------------------------------------------------
' 引数:
' ByRef varKeys() As Variant: 設定のキー名(プロパティ名)を含む配列。
' ByRef varValues() As Variant: 設定の値(プロパティ値)を含む配列。
' 注意:
' varKeys と varValues の LBound, UBound は一致している必要があります。
'==============================================================================
Public Sub SaveSettingsToProperty(ByRef varKeys() As Variant, ByRef varValues() As Variant)
Dim i As Long
Dim doc As Document
Dim keyName As String
Dim prefixedKeyName As String
Dim propType As Long
Dim prop As DocumentProperty
' プロシージャを実行しているアドインファイル自身を参照
Set doc = ThisDocument
' 配列の要素数が一致しない場合はエラー (LBoundもチェック)
If LBound(varKeys) <> LBound(varValues) Or UBound(varKeys) <> UBound(varValues) Then
MsgBox "キーと値の配列のインデックス範囲が一致しません。", vbCritical
GoTo CleanExit
End If
On Error GoTo SaveError
For i = LBound(varKeys) To UBound(varKeys)
keyName = varKeys(i)
prefixedKeyName = c_Prefix & keyName ' プレフィックスを付加
' 値の型に応じてプロパティの型を決定
Select Case VarType(varValues(i))
Case vbInteger, vbLong
propType = msoPropertyTypeNumber
Case vbSingle, vbDouble, vbCurrency
propType = msoPropertyTypeFloat
Case vbDate
propType = msoPropertyTypeDate
Case vbBoolean
propType = msoPropertyTypeBoolean
Case Else ' vbString, vbEmpty, vbNull, etc.
propType = msoPropertyTypeString
End Select
' 既存のプロパティを検索
On Error Resume Next
Set prop = doc.CustomDocumentProperties(prefixedKeyName)
If Err.Number <> 0 Then
' プロパティが存在しない場合:新規作成
Err.Clear
Else
' プロパティが存在する場合:更新
' 型の不一致エラーを避けるため、一度削除する
prop.Delete
End If
' プロパティを(再)追加
doc.CustomDocumentProperties.Add _
Name:=prefixedKeyName, _
LinkToContent:=False, _
Type:=propType, _
value:=varValues(i)
On Error GoTo SaveError ' エラー処理を元に戻す
Next i
' Wordにファイルに変更があったことを明示的に通知する
doc.Saved = False
' 変更を保存するために、アドインファイルを明示的に保存する
doc.Save
GoTo CleanExit
SaveError:
MsgBox "設定値の保存に失敗しました。" & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical
CleanExit:
' オブジェクトを解放
Set doc = Nothing
Set prop = Nothing
End Sub
読み込み処理(Word VBA)
'==============================================================================
' 機能: アドインファイル(ThisDocument)のカスタムドキュメントプロパティから
' このモジュールが管理する設定値(プレフィックス付き)のみを読み込み、
' キーと値の配列に格納します。
'------------------------------------------------------------------------------
' 引数:
' ByRef varKeys() As Variant: 読み込んだ設定のキー名(プレフィックス除去後)を格納する配列。
' ByRef varValues() As Variant: 読み込んだ設定の値(プロパティ値)を格納する配列。
'==============================================================================
Public Sub LoadSettingsFromProperty(ByRef varKeys() As Variant, ByRef varValues() As Variant)
Dim doc As Document
Dim prop As DocumentProperty
Dim tempKeys() As Variant
Dim tempValues() As Variant
Dim count As Long
Dim i As Long
Dim prefixLen As Long
' プロシージャを実行しているアドインファイル自身を参照
Set doc = ThisDocument
' 配列を初期化
Erase varKeys
Erase varValues
count = 0
prefixLen = Len(c_Prefix)
On Error GoTo LoadError
' プレフィックスに一致するプロパティの数を先に数える
For Each prop In doc.CustomDocumentProperties
If Left(prop.Name, prefixLen) = c_Prefix Then
count = count + 1
End If
Next prop
If count = 0 Then GoTo CleanExit ' 該当プロパティなし
' 配列のサイズを一度だけ確保 (1ベース配列)
ReDim tempKeys(1 To count)
ReDim tempValues(1 To count)
i = 1
' 再度ループして、該当するキーと値を配列に格納
For Each prop In doc.CustomDocumentProperties
If Left(prop.Name, prefixLen) = c_Prefix Then
' プレフィックスを除去したキー名
tempKeys(i) = Mid(prop.Name, prefixLen + 1)
tempValues(i) = prop.value
i = i + 1
If i > count Then Exit For ' 念のため (count分取得したら抜ける)
End If
Next prop
' 結果を引数に代入
varKeys = tempKeys
varValues = tempValues
GoTo CleanExit
LoadError:
MsgBox "設定値の読み込みに失敗しました。" & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical
CleanExit:
' オブジェクトを解放
Set doc = Nothing
Set prop = Nothing
Erase tempKeys
Erase tempValues
End Sub
実際の利用方法は、こちらをご確認ください。
参考:VBAを使わずに設定値を確認・編集する
カスタムドキュメントプロパティは、次の手順でVBAを使わずに直接確認・編集することもできます。
- 対象のアドインファイルを開く
- 「ファイル」タブ →「情報」に移動
- 右側の「プロパティ」ドロップダウンをクリック
- 「詳細プロパティ…」を選択
- 「ユーザー設定」タブをクリック


コメント