Windowsでファイルを関連付けから開く際、引数として親フォルダを渡す

## 環境
Windows7 64bit

## 動機

- Visual Studio Codeで関連付けからファイルを開く時、既にウィンドウが開いている場合、同じウィンドウ内で開く
    - 既にワークスペースが開いていてもお構いなし
        - 対象ファイルの親ディレクトリがワークスペースとして開いている場合は、そこで開いてアクティベートしてはくれる
    - いったいどこでなにが開いてるんだかわけわかんなくなる
    - 全てのファイルを、適切なワークスペースで開いてほしい
        - 子ウィンドウがじゃんじゃん増えてもいいから

## 余談：Visual Studio Codeのワークスペースとは？
- 作業を行うディレクトリの集合。
    - ひとつのワークスペースに対して複数のディレクトリ（ワークスペースルート）を登録できる。（Multiroot workspace）
    - ワークスペースルートの配下でないファイルも開くことができる。
- 汎用の設定とは別にワークスペースごとの設定を行うことができる。
- ファイルとして保存できる。
    - マルチルートワークスペースを開くと、`C:\Users\<ユーザー名>\AppData\Roaming\Code\Workspaces`以下にworkspace.jsonファイルが生成されるが、ワークスペースを閉じると削除される。
    - 「ファイル＞名前を付けてワークスペースを保存」から、*.code-workspaceファイルとして保存できる。
- 1ウィンドウ1ワークスペース

## Visual Studio Codeのコマンドライン引数
通常は以下のようになる。

`"C:\Program Files\Microsoft VS Code\Code.exe" <パス>`

ディレクトリパスを渡した場合、ディレクトリをワークスペースとして開く。

つまり、「統合シェルのカレントディレクトリ」を「Codeエディタでワークスペースとして」開きたい、といった場合、bashなら`code "$PWD"`とかすれば開いてくれる。

そして、引数を複数取ることもできる。
引数にディレクトリパスが含まれる場合、それをワークスペースルートとして開く。
引数にディレクトリパスとファイルパスが両方含まれる場合、ディレクトリパスをルートとしたワークスペースでそのファイルを開く。

`"C:\Program Files\Microsoft VS Code\Code.exe" <ディレクトリパス> <ファイルパス>`

とかね。

## 関連付けの変更
今回は、拡張子「.ini」のファイルに対して行った。

### Windows関連付けの構造

- [本当に知っている？Windowsの基礎 \- なぜファイルをダブル・クリックするとアプリケーションが起動するのか？：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20081023/317579/)

ともすると**拡張子にアプリを関連付けている**と考えがちだが、実際には違う。

まず、「拡張子」に「ファイルタイプ」の名前が結びついている。例えば、拡張子「.ini」のファイルは「inifile」というファイルタイプである、というように。

そして、「ファイルタイプ」ごとに、「エクスプローラでダブルクリック（開く）される」などのイベントに応じた「コマンド」が結びついている。「inifile」は「Visual Studio Codeで開く」というように。ただし、このコマンドは、必ずしもアプリで開くというものばかりではない。

今回の場合、「.ini」拡張子のファイルを「inifile」（じゃなくてもなんでもいいけど）というファイルタイプであると定義し、「inifile」を開くイベントに対して、「Visual Studio Codeで**よしなに**開く」コマンドを定義すればよい、ということになる。

### 出鼻をくじかれる
実は、最初ポチエス（名称による関連付け専用版）でなんとかしようとしていた。

