コンテキストメニューを作る

# はじめに
Windowsでディレクトリやファイルを選択、又は背景の位置で右クリックすると"プロパティ"等のメニューが現れます。コンテキストメニューと呼ばれます。

Windows 10まで又はWindows 11の"その他"をクリックした時に現れるメニューは、独自のメニューを追加する事ができます。

# レジストリ エディタ
**スタート** → **Windows 管理ツール** → **レジストリ エディタ** から起動できます。
(起動には管理者権限が必要)

わかりやすい使い方 (Think IT様):

https://thinkit.co.jp/story/2015/06/30/6147

:::note warn
この記事ではレジストリの各キーを操作するための権限について等、レジストリの仕組みについては詳細に触れません。
レジストリの変更はWindowsの動作に影響を与えるため、取り扱いにはご注意下さい。
:::

# 右クリックの対象
設定できる対象のファイルシステムには、次のようなものがあります。

- ファイル
- ディレクトリ
- ディレクトリの背景
- ドライブ
- フォルダ

:::note
ディレクトリとフォルダは何が違うの？と思いますが、  
フォルダに設定するとディレクトリ、ディレクトリの背景、ドライブ及びライブラリ内の項目にコンテキスト メニューが設定されます。
但しライブラリ以下からコマンドにパスを渡すにはここで紹介する方法ではできません。
:::

# 設定する対象
対象の利用者は、次のようなものがあります。

- システム (同じPCを利用する全てのユーザー)
- ユーザー (現在ログインしているユーザーのみ)

# 設定方法

## 設定範囲
まずはシステムに設定するかユーザーに設定するかを考えます。
それぞれの場合によって設定するレジストリのパスが異なります。

- ユーザーに設定する場合(影響が狭いため推奨)  
  `HKEY_CURRENT_USER\SOFTWARE\Classes\`**\*** / **拡張子** / **Directory** / **Directory\Background** / **Drive** / **Folder**

- システムに設定する場合  
  `HKEY_CLASSES_ROOT\`**\*** / **拡張子** / **Directory** / **Directory\Background** / **Drive** / **Folder**

:::note warn
`HKEY_CURRENT_USER\SOFTWARE\Classes`に設定したキーは`HKEY_CLASSES_ROOT`からも見えるようになります。
`HKEY_CURRENT_USER\SOFTWARE\Classes`に設定したキーを`HKEY_CLASSES_ROOT`から変更/削除すると`HKEY_CURRENT_USER\SOFTWARE\Classes`の中身も変更/削除されます。
:::

## 設定箇所

- 全てのファイル  
  `*`
- ファイル
  拡張子 (`.txt`等)
- ディレクトリ  
  `Directory`
- ディレクトリの背景
  `Directory\Background`
- ドライブ  
  `Drive`
- フォルダ  
  `Folder`

## 設定するキーと値

- **拡張子\shell\名前\\**
    - **(既定)**: メニューに表示される名前
    - **Icon**: アイコン ファイルのパス
        - **command\\**
            - **(既定)**: コマンド

例えば全てのファイルに対し "メモ帳で開く" 設定をユーザーに設定する場合は次のようにします。

:::note warn
メモ帳で大きなファイルを開こうとすると動作が不安定になります。ご注意下さい。
:::

|キー|名前|種類|データ|
|---|---|---|---|
|**HKEY_CURRENT_USER\Classes\\*\shell\mynotepad**|(既定)|REG_SZ|メモ帳で開く|
|(同上)|`Icon`|REG_EXPAND_SZ|`%WINDIR%\System32\notepad.exe`|
|**HKEY_CURRENT_USER\Classes\\*\shell\mynotepad\command**|(既定)|REG_EXPAND_SZ|`%WINDIR%\System32\notepad.exe "%V"`|

又は

|キー|名前|種類|データ|
|---|---|---|---|
|**HKEY_CURRENT_USER\Classes\\*\shell\mynotepad**|(既定)|REG_SZ|メモ帳で開く|
|(同上)|`Icon`|REG_SZ|`C:\Windows\System32\notepad.exe`|
|**HKEY_CURRENT_USER\Classes\\*\shell\mynotepad\command**|(既定)|REG_SZ|`C:\Windows\System32\notepad.exe "%V"`|

**command** の **(既定)** に設定する`"%V"`は notepad.exe に渡すコマンドライン引数で、ここに右クリックした時のファイルが入ることによりメモ帳(notepad.exe)がそのファイルを開いてくれます。
コマンドライン引数をどのように扱うかはアプリケーションにより様々です。

:::note info
`%V`ではなく`%1`も使用できますが、ディレクトリの背景等では`%1`は使えません。
:::

`"`で囲んでいるのは、例えば`C:\My Data.txt`のような完全パスに空白を含むファイルから呼び出した場合  
`C:\My`と`Data.txt`という2つのコマンドライン引数に別れてしまうのを防ぐためです。

