Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2007年09月16日] [更新 2017年03月21日] Delphi サンプルプログラム集
   
870_拡張子の関連付けの登録と削除
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
870_RegistExt.zip [5,912 KB] 2017年03月21日版(EXE同梱)



  • 2013年07月29日
  • コードを整備して,Windows 7 U64(SP1) + Delphi XE(UP1) Proで動作確認
  • 2014年08月12日
  • 既存の拡張子の情報を書き換えないにようにするため,登録している拡張子の場合は,登録作業を行わないようするコードを追加した
  • キーの値だけの削除だったのを,登録したキーそのものをレジストリから削除するようにした
  • 関連付けするサンプル,SID を取得するサンプル,その他のサンプルを追加
  • 記事を整備
  • 2014年08月15日
  • レジストリのキーの保存と復元の関数を汎用的なコードに変更,修正
  • その他,記事の修正等
  • 2014年08月16日
  • 登録済みの拡張子とアプリケーションの関連付けのサンプルを追加




拡張子とアプリケーションの関連付け


拡張子と既存のアプリケーションあるいは,自作のプログラムとの関連付けの設定と,それに関係するサンプルです.
本ページのサンプルは,エクスプローラの [開く] 操作だけの設定用サンプルです.アプリケーションによっては,[印刷] 等の動作も実装しているものがあります.

関連付けを設定すると,エクスプローラでファイルをダブルクリックすると,そのファイルを,関連付けしたアプリケーションで表示するようになります.
また,ShellExecute 関数,あるいは ShellExecuteEx 関数でそのファイルを表示する時に,関連付けしたアプリケーションで表示できるようになります.CreateProcess 関数でファイルを表示する場合は,基本的に,起動するプログラムを指定します.本ページのサンプルのような,拡張子とアプリケーションの関連付けを設定する必然性はありません.
ShellExecute 関数,あるいは ShellExecuteEx 関数でも,指定したアプリケーションでファイルを開くことができます.

ファイル一覧のような機能のアプリケーションを自作すれば,任意のアプリケーションを起動して,選択したファイルの操作等が可能です.本ページのような方法で,拡張子とアプリケーションとの関連付けを行う必要はありません.






01_拡張子とその関連付けをレジストリに登録・削除


新しい拡張子をシステムに登録し,その拡張子にアプリケーション (EXE ファイル) に関連付けるサンプルプログラムです.
拡張子の関連付けを行った後,その拡張子のファイルをダブルクリックすると,関連付けしたアプリケーションが起動するようになります.このサンプルでは,拡張子が plXRAY (実は wmf 画像) のファイルを [開く] と,本サンプルのプログラが起動してその画像を表示します.
既にシステムに登録済みの拡張子は登録できないようにしています.

このサンプルは,レジストリを操作します.したがって,管理者権限で実行しないと失敗します.
[拡張子登録] は,レジストリに拡張子と,参照名のキーをレジストリに作成します.[登録削除] は,レジストリからそれらのキーを削除します.

TRegistry には,DeleteKey メソッドがありますが,サブキーは削除しません (Windows 9X 系ではサブキーも削除可能).そこで,[登録削除] のコードでは,コマンドラインツールである Reg コマンドを使用しています.パイプを使用したコマンドラインのリダイレクトのユニットを使用してコマンドを実行しています.



[備考]
本サンプルの方法でレジストリに登録した拡張子は,ファイルの関連付けダイアログの,拡張子の一覧には表示されません.ファイルの関連付けのダイアログとは,後述の [06_関連付けを設定するダイアログの表示] のサンプルプログラムで表示するダイアログのことです.



図1
設計時画面
図2
サンプルプロジェクトのフォルダ内
図3
実行して [拡張子登録] をクリック
図4
指定拡張子の種類の表示とアイコンが変わる
図5
添付の E_frog.plXRAY をダブルクリック
  • 本プログラムが起動し画像を表示

リスト1
拡張子とその関連付けをレジストリに登録・削除
unit RegistExtUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
    RegistFileExt : String;
    function SetResistryFileExt(RegistAppExePath, RegistAppParams, RegistFileExt,
      RegistAppReff, RegistAppText: String): Integer;
    function DeleteResistryFileExt(RegistFileExt: String): Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  Registry,
  ShlObj,
  plCommandRedirect;

{$R *.dfm}

//=============================================================================
//  拡張子の関連付けがされていればそのファイル(ここではwmf画像)を表示
//
//  サンプルプログラムのフォルダ内のE_frog.plXRAYの本来の拡張子はwmf
//  拡張子がTPictureが扱えるタイプではないのでTPicture.LoadFromFileは使用不可
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LFilePath : string;
  LMetaFile : TMetaFile;
begin
  Image1.Stretch := True;

  if ParamStr(1) <> '' then begin
    LMetaFile := TMetafile.Create;
    try
      try
        LFilePath := ParamStr(1);
        LMetaFile.LoadFromFile(LFilePath);
        Image1.Picture.Assign(LMetaFile);
      except
      end;
    finally
      FreeAndNil(LMetaFile);
    end;
  end;

  //登録する拡張子
  RegistFileExt := 'plXRAY';
end;

//=============================================================================
//  レジストリに新しい拡張子と関連付けを登録
//  管理者権限での実行が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LAppPath       : String;
  LStrParams     : String;
  LRegistAppReff : String;
  LRegistAppText : String;
  LiRet          : Integer;
begin
  //拡張子に関連付けるアプリのEXEのフルパス
  //ここではこのサンプルプログラムとする
  LAppPath   := Application.ExeName;
  LStrParams := '%1';
  //参照名
  LRegistAppReff := 'plXRAYFile';
  //エクスプローラの種類名
  LRegistAppText := '特別画像ファイル';


  LiRet := SetResistryFileExt(LAppPath,
                              LStrParams,
                              RegistFileExt,
                              LRegistAppReff,
                              LRegistAppText);
  if LiRet = 1 then begin
    MessageBox(Handle, '拡張子を登録しました.', '情報', MB_ICONINFORMATION);
  end else
  if LiRet = 0 then begin
    MessageBox(Handle, '拡張子の登録に失敗しました.', '情報', MB_ICONWARNING);
  end else begin
    MessageBox(Handle, 'この拡張子は登録済です.', '情報', MB_ICONWARNING);
  end;
end;

//=============================================================================
//  レジストリから拡張子の情報を削除
//  管理者権限での実行が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if DeleteResistryFileExt(RegistFileExt) then begin
    MessageBox(Handle, '拡張子情報を削除しました.', '情報', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, ' この拡張子は登録されていません.', '情報', MB_ICONWARNING);
  end;
end;

//=============================================================================
//  レジストリに新しい拡張子とアプリケーションの関連づけを登録
//  管理者権限での実行が必要
//
//  RegistAppExePath 起動するアプリケーションのフルパス
//  RegistAppParams  そのアプリの起動時パラメータ
//  RegistFileExt    そのアプリに関連付けする拡張子名(.は付けない)
//  RegistAppReff    参照名(適当に.レジストリから関連付けを削除する際に必要)
//  RegistAppText    エクスプローラで表示した時のファイルの説明用
//
//  TRegistryの使用にはusesにRegistryが必要
//  SH...の使用にはusesにはShlObjが必要
//=============================================================================
function TForm1.SetResistryFileExt(RegistAppExePath, RegistAppParams, RegistFileExt,
  RegistAppReff, RegistAppText: String): Integer;
var
 LRegistry : TRegistry;
