本記事は、C#での設定ファイル管理とパラメータのバージョンアップの連載の一部として、レジストリによる設定情報やパラメータの扱いについて記載します。アプリケーションの設定情報を保存し、読み込む方法はいろいろあり、レジストリを利用するのはその中でも一番古い方法の一つであるが、設定ファイルをレジストリだけで構成するアプリケーションはほぼないでしょう。アプリケーションにとって本当に大事な情報を扱う時、どうしても必要だと判断した場合のみ使うとよいでしょう。
Contents
レジストリのどこに書き込むべきか
どんな設定情報を管理する方法でも、設定情報をどこに置くかは悩みのところですね。レジストリを利用する場合は、大きく以下の2つに分けることができます。
フィールド | 主な使い道 |
Registry.LocalMachine | Windowsのログインユーザに何系なく、全ユーザ共有の領域 |
Registry.CurrentUser | Windowsのログインユーザ毎の領域 |
x86とx64のアプリケーションとOS環境
WindowsのOS(32bit,64bit)とアプリケーションのBuild条件によって実際扱われる場所が変わりますので注意が必要です。
・以下、Registry.CurrentUser.OpenSubKey()利用した場合、固定されるレジストリの場所(パス)です。
Build条件と実行OS環境 | レジストリ場所(パス) | |
Build | OS | |
x86 | 32bit | HKEY_CURRENT_USER\Software |
64bit | HKEY_CURRENT_USER\Software\Wow6432Node | |
x64 | 32bit | x64でBuildし、32bitOSで使うことはできません |
64bit | HKEY_CURRENT_USER\Software | |
AnyCPU | 32bit | HKEY_CURRENT_USER\Software |
64bit | HKEY_CURRENT_USER\Software |
管理者の場合は、64bitのOSがはやり始まった時、Win32(x86)のアプリケーションを開発することになり、Wow6432Nodeに保存されることで、苦労したことがあります。(開発の初期にAnyCPUとx86が両方存在した状態でデバッグしていたためでした。開発環境統一は大事ですね)
レジストリの読み書き
レジストリを利用するためのクラス
レジストリに書き込むフィールド(場所)によって、使うクラスが別途用意されています。
フィールド | レジストリを利用するためのクラス |
Registry.LocalMachine | Microsoft.Win32.Registry.LocalMachine |
Registry.CurrentUser | Microsoft.Win32.Registry.CurrentUser |
レジストリ読み書きとデータクラスの結び付け
レジストリは利用するのが特集なケースが多いと思うので、必ずしもINIのように起動時にクラスへ変換する必要がないかもしれませんが、管理者は好みによってレジストリ読み書きクラスを作り、データクラスと結び付けてみました。
-
サンプルで使うデータクラス
1 2 3 4 5 6 |
public class ParamUserInfo { public int ID { get; set; } public string Name { get; set; } public string[] Grades{ get; set; } } |
-
呼び出し側
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static void Main(string[] args) { //ParamData_Dummy.MakeDummy(); var usrcont = new RegistryUserInfoControl(); //起動時の処理 var paramLoaded = usrcont.LoadUserInfo(); //GUIなどの画面に表示にparamLoadedを渡す:コードは省略 //終了時の処理 var usrcontWrite = new RegistryUserInfoControl(); var bWrite = usrcontWrite.WriteUserInfo(paramLoaded); Console.WriteLine($"WriteUserInfo={bWrite}"); } |
-
レジストリの読み書きを扱うための規定クラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
public class RegistryControlBase { protected string _subKeyName; protected Microsoft.Win32.RegistryKey _regKey; public RegistryControlBase() { } public void OpenSubKey() { _regKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(_subKeyName, false); } public void CreateSubKey() { _regKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(_subKeyName); } protected virtual void Close() { if (_regKey != null) _regKey.Close();//閉じる } #region SetのWrapper関数 /// <summary> /// 文字列:REG_BINARY型 /// </summary> /// <param name="skey"></param> /// <returns></returns> protected void SetValueString(string skey, string svalue) { _regKey.SetValue(skey, svalue); } /// <summary> /// 32bit(int)数値扱い:REG_DWORD型 /// </summary> protected void SetValueInt(string skey, int nvalue) { _regKey.SetValue(skey, nvalue); //DWord } /// <summary> /// 64bit(long)数値扱い:REG_QWORD型 /// </summary> protected void SetValueDWord(string skey, long lvalue) { _regKey.SetValue(skey, lvalue); } /// <summary> /// バイナリ:REG_BINARY型 /// </summary> protected void SetValueByte(string skey, byte[] svalues) { _regKey.SetValue(skey, svalues); } /// <summary> /// 文字列の配列:REG_MULTI_SZ型 /// </summary> protected void SetValueStringArray(string skey, string[] svalues) { _regKey.SetValue(skey, svalues); } /// <summary> /// 展開可能な文字列値:REG_EXPAND_SZ型 /// </summary> protected void SetValueExpandString(string skey, string[] svalues) { _regKey.SetValue(skey, svalues); } #endregion #region GetのWrapper関数 /// <summary> /// 文字列:REG_BINARY型 /// </summary> protected string GetValueString(string skey) { return (string)_regKey.GetValue(skey); } /// <summary> /// 32bit(int)数値扱い:REG_DWORD型 /// </summary> protected int GetValueInt(string skey) { return (int)_regKey.GetValue(skey); //DWord } /// <summary> /// 64bit(long)数値扱い:REG_QWORD型 /// </summary> protected long GetValueDWord(string skey) { return (long)_regKey.GetValue(skey); } /// <summary> /// バイナリ:REG_BINARY型 /// </summary> protected byte[] GetValueByte(string skey) { return (byte[])_regKey.GetValue(skey); } /// <summary> /// 文字列の配列:REG_MULTI_SZ型 /// </summary> protected string[] GetValueStringArray(string skey) { return (string[])_regKey.GetValue(skey); } /// <summary> /// 展開可能な文字列値:REG_EXPAND_SZ型 /// </summary> protected string GetValueExpandString(string skey) { return (string)_regKey.GetValue(skey); } #endregion } |
レジストリを扱うための規定クラスを定義してみました(細かい例外処理などは行っていませんので、ご了承ください)。このクラスをそのまま使うか、少し変更して使うと、レジストリを扱う時には少し楽になると思います。
レジストリを扱うにおいて、注意したいこと
・文字列扱う型(REG_SZ,REG_EXPAND_SZ)の場合、最大1024bytesまでしか扱いません。もし、このサイズを超える場合は、文字列を分割して複数のkeyにするか、他の設定ファイルをなどを用意すると良いです。
・Registry.LocalMachineを扱う時は、管理者権限が必要になります。アプリケーションを使う側が管理者権限を持っているいるユーザになる予定なのか、仕様を確認して注意して扱うと良いです。
実際のクラスを使ってレジストリを使う
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public class RegistryUserInfoControl : RegistryControlBase { const string SUB_KEY_NAME = @"SOFTWARE\Naruhodo\RegTest"; public RegistryUserInfoControl() { _subKeyName = SUB_KEY_NAME; } public ParamUserInfo LoadUserInfo() { this.OpenSubKey(); var user = new ParamUserInfo(); user.ID = this.GetValueInt(nameof(user.ID)); user.Name = this.GetValueString(nameof(user.Name)); user.Grades = this.GetValueStringArray(nameof(user.Grades)); this.Close();//閉じる return user; } public bool WriteUserInfo(ParamUserInfo user) { bool ret = true; this.CreateSubKey(); try { this.SetValueInt(nameof(user.ID), user.ID); this.SetValueString(nameof(user.Name), user.Name); this.SetValueStringArray(nameof(user.Grades), user.Grades); } catch (Exception) { ret = false; } finally { this.Close();//閉じる } return ret; } } |
基本は、[C#のINIによる設定ファイルとバージョンアップ]の記載と同じく、レジストリを直接扱うのではなく、Wrapperクラスを作成しています。1~2個レベルのクラスメンバーであれば、ここまでやる必要はないでしょう。都合によりレジストリを使うパラメータが多くなった場合は、このようにWrapperクラスを作成して使うとよいでしょう。複数のクラスになる場合は、今回のサンプルのように規定クラスにGet,Set関連関数を移動すると良いです。
パラメータバージョンアップにおけるレジストリ管理による影響
レジストリを使うというのは、INIファイルによって管理するのと同じく、クラス情報をそのままレジストリに保存することはなく、一度、変換を行うことになる。そのため、INIファイルによる設定ファイルの管理と同じく、パラメータバージョンアップの際も、基本は影響を受けません。なので、クラスのメンバー毎に読み込みと書き込みの変換の実装が必要ですが、その分、バージョンアップによってパラメータが増えても素直に実装できるメリットがあるのが特徴です。
まとめ
レジストリによるパラメータ管理は、レジストリだけを使って大規模なアプリケーションを構築することはないでしょう。アプリケーションとして本当に大事な情報やレジストリである必要がある部分だけをレジストリを使って管理すると良いです。たとえば、アプリケーションのインストーラと関わる情報(例えば、ユーザが任意のフォルダを削除しても動作し続けるようにした場合など)や他のモジュールとのインタフェースでどうしても共通の部分はレジストリを使って実装することがありえると思います。