**REG_EXPAND_SZ** は、データ内に含まれる環境変数を展開する文字列です。
`%WINDIR%`は環境変数で、通常はWindows環境では`C:\Windows`に設定されています。
**REG_SZ** (通常の文字列)では環境変数が展開されないため、正しく動作しません。
レジストリ エディタでキーを作成すると、(既定)の種類が自動的に **REG_SZ** になってしまい、**REG_EXPAND_SZ** に変更できません。

そのような場合は、コマンド プロンプトから作成することができます。

```
REG ADD HKCU\SOFTWARE\Classes\*\shell\mynotepad /t REG_SZ /ve /d メモ帳で開く
REG ADD HKCU\SOFTWARE\Classes\*\shell\mynotepad /t REG_EXPAND_SZ /v Icon /d ^%WINDIR^%\System32\notepad.exe
SET MYVAR=^%WINDIR^%
REG ADD HKCU\SOFTWARE\Classes\*\shell\mynotepad\command /t REG_EXPAND_SZ /ve /d "%MYVAR%\System32\notepad.exe \"%1\""
```

|REG_ADDで使うプレースホルダ|レジストリ上のルート|
|---|---|
|HKLM|HKEY_LOCAL_MACHINE|
|HKCU|HKEY_CURRENT_USER|
|HKCR|HKEY_CLASSES_ROOT|
|HKU|HKEY_USERS|
|HKCC|HKEY_CURRENT_CONFIG|

:::note
`^%`と記述しているのは、コマンド中で環境変数を展開しないように`%`をエスケープしている(`%`を値として認識させる)ためです。
もしも
`REG ADD HKCU\SOFTWARE\Classes\*\shell\mynotepad /t REG_EXPAND_SZ /v Icon /d %WINDIR%\notepad.exe`
と打ってしまうとコマンド中で環境変数が展開され
`REG ADD HKCU\SOFTWARE\Classes\*\shell\mynotepad /t REG_EXPAND_SZ /v Icon /d C:\Windows\notepad.exe`
と入力したのと同じになってしまいます。

又、`\"`も`"`をエスケープするための記法です。
:::

:::note
`"`で囲んだ内側で`%WINDIR%`と記述すると`C:\Windows`に展開されてしまい、  
`^%WINDIR^%`と記述するとエスケープを受け付けず`^%WINDIR^%`がそのままレジストリに書き込まれてしまうため  
苦肉の策で`SET MYVAR`で変数`MYVAR`に`%WINDIR%`を代入し、  
`REG ADD`の`/d "%MYVAR%\System32\notepad.exe`の部分で`/d "%WINDIR%\System32\notepad.exe`に展開させています。
:::

:::note warn
キーによっては、管理者権限でコマンド プロンプトを実行していないと読み書きができない場合もあります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116331/237d1edd-5b19-75a2-acb3-6aee68ad653e.png)

# 元に戻すには
レジストリ エディタで **shell の後(名前)** のキーを全て削除します(上記の例で言えば **mynotepad** の部分)。
コマンド プロンプトからは

```
REG DELETE HKCU\SOFTWARE\Classes\*\shell\mynotepad
```

# ここで紹介した方法ではできないこと

- 動的なメニュー
    - WinMergeの拡張メニュー(1度目の選択で"Select Left"、2度目の選択で"Compare")のようなもの。  
    [ハンドラーの登録が必要。](https://learn.microsoft.com/ja-jp/windows/win32/shell/context-menu-handlers)
- Windows 11で採用されたコンテキスト メニュー
    - [IExplorerCommandを実装したパッケージ化アプリの開発が必要。](https://learn.microsoft.com/ja-jp/windows/apps/get-started/make-apps-great-for-windows#context-menus)
- 複数を選択して1つのアプリケーションで処理をする
    - .zipファイルへの圧縮等。  
    この方法では選択したファイルの分だけアプリケーションが起動してしまうので、アプリケーション側でテクニックが必要([ShellExtension等](https://stackoverflow.com/questions/27088510/implement-explorer-contextmenu-and-pass-multiple-files-to-one-program-instance))
- 管理者権限で開かせる
    - 別途工夫が必要。
    `shell\名前`に **HasLUAShield** という空の値を作成することで盾付きのアイコンを表示させる事は可能。

# さいごに
コマンド プロンプトではなくPowerShellでもレジストリの操作はできます。

https://learn.microsoft.com/ja-jp/powershell/scripting/samples/working-with-registry-entries