begin
  LRegistry := TRegistry.Create(KEY_READ or KEY_WRITE);
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;

    //登録済みの場合は何もしない
    if LRegistry.OpenKeyReadOnly('.' + RegistFileExt) then begin
      LRegistry.CloseKey;
      Result := -1;
    end else begin
      if (RegistFileExt = '') or (RegistFileExt = '.') or
          (RegistAppReff = '') then begin
        Result := 0;
      end else begin

        //パス名に空白文字が含まれる場合はダブルクォーテーションが必要
        //バス名に漢字が含まれる場合も必要なようである
        RegistAppExePath := AnsiQuotedStr(RegistAppExePath, '"');
        RegistAppParams  := AnsiQuotedStr(RegistAppParams, '"');

        //拡張子のキーを作成
        if LRegistry.OpenKey('\.' + RegistFileExt, True) then begin

          if (RegistFileExt = '') or (RegistFileExt = '.') or
               (RegistAppReff = '') then begin
            LRegistry.CloseKey;
            Result := 0;
          end else begin
            //参照名を登録
            LRegistry.WriteString('', RegistAppReff);

            //参照名のキーを作成
            if LRegistry.OpenKey('\' + RegistAppReff, True) then begin
              //エクスプローラに表示する説明用文字列の登録
              LRegistry.WriteString('', RegistAppText);

              //アイコンの参照キー指定
              //ここでは登録するアプリのアイコンとする
              LRegistry.OpenKey('\' + RegistAppReff + '\DefaultIcon', True);
              LRegistry.WriteString('', RegistAppExePath + ', 0');

              //アプリケーションのOpen\Commandキーを作成してEXEのパス名を記録
              LRegistry.OpenKey('\' + RegistAppReff + '\shell\open\command', True);
              if Trim(RegistAppParams) = '' then begin
                LRegistry.WriteString('', RegistAppExePath);
              end else begin
                LRegistry.WriteString('', RegistAppExePath + ' ' + RegistAppParams);
              end;
            end;

            //システムに変更を通知
            SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, nil, nil);
            Result := 1;
          end;
        end else begin
          Result := 0;
        end;
      end;
    end;
  finally
    FreeAndNil(LRegistry);
  end;
end;

//=============================================================================
//  登録した拡張子関係のキーをレジストリから削除
//  管理者権限での実行が必要
//
//  RegistFileExt 登録済みの拡張子名(.は付けない)
//
//  TRegistryの使用にはusesにRegistryが必要
//=============================================================================
function TForm1.DeleteResistryFileExt(RegistFileExt: String): Boolean;

  //---------------------------------------------------------------------------
  //  ファイルの拡張子からアプリケーションの参照名の値を取得する
  //  AppFileExe : ファイルの拡張子(.は付けない)
  //---------------------------------------------------------------------------
  function GetAppProgIDName(AppFileExt: String): String;
  var
    LRegistry : TRegistry;
  begin
    Result := '';
    LRegistry := TRegistry.Create(KEY_READ);
    try
      LRegistry.RootKey := HKEY_CLASSES_ROOT;

      //該当する拡張子が登録されていなければ取得できない
      if LRegistry.OpenKeyReadOnly('\.' + AppFileExt) then begin
        Result := LRegistry.ReadString('');
      end;
    finally
      FreeAndNil(LRegistry);
    end;
  end;
  //---------------------------------------------------------------------------

var
  LRegistAppReff  : string;
  LRegAppFileExt  : string;
  LRegAppProgID   : string;
  LRegExplorerExt : string;
  LCmdLine        : TplCommandRedirect;
  LCmdStr         : string;
begin
  LRegistAppReff := GetAppProgIDName(RegistFileExt);

  if (RegistFileExt = '') or (RegistFileExt = '.') or
      (LRegistAppReff = '') then begin
    Result := False;
  end else begin

    LRegAppFileExt  := 'HKEY_CLASSES_ROOT\' + '.' + RegistFileExt;
    LRegAppProgID   := 'HKEY_CLASSES_ROOT\' + LRegistAppReff;
    LRegExplorerExt := 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\'
                    + 'Explorer\FileExts\' + '.' + RegistFileExt;

    //対象のキーの有無に関わらず削除のコードを実行
    LCmdLine := TplCommandRedirect.Create;
    try
      //ここには記録されていない場合がある
      LCmdStr := 'Cmd.exe /k reg delete ' + LRegExplorerExt + ' /f';
      LCmdLine.GrabStdOutText(LCmdStr);

      LCmdStr := 'Cmd.exe /k reg delete ' + LRegAppProgID + ' /f';
      LCmdLine.GrabStdOutText(LCmdStr);

      LCmdStr := 'Cmd.exe /k reg delete ' + LRegAppFileExt + ' /f';
      LCmdLine.GrabStdOutText(LCmdStr);

      //システムに変更を通知
      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, nil, nil);

      Result := True;
    finally
      FreeAndNil(LCmdLine);
    end;
  end;
end;

end.




02_設定結果をレジストリエディタで確認


登録操作の結果をレジストリエディタで確認してみます.
コンピュータにインストールされている他のプログラムも,拡張子関係の情報に関しては,以下の説明と同じような構成になっています.

拡張子を登録すると,HKEY_CLASSES_ROOT に拡張子名と同じ名前のキーができます.
この拡張子と同じ名前のキーに [参照名] が登録されています.これは,プログラム識別子 (ProgID) と呼ばれているものに相当します.
今回の場合,拡張子が .plXRAY の既定の参照名は plXRAYFile ということになります.そして,この参照名に関連付けされたのが,本サンプルのプログラムということになります.


HKEY_CLASSES_ROOT\.plXRAY


[備考 1]
レジストリエディタで何回も同じ場所を開く必要がある場合,レジストリエディタの [お気に入り] に登録しておくと便利です.


[備考 2]
本ページでは,参照名に ProgID (プログラム識別子) を添え書きしていますが,正確には,ProgID は COM アプリケーションの識別子のことです.実際レジストリには,(既定) の値の他に,Progid という項目名があるアプリケーションがあります.



  • 図6
  • 登録した拡張子 .plXRAY の [参照名 (プログラム識別子)] の登録場所

拡張子に関連付けされた実行ファイル関係の情報は,参照名のキー項目にあります.これは,同じルートキーである HKEY_CLASSES_ROOT 直下にできます.
既定 (デフォルト) の実行ファイルのパス名は,下図のように command キーに記録されます.

拡張子が .plXRAY のファイルをダブルクリックすると,レジストリの .plXRAY キーの参照名を参照します.これは plXRAYFile です.そこで,次に plXRAYFile キーの階層下の command キーのパス名を調べます.そして,そのパス名のプログラムを起動する.ということになります.
ダブルクリックしたファイルは,そのプログラムのコマンドラインの最初の引数となります.


HKEY_CLASSES_ROOT\plXRAYFile


  • 図7
  • 拡張子関係の情報が登録されている場所

エクスプローラの [種類] に表示される説明用の文字と,アイコンの設定は,同じ plXRAYFile の (既定) と,[DefaultIcon] キーに記録されます.今回の場合,アイコンは,RegistExt.exe に含まれる最初のアイコンということになります.

下図は [480_拡張子に関連付けされた実行ファイル名の取得] のサンプルプログラムを使用して,拡張子 .plXRAY に関連付けされた実行ファイルのフルパスを取得した結果です.



図8
関連付けの EXE 名を調査

拡張子に関係する情報は,レジストリの以下の場所にあります.これらと同じ階層に,拡張子と同じ名前のキーと,参照名のキーがあります.
参照名のキーは,(5) を除いて,アプリケーションによっては,これらの階層の Applications というキーの階層下にもあります.
(1) については,後述の [06_関連付けを設定するダイアログの表示] で説明しています.

本ページのサンプルでは,HKEY_CLASSES_ROOT を操作しています.HKEY_CLASSES_ROOT は,(2),(3) のエイリアスとなっています.HKEY_CLASSES_ROOT のキーの内容を変更すると,(2),(3) の該当するキー項目の内容も更新されます.(2),(3) の拡張子関係の内容の変更も HKEY_CLASSES_ROOT の該当キーの内容に反映されます.
したがって,ユーザ側からは,HKEY_CLASSES_ROOT を操作すればいいことになります.



(1) HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts
(2) HKEY_CURRENT_USER\Software\Classes
(3) HKEY_LOCAL_MACHINE\SOFTWARE\Classes
(4) HKEY_CLASSES_ROOT
(5) HKEY_CLASSES_ROOT\SystemFileAssociations





03_拡張子 .plXRAY をペイントに関連付ける


今度は,同じ拡張子を別のアプリケーションに関連付けしてみます.
ここでは,Windows に付属しているペイントを起動するようにしています.拡張子を登録して,拡張子が .plXRAY のファイルをダブルクリックすると,ペイントが起動して画像を表示します.

前のサンプルプログラムのコードの内,下に示す部分のコードを変更します.本サンプルの EXE を実行する前に,前のサンプルで [登録削除] を実行しておきます.



図9
E_frog.plXRAY をダブルクリック
  • ペイントが起動して画像を表示する

リスト2
拡張子 plXRAY をペイントに関連付ける
procedure TForm1.Button1Click(Sender: TObject);
var
  LAppPath       : string;
  LStrParams     : string;
  LRegistAppReff : string;
  LRegistAppText : string;
  LiRet          : Integer;
begin
  //拡張子に関連付けるアプリのEXEのフルパス
  //ここではこのサンプルプログラムとする
  LAppPath   := 'C:\Windows\System32\mspaint.exe';
  LStrParams := '%1';
  //参照名
  LRegistAppReff := 'plXRAYFile';
  //エクスプローラの種類名
  LRegistAppText := '特別画像ファイル';

  //以下,省略




04_拡張子 .plDoc をワード操作フォームEx のアプリに関連付ける


次は,拙作のワード操作フォームEx で作成したアプリケーションに関連付けしてみます.
前のサンプルのコードを次に示すコードに変更します.

ワード操作フォームEx はワードの文書を表示するものです.添付のテスト文書.plDoc はワードの文書ファイルです.本サンプルは,拡張子 .plDoc を [TestApp] フォルダ内にある SimpleWord.exe というプログラムに関連付けます.
このサンプルのように,同じワードの文書ですが,全く別のアプリケーションを起動するようにすることができます.他のワード文書の拡張子も .plDoc に変更すると,この [TestApp] 内のプログラムで表示することになります.
全てのワード文書ではなく,一部の文書だけを独自のアプリで操作することができます.



図10
.plDoc ファイルをダブルクリックした結果の例

リスト3
拡張子 plDoc をワード操作フォームEx のアプリに関連付ける
//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //登録する拡張子
  RegistFileExt := 'plDoc';
end;

//=============================================================================
//  レジストリに新しい拡張子と関連付けを登録
//  管理者権限での実行が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LAppPath       : string;
  LStrParams     : string;
  LRegistAppReff : string;
  LRegistAppText : string;
  LiRet          : Integer;
begin
  //拡張子に関連付けるアプリのEXEのフルパス
  LAppPath   := ExtractFilePath(Application.ExeName) + 'TestApp\SimpleWord.exe';
  LStrParams := '%1';
  //参照名
  LRegistAppReff := 'plXWordExFile';
  //エクスプローラの種類名
  LRegistAppText := 'ワード操作フォームファイル';

  //以下,省略




05_ログインユーザの SID とローカルコンピュータの SID の取得


前のサンプルで,レジストリの登録を実行して,レジストリを見てみると,HKEY_USERS の階層化に下図のようなキーがあるのが分かります.HKEY_USERS は,そのコンピュータで有効なユーザのプロファイルが格納されているキーです.

下図の数字は SID (Security Identifier) と呼ばれ,レジストリ内でユーザを識別する値です.
ユーザを識別する一意の値となっています.例えば,あるユーザ名をシステムから削除して,同じ名前を登録しても別の SID が割り当てられる仕組みになっています.

SID を必要する場面はほとんどないと思われますが,レジストリの操作のサンプルのついでに,これらの値を取得するサンプルを作成してみました.取得用の関数類は別ユニットにしています.
ユーザの SID とコンピュータの SID は,末尾に 4 桁の数字があるかの違いです.それ以外はユーザごとに同じ値となっています.

次のコードは作成したユニットのテストプログラムと,その実行結果の例です.
登録した拡張子の情報は,操作内容によっては,このキーの階層下に記録されることがあります.



図11
ログインユーザの SID の値
  • S-1-5-21 は管理者を意味する
図12
ローカルコンピュータの SID の値
  • S-1-5-21 は管理者を意味する

リスト4
SID を取得するユニットのテストプログラム
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plSIDUtilty;

{$R *.dfm}

//=============================================================================
//  ログインユーザのSIDを取得
//  文字列に変換して表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  StrText : String;
begin
  StrText := plSIDUtilty.GetCurrentUserSIDString;
  ShowMessage(StrText);
end;

//=============================================================================
//  ローカルコンピュータのSIDを取得
//  文字列に変換して表示
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  StrText : String;
begin
  StrText := plSIDUtilty.GetLocalComputerSIDString;
  ShowMessage(StrText);
end;

end.


下のコードが実際に SID を取得するための関数類を収めたユニットのコードです.
LookupAccountName という関数を使用しています.



リスト5
SID の値を取得するための関数類のユニット
unit plSIDUtilty;

interface

uses Windows, ShlObj;

function GetCurrentUserSID: PSID;
function GetCurrentUserSIDString: String;
function GetLocalComputerSID: PSID;
function GetLocalComputerSIDString: String;

implementation

function ConvertSidToStringSid(Sid: PSID; out StringSid: PChar): BOOL; stdcall;
  external 'ADVAPI32.DLL' name 'ConvertSidToStringSidW';

//-----------------------------------------------------------------------------
//  ローカルコンピュータ名を取得する関数
//-----------------------------------------------------------------------------
function GetLocalComputerName: String;
var
  Buff  : array [0..MAX_COMPUTERNAME_LENGTH-1] of Char;
  ASize : Cardinal;
begin
  ASize := SizeOf(Buff);
  if Windows.GetComputerName(Buff, ASize) then begin
    Result := String(Buff);
  end else begin
    Result := '';
  end;
end;

//-----------------------------------------------------------------------------
//  ログインユーザ名を取得する関数
//-----------------------------------------------------------------------------
function GetLoginUserName: String;
var
  Buff  : array [0..MAX_PATH-1] of Char;
  ASize : Cardinal;
begin
  ASize := SizeOf(Buff);
  if Windows.GetUserName(Buff, ASize) then begin
    Result := String(Buff);
  end else begin
    Result := '';
  end;
end;

//-----------------------------------------------------------------------------
//  ログインユーザまたはローカルコンピュータのSIDを取得する関数
//  IsUserSIDがTureの時はログインユーザのSIDを取得する
//-----------------------------------------------------------------------------
function GetCurrentUserOrComputerSID(IsUserSID: Boolean): PSID;
var
  lpSystemName  : String;
  lpAccountName : String;
  Sid           : PSID;
  cbSid         : Cardinal;
  RefDomainName : array[0..MAX_PATH-1] of char;
  cbDomainName  : Cardinal;
  peUse         : Cardinal;
begin
  lpSystemName:='';
  if IsUserSID then begin
    lpAccountName := GetLoginUserName;
  end else begin
    lpAccountName := GetLocalComputerName;
  end;


  Sid          := nil;
  cbSid        := 0;
  cbDomainName := SizeOf(RefDomainName);

  //SID情報取得に必要なバッファサイズを求める
  LookupAccountName(PChar(lpSystemName),
                    PChar(lpAccountName),
                    Sid,
                    cbSid,
                    nil,
                   cbDomainName,
                    peUse);

  //取得したバッファサイズを確保してSIDの値を取得
  Sid := GetMemory(cbSid);
  try
    LookupAccountName(PChar(lpSystemName),
                      PChar(lpAccountName),
                      Sid,
                      cbSid,
                      RefDomainName,
                      cbDomainName,
                      peUse);
    Result := Sid;
  finally
    FreeMemory(Sid);
  end;
end;

//=============================================================================
//  ログインユーザのSIDの取得
//=============================================================================
function GetCurrentUserSID: PSID;
begin
  Result := GetCurrentUserOrComputerSID(True);
end;

//=============================================================================
//  ログインユーザのSIDの値を文字列に変換
//=============================================================================
function GetCurrentUserSIDString: String;
var
  UserSID    : PSid;
  pStringSid : PChar;
begin
  UserSID := GetCurrentUserOrComputerSID(True);
  //SIDの値を文字列に変換
  ConvertSidToStringSid(UserSID, pStringSid);
  Result := String(pStringSid);
end;

//=============================================================================
//  ローカルコンピュータのSIDの取得
//=============================================================================
function GetLocalComputerSID: PSID;
begin
  Result := GetCurrentUserOrComputerSID(False);
end;

//=============================================================================
//  ローカルコンピュータのSIDの値を文字列に変換
//=============================================================================
function GetLocalComputerSIDString: String;
var
  ComputerSID : PSid;
  pStringSid  : PChar;
begin
  ComputerSID := GetCurrentUserOrComputerSID(False);
  //SIDの値を文字列に変換
  ConvertSidToStringSid(ComputerSID, pStringSid);
  Result := String(pStringSid);
end;

end.




06_[ 関連付けを設定する ] ダイアログの表示


既にレジストリに登録された拡張子とアプリケーションとの関連付けは,関連付けのダイアログを表示して変更することができます.
これは,Windows Vista 以降では,[ファイル名を指定して実行] のダイアログです.
このダイアログは,次のコマンドを実行すると表示できます.


control.exe /name Microsoft.DefaultPrograms /page pageFileAssoc

ShellExecute 関数でこのダイアログを表示するには,下の示すコードを実行します.
下図は実行結果です.docx を選択して,[プログラムの変更] ボタンをクリックしたところです.


[備考]
本ページの拡張子の登録と関連付けのサンプルでレジストリに登録した拡張子は,本サンプルで表示するダイアログの拡張子の一覧には表示されません.



図13

リスト6
[関連付けを設定する] ダイアログを表示
uses ShellAPI;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  CmdStr  : String;
  ParaStr : String;
begin
  CmdStr  := 'control.exe';
  ParaStr := '/name Microsoft.DefaultPrograms /page pageFileAssoc';
  ShellExecute(Handle, nil, PChar(CmdStr), PChar(ParaStr), nil, SW_SHOW);
end;


上の図で [ファイルを開くプログラム] を選択すると,設定内容は,レジストリの以下の階層下の拡張子名のキーの,[UserChoice] に参照名が記録されます.エクスプローラでこの拡張子のファイルをダブルクリックすると,その参照名のキーの内容から起動パスを読み取って起動します.下のレジスタの階層下のキーの内容 (値 - データ) は,ユーザが直接編集することはできません.
キーや項目を削除することは可能です.

レジストリに登録していないプログラムも,上図の GUI 操作で,拡張子の関連付けが可能です.そのために,システムは,選択したプログラムに一時的な参照名を割り当てます.


HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts


上で述べた方法で開くプログラムを選択します.すると,次にこのダイアログ表示すると,前回選択したプログラムもリストに表示されます.
下図は,拡張子が .pdf のファイルで,今回テストした RegistExt.exe を選択した結果です.

この項目をダイアログのリストで表示しないようにするには,上のレジストリの階層下にある拡張子名と同じ .pdf というキーのサブキー [OpenWithProgids] の 該当項目を削除します.
他の拡張子の場合も,同じように操作できます.



図14
RegistExt.exe がリストにある
図15
RegistExt.exe をリストから削除
  • RegistExt.exe の参照名である名前 plXRAYFile を削除する




07_[ ファイルを開くプログラムの選択 ] ダイアログ - TFileOpenWith


TActionList のメンバである TFileOpenWith クラスを使用すると,直接 [ファイルを開くプログラムの選択] のダイアログを表示することができます.
開く対象のファイルを選択すると [ファイルを開くプログラムの選択] ダイアログを表示します.

このダイアログは,エクスプローラの [プログラムから開く] で [既定のプログラムの選択] をクリックした時に表示されるダイアログと同じです.アクションリストでは [指定したアプリケーションで開く...] というタイルとになります.



リスト7
TFileOpenWith を使用して [ファイルを開くプログラムの選択] ダイアログを表示
uses
  StdActns;

{$R *.dfm}

//=============================================================================
//  TFileOpenWithを使用した[ファイルを開くプログラムの選択]ダイアログの表示
//  TFileOpenWithの使用にはusesにStdActnが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFileOpen : TFileOpenWith;
begin
  LFileOpen := TFileOpenWith.Create(Self);
  try
    LFileOpen.Dialog.Filter := 'テキストファイル (*.txt)|*.txt';
    if LFileOpen.Execute then begin
      //ダイアログを閉じた時の処理
      //LFileOpen.Dialog.FileNameが選択ファイルのフルパス
      //選択しなかった時は空文字
    end;
  finally
    FreeAndNil(LFileOpen);
  end;
end;




08_アプリケーションに関連付ける拡張子を設定するダイアログ


IApplicationAssociationRegistrationUI インターフェイスの LaunchAdvancedAssociationUI 関数を使用すると,アプリケーションに関連付ける拡張子をまとめて設定できます.
このインターフェイスは,Windwos Vista で実装されました.Delphi 2010 以降で使用できます.設定可能なアプリは,レジストリの以下のどちらかの場所に登録されているアプリだけです.

HKEY_CURRENT_USER\Software\RegisteredApplications
HKEY_LOCAL_MACHINE\Software\RegisteredApplications

下図は,筆者の Windows 7 U64(SP1) の環境での実行結果です.
設定の解除はできないようです.前項の記事のように,拡張子に関連付けするアプリを設定すれば,自動的に設定が解除され,チェックなしとなります.


図16
設定可能なアプリの一覧
  • 関数の引数に指定可能なアプリ名
図17
設定ダイアログを表示
  • ワードパッドの例
  • 筆者の場合,ワードパッドで表示する設定をしている拡張子はない
  • 設定の解除はできない

リスト8
アプリケーションに関連付けする拡張子を設定するダイアログを表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ActiveX, ShlObj, ComObj;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := 'Wordpad';
end;

//=============================================================================
//  アプリケーションに関連付けする拡張子を設定するダイアログを表示
//  IApplicationAssociationRegistrationUIインターフェイスを使用
//
//  設定可能なアプリケーションはレジストリの以下の場所に記録されいるものだけ
//  HKCU\Software\RegisteredApplications
//  HKLM\Software\RegisteredApplications
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  iAppAssocRegUI : IApplicationAssociationRegistrationUI;
  pszAppRegName  : String;
  iRet           : HRESULT;
begin
  //IApplicationAssociationRegistrationUIのインスタンスを生成
  iAppAssocRegUI := CreateComObject(CLSID_ApplicationAssociationRegistrationUI)
                    as IApplicationAssociationRegistrationUI;
  if iAppAssocRegUI = nil then exit;

  //アプリケーション名の指定
  //pszAppRegNameが無効な場合は表示しない
  pszAppRegName := Trim(Edit1.Text);
  iRet := iAppAssocRegUI.LaunchAdvancedAssociationUI(PChar(pszAppRegName));

  if iRet <> S_OK then begin
    MessageBox(Handle, '登録されていないアプリです', '情報', MB_ICONINFORMATION);
  end;
  iAppAssocRegUI := nil;
end;

end.


IApplicationAssociationRegistration インターフェイスの COM オブジェクトのインスタンスは,次のコードでも生成できます.



  if Failed(CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
                             nil,
                             CLSCTX_INPROC_SERVER,
                             IApplicationAssociationRegistrationUI,
                             iAppAssocRegUI)) then exit;




