日本Delphi振興会 Delphiはじめて物語 Delphiつれづれ記 Delphiリンク互助会 Delphiアクセサリ

Delphi壁の穴

その三:レジストリを覗く

ここではレジストリの操作方法をご紹介します。

目次

Delphi壁の穴 Delphiを覗く システムを覗く アプリケーションを覗く

・レジストリ操作の基本を知る。

 Delphiは、レジストリの操作をするためのユニット「Registry」を提供しています。レジストリを操作するにはこのユニットをuses節に追加する必要があります。Registryユニットは、レジストリ操作のために2つのクラスを用意しています。「TRegistry」と「TRegIniFile」です。両者の違いを以下にまとめます。

機能TRegistryTRegIniFile
用途システム全般を操作アプリケーションの設定を保存
長所
  • レジストリの3つの値「文字列」「バイナリ」「DWORD値」が扱える。
  • キーの作成/不作成を任意に行える。
  • コーディングがTRegistryと比べて簡単に行える。
  • Windows3.1の時に使われたTIniFileと互換性がある。
  • 読み込むべき値がない場合でも例外が発生しない。
短所
  • TRegIniFileと比べてコーディングが面倒なところもある。
  • 読み込むべき値がない場合に備えて例外処理が必要である(ReadStringでは不要)。
  • 値を「文字列」でしか書き込めない。「バイナリ」「DWORD値」を直接読み込めない。
  • キーを勝手に作ってしまう。

 この中で特に重要なのが、TRegistryは「バイナリ」も「DWORD値」も扱えるが、TRegIniFileは扱えないということです。Registy.pasのソースコードを見れば分かると思いますが、RegIniFileでWriteIntegerするとき、すなわちレジストリに数値を書き込むときにはIntToStrを使って文字列に直してから書き込みます。反対にレジストリから数値を読むとき、ReadIntegerでは、StrToIntを使って文字列を数値に変換しているのです。TRegistryを使えば、数値はDWORD値として保存されます。これは良くも悪くもINIファイルの名残だと思いますが、自分で作ったアプリケーションの設定値を保存するのでしたら、数値が文字列に変換されてレジストリに書き込まれても特段問題はないので、簡単に書けるTRegIniFileを使うことをお勧めします。

 しかし、レジストリ全般を扱うとなれば、DWORD値を読み込み、書き込むことが必要になってくるでしょう。そういうときにTRegistryの方を使えばいいのです。また、TRegIniFileは、読み込みたい値のあるキーを勝手に作ってしまう性質があります。自分で作ったアプリケーションの設定場所であるキーならば特に問題ないでしょうが、システム関連ともなれば勝手にキーを作るのは困りものです。TRegistryで必ず使うOpenKeyメソッドでは「キーがなければとにかく作る」と「キーがなければ作らない」の2種類を選択できるようになっています。

 次に両者の基本的なコーディング方法をご紹介します。

TRegistryTRegIniFile
procedure TForm1.Create(Sender: TObject);
const
 {データが入っているキーを指定}
 AppKey='\Software\Test\Setting';
var
 Reg:TRegistry;
begin
 //Registryオブジェクトを作成
 Reg:=TRegistry.Create; 
 with Reg do
 begin
  try
   RootKey:=HKEY_CURRENT_USER;
   {キーを開く。第2引数をTrueにするとキーがなければ作る}
   if OpenKey(AppKey,True) then 
    begin
     //データ読み込み。
     {TRegistryという名前の文字列データを取得}
     Label1.Caption:=ReadString('TRegistry'); 
     try
      {Valueという名前の DWORDデータを取得}
      SpinEdit1.Value:=ReadInteger('Value');
     //Valueが存在しない場合に備え、例外処理を行っておく 
     except
      SpinEdit1.Value:=0; 
     end;
    end;
  finally
   //Registryオブジェクトを解放。
   {何があっても解放できるように必ずtry..finallyでくくる。}
   {Freeするとき、CloseKeyも同時になされる}
   Free;
  end;
 end;
