第13回  「レジストリへの書き込み関数」



レジストリを書き換えるには

VBの標準関数には、レジストリを読み書きする「GetSetting」「SaveSetting」というものが用意されています。
それなら、簡単に書き換えできそうだと思うかもしれません。

ですが、この関数はレジストリのごく一部分しか読み書きする事ができないのです。
具体的には、「HKEY_CURRENT_USER\Software\VB and VBA Program」というキーの部分のみ読み書き可能です。

VBで作成されたプログラムなら、この部分に情報を保存している事が多いでしょうが、
一般のゲームソフトが、こんな所にデータを記録している事はまずないでしょう。

レジストリ全体を読み書きするためには、やはりAPIを使う事になります。


レジストリ書き換え関数(RegistryEdit)

では、レジストリの書き換えを行う自作関数を見ていきましょう。
ファイルやメモリの時とは、指定内容に違いがありますので注意して下さい。

この関数は、「レジストリのキー名」、「レジストリの値名」、「書き込む値」、「書き込む値の型
を受け取り、指定のレジストリを書き換える関数です。


RegistryEdit ソースプログラム1

Function RegistryEdit(ByVal Key As String, ByVal Name As String, _
  ByVal PutValue As String, ByVal ValueType As String) As Long

Dim hWnd As Long    'オープンされたキーのハンドル
Dim cbData As Long   '書き込むデータの文字数
Dim Buff() As Byte    '書き込むデータ
Dim SecurityAttributes As SECURITY_ATTRIBUTES  'セキュリティ属性
Dim rc As Long      '関数の戻り値
Dim RootKey As Long   'キー名の左端の情報("HKEY_LOCAL_MACHINE")
Dim SubKey As String  'サブキー名("Software\MakerName\SoftName\Paramater")
Dim dwType As Long   '書き込むデータの型
Dim i As Integer      'カウンタ