09_拡張子に関連付けるアプリケーションを変更  -  SetAppAsDefault


拡張子に関連付けするアプリケーションを設定するサンプルです.
IApplicationAssociationRegistration インターフェイスの SetAppAsDefault 関数を使用します.この関数も,前項のサンプルと同じく,関連付けが可能なアプリは,レジストリの以下のどちらかの場所に登録されているアプリだけです.
このインターフェイスは,Windwos Vista で実装されました.Delphi 2010 以降で使用できます.
このインターフェイスは,Windows 8 では使用できません (確認済み).マイクロソフトでは GUI での操作を推奨しています.

HKEY_CURRENT_USER\Software\RegisteredApplications
HKEY_LOCAL_MACHINE\Software\RegisteredApplications

下図は,筆者の Windows 7 U64(SP1) の環境での実行結果です.
拡張子 .txt をワードパッドに関連付けします.

このインターフェイスに SetAppAsDefaultAll という関数があります.この関数は,指定アプリに関連付け可能な拡張子を全て関連付けします.例えば,本サンプルの設定用コードを次のようにすると,ワードパッドで利用可能な拡張子のファイル全てを,ワードパッドで表示するようになります.
これは,前項のサンプルのダイアログで,[すべて選択] にチェックを入れることと同じです.



  pszAppRegistryName := 'Wordpad';
  iAppAssocReg.SetAppAsDefaultAll(PChar(pszAppRegistryName));