end;
procedure TForm1.Create(Sender: TObject);
const
 {データが入っているキーの1つ上のキーを指定}
 AppKey='\Software\Test';
var
 RegIni:TRegIniFile;
begin
 //RegIniFileオブジェクトを作成
 RegIni:=TRegIniFile.Create(AppKey);
 with RegIni do
 begin
  try
   RootKey:=HKEY_CURRENT_USER;
   //データ読み込み。ここでデータが入っているキー(Setting)を指定する
   {Stringデータを取得}
   Label1.Caption:=ReadString('Setting','TRegistry','');
   {Integerデータを取得。例外処理は不要。Value がなければ0が代入される}
   SpinEdit1.Value:=ReadInteger('Setting',Value',0); 
  finally
   //RegIniFileオブジェクトを解放。
   {何があっても解放できるように必ずtry..finallyでくくる。}
   {Freeするとき、CloseKeyも同時になされる}
   Free;
  end;
 end;
end;
TRegistry TRegIniFile

目次へ戻る


・レジストリにアプリケーションの設定を保存する。

 アプリケーションの設定保存場所は、HKEY_CURRENT_USER\Software の下と決まっています。TRegIniFileを使えば簡単に設定を保存できます。
 例として、Softwareの下にMySoftというキーを作り、さらにその下にWindowキーを作って、フォームの位置と大きさを保存してみましょう。
 uses ..... , Registry; {uses節に追加}

(略)

{終了時に保存} procedure TForm1.FormCloseQuery(Sender: TObject); const AppKey='\Software\MySoft'; var RegIniFile:TRegIniFile; begin RegIniFile:=TRegIniFile.Create(AppKey); with RegIniFile do begin try WriteInteger('Window','Left',Left); WriteInteger('Window','Top',Top); WriteInteger('Window','Height',Height); WriteInteger('Window','Width',Width); finally Free; end; end; end;

 数値を保存するときは「WriteInteger」、文字列は「WriteString」、論理値(True/False)は「WriteBool」を使います。
 かっこ内には、(' 保存先のキー ',' 保存するデータの名前 ',保存するデータ); のように書きます。

 ※FormCloseはアプリケーションを自分で終了させたときに発生するイベントですが、FormCloseQueryは、アプリケーションが起動しているときにWindowsが終了した場合にも、発生するところがFormCloseとの違いです。だからFormCloseQueryの方が確実に設定を保存できます。

 次に、保存した設定内容を起動時に読み込んでみましょう。

{設定を読み込む}
procedure TForm1.FormCreate(Sender: TObject);
const
 AppKey='\Software\MySoft';
var
 RegIniFile:TRegIniFile;
begin
 RegIniFile:=TRegIniFile.Create(AppKey);
 with RegIniFile do
  begin
   try
    Left:=ReadInteger('Window','Left',0);
    Top:=ReadInteger('Window','Top',0);
    Height:=ReadInteger('Window','Height',300);
    Width:=ReadInteger('Window','Width',385); 
   finally
    Free;
   end;
  end;
end;

 数値を読み込むときは「ReadInteger」、文字列は「ReadString」、論理値(True/False)は「ReadBool」を使います。
 かっこ内には、(' 読込先のキー ',' 読み込むデータの名前 ',デフォルト値); のように書きます。デフォルト値は読み込むデータがない場合に使われます。
 「ReadInteger('Window','Left',0);」は、WindowキーにあるLeftという名前に入っているデータを読み出します。

目次へ戻る


・レジストリにファイルの関連づけを設定する。

 自分で作ったソフトが独自の拡張子を持つファイルを扱う場合、そのファイルをダブルクリックで開けたら便利です。そのためには、レジストリに関連づけを設定しなければなりません。以下にその方法をご紹介します。

 関連づけの設定場所は、HKEY_CLASSES_ROOTの下にあります。レジストリエディタで見ればわかると思いますが、「.ini」のように拡張子の名前が付いたキーがずらっと並んでいます。
 「.ini」キーを開いてみると、右側に「(標準) "inifile"」のように表示されます。「inifile」が重要です。下の方のキーをさらに見ていくと、「inifile」という名前のキーがあるはずです。

ini

 「inifile」キーを開くと「DefaultIcon」キーと「shell」キーがあり、「shell」キーを開いていくと、「command」キーがあります。「DefaultIcon」キーにファイルのアイコンを指定し、「command」キーにダブルクリックで起動するアプリケーションを指定します。

inifile

 ちなみに、どうして「.ini」と「inifile」が分かれているかというと、例えば拡張子が「pas」のファイルを「ini」ファイルと同じアプリケーションで起動したい場合、「.pas」キーの値に「(標準) "inifile"」と指定すれば、pasファイルはiniファイルと同一のアプリケーションで起動できるようになるのです。「.htm」と「.html」には同じデータが入っているはずです。

 アプリケーションの設定を保存するにはTRegIniFileを使いましたが、一般的にレジストリ全般をいじくるには、TRegistryを使います。ファイルの関連づけ設定にもこれを使います。例として拡張子を「epl」、関連づける名前を「example」とし、手続き LinkExt を記述します。

 uses ..... , Registry, ShlObj; {uses節に追加}

(略)

{ファイルの関連づけ} procedure LinkExt; const CommandKey='\example\shell\open\command'; var Reg:TRegistry; begin Reg:=TRegistry.Create; with Reg do begin try RootKey:=HKEY_CLASSES_ROOT; {ルートキーを指定} OpenKey('\.epl',True); {.eplキーを作成して開く} WriteString('','example'); {(標準)のデータにexampleを書き込む} OpenKey('\example',True); WriteString('','関連づけファイル'); {ファイルの種類を指定} OpenKey('\example\DefaultIcon',True); WriteString('',ParamStr(0)+',0'); {ファイルのアイコンを指定} OpenKey(CommandKey,True); {commandキーを作成して開く} WriteString('','"'+ParamStr(0)+'" "%1"'); {開くアプリケーション名を指定} finally Free; end; end; //関連づけの変更をエクスプローラに反映させる SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_FLUSH,nil,nil); end;

  1. RootKeyには「HKEY」で始まるキーの名前を指定します。HKEY_CLASSES_ROOT\.epl のように指定するとエラーになります。必ず HKEY_XXX_XXX の部分だけを指定してください。
  2. OpenKeyでキーを開きます。開いた後でないとその中にデータを書き込めません。書式は、OpenKey(開くキーの名前,キーがないとき作成するか否か); です。キーの名前の先頭には普通「\」マークを付けます。そうするとルートキーからのパスを指定したことになり、HKEY_CLASSES_ROOT\.epl を意味します。キーがないときに作成するか否かは、True/Falseで指定します。キーがないとデータを書き込めないので、今回はTrueにします。
  3. WriteStringでデータを書き込みます。データは「(標準)」に書き込むので、第一引数を「''」のように空文字にします。
     ここで注意しなければならないのは、TRegIniFileのときのWriteStringとTRegistryのWriteStringは、パラメータが違うことです。TRegIniFileでは、(' 保存先のキー ',' 保存するデータの名前 ',保存するデータ); でしたが、TRegistryでは保存先のキーはすでにOpenKeyで指定されているので、(' 保存するデータの名前 ',保存するデータ); となります。ちなみに、ReadStringでも同様のことがいえます。
  4. 「ファイルのアイコン指定」では「0」を指定して、アプリケーションのデフォルトアイコンを使うようにしています。1ならば2つ目のアイコンが、2ならば3つ目のアイコンがファイルのアイコンとして使われます。どうせならオリジナルアイコンをファイルに付けたいというのが人情なので、その方法をここで紹介しています。
  5. 「関連づけファイル」の所に、エクスプローラなどで表示するファイルの種類を指定します。
  6. アプリケーション名の指定では、必ず「""」で囲みます。スペースが入ったロングファイル名に対応するためです。「%1」も同様の意味から「""」で囲みます。
  7. 最後に「SHChangeNotify」で関連づけの変更をエクスプローラに反映させます。これを使うには uses節に必ず「ShlObj」を付け加えて下さい。

 ●次に、アプリケーションをファイルの関連づけに対応させる方法をご紹介します。

 関連づけやドラッグドロップ(以下D&Dと略す)を行うと、アプリケーションの第一パラメータにそのファイルのフルパスが渡されます。この第一パラメータを参照することで、ファイルを開くことが出来ます。

 参照するにはParamStrを使います。ParamStrには、パラメータの値がすべて入っています。例えば「C:\Delphi 3\delphi.exe c:\Delphi 3\project1.dpr /p」では、ParamStr(0)に C:\Delphi~1\delphi.exe が、ParamStr(1)に c:\Delphi~1\project1.dprが、ParamStr(2)に /p が入っています。今回は第一パラメータが必要なので、ParamStr(1)を使います。例としてTMemoでファイルを開きましょう。

procedure TForm1.FormCreate(Sender: TObject);
begin
 Memo1.Lines.LoadFromFile(ParamStr(1));
end;

複数のファイルが選択されている場合は、ParamCountが役立ちます。ParamCountにはパラメータの数が入っているからです。

procedure TForm1.FormCreate(Sender: TObject);
begin
 for i:=1 to ParamCount do
 begin
  {開く処理 - ParamStr(i)を使う}
 end;
end;

目次へ戻る


・レジストリにDWORD値を記録させる

 TRegistryのWriteIntegerを使います。ここで重要なのは、TRegistryを使うことです。TRegIniFileだと、文字列に変換して記録されてしまいます。
uses ..... , Registry; {uses節に追加}

(略)

procedure NewsIdSetting; const News='\Software\Microsoft\Internet Mail and News'; var Reg:TRegistry; begin Reg:=TRegistry.Create; with Reg do begin try RootKey:=HKEY_CURRENT_USER; if OpenKey(News , False) then WriteInteger('DoNotGenerateNewsMessageId' , 1); finally Free; end; end; end;

実行画面

目次へ戻る


・レジストリにバイナリ値を記録させる

 TRegistryのWriteBinaryDataを使います。ここで重要なのは、TRegistryを使うことです。
procedure TForm1.Button1Click(Sender: TObject);
const
 FsKey='\SOFTWARE\Microsoft\Windows\CurrentVersion\FS Templates\';
var
 Reg:TRegistry;
 NameCache,PathCache:integer;
begin
 Reg:=TRegistry.Create;
 with Reg do
 begin
  try
   RootKey:=HKEY_LOCAL_MACHINE;
   
   OpenKey(Fskey+'Mobile',True);
   NameCache:=337;  {51 01 00 00}
   PathCache:=16;   {10 00 00 00}
   WriteBinaryData('NameCache',NameCache,4);
   WriteBinaryData('PathCache',PathCache,4);
   
   OpenKey(Fskey+'Server',True);
   NameCache:=2729; {a9 0a 00 00}
   PathCache:=64;   {40 00 00 00}
   WriteBinaryData('NameCache',NameCache,4);
   WriteBinaryData('PathCache',PathCache,4);

  finally
   Free;
  end;
 end;
end;
実行画面

目次へ戻る


・各種のシェルフォルダを得る(レジストリ版)。

 シェルフォルダというのは、Windows\ 以下にある、シェルに関するフォルダのことです。これらの情報はレジストリに記録されているので、それを読み込んでやれば得ることが出来ます。そのような関数を作ってみました。
uses Windows.....Registry;

type
 TShellFolder=(gsfDesktop,gsfFavorites,gsfFonts,gsfNetHood,
   gsfPersonal,gsfPrograms,gsfRecent,gsfSendTo,
   gsfStartMenu,gsfStartUp,gsfTemplates);

(略)

{各種シェルフォルダを得る}
function GetShellFolder(Kind: TShellFolder):string;
const
 FolderKey='\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders';
 Folder:array[TShellFolder] of string =
  ('Desktop','Favorites','Fonts','NetHood','Personal',
   'Programs','Recent','SendTo','Start Menu','Startup',
   'Templates');
var
 Reg:TRegistry;
begin
 Reg:=TRegistry.Create;
 with Reg do
 begin
  try
   RootKey:=HKEY_CURRENT_USER;
   if OpenKey(FolderKey,False) then
     Result:=ReadString(Folder[Kind])+'\'
   else Result:='';
  finally
   Free;
  end;
 end;
end;

 使い方は、「Label1.Caption:=GetShellFolder(gsfRecent);」のようにします。

なお、APIを使うバージョンもあります(こちらの方が一般的でしょう)。

目次へ戻る


・REGファイルを作る。

 レジストリの内容を保存するには、TRegistryのSaveKeyを使いますが、これで作られたファイルはバイナリファイルで、いわゆる「.reg」を拡張子とするファイルとは違います。普通はバイナリファイルの方でいいのですが、レジストリをいじるアプリを作った場合に、ユーザーにRegファイルをダブルクリックさせてレジストリを復旧させるような時には、Regファイルの方が便利です。
 しかし、Regファイルを直接作るAPIなどは存在しません。Regファイルを作るのは実はレジストリエディタ自身の機能なのです。コマンドラインからパラメータを指定することでRegファイルを作成できます。以下にパラメータを示します。

-----ここから------

レジストリファイルのインポートとエクスポート行います.

REGEDIT [/L:システム] [/R:ユーザー] ファイル名1
REGEDIT [/L:システム] [/R:ユーザー] /C ファイル名2
REGEDIT [/L:システム] [/R:ユーザー] /E ファイル名3 [レジストリパス]

/L:システム------SYSTEM.DAT ファイルの位置を指定します.
/R:ユーザー------USER.DAT ファイルの位置を指定します.
ファイル名1------レジストリにインポートするファイル(複数可)を指定します.
/C ファイル名2---レジストリを作成するためのファイルを指定します.
/E ファイル名3---レジストリをエクスポートするファイルを指定します.
レジストリパス---エクスポートを開始するレジストリキー指定します.
(指定がなければ, レジストリ全体をエクスポートします.)

----ここまで-------

 例えば、HKEY_CURRENT_USER以下を「User.reg」に保存するには、

Regedit /E User.reg HKEY_CURRENT_USER

とDOSプロンプトから打ち込めばいいことになります。詳しくは、Regedit.exeを適当なテキストエディタで開いてみて下さい。

 これをDelphiで行うには、

uses Windows,......,ShellAPI; {ShellAPIを追加}

(略)

procedure TForm1.Button1Click(Sender: TObject);
begin
 ShellExecute(Handle,'Open','Regedit.exe',
   '/E User.reg HKEY_CURRENT_USER','',SW_SHOW);
end;

のようにします。

#ちなみに、REGファイルの中身を「レジストリ スクリプト」と呼ぶようです。試しに適当なファイルの拡張子を「.reg」に変えてダブルクリックしてみて下さい。「...指定されたファイルはレジストリ スクリプトではありません。...」という内容のエラーメッセージが出てきます。ほんとにそう呼ぶのかは分かりませんけどね。

目次へ戻る


・TRegistryでDeleteKeyできない。

 例えば以下のような場合は、DeleteKeyできません。
procedure TForm1.Button1Click(Sender: TObject);
var
 Reg:TRegistry;
begin
 Reg:=TRegistry.Create;
 with Reg do
 begin
  try
   RootKey:=HKEY_LOCAL_MACHINE;
   OpenKey('\aaa\bbb\ccc',True); {cccキーを開く}
   WriteString('Gojuon','aiueo'); {値を書き込む}
   DeleteKey('\aaa'); {aaaキー以下を削除}
  finally
   Free;
  end;
 end;
end;

 DeleteKeyは指定したキーの下にあるサブキーも一緒に削除します。だからcccキーも削除されます。しかし、cccキーはOpenKeyによって開かれているので、削除できません。そのため、aaaキーを削除しようとしても無効になってしまうのです(文法エラーにはならない)。
 この場合は、DeleteKeyする前に、明示的にキーをクローズしてやる必要があります。そのためには、CloseKeyを使います。

procedure TForm1.Button1Click(Sender: TObject);
var
 Reg:TRegistry;
begin
 Reg:=TRegistry.Create;
 with Reg do
 begin
  try
   RootKey:=HKEY_LOCAL_MACHINE;
   OpenKey('\aaa\bbb\ccc',True);
   WriteString('Gojuon','aiueo');
   CloseKey; {現在開いているキーを閉じる}
   DeleteKey('\aaa'); {aaaキー以下を削除できる}
  finally
   Free;
  end;
 end;
end;

 なお、現在開いているキー以外のキーを削除する場合は、CloseKeyする必要はありません。

目次へ戻る


・TRegIniFileでキーを丸ごと削除する

キーを削除するにはDeleteKeyを使うのですが、TRegIniFileのDeleteKeyはなぜかキーの中にあるデータを削除する仕様になっています。このため、キーを丸ごと削除するにはTRegistryのDeleteKeyを使うしかありません。
そこで登場するのがキャストです。TRegIniFileはTRegistryを元にして作られているので、TRegistryの各メソッドを呼び出して使うことが出来ます。そのためにキャストを使います。
例として、「HKEY_CURRENT_USER\Software\TestApp」にある「TestKey」を削除してみましょう。
procedure TForm1.Button1Click(Sender: TObject);
var
 RegIni: TRegIniFile;
begin
 RegIni:=TRegIniFile.Create('\Software\TestApp');
 with RegIni do
 begin
  try
   TRegistry(RegIni).DeleteKey('TestKey');  //キャストしてキーを削除
  finally
   Free;
  end;
 end;
end;

現在開いているキー(カレントキー)は「\Software\TestApp」なので、その下にあるキーは上記のようにして消すことが出来ます。しかし、カレントキーよりも上にあるキーを消すには、カレントキー自体の移動が必要です。その場合は、やはりTRegistryにキャストしてOpenKeyメソッドを使います。例としてTestApp自体を消してみましょう。

procedure TForm1.Button1Click(Sender: TObject);
var
 RegIni: TRegIniFile;
begin
 RegIni:=TRegIniFile.Create('\Software\TestApp');
 with RegIni do
 begin
  try
   TRegistry(RegIni).OpenKey('\Software',True);  //キャストしてカレントキーを移動
   TRegistry(RegIni).DeleteKey('TestApp');  //キャストしてキーを削除
   TRegistry(RegIni).OpenKey('\Software\TestApp',True);  //新しくカレントキーを作成
  finally
   Free;
  end;
 end;
end;

こうすれば、従前のキーをきれいさっぱり消して、新しくキーを作成し直すことが出来るわけです。

目次へ戻る


・キーの指定で「\」付きとなしではどう違うのか

一言でいえば、円マーク付きはルートキーからのパス、なしはカレントキーからのパスです。

例えば、ルートキーが「HKEY_CURRENT_USER」でカレントキーが「Software\TestApp」だとしましょう。

という違いが出てくるのです。
円マーク一つでパスが全く違ってくるので気を付けてください。

以下の例を試すとどうなるでしょうか。

procedure TForm1.Button1Click(Sender: TObject);
var
 Reg: TRegistry;
begin
 Reg:=TRegistry.Create;
 with Reg do
 begin
  try
   RootKey:=HKEY_CURRENT_USER;
   OpenKey('\TestKey',True);
   OpenKey('TestKey',True);
  finally
   Free;
  end;
 end;
end;

分かりましたか?正解は、最初のOpenKeyで「HKEY_CURRENT_USER\TestKey」が出来、次のOpenKeyで「HKEY_CURRENT_USER\TestKey\TestKey」が出来るのです。
2番目のOpenKeyにブレークポイントを置いてレジストリエディタを起動し、コードを実行してみると出来る様が分かると思います。

目次へ戻る


一番上へ
Delphi壁の穴 Delphiを覗く システムを覗く アプリケーションを覗く

リンクは日本Delphi振興会から張ってください。