'ルートキーの取得
Select Case UCase(Mid(Key, 1, InStr(1, Key, "\") - 1))
Case Is = "HKEY_CLASSES_ROOT"
  RootKey = HKEY_CLASSES_ROOT
Case Is = "HKEY_CURRENT_CONFIG"
  RootKey = HKEY_CURRENT_CONFIG
Case Is = "HKEY_CURRENT_USER"
  RootKey = HKEY_CURRENT_USER
Case Is = "HKEY_DYN_DATA"
  RootKey = HKEY_DYN_DATA
Case Is = "HKEY_PERFORMANCE_DATA"
  RootKey = HKEY_PERFORMANCE_DATA
Case Is = "HKEY_USERS"
  RootKey = HKEY_USERS
Case Is = "HKEY_LOCAL_MACHINE"
  RootKey = HKEY_LOCAL_MACHINE
Case Else
End Select

'サブキーの取得
SubKey = Mid(Key, InStr(1, Key, "\") + 1)


長いので3つに分けて解説します。
上記の部分では、引数として受け取った「レジストリのキー名」を、
「ルートキー」と「サブキー」に分割して、変数「RootKey」「SubKey」にそれぞれ格納しています。

左から数えて最初に出てくる「¥」の左側がルートキー、右側以降がサブキーとなります。
なぜ、「ルートキー」と「サブキー」に分割する必要があるのかは後述します。


RegistryEdit ソースプログラム2

01>  '書き込むデータの種類を決め、それぞれの種類にデータを対応させる
02>  Select Case UCase(ValueType)

03>  Case Is = "STRING"
04>    dwType = REG_SZ
05>    cbData = Len(PutValue)
06>    Buff() = String(cbData, vbNullChar)
07>    Buff() = StrConv(PutValue & Chr(0), vbFromUnicode)

08>  Case Is = "BINARY"
09>    dwType = REG_BINARY
10>    '配列のサイズを変更
11>    ReDim Buff(Len(PutValue) / 2)
12>    '左端から2文字ずつ16進数に変換して、Buff に代入
13>    For i = 0 To Len(PutValue) / 2 - 1
14>      Buff(i) = CLng("&h" & Mid(PutValue, i * 2 + 1, 2))
15>    Next i
16>    'バイナリは、2文字で1バイト
17>    cbData = Len(PutValue) / 2

18>  Case Is = "DWORD"
19>    'DWORD とは、32ビットの符号無し整数
20>    dwType = REG_DWORD
21>    ReDim Buff(4)
22>    '10進数から16進数に変換して、PutValue に代入
23>    PutValue = Hex(CLng(PutValue))
24>    '8桁に固定する
25>    PutValue = Right("00000000" & PutValue, 8)
26>    '2文字ずつ取り出して数値に変換し、PutValue に代入
27>    For i = 0 To 3
28>      Buff(3 - i) = CLng("&h" & Mid(PutValue, i * 2 + 1, 2))
29>    Next i
30>    cbData = 4

31>  Case Else
32>  End Select


ここでは、引数の「書き込む値」を「書き込む値の型」に対応したデータに書き換えています。
第5回で解説したように、レジストリのデータの型は3種類あります。

1.「STRING」の場合(3〜7行目)
 データの型を格納するための変数「dwType」に、String 型である事を示す定数「REG_SZ」を代入します。
 これらの定数は、「EditEngine.bas」の最初の方に「Public Const」として宣言してあります。

 そして、書き込むデータの長さ分のヌル文字を「Buff()」に確保しておき、
 そこに書き込む値を、Unicode から、システム規定の文字コードに変換して代入します。
 (「vbNullChar」と「Chr(0)」は同じものです)

2.「BINARY」の場合(8〜17行目)
 データの型を格納するための変数「dwType」に、BINARY 型である事を示す定数「REG_BINARY」を代入します。

 続いて、配列の大きさを必要な分だけ確保します。
 1バイトで2文字分のデータ(16進数2桁分)を表すので、必要な大きさは、文字数の1/2です。

 後は、引数「書き込む値」を左から順番に2文字ずつ抜き出し、16進数に変換して変数に格納しています。

 ところで、今このコードを見ていて気付いたんですが、17行目を10行目の所に持ってきて、
 11行目、13行目の「Len(PutValue) / 2」の所を「cbData」で置き換えた方が効率いいですね。(汗)

3.「DWORD」の場合(18〜30行目)
 データの型を格納するための変数「dwType」に、DWORD 型である事を示す定数「REG_DWORD」を代入します。

 また、DWORD の場合は、32ビット(4バイト)で固定ですから、必要な配列のサイズは4バイトです。

 そして、引数「書き込む値」を16進数に変換し、8桁に固定します。
 8桁未満の場合は、上位に「0」を補ってやります。

 後は、BINARY の時と同様に、左から順番に2文字ずつ抜き出していきます。


RegistryEdit ソースプログラム3

'セキュリティ属性を決定
With SecurityAttributes
  .nLength = Len(SecurityAttributes)
  .lpSecurityDescriptor = 0
  .bInheritHandle = 1
End With

'キーをオープンする
rc = RegOpenKeyEx(RootKey, SubKey, 0, KEY_QUERY_VALUE, hWnd)

'オープンに成功したならば、書き込みを実行
If rc = ERROR_SUCCESS Then
  rc = RegSetValueEx(hWnd, Name, 0, dwType, Buff(0), cbData)
  rc = RegCloseKey(hWnd)
  RegistryEdit = 0
Else
  RegistryEdit = 1
End If

End Function


初めに出てくる「SecurityAttributes」とは「SECURITY_ATTRIBUTES」という型の変数です。
この型(構造体)は、「EditEngine.bas」の最初の方に「Type」として宣言してあります。

まあ、この部分はあまり気にしなくても結構です。
一応この構造体と、3つのメンバの解説をヘルプから抜粋しておきます。

<Win32APIのヘルプより>
SECURITY_ATTRIBUTES構造体は、 オブジェクトに対するセキュリティ記述子を格納し、
この構造体の指定により取得されたハンドルが継承可能かどうかを指定します。

nLength
この構造体のサイズをバイト単位で指定します。
この値は、 SECURITY_ATTRIBUTES構造体のサイズに設定されます。

lpSecurityDescriptor
オブジェクトの共有を制御する、 オブジェクトに対するセキュリティ記述子を指すポインタです。
このメンバがNULLの場合は、 オブジェクトに呼び出し側プロセスのデフォルトの
セキュリティ記述子が割り当てられる場合があります。

bInheritHandle
新しいプロセスを作成したときに返されるハンドルが継承可能かどうかを指定します。
このメンバがTRUEの場合、 新しいプロセスはそのハンドルを継承します。


・・・とまあ、このような内容となっております。
えっ、何がなんだか分からないって?
大丈夫!それで良いのです!!
ヘルプファイルの説明なんて、大抵こんなものなのですから。(汗)

では、先ほどの事は忘れて、次の説明に移りましょう。

まずは、使いたいキーをオープンする必要があります。
ファイルやメモリもそうでしたが、読み書きの前には必ずオープンし、使い終わったら必ずクローズします。

レジストリを開くためには「RegOpenKeyEx」というAPI関数を使用します。
この時の引数は、「ルートキー」、「サブキー」、「(ここは0で固定)」、
セキュリティアクセスマスク」、「オープンされたキーのハンドルを受け取る」となります。

そうです、この際の指定には「ルートキー」と「サブキー」を分けなければなりません。
ですから前の方で、受け取ったキー名を分割していたんですね。

また、3番目の引数は予約済みですので、必ず「0」を指定します。

4番目の引数には、アクセス権を指定します。
ここでは、サブキーのデータを問い合わせる為のアクセス権「KEY_QUERY_VALUE」を指定します。

5番目の引数は、戻り値としての役割を果たします。


続いて、オープンに成功していれば書き換えを行い、失敗していれば「RegistryEdit」を終了します。

レジストリを書き換える為には「RegSetValueEx」というAPI関数を使用します。
この時の引数は、「キーのハンドル」、「レジストリの値名」、「(ここは0で固定)」、
データの型」、「書き込むデータ」、「書き込むデータのサイズ」となります。

後は、書き換えが終了した後でレジストリをクローズします。

ちなみに、ここでは、「RegSetValueEx」関数が成功したかどうかのチェックを行っていません。
関数が成功すれば、「ERROR_SUCCESS」を返してくれるので、本来ならばその値をチェックする方がよいでしょう。
(rc = RegCloseKey(hWnd) の上に、IF文を追加する)


さて、ようやく汎用ゲーム改造ツールの解説を終了する事ができました。
次回からは、ファイル・メモリ・レジストリの読み込み関数について解説する予定です。
これらをどのように活用するかは、その都度説明していきたいと思います。



前に戻る     次に進む

講座の初めに戻る