図18
実行開始画面
  • 設定する拡張子と,その拡張子に関連付けるアプリの登録名を指定
図19
設定結果を前項のサンプルで確認
  • 拡張子 .txt の既定アプリが,ワードパッドに変更されている

リスト9
IApplicationAssociationRegistration を使用した拡張子の関連付けアプリの設定
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ActiveX, ShlObj, ComObj;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  Edit1.Text := '.txt';
  Edit2.Text := 'Wordpad';
end;

//=============================================================================
//  登録済みの拡張子に指定アプリを関連付ける
//  IApplicationAssociationRegistrationインターフェイスを使用
//
//  設定可能なアプリケーションはレジストリの以下の場所に記録されいるものだけ
//  HKCU\Software\RegisteredApplications
//  HKLM\Software\RegisteredApplications
//
//  管理者権限で実行しなくても設定可能
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  iAppAssocReg       : IApplicationAssociationRegistration;
  pszQuery           : String;
  pszAppRegistryName : String;
  iRet               : HRESULT;
begin
  //IApplicationAssociationRegistrationのインスタンスを生成
  iAppAssocReg := CreateComObject(CLSID_ApplicationAssociationRegistration)
                  as IApplicationAssociationRegistration;
  if iAppAssocReg = nil then exit;


  pszQuery           := Trim(Edit1.Text);
  pszAppRegistryName := Trim(Edit2.Text);

  iRet := iAppAssocReg.SetAppAsDefault(PChar(pszAppRegistryName),
                                       PChar(pszQuery),
                                       AT_FILEEXTENSION);

  if iRet = S_OK then begin
    MessageBox(Handle, '関連付けに成功しました', '情報', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '関連付けに失敗しました', '情報', MB_ICONINFORMATION);
  end;
  iAppAssocReg := nil;
