Windows Installer手引書 Part.5 ファイル拡張子の関連付け

[**前の記事へ**](https://qiita.com/tohshima/items/1788a01745c631157a77)　　[**目次へ**](https://qiita.com/tohshima/items/dc4c4721d50a2ad70c7a)　　[**次の記事へ**](https://qiita.com/tohshima/items/32e6130dd8158020bf75)

# ファイル拡張子の関連付け
インストーラーで最もよく使われるレジストリは、ファイル拡張子の関連付けではないかと思います。

## 基本
プログラムをインストールする際、そのプログラムのデータファイルをダブルクリックしたときにプログラムを起動してデータを読ませるよう設定する、という要求はよくあります。関連付けはレジストリに書き込みます。例えば、プログラム`yourapp.exe`が拡張子`.papa`というデータファイルを使用するなら、次のようになります。

```
HKEY_CLASSES_ROOT
 + .papa
 |  + (Default) = YourProgID
 + YourProgID
 |  + shell
 |     + open
 |        + command
 |           + (Default) = "fullpath\yourapp.exe" "%1"
```

ここで、`YourProgID`は`[Vendor or Application].[Component].[Version]`といった[形式が推奨され](https://docs.microsoft.com/en-us/windows/desktop/shell/fa-progids)ています。全体的な情報は、[Application Registration](https://docs.microsoft.com/en-us/windows/desktop/shell/app-registration)や[File Types](https://docs.microsoft.com/en-us/windows/desktop/shell/fa-file-types)あたりから読み進めていけば一通り得られます。例えばWiXでは、下記のようなソースで表現できます。このコードで、プログラム`prTxtA`のデータファイルとして、拡張子`.papa`のファイルを関連付けます。

```xml
 1: <?define prgid="prTxtA.extcmd.1"?>
 2: 
 3: <Component Id="prgCompo" Guid="956fe4e0-24eb-4e2f-89c4-18fd051f216e">
 4:   <File Id="prTxExeFile" KeyPath="yes" Source="..\prTxtA_SRC\prTxtA.exe" />
 5:   <RegistryValue Id="extRegv" Root="HKCR" Key=".papa" Type="string" Value="$(var.prgid)" />
 6:   <RegistryValue Id="comRegv" Root="HKCR" Key="$(var.prgid)\shell\open\command" Type="string" Value="&quot;[#prTxExeFile]&quot; &quot;%1&quot;" />
 7:              :
 8:              :
 9: </Component>
```

基本はこれで良いのですが、ちょっとした落とし穴があります。

## 他のアプリと利用する拡張子がかぶる

よく使われるメジャーなデータ形式は色々なアプリが拡張子の関連付けを奪い合います。また、同じアプリの異なるバージョンを一つのPCで利用できるケースもあり、どのように拡張子の関連付けを管理していくか悩むところです。先の基本で例示したレジストリ構造を見れば分かるように、何も考えずにすでに関連付け済みの拡張子に関連付けをすると、古い関連付けの情報は消えてしまいます。そこでまず、この部分

```
HKEY_CLASSES_ROOT
 + .papa
 |  + (Default) = YourProgID
```

は[アンインストール時に削除しないことが推奨されています](https://docs.microsoft.com/en-us/windows/desktop/shell/fa-file-types#deleting-registry-information-during-uninstallation)。

さらにWindowsには、ファイルアイコンを右クリックするとメニューに現れる「プログラムから開く」や、「ファイルの種類ごとに既定のアプリを選ぶ」機能があります。ここに[インストールしたアプリを一覧表示させる](https://docs.microsoft.com/en-us/windows/desktop/shell/how-to-include-an-application-on-the-open-with-dialog-box)ことで、容易にユーザーが拡張子の関連付けを変更できるようになります。下記のように、拡張子のキーの下に`OpenWithProgids`キーを置くことで実現します。これは、prTxtAとprTxtBの2つのアプリをインストールした後の状態を示します。

```
HKEY_CLASSES_ROOT
 + .papa
 |  + (Default) = prTxtB.extcmd.1
 |  + OpenWithProgids
 |     + prTxtA.extcmd.1 = ""
 |     + prTxtB.extcmd.1 = ""
 + prTxtA.extcmd.1
 |  + shell
 |     + open
 |        + command
 |           + (Default) = "fullpath\prTxtA.exe" "%1"
 + prTxtB.extcmd.1
 |  + shell
 |     + open
 |        + command
 |           + (Default) = "fullpath\prTxtB.exe" "%1"
```

こうすると一覧から自分のアプリを容易に選べるようになります。
![Part5Fig01.png](https://qiita-image-store.s3.amazonaws.com/0/83170/65e28880-e750-aa4a-d72a-8ad2b4bcb5f2.png)

削除されないレジストリ（やファイル）をインストールする最も簡単な方法はComponentにPermanent属性を付けることでしょう。下記のソースのように削除しないレジストリを別のComponentに切り出せば実現できます。アンインストールすると実体のないProgIDが残ることになりますが、[ProgIDが登録されていない場合は無視される](https://docs.microsoft.com/en-us/windows/desktop/shell/fa-file-types#deleting-registry-information-during-uninstallation)ので問題ありません。ただし気を付けなければいけないのは、ComponentにPermanent属性を付けると、アンインストールしても環境にComponentの登録情報が残ることです。インストーラーのテストの都合でComponentの登録情報を削除する場合は、手動でレジストリから情報を消す必要があります。

```xml
 1: <?define prgid="prTxtA.extcmd.1"?>
 2: 
 3: <Component Id ="extCompo" Permanent="yes" Guid="bcadce47-ae51-4ad1-9391-227c998dea85">
 4:   <RegistryValue Id="extReg"      Root="HKCR" Key=".papa"                           Type="string" Value="$(var.prgid)" KeyPath="yes" />
 5: </Component>
 6: <Component Id="prgCompo" Guid="a5c70bd9-9158-4c0c-b8f4-4376bbc6e1fb">
 7:   <File Id="prTxExeFile" KeyPath="yes" Source="..\prTxtA_SRC\prTxtA.exe" />
 8:   <RegistryValue Id="openwithReg" Root="HKCR" Key=".papa\OpenWithProgids"           Type="string" Name="$(var.prgid)" Value="" />
 9:   <RegistryValue Id="comRegv"     Root="HKCR" Key="$(var.prgid)\shell\open\command" Type="string" Value="&quot;[#prTxExeFile]&quot; &quot;%1&quot;" />
10:              :
11:              :
12: </Component>
```

## アイコン画像をデータファイルに割り当てる

MSDN Libraryの情報では、[Programmatic Identifiers](https://docs.microsoft.com/en-us/windows/desktop/shell/fa-progids)が参考になります。

- アイコンファイルに割り当てる場合

progIDのキーの下にDefaultIconキーを置き、下記のようにアイコンファイルへのパスを記述します。

```
HKEY_CLASSES_ROOT
 + YourProgID
 |  + DefaultIcon
 |     + (Default) = fullpath\blueData.ico
```

WiXのソースは、例えば下記のようになります。

```xml
 1: <?define prgid="prTxtA.extcmd.1"?>
 2: 
 3: <Component Id="prgCompo" Guid="a5c70bd9-9158-4c0c-b8f4-4376bbc6e1fb">
 4:              :
 5:              :
 6:   <File Id="iconFile" Source="blueData.ico" />
 7:   <RegistryValue Id="iconReg" Root="HKCR" Key="$(var.prgid)\DefaultIcon" Type="string" Value="&quot;[$prgCompo]\blueData.ico&quot;" />
 8: </Component>
```

- EXE,DLLのリソース中にあるアイコン画像を使う場合

ファイル名の後に何番目（0が1番目）のアイコンか指定します。例えば、リソースの2番目のアイコン画像を使うなら、次のようになります（DLLの場合も同じ書式です）。

```
HKEY_CLASSES_ROOT
 + YourProgID
 |  + DefaultIcon
 |     + (Default) = fullpath\prTxtA.exe,1
```

WiXのソースは、例えば下記のようになります。

```xml
 1: <?define prgid="prTxtA.extcmd.1"?>
 2: 
 3: <Component Id="prgCompo" Guid="387e3686-06e3-4cdf-9992-9c8ea7da56bc">
 4:              :
 5:              :
 6:   <File Id="prTxExeFile" KeyPath="yes" Source="..\prTxtA_SRC\prTxtA.exe" />
 7:   <RegistryValue Id="iconReg" Root="HKCR" Key="$(var.prgid)\DefaultIcon" Type="string" Value="[$prgCompo]\prTxtA.exe,1" />
 8: </Component>
```

## [#prTxExeFile]や[$prgCompo]などの表現の意味

これまで何の説明もなくWiXのソースに使ってきましたが、これらは[MSIの機能](https://docs.microsoft.com/en-us/windows/desktop/Msi/formatted)です。`[#prTxExeFile]`は、prTxExeFileというIdで定義したファイルへのフルパスを示します。`[$prgCompo]`は、prgCompoというIdで定義したコンポーネントのフォルダ・パスを示します。これらはインストールの実行時に値が入れられるのでパスをハード・コーディングするよりは良い方法です。

## パスを表現する方法

[環境変数を使ってパスを指し示す方法](https://docs.microsoft.com/en-us/windows/desktop/shell/fa-best-practices#avoid-hard-coding-paths-into-the-registry-where-possible)もあります。

[**前の記事へ**](https://qiita.com/tohshima/items/1788a01745c631157a77)　　[**目次へ**](https://qiita.com/tohshima/items/dc4c4721d50a2ad70c7a)　　[**次の記事へ**](https://qiita.com/tohshima/items/32e6130dd8158020bf75)