- [ポチエスの特集 \-moewe\-](http://moewe.xrea.jp/soft/pochi/)
- [「ポチエス」名称による関連付け専用版を使う \- つかさのほえほえ日記](http://d.hatena.ne.jp/tukasa1919/20090825/1251164512)

そのため、エクスプローラの右クリックメニュー→「規定のプログラムを選択」から、iniファイルにポチエス名称（略）＝EsExt_c.exeを関連付けしてあった。実際、ダブルクリックしたらEsExt_cが起動する状態だった。

が、後述のftypeコマンドや、レジストリエディタで確認してみたところ、明らかにメモ帳が関連付けされており、メモ帳を別のアプリに変更したところで、なんら動作に変化がなかった。

ポチエスでやる分には問題ないが、きもちわるい。

### 規定の関連付け情報の削除
ムッカついたので、とりあえず関係するレジストリ情報をぶっ殺した。

- [Windows レジストリ 解剖記: HKEY\_CLASSES\_ROOT \- Programming Field](http://pf-j.sakura.ne.jp/program/winreg/classes.htm)

レジストリの検索には、以下のアプリを利用した。

- [RegGlass](http://hp.vector.co.jp/authors/VA037892/regglass/regglass.html)

色々レジストリを検索してみたところ、削除すべきは以下の3点。

1. `HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.ini`
    - 「拡張子のファイルタイプへの関連付け」に該当。
    - 現在は「`inifile`」。
2. `HKEY_LOCAL_MACHINE\SOFTWARE\Classes\inifile`
    - 「ファイルタイプのコマンドへの関連付け」に該当。
    - 現在は「`%SystemRoot%\SysWow64\NOTEPAD.EXE "%1"`」
3. `HKEY_USERS\<なんかのID>\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ini\UserChoice`
    - なんかようわからんが、規定のプログラムを選択した時に登録されたやつっぽい。
    - 現在は、「`Applications/EsExt.ini "%1"`」

いちおうレジストリのエクスポートをして、上記3箇所を、（値を空にするのではなく）キーごとバサッと削除した。したらば、iniファイルの関連付けが白紙に戻った。

つまり、エクスプローラで表示されるファイルアイコンが空白になり、ダブルクリックした際にプログラムの選択を求められた。

あとは、通常通り関連付けを行えばよい。

### assoc/ftypeコマンドによる関連付け

- [Tech TIPS：Windowsのコマンドプロンプトでファイル拡張子の関連付けを変更する \- ＠IT](http://www.atmarkit.co.jp/ait/articles/0701/27/news021.html)

assoc(iation)が拡張子の関連付けを行うコマンド、f(ile)typeがファイルタイプごとの規定の動作を定義するコマンド。わかりやすい。

とりあえず、assocでファイルタイプを定義するところまではいい。

```bat
> assoc .ini=inifile
.ini=inifile
```

問題は、ftypeでどのようなコマンドを指定するかだ。

### 引数の展開

今回の場合、Visual Studio Codeには第2引数までを与えるが、ftypeで定義されるコマンドにはファイルパス1つだけしか与えられない。ファイルをダブルクリックするだけだから。

つまり、ファイルパスから親ディレクトリパスを求める必要がある。

で、2つ方法が見つかった。

1. [「%」 \- DOS コマンド一覧 \- Programming Field](http://pf-j.sakura.ne.jp/program/dos/doscmd/str_percent.htm)
2.  [Windows レジストリ 解剖記: HKEY\_CLASSES\_ROOT \- Programming Field](http://pf-j.sakura.ne.jp/program/winreg/classes.htm)

それぞれ、以下のようになる。

①チルダ（`~`）記号による引数展開を利用する場合。

```bat
ftype inifile="C:\Program Files\Microsoft VS Code\Code.exe" "%~dp1" "%1"
```

②`"%V"`および`%W`変数を利用する場合。

```bat
ftype inifile="C:\Program Files\Microsoft VS Code\Code.exe" "%W" "%V"
```

結論から言うと、**正解は②**。

①の方法でも、ファイルのフルパスから親ディレクトリのパスを得ること自体はできる。が、スペースを含むファイルパスに対して、ダブルクォートで囲む際に問題が生じる。

`%~dp1`と`%1`のどちらもスペースを含む可能性があるので、どちらもダブルクォートで囲む必要がある。囲まないと、スペースが含まれる場合にフツーにわやくちゃになる。

が、両方をダブルクォートで囲った場合、2つの引数「`"%~dp1" "%1"`」が全体として1つの引数とみなされ、その両端のダブルクォートを削除した上でVisual Studio Codeに渡される。

例えば、「`"C:\Program Files\hoge\fuga.ini"`」というファイルを開いたとすると、「`C:\Program Files\hoge\" "C:\Program Files\hoge\fuga.ini`」という、真ん中あたりにへんなダブルクォートの入った1つの引数として渡される。

これが②の方法の場合、`%V`変数は引数として渡されたファイルのフルパス、`%W`変数はその親ディレクトリのフルパスとして、**よしなに**処理される。

いや、本当言うと、色々試したら`%W`変数が**親の親ディレクトリ**になることがあったりもしたんだが、まあ概ね問題はない。なにも開けない、ということはなかった。

ちなみに、以下の方法で検証済み。開くファイルのパスは「`C:\Program Files\hoge\fuga.ini`」だとする。

1. エクスプローラで`C:\Program Files\hoge\fuga.ini`をダブルクリック  
2. または右クリックメニューから「開く(O)」を選択
3. fenrirから検索して開く（コマンドは`ShellExecute|"%P"`）
4. コマンドプロンプトで`C:\Program Files\hoge\fuga.ini`を実行

fenrirの作業ディレクトリは`C:\apps\fenrir`だし、コマンドプロンプトの作業ディレクトリは`C:\User\<ユーザー名>`だったし、そもそもVisual Studio Code自体の作業ディレクトリは`C:\Program Files\Microsoft VS Code`なのだが、大丈夫だった。

（ややこしいが、Visual Studio Codeにとって、「ワークスペース」と「作業ディレクトリ」は全く違う。あと、Visual Studio Codeの作業ディレクトリと、統合シェルの作業ディレクトリも違う。）

`%W`=W(oriking Directory)なんていう変数名がついているが、どう考えても親ディレクトリ以外の何者でもない。会社ではパパと呼んではいけませんといっても、コネ入社なのは変わらないだろう。おじいちゃん会長のコネかもしれないが。

## まとめ
関連付けするときは`%W`変数使うと親ディレクトリがとれるよ

    　　　　　　ハ_ハ
    とれるよ!　((ﾟ∀ﾟ∩
    　　　　　　 ＼　〈
    　　　　　　　 ヽヽ)