end;

end.


IApplicationAssociationRegistration インターフェイスの COM オブジェクトのインスタンスは,次のコードでも生成できます.



  if Failed(CoCreateInstance(CLSID_ApplicationAssociationRegistration,
                             nil,
                             CLSCTX_INPROC_SERVER,
                             IApplicationAssociationRegistration,
                             iAppAssocReg)) then exit;




10_登録済みの拡張子とアプリケーションの関連付け


実務的に,登録済みの拡張子と,システムにインストールしていないアプリケーションの関連付けの必要性があるかどうかは不明ですが,もし,実際に行うとすると,レジストリの操作になります.
その参考用のサンプルです.ただし,ここではインストール済みのアプリを操作しています.手順そのものは基本的に同じです.

一般的には,HKEY_CLASSES_ROOT 階層下のプログラム識別子名のキーにある,起動するプログラムのパス名の部分を変更すれば可能です.拡張子のアイコンも [DefaultIcon] キーの値を設定すれば変更できます.[DefaultIcon] キーがなければキーを作成してから設定します.

対象となる拡張子の参照名のキーのコマンドを変更するこの方法は,Microsoft Office に関連付けされているファイルの関連付けは変更できません.そこで,以下のサンプルでは,本ページの最初のサンプルと同じような方法を使用しています.

  • HKEY_CLASSES_ROOT に新しい参照名のキーを作成する
  • その参照名の [shell] [open] [command] キーに,起動する EXE のパスを設定する
  • HKEY_CLASSES_ROOT の,拡張子名のキーの (既定) の値をその参照名にする
  • 元に戻すには,この拡張子名のキーの (既定) の値を元の値に戻す
    (本サンプルでは,作成した新しい参照名のキーも削除している)

ただし,ユーザが [既定のプログラム] の設定を行っていると,単に上記の手順だけでは,起動するアプリケーションを変更することはできません.
[既定のプログラム] を設定すると,[06_関連付けを設定するダイアログの表示] で説明したレジストリの拡張子名のキーの下に,[UserChoice] という名前のキーが作成されます.そこに [既定のプログラム] の情報が記録されています.このキーを削除しないと,起動するアプリケーションの設定が有効とはなりません.

このキーは,管理者権限で実行しても,値の編集も削除できません.アクセスが拒否されます.レジストリエディタを起動して,手動であれば削除可能です.

そこで,次のサンプルでは,レジストリのアクセスに REG_OPTION_BACKUP_RESTORE を指定して,このキーの削除を可能にしています.ただし,この指定だけでは削除できません.SE_SECURITY_NAME の特権を有効する必要があります.この特権の設定に,DEKO さんの「ちっぷす」のサンプルコードを別ユニットにして利用しています.

レジストリの構成と機能は,Windows のバージョンが上がるたびに変更されています.古いバージョンとの互換性を保つために,その構成と機能はより複雑になってきています.ここで述べた方法が全てのアプリケーションの場合,あるいは将来的にも有効である保障はありません.



下図がサンプルの実行結果です.ここで設定している値は筆者の環境のものです.
拡張子が .pdf のファイルを対象にしています.

Button1 は,.pdf ファイルをダブルクリックすると,本来起動する Adobe Reader でなく,ワードパッドを起動するようにしています.Button2 は,元の Adobe Reader が起動するようにするためのコードを実行します.つまり,元に戻す操作ということになります.
終了時にも,元に戻すコードを実行するようにしています.



図20
Button1 をクリック
  • フォームには TEdit を 2 つ配置
  • 上の TEdit は対象の拡張子名用
  • 次は,その拡張子に関連付けするプログラムの実行ファイル名と,コマンドライン引数
  • 拡張子を変更して [関連付け変更] ボタンをクリックすると,前の拡張子の関連付けを,自動で元に戻すようにしている
図21
.pdf ダブルクリックでワードパッドが起動
  • 元々は Adobe Reader が起動するファイル
  • アイコンは設定していないので,変更したワードパッドのアイコンとなる
図22
Button2 をクリック
図23
.pdf ダブルクリックで Adobe Reader が起動

本サンプルで起動するプログラムの EXE 名をセットしているのは,レジストリの下図の場所です.
このキーは,HKEY_CLASSES_ROOT の階層に,新規で作成したキーです.このキーは,サンプルのプログラムを終了する時に削除するようにしています.



図24
起動パス名が記録されている場所
  • plApplicationEXE が今回のテスト用に作成したキー
  • このパス名を変更すると,そのパス名のプログラムが起動する

本サンプルで .pdf に関連付けるプログラムを変更すると,[ファイルを開くプログラムの選択] のダイアログで表示されるアプリの一覧が,下図のように変わります.
ワードパッドがダイアログのリストにある時に,ワードパッドを選択してファイルを起動すると,それ以降,ワードパッドがこのダイアログのリストに表示されるようになります.

本サンプルはテストプログラムですので,終了時に,この新たに関連付けしたプログラムを,リストにから削除する処理コードを追加しています.



図25
ワードパッドに変更した場合
  • ワードパッドが追加される
図26
元に戻した場合
  • 選択のリストからワードパッドがなくなる

リスト10
登録済みの拡張子とアプリケーションの関連付けを変更する例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private 宣言 }
    OldFileExt        : string;
    OldExePath        : string;
    NewFileExt        : string;
    NewExePath        : string;
    OriginalAppProgID : string;
    MyAppProgID       : string;
    function CreateProgIDKey(ProgIDName, Description, ExeName: string): Boolean;
    function GetProgIDName(ExtName: string): string;
    function SetProgIDName(ExtName, ProgIDName: string): Boolean;
    function DeleteProgIDKey(ProgIDName: string): Boolean;
    function DeleteFileExts_UserChoice(AExt, AppExeName: string): Boolean;
  public
    { Public 宣言 }
  end;


var
  Form1: TForm1;

implementation

uses
  Registry,
  ShlObj,
  plPrivilegeUnit,
  plCommandRedirect,
  StrUtils;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  OldFileExt := '';
  OldExePath := '';
  //新しく作成する参照名の名前
  MyAppProgID := 'plApplicationEXE';

  //対象の拡張子
  Edit1.Text := '.pdf';
  //その拡張子に関連付けるプログラムのEXE名
  Edit2.Text := '"wordpad.exe" "%1"';
end;

//=============================================================================
//  フォームを閉じる時の処理
//  起動するプログラムを元に戻しておく
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  //参照名を元に戻す
  if GetProgIDName(OldFileExt) <> OriginalAppProgID then begin
    Button2Click(nil);
  end;
end;

//=============================================================================
//  指定拡張子のファイルをダブルクリックしたら新しいプログラム起動するようにする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LflgRet : Boolean;
begin
  LflgRet := True;

  NewFileExt := Trim(Edit1.Text);
  NewExePath := Trim(Edit2.Text);

  //設定する拡張子が変更になった場合
  //参照名を元に戻して,新しい拡張子の参照名を取得しておく
  if (OldFileExt <> NewFileExt) then begin
    if (OldFileExt <>'') and (OriginalAppProgID <> '') then begin
      LflgRet := SetProgIDName(OldFileExt, OriginalAppProgID);
    end;
    if LflgRet then begin
      OldFileExt := NewFileExt;
      OriginalAppProgID := GetProgIDName(NewFileExt);
    end;
  end;

  if LflgRet then begin
    //既存の拡張子名のキーに新しく作成する参照名をセット
    LflgRet := SetProgIDName(NewFileExt, MyAppProgID);
    if LflgRet then begin
      //新しく作成した参照名のキーに起動するEXE名をセット
      if CreateProgIDKey(MyAppProgID, '自分専用', NewExePath) then begin
        OldExePath := NewExePath;
      end;
    end;
  end;


  if LflgRet then begin
    //既定のプログラムの設定を削除
    DeleteFileExts_UserChoice(NewFileExt, '');
    //システムに変更を通知
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, nil, nil);

    MessageBox(Handle, '関連付けアプリを変更しました.', '情報', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '関連付けの変更に失敗しました.', '情報', MB_ICONWARNING);
  end;
end;

//=============================================================================
//  指定拡張子のファイルをダブルクリックしたら元のプログラムが起動するようにする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LflgRet : Boolean;
begin
  LflgRet := True;

  if GetProgIDName(OldFileExt) <> OriginalAppProgID then begin
    //参照名を元に戻す
    LflgRet := SetProgIDName(OldFileExt, OriginalAppProgID);
    //作成した参照名のキーを削除
    DeleteProgIDKey(MyAppProgID);
    //既定のプログラムの設定を削除
    DeleteFileExts_UserChoice(NewFileExt, NewExePath);
    //システムに変更を通知
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, nil, nil);
  end;


  if LflgRet then begin
    MessageBox(Handle, '関連付けを元に戻しました.', '情報', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '関連付けを戻すのに失敗しました.', '情報', MB_ICONWARNING);
  end;
end;

//-----------------------------------------------------------------------------
//  指定の独自の参照名のキーがなければを作成して\shell\open\commandに値をセット
//  作成済みであれば\shell\open\commandに値をセット
//-----------------------------------------------------------------------------
function TForm1.CreateProgIDKey(ProgIDName, Description, ExeName: String): Boolean;
var
  LRegistry : TRegistry;
begin
  Result := False;
  if (ProgIDName = '') or (ProgIDName = '.') or (ExeName = '') then exit;

  LRegistry := TRegistry.Create(KEY_READ or KEY_WRITE);
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;

    if LRegistry.OpenKey('\' + ProgIDName, True) then begin
      //エクスプローラに表示する説明用文字列の登録
      if Description <> '' then begin
        LRegistry.WriteString('', Description);
      end;
      //Shell\Open\Commandキーを作成してEXEのパス名を登録
      LRegistry.OpenKey('\' + ProgIDName + '\shell\open\command', True);
      LRegistry.WriteString('', ExeName);
      Result := True;
    end;
  finally
    FreeAndNil(LRegistry);
  end;
end;

//-----------------------------------------------------------------------------
//  拡張子名のキーからその拡張子に関連付けされている参照名を取得
//-----------------------------------------------------------------------------
function TForm1.GetProgIDName(ExtName: String): String;
var
  LRegistry  : TRegistry;
begin
  Result := '';
  if (ExtName = '') or (ExtName = '.') then exit;

  LRegistry := TRegistry.Create(KEY_READ);
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;
    if LRegistry.OpenKey('\' + ExtName, False) then begin
      Result := LRegistry.ReadString('');
    end;
  finally
    FreeAndNil(LRegistry);
  end;
end;

//-----------------------------------------------------------------------------
//  拡張子名のキーに参照名を書き込む
//-----------------------------------------------------------------------------
function TForm1.SetProgIDName(ExtName, ProgIDName: String): Boolean;
var
  LRegistry  : TRegistry;
begin
  Result := False;
  if (ExtName = '') or (ExtName = '.') or (ProgIDName = '') then exit;

  LRegistry  := TRegistry.Create(KEY_READ or KEY_WRITE);
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;
    if LRegistry.OpenKey('\' + ExtName, False) then begin
      LRegistry.WriteString('', ProgIDName);
      Result := True;
    end;
  finally
    FreeAndNil(LRegistry);
  end;
end;

//-----------------------------------------------------------------------------
//  操作用に作成した参照名のキーをレジストリから削除
//-----------------------------------------------------------------------------
function TForm1.DeleteProgIDKey(ProgIDName: String): Boolean;
var
  LRegAppProgID : String;
  LCmdLine      : TplCommandRedirect;
  LCmdStr       : String;
begin
  Result := False;
  if (ProgIDName = '') then exit;

  LRegAppProgID := 'HKEY_CLASSES_ROOT\' + ProgIDName;

  //対象のキーの有無に関わらず削除のコードを実行
  LCmdLine := TplCommandRedirect.Create;
  try
    LCmdStr := 'Cmd.exe /k reg delete ' + LRegAppProgID + ' /f';
    LCmdLine.GrabStdOutText(LCmdStr);
    Result := True;
  finally
    FreeAndNil(LCmdLine);
  end;
end;

//-----------------------------------------------------------------------------
//  [既定のプログラム]を設定すると作成される[UserChoice]キーを削除する関数
//
//  このキーはレジストリの以下の場所の拡張子名のキーに作成される
//  HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts
//
//  このキーの削除には,レジストリのアクセスにREG_OPTION_BACKUP_RESTOREが必要
//  SE_SECURITY_NAMEの特権が必要
//-----------------------------------------------------------------------------
function TForm1.DeleteFileExts_UserChoice(AExt, AppExeName: String): Boolean;
var
  LRegistry    : TRegistry;
  LBaseKeyName : String;
  LKeyName     : String;
  LStrList     : TStringList;
  i            : Integer;
begin
  Result := False;
  if (AExt = '') or (AExt = '.') then exit;

  LBaseKeyName := '\Software\Microsoft\Windows\CurrentVersion\'
                + 'Explorer\FileExts\' + AExt;

  plPrivilegeUnit.SetPrivilege(SE_SECURITY_NAME, True);
  LRegistry := TRegistry.Create(REG_OPTION_BACKUP_RESTORE or KEY_READ or KEY_WRITE);
  try
    LRegistry.RootKey := HKEY_CURRENT_USER;

    LKeyName := LBaseKeyName + '\UserChoice';
    if LRegistry.KeyExists(LKeyName) then begin
      if LRegistry.OpenKey(LKeyName, False) then begin
        LRegistry.DeleteKey('');
        Result := True;
      end;
    end else begin
      Result := True;
    end;

    //[OpenWithList]から関連付けたアプリを削除してしまうことにする
    if AppExeName <> '' then begin
      LKeyName := LBaseKeyName + '\OpenWithList';
      if LRegistry.OpenKey(LKeyName, False) then begin
        LStrList := TStringList.Create;
        try
          LRegistry.GetValueNames(LStrList);
          for i := 0 to LStrList.Count - 1 do begin
            if ContainsText(AppExeName, LRegistry.ReadString(LStrList[i])) then begin
              LRegistry.DeleteValue(LStrList[i]);
              Break
            end;
          end;
        finally
          FreeAndNil(LStrList);
        end;
      end;
    end;
  finally
    plPrivilegeUnit.SetPrivilege(SE_SECURITY_NAME, False);
    FreeAndNil(LRegistry);
  end;
end;

end.




11_レジストリのキー情報の保存と復元


本ページのサンプルをテストした際に作成したプログラムです.
レジストリの指定キーの内容を,サブキーも含めて全てディスクに保存,復元します.拡張子関係のテストで使用したので,ルートキーは HKEY_CLASSES_ROOT に固定しています.

レジストリのキー情報の保存と復元には,TRegistry の SaveKey, RestoreKey メソッドも使用できますが,SaveKey は上書き保存ができないので,Reg コマンドを使用しています.上書き保存ができない方がいい場合もあります.その場合は,一度ファイルを削除して新規に作成するか,別名で保存して,確実に保存できたかを確認できます.

復元する時は,Edit1, Edit2, Edit3 の値を,保存した時と同じ値にする必要があります.

復元の時に,保存したキーよりも上の階層のキーを指定しないでください.指定したキーの階層下の内容が全て,保存した内容に置き換わってしまいます.特に空文字 (空白文字) を指定すると,ルートキー以下が全て置き換わってしまい,システムの復元操作をしない限り,元に戻すことができなくなってしまいます.以下のコードでは,空文字の時は処理しないようにしています.



[備考]
前項のサンプルでは,セキュリティの特権を有効にしました.本サンプルではその特権を有効にしていません.したがって,レジストリのキーによっては,保存あるいは復元できないキーがあります0HKEY_CLASSES_ROOT 階層下のキーであれば,保存,復元ともに可能です.



図27
設計時画面
  • Edit1 はルートキー指定 (本サンプルでは固定)
  • Edit2, Edit 3 は,キーの指定用
  • Edit2, Edit3 の値の処理コードは同じ
  • キーの指定値を空文字にすると,処理しない
図28
実行開始時の画面
  • Edit2, Edit3 は同じ処理コードなので,拡張子専用が Edit2 というわけではない
  • Edit2 に拡張子をタイプして,[Enter] を押下すると参照名を取得して Edit3 に表示する

リスト11
レジストリのキー情報の保存と復元
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Edit2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    { Private 宣言 }
    RegKeyInfoPath    : string;
    RegSavePathExt    : string;
    RegSavePathProgID : string;
    function BackUpProgIDKeyValues(ARootKey: Int64; AKeyName: string;
      ASaveFilePath: String): Boolean;
    function RestoreProgIDKeyValues(ARootKey: Int64; AKeyName: string;
      ASaveFilePath: String): Boolean;
    function GetAppProgIDName(ARootKey: Int64;AppFileExt: string): String;
    function GetHKEYKeyRoot(StrKeyName: string): Int64;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  Registry,
  ShlObj,
  plCommandRedirect;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LFilePath : string;
  LRegistry : TRegistry;
  LKeyList  : TStringList;
begin
  //レジストリのキー情報の保存先
  LFilePath         := ExtractFilePath(Application.ExeName);
  RegKeyInfoPath    := LFilePath + 'RegBackUpData\KeyInfoData.txt';
  RegSavePathExt    := LFilePath + 'RegBackUpData\RegSaveExt.hiv';
  RegSavePathProgID := LFilePath + 'RegBackUpData\RegSaveProgID.hiv';

  LRegistry := TRegistry.Create;
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;

    //保存したデータがあれば読み出す
    if FileExists(RegKeyInfoPath) then begin
      LKeyList := TStringList.Create;
      try
        LKeyList.LoadFromFile(RegKeyInfoPath);
        LRegistry.RootKey := StrToInt64(LKeyList[0]);

        Edit1.Text := LRegistry.RootKeyName;
        Edit2.Text := LKeyList[1];
        Edit3.Text := LKeyList[2];
      finally
        FreeAndNil(LRegistry);
        FreeAndNil(LKeyList);
      end;
    end else begin
      Edit1.Text := LRegistry.RootKeyName;
      Edit2.Text := '.plXRAY';
      Edit3.Text := GetAppProgIDName(LRegistry.RootKey, Edit2.Text);
    end;
  finally
    FreeAndNil(LRegistry);
  end;
end;

//=============================================================================
//  指定のレジストリのキー情報を保存
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LRootKeyName : Int64;
  LKeyList     : TStringList;
  LStrText     : string;
begin
  LRootKeyName := GetHKEYKeyRoot(Trim(Edit1.Text));

  LKeyList := TStringList.Create;
  try
    LKeyList.Add(IntToStr(GetHKEYKeyRoot(Edit1.Text)));

    if (Edit2.Text <> '') and (Edit2.Text <> '.') then begin
      if BackUpProgIDKeyValues(LRootKeyName, Edit2.Text, RegSavePathExt) then begin
        LKeyList.Add(Edit2.Text);
        LStrText := '拡張子のキー情報を保存しました';
        MessageBox(Handle, PChar(LStrText), '情報', MB_ICONINFORMATION);
      end else begin
        LKeyList.Add('');
        LStrText := '拡張子のキー情報の保存に失敗しました';
        MessageBox(Handle, PChar(LStrText), '情報', MB_ICONERROR);
      end;
    end else begin
      LKeyList.Add('');
    end;

    if (Edit3.Text <> '') and (Edit3.Text <> '.') then begin
      if BackUpProgIDKeyValues(LRootKeyName, Edit3.Text, RegSavePathProgID) then begin
        LKeyList.Add(Edit3.Text);
        LStrText := '参照名のキー情報を保存しました';
        MessageBox(Handle, PChar(LStrText), '情報', MB_ICONINFORMATION);
      end else begin
        LKeyList.Add('');
        LStrText := '参照名のキー情報の保存に失敗しました';
        MessageBox(Handle, PChar(LStrText), '情報', MB_ICONERROR);
      end;
    end else begin
      LKeyList.Add('');
    end;

    ForceDirectories(ExtractFileDir(RegKeyInfoPath));
    LKeyList.SaveToFile(RegKeyInfoPath);
  finally
    FreeAndNil(LKeyList);
  end;
end;

//=============================================================================
//  退避したレジストリの該当キー情報を復元する
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LRootKeyName : Int64;
  LStrText     : string;
begin
  LRootKeyName := GetHKEYKeyRoot(Trim(Edit1.Text));

  if (Edit2.Text <> '') and (Edit2.Text <> '.') then begin
    if RestoreProgIDKeyValues(LRootKeyName, Edit2.Text, RegSavePathExt) then begin
      LStrText := '拡張子のキー情報を復元しました';
      MessageBox(Handle, PChar(LStrText), '情報', MB_ICONINFORMATION);
    end else begin
      LStrText := '拡張子のキー情報の復元に失敗しました';
      MessageBox(Handle, PChar(LStrText), '情報', MB_ICONERROR);
    end;
  end;

  if (Edit3.Text <> '') and (Edit3.Text <> '.') then begin
    if RestoreProgIDKeyValues(LRootKeyName, Edit3.Text, RegSavePathProgID) then begin
      LStrText := '参照名のキー情報を復元しました';
      MessageBox(Handle, PChar(LStrText), '情報', MB_ICONINFORMATION);
    end else begin
      LStrText := '参照名のキー情報の復元に失敗しました';
      MessageBox(Handle, PChar(LStrText), '情報', MB_ICONERROR);
    end;
  end;
end;

//=============================================================================
//  Edit2のOnKeyDownイベント処理
//=============================================================================
procedure TForm1.Edit2KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = VK_RETURN then begin
    Edit3.Text := GetAppProgIDName(GetHKEYKeyRoot(Edit1.Text), Edit2.Text);
  end;
end;

//-----------------------------------------------------------------------------
//  ルートキーの文字列を,該当するルートキーの値に変換する関数
//-----------------------------------------------------------------------------
function TForm1.GetHKEYKeyRoot(StrKeyName: String): Int64;
begin
  Result := 0;

  if StrKeyName = 'HKEY_CLASSES_ROOT'     then Result := HKEY_CLASSES_ROOT;
  if StrKeyName = 'HKEY_CURRENT_USER'     then Result := HKEY_CURRENT_USER;
  if StrKeyName = 'HKEY_LOCAL_MACHINE'    then Result := HKEY_LOCAL_MACHINE;
  if StrKeyName = 'HKEY_USERS'            then Result := HKEY_USERS;
  if StrKeyName = 'HKEY_PERFORMANCE_DATA' then Result := HKEY_PERFORMANCE_DATA;
  if StrKeyName = 'HKEY_CURRENT_CONFIG'   then Result := HKEY_CURRENT_CONFIG;
  if StrKeyName = 'HKEY_DYN_DATA'         then Result := HKEY_DYN_DATA;
end;

//-----------------------------------------------------------------------------
//  ファイルの拡張子からアプリケーションの参照名の値を取得する
//-----------------------------------------------------------------------------
function TForm1.GetAppProgIDName(ARootKey: Int64; AppFileExt: String): String;
var
  LRegistry : TRegistry;
begin
  Result := '';
  LRegistry := TRegistry.Create(KEY_READ);
  try
    LRegistry.RootKey := ARootKey;

    //該当する拡張子が登録されていなければ取得できない
    if LRegistry.OpenKeyReadOnly('\' + AppFileExt) then begin
      Result := LRegistry.ReadString('');
    end;
  finally
    FreeAndNil(LRegistry);
  end;
end;

//-----------------------------------------------------------------------------
//  レジストリの指定キーの内容をサブキーも含めて保存する
//
//  ARootKey      : 保存するルートキーの値
//  AkeyName      : 保存するキーの名前
//  ASaveFilePath : 保存先のフルパス
//-----------------------------------------------------------------------------
function TForm1.BackUpProgIDKeyValues(ARootKey: Int64; AKeyName: String;
  ASaveFilePath: String): Boolean;
var
  LRegistry    : TRegistry;
  LRootKeyName : string;
  LRegPath     : string;
  LCmdLine     : TplCommandRedirect;
  LCmdStr      : string;
begin
  Result := False;

  LRegistry := TRegistry.Create(KEY_READ or KEY_WRITE);
  LCmdLine  := TplCommandRedirect.Create;
  try
    LRegistry.RootKey := ARootKey;
    LRootKeyName      := LRegistry.RootKeyName;

    if (AKeyName <> '') and (AKeyName <> '.') then begin
      //該当するキーの存在を確認.なくても作成はしない
      if LRegistry.OpenKey('\' + AKeyName, False) then begin
        LRegistry.CloseKey;

        //上書き保存
       //ディレクトリを作成
        ForceDirectories(ExtractFileDir(ASaveFilePath));

        LRegPath       := LRootKeyName + '\' + AKeyName;
        ASaveFilePath := AnsiQuotedStr(ASaveFilePath, '"');
        LCmdStr := 'Cmd.exe /k reg save ' + LRegPath + ' ' + ASaveFilePath + ' /y';
        LCmdLine.GrabStdOutText(LCmdStr);

        Sleep(10);
        Result := True;
      end;
    end;
  finally
    FreeAndNil(LRegistry);
    FreeAndNil(LCmdLine);
  end;
end;

//-----------------------------------------------------------------------------
//  退避したレジストリの該当キーを復元する
//
//  ARootKey      : 復元するルートキーの値
//  AkeyName      : 復元するキーの名前
//  ASaveFilePath : キー情報を保存したファイルのフルパス
//-----------------------------------------------------------------------------
function TForm1.RestoreProgIDKeyValues(ARootKey: Int64; AKeyName: String;
  ASaveFilePath: String): Boolean;
var
  LRegistry    : TRegistry;
  LRootKeyName : string;
  LRegPath     : string;
  LCmdLine     : TplCommandRedirect;
  LCmdStr      : string;
begin
  Result := False;

  LRegistry := TRegistry.Create(KEY_READ or KEY_WRITE);
  LCmdLine  := TplCommandRedirect.Create;
  try
    LRegistry.RootKey := ARootKey;
    LRootKeyName      := LRegistry.RootKeyName;

    if (AKeyName <> '') and (AKeyName <> '.') then begin
      //バックアップファイルがなければ復元できない
      if FileExists(ASaveFilePath) then begin
        //元のキーが存在しないと復元できないので,なければ作成する
        if LRegistry.OpenKey('\' + AKeyName, True) then begin
          LRegistry.CloseKey;

          //指定キー以下のサブキーも含めて復元する
          LRegPath      := LRootKeyName + '\' + AKeyName;
          ASaveFilePath := AnsiQuotedStr(ASaveFilePath, '"');
          LCmdStr := 'Cmd.exe /k reg restore ' + LRegPath + ' ' + ASaveFilePath;
          LCmdLine.GrabStdOutText(LCmdStr);

          Sleep(10);
          Result := True;
        end;
      end;
    end;

    //システムに変更を通知
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, nil, nil);
  finally
    FreeAndNil(LRegistry);
    FreeAndNil(LCmdLine);
  end;
end;

end.