Windowsシェル統合の今 ── 右クリックメニュー・ファイル関連付け・Windows 11の変化

· 更新日: · · Windows, シェル拡張, 右クリックメニュー, ファイル関連付け, COM, Windows 11, エクスプローラー, MSIX, Windows開発

「Windows 11のPCに入れ替えたところ、御社に昔作ってもらったアプリの右クリックメニューが出てこなくなった」という相談を受けました。よく聞くと、なくなったわけではありません。ファイルを右クリックして、メニュー末尾の「その他のオプションを確認」を選ぶと、見慣れたメニューが今までどおり現れる。つまり自社アプリのメニュー項目は、ワンクッション奥に隠れてしまったのです。現場からは「クリックが1回増えた」「項目が見つからないという問い合わせが増えた」という声が上がります。

これは故障でも設定ミスでもなく、Windows 11の設計変更です。エクスプローラーの右クリックメニューは新旧の二重構造になり、新しいメニューに項目を載せるための条件が、従来とはまったく別物になりました。

一方で、その土台にあるファイル関連付けやシェル拡張の仕組みは、いまだに昔ながらのCOMとレジストリの世界です。拡張子キーがProgIDを指し、ProgIDのverbがコマンドラインを持ち、込み入った拡張はエクスプローラーに読み込まれるインプロセスCOMサーバー(DLL)として動く ── この構造は20年以上変わっていません。変わっていない土台と、Windows 11で二重化したメニューの両方を知らないと、「メニューが出ない」「隠れた」「二重に出る」の切り分けができません。

この記事では、中小企業の情シス担当者と業務アプリを預かるWindows開発者を対象に、ファイル関連付けの3段構造から、従来型シェル拡張の注意点、Windows 11の新コンテキストメニューへの対応方法、インストーラでの登録・掃除・トラブルシューティングまでを一枚につなぎます。

1. まず結論

  • 右クリックメニューとファイル関連付けの土台は、レジストリの「拡張子キー→ProgID→verb」という3段構造です。拡張子キーはProgIDを指すポインター、ProgIDが実体で、その配下のshell\<verb>\commandがコマンドラインを持ちます。1
  • HKEY_CLASSES_ROOT(HKCR)は独立したハイブではなく、HKLM\Software\ClassesとHKCU\Software\Classesの合成ビューです。全ユーザー向け登録はHKLM、ユーザー単位はHKCUに書き、HKCRは読み取り用と考えます。2
  • 既定アプリ(ダブルクリックで開くアプリ)はユーザーが選ぶ設計で、プログラムからは奪えません。ユーザーの選択はOSが保護しており、インストーラにできるのは候補として登録するところまでです。3
  • 従来型シェル拡張はエクスプローラーに読み込まれるインプロセスCOM DLLです。拡張のクラッシュや遅延はエクスプローラー全体(とシェルを使う他アプリ)へ波及し、64bit環境では64bit DLLが必須、マネージコードでの実装は非サポートです。45
  • Windows 11で右クリックメニューは二重化しました。新メニューに載るのはIExplorerCommand+パッケージ識別で登録されたコマンドだけで、従来型IContextMenu拡張は「その他のオプションを確認」(Shift+F10)の旧メニュー側に退避されます。67
  • 新メニューに独自コマンドを出す公式ルートは、IExplorerCommandを実装したネイティブDLLをMSIXマニフェスト(desktop4:FileExplorerContextMenus)で登録することです。MSIX化できないアプリはsparse package(外部ロケーション付きMSIX)で識別だけを与えられます。78
  • 単に「このアプリで開く」を実現したいだけなら、今も関連付けと静的verbで足ります。シェル拡張DLLは不要で、Microsoft自身が「要件を満たす最も単純な方法(静的verb)を選べ」と明言しています。9
  • 登録・変更後はSHChangeNotify(SHCNE_ASSOCCHANGED)で通知し、アンインストール時はProgIDを消しても拡張子キーの既定値は消さないのが公式ガイドです。掃除の設計まで含めてシェル統合です。110

一言でまとめると、関連付けとverbの世界は不変、メニューの見せ方だけがWindows 11で二重化したということです。以下、土台から順に見ていきます。

2. ファイル関連付けの仕組み ── 拡張子キー→ProgID→verbの3段構造

2.1. 3段構造を1つの例で読む

ある拡張子のファイルをダブルクリックしたとき何が起きるかは、レジストリの3段のキーで決まります。1

HKEY_CLASSES_ROOT
   .kmrpt                                  ← (1) 拡張子キー
      (Default) = KomuraSoft.Report.1      ←     ProgIDを指すだけのポインター
      OpenWithProgids
         KomuraSoft.Report.1               ←     「プログラムから開く」の候補
   KomuraSoft.Report.1                     ← (2) ProgID(関連付けの実体)
      (Default) = 小村レポート文書
      DefaultIcon
         (Default) = "C:\Program Files\KomuraSoft\Report.exe",0
      shell                                ← (3) verb(動詞)の一覧
         open
            command
               (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
  • (1) 拡張子キー(.kmrpt)は、既定値でProgIDの名前を指すだけです。ここに直接コマンドを書くのは誤りです。
  • (2) ProgID(KomuraSoft.Report.1)が関連付けの実体で、表示名・アイコン・verb一覧を持ちます。
  • (3) verbは「開く」「印刷」といった動詞で、shell\open\commandの既定値が実際に起動されるコマンドラインです。

この分離のおかげで、複数の拡張子(.kmrpt.kmrpt-fileなど)を同じProgIDに向けたり、アプリのバージョンアップでProgIDを差し替えたりできます。

2.2. HKCRは「合成ビュー」── どこに書くかで意味が変わる

上の例はHKEY_CLASSES_ROOT(HKCR)で示しましたが、HKCRは物理的な保存場所ではなく、HKLM\Software\ClassesとHKCU\Software\Classesを重ねた合成ビューです。同じキーが両方にあればHKCU側が勝ちます。2

書き込み先 意味 必要な権限
HKLM\Software\Classes 全ユーザー共通の登録 管理者権限
HKCU\Software\Classes そのユーザーだけの登録 不要
HKCRへ直接書く 既存キーの場所に依存して振り分けられる 場合による

実務では、登録は必ずHKLMかHKCUのどちらかを明示して書き、HKCRは読み取り(確認)用と割り切るのが安全です。なお、WOW64のレジストリリダイレクトとの関係も整理しておきます。拡張子キーやProgIDなどHKLM\Software\Classes直下の関連付けデータは、Windows 7以降は32bit/64bitのレジストリビュー間で共有されており、32bitインストーラが書いてもWow6432Node側へ逃げることはありません。一方、Classes\CLSIDなどCOM登録系の一部サブキーはリダイレクト対象で、後述するシェル拡張(インプロセスCOM)の登録では32bit/64bitの書き分けが効いてきます。詳しくは「レジストリの32bit/64bitリダイレクトと仮想化の落とし穴」で扱っています。

2.3. アプリ側の登録 ── App Paths・Applications・RegisteredApplications

ファイル側(拡張子とProgID)と対になる、アプリ側の登録も3種類あります。11

  • App Paths(HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths): 実行ファイル名だけでShellExecuteExから起動できるようにする登録。PATH環境変数を汚さずに済むため、Microsoftはこちらを推奨しています。
  • Applications(HKCR\Applications\<アプリ.exe>): 「プログラムから開く」で任意のファイルを渡されたときの既定の開き方と、アプリの表示名(FriendlyAppName)を定義します。
  • RegisteredApplications + Capabilities: アプリが扱える拡張子・MIMEタイプを宣言し、Windowsの既定アプリ設定画面に候補として並ぶための登録です。

「うちのアプリが既定アプリの一覧に出てこない」という相談の大半は、ProgIDは登録したのにこのCapabilities登録を省略しているケースです。

2.4. 既定アプリはユーザーのもの ── UserChoiceの保護

拡張子キーの既定値にProgIDを書いても、それだけで既定アプリになるとは限りません。ユーザーが「プログラムから開く」等で明示的に選んだ結果は、HKCU\...\Explorer\FileExts\<拡張子>\UserChoiceに保持され、関連付けの解決ではこちらが優先されます。

そして重要なのは、Windowsは既定アプリのプログラムによる変更をサポートしていないことです。既定アプリの設定はシステム設定UIを通じてユーザーが行う設計で、UserChoiceのデータは難読化され、フィルタードライバー(UCPD.sys)がアプリからの書き込みをブロックします。管理環境ではグループポリシー/MDMポリシーが公式の手段です。3

かつてSetUserFTAのような「ハッシュを模倣して書き換える」ツールが使われてきたのは、この保護の裏返しです。自社アプリのインストーラに組み込むべきは既定の奪取ではなく、(a)ProgIDとverbの正しい登録、(b)OpenWithProgIdsへの追加、(c)必要なら設定画面への誘導の3点です。

3. 「開く」以外のverb ── print・edit・runas・カスタムverb

verbはopenだけではありません。OSが意味を知っている標準verbにはopenのほかeditprintplaypreviewなどがあり、標準verbはOSのロケールに応じた表示名が自動で付きます。ダブルクリック時に使われる既定verbは、shellキーの既定値→レジストリ上の最初のverb→openopenwithの順で決まります。12

独自の動詞を足したいときはカスタムverbを登録します。

KomuraSoft.Report.1
   shell
      open
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
      print
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /print "%1"
      verify                          ← カスタムverb
         (Default) = 帳票を検証(&V)   ← メニューの表示名
         command
            (Default) = "C:\Program Files\KomuraSoft\Report.exe" /verify "%1"

知っておくと役立つ小ネタを3つ挙げます。

  • runasというverbを登録すると「管理者として実行」に相当する昇格起動を定義でき、ShellExecute系APIからrunasを指定した起動にも使われます。
  • verbキーにExtendedという空の値を置くと、Shiftキーを押しながら右クリックしたときだけ表示される拡張verbになります。めったに使わない危険な操作を隠すのに便利です。12
  • 古いアプリの関連付けにはDDE(ddeexecキー)で既存プロセスへ文書を送り込む構成が残っていることがありますが、DDEによるverb起動はすでに非推奨(Deprecated)の遺産です。新規に書く理由はありません。12

もう1つ、事故が多いのがコマンドラインの引用符です。コマンド文字列の要素にスペースが入り得るなら、必ず引用符で囲む必要があります。C:\Program Files\...のようなEXEパスはもちろん、%1(選択ファイルのパス)は常に"%1"と書くべきです。ユーザーのファイルパスにスペースが含まれないことは保証できないからです。引用符のないMy Program.exeは「MyProgram.exeという引数付きで起動する」と解釈されます。13

ここまでのレジストリだけの仕組み(静的verb)は、DLLを1つも書かずに実現でき、エクスプローラーを不安定にするリスクもありません。Microsoft自身が「シェル拡張を書く前に、要件を満たす最も単純な静的verbで済まないか検討せよ」と繰り返し述べています。9

4. 従来型シェル拡張 ── エクスプローラーの中で動くDLL

4.1. シェル拡張の種類

静的verbでは足りない「選択内容に応じてメニューを動的に変える」「アイコンやプロパティ画面を差し替える」といった要件には、シェル拡張ハンドラーを使います。代表的な種類は次のとおりです。4

ハンドラー 主なインターフェース できること
コンテキストメニューハンドラー IContextMenu + IShellExtInit メニュー項目を動的に追加・制御
アイコンハンドラー / アイコンオーバーレイ IExtractIcon / IShellIconOverlayIdentifier ファイルごとのアイコン・重ね表示
プロパティシートハンドラー IShellPropSheetExt プロパティ画面へのタブ追加
サムネイル / インフォチップ IThumbnailProvider / IQueryInfo 縮小表示・ホバー時の説明
ドラッグ&ドロップ / コピーフックハンドラー IDropTarget / ICopyHook ドロップ時・コピー移動時の介入

これらはいずれもCOMのクラスとして実装し、CLSIDをレジストリに登録します。COM自体の考え方は「COM / ActiveX / OCX とは何か」を参照してください。

4.2. インプロセスCOMサーバーであることの意味

従来型シェル拡張の本質は、エクスプローラー(や共通ファイルダイアログを開いた任意のアプリ)のプロセス内に読み込まれるインプロセスCOMサーバー(DLL)だということです。ここからすべての注意点が導かれます。4

  • 拡張がクラッシュすれば、エクスプローラーが巻き添えでクラッシュします。ハングすれば右クリックが数秒固まります。しかも被害はエクスプローラーに限らず、ファイルを開くダイアログを表示したすべてのアプリに及びます。
  • メニュー構築はUIスレッドで行われるため、ネットワークアクセスやファイルI/Oのような遅い処理をメニュー表示時に行ってはいけません
  • スレッディングモデルはApartmentで登録するのが原則です。

「特定のフォルダーを開くとエクスプローラーが固まる」「右クリックに5秒かかる」という相談を調査すると、原因が自社アプリではなくサードパーティ製のシェル拡張だった、ということは珍しくありません。切り分け方法は8章で扱います。

4.3. ビット数の一致 ── 64bit環境では64bit DLLが必須

インプロセスDLLは、読み込むプロセスとビット数が一致しなければなりません。64bit Windowsのエクスプローラーは64bitプロセスなので、32bitでしかビルドしていないシェル拡張DLLは、そもそも読み込まれず、メニューに一切現れません。エラーも出ないため「登録したのに出ない」原因の定番です。32bitアプリ本体と64bitシェル拡張DLLの組み合わせは正当な構成ですが、COM登録がbitnessごとに分かれる(Wow6432Node)ことに注意が必要です。なお、verbのcommandから起動するのは別プロセスのEXEなので、この制約を受けません(32bit EXEのままで問題ありません)。

4.4. マネージコードで書いてはいけない理由

「C#でシェル拡張を書けないか」という質問をよく受けますが、Microsoftはインプロセスのシェル拡張をマネージコード(.NET)で書くことを非推奨とし、サポート対象外と明言しています5

理由は、拡張が任意のプロセスに読み込まれるという性質にあります。CLRのバージョン競合(特に.NET Framework 4未満)、ロック待ちでCLRがメッセージループへ再入する問題、ガベージコレクションによるオブジェクト寿命の非決定性がCOMの参照カウント契約と衝突する問題など、宿主アプリを不安定にする要因が構造的に存在するためです。.NET Framework 4以降やモダンな.NETで緩和された項目もありますが、公式の立場は変わっていません。

実務の指針はシンプルです。インプロセス拡張はネイティブC++で書く。マネージコードを使いたければ、verbのcommandで起動する通常のEXEにするか、別プロセスで動くアウトプロセス拡張(プレビューハンドラー等)にする。5

5. Windows 11の新コンテキストメニュー ── メニューの二重化

5.1. 何が起きたか

Windows 11は、エクスプローラーの右クリックメニューを刷新しました。切り取り・コピー等が上部のアイコン列になり、「開く」「プログラムから開く」がまとめて上部に配置され、アプリが追加するコマンドはシェル標準コマンドの下にグループ化されます。1つのアプリが複数のコマンドを追加する場合は、アプリ名付きのフライアウト(サブメニュー)に集約されます。6

そして肝心の点がこれです。従来型のIContextMenuベースのシェル拡張は削除されたのではなく、「その他のオプションを確認」(Shift+F10)で開く、Windows 10のメニューをそのまま読み込んだ旧メニュー側に退避されました。6 冒頭の相談の「メニューが隠れた」の正体はこの二重化です。

5.2. 新メニューに載せる公式ルート ── IExplorerCommand+マニフェスト登録

新メニューに独自コマンドを出す方法は1つです。IExplorerCommandインターフェースを実装したネイティブDLLを用意し、MSIXパッケージのマニフェストでCOMサーバーとコンテキストメニュー拡張を宣言することです。7

<!-- パッケージマニフェスト(抜粋) -->
<com:Extension Category="windows.comServer">
  <com:ComServer>
    <com:SurrogateServer DisplayName="Komura commands">
      <com:Class Id="01234567-89AB-CDEF-0123-456789ABCDEF"
                 Path="KomuraCommand.dll" ThreadingModel="STA" />
    </com:SurrogateServer>
  </com:ComServer>
</com:Extension>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
  <desktop4:FileExplorerContextMenus>
    <desktop5:ItemType Type=".kmrpt">
      <desktop5:Verb Id="VerifyReport"
                     Clsid="01234567-89AB-CDEF-0123-456789ABCDEF" />
    </desktop5:ItemType>
  </desktop4:FileExplorerContextMenus>
</desktop4:Extension>

ItemTypeTypeには特定の拡張子のほか、*(すべてのファイル)、Directory(フォルダー)、Directory\Background(フォルダーの背景)を指定できます。DLLはエクスプローラーのアーキテクチャ(64bit/ARM64)に一致させます。7

IExplorerCommand自体はWindows 7時代からあるインターフェースで、タイトル(GetTitle)、アイコン(GetIcon)、有効/無効/非表示の状態(GetState)、実行(Invoke)を実装します。メソッドはUIスレッドから呼ばれるため、ネットワーク資源へのアクセスは禁止で、メニュー構築系のメソッドは高速に返す必要があります。重い処理はInvoke後に行います。147

5.3. 非パッケージアプリの選択肢 ── sparse packageで識別だけを得る

「うちのアプリはMSIじゃないと配れない。MSIX化は無理」という場合の逃げ道が、sparse package(外部ロケーション付きMSIX)です。アプリ本体を含まない、マニフェストだけの小さなMSIXを署名して作り、既存インストーラの最後に登録します。これでアプリはパッケージ識別を獲得し、上記のマニフェスト登録(=新メニューへの表示)が可能になります。Windows 10バージョン2004以降で利用でき、パッケージは対象マシンで信頼される証明書での署名が必要です。8

インストーラを差し替えずに済むのが最大の利点で、既存のMSI/EXEインストーラ資産を持つアプリの現実解です。MSIXへの全面移行との比較は「Windowsアプリ配布方式の選び方」も参照してください。

5.4. 関連付けのverbは新メニューでどう見えるか

誤解されやすい点ですが、2〜3章の関連付け(ProgIDとverb)は、新メニューでも生きています。ダブルクリックの既定verb、「開く」、「プログラムから開く」の候補は関連付けから解決され、新メニューの上部に表示されます。つまり「このアプリで開けるようにしたい」だけなら、Windows 11でも何も追加対応は要りません。一方、関連付けは汎用のメニュー拡張ではないため、任意のカスタムコマンドを新メニューの第一階層に出したければIExplorerCommand+識別が必要、という役割分担です。7

6. 実務の判断表 ── 3つの選択肢のどれを取るか

ここまでを、実務の3択に整理します。

実現したいこと 推奨する手段 Windows 11での見え方 必要な作業・コスト
(a) ダブルクリックや「開く」で自社アプリを起動したい 関連付け+静的verb(レジストリ登録のみ) 新メニューの「開く」「プログラムから開く」に統合表示 インストーラのレジストリ登録のみ。DLL不要・署名の追加要件なし
(b) 選択したファイル/フォルダーへの独自コマンドを新メニューに出したい IExplorerCommand実装+MSIXマニフェスト登録。非パッケージアプリはsparse packageで識別を付与 新メニューの第一階層(複数コマンドはアプリ名フライアウトに集約) ネイティブC++ DLL+パッケージ識別+コード署名
(c) 既存の従来型IContextMenu拡張を使い続ける 当面そのまま維持(新規開発には選ばない) 「その他のオプションを確認」(Shift+F10)の旧メニュー側のみ 64bitビルドとCOM登録の維持。将来は(b)への移行を計画

判断のポイントは2つです。第一に、(a)で済む要件に(b)や(c)を持ち出さないこと。シェル拡張は書いた瞬間からエクスプローラーの安定性に対する責任を負います。第二に、(c)は「壊れてはいない」だけで、ユーザー体験としては一段劣る位置に置かれ続けるということです。日常操作で使う頻度が高いコマンドほど、(b)への移行の投資対効果が大きくなります。

7. 展開・登録の実務 ── インストーラ・sparse package・掃除

7.1. HKLMかHKCUか

インストーラの形態と合わせます。全ユーザー向け(Program Filesへ配置、管理者権限)ならHKLM\Software\Classes、ユーザー単位インストール(権限昇格なし)ならHKCU\Software\Classesです。混ぜると「Aさんでは開けるがBさんでは開けない」型の問い合わせを生みます。CLSID登録を伴うシェル拡張では、レジストリ登録そのものを不要にするReg-Free COMという選択肢もアプリ内COM利用では有効ですが、エクスプローラーが読み込むシェル拡張には適用できないため、正攻法の登録が必要です(「Reg-Free COMとは」)。

7.2. 変更したら通知する ── SHChangeNotify

関連付けを登録・変更・削除したら、SHChangeNotifyでSHCNE_ASSOCCHANGEDイベントを通知します。これを省くと、変更が再起動までエクスプローラーに認識されないことがあります。110

// インストーラのカスタムアクション等で、関連付け変更の後に1回呼ぶ
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);

7.3. sparse packageの登録と解除

sparse packageの登録・解除はインストーラの仕事です。登録はファイル配置の後、解除はファイル削除の前に行います。8

# インストール時: ファイル配置後に、インストール先を外部ロケーションとして登録
Add-AppxPackage -Path "C:\Program Files\KomuraSoft\KomuraReport.identity.msix" `
                -ExternalLocation "C:\Program Files\KomuraSoft"

# アンインストール時: ファイルを消す前にパッケージ登録を解除
Remove-AppxPackage <パッケージのフルネーム>

注意点として、Add-AppxPackage実行したユーザーに対して登録されます。per-machineのMSIからカスタムアクションで呼ぶ場合、LocalSystemで実行するとインストールしたユーザーに識別が付与されないため、ユーザー偽装(impersonate)で実行する構成にします。ただし、偽装で登録されるのもそのインストールを実行したユーザーだけです。1台のPCを複数ユーザーで使う環境では、他のユーザーや後から作られたユーザーにはパッケージ識別が無く、新メニューにコマンドが出ません。全ユーザーで使わせるには、アプリの初回起動時に自分のパッケージ登録を確認して未登録なら登録する(ユーザーごとの登録)といった仕組みを用意し、アンインストール時も登録済みの各ユーザーからの解除を計画に含めます。また、マニフェスト登録の反映にはエクスプローラーの再起動(またはサインアウト)が必要な場合があります。7

7.4. アンインストール時の掃除 ── 消すものと残すもの

アンインストール時の掃除には、公式ガイドに明確な指針があります。1

  • 消すもの: 自社のProgIDキー一式、Capabilities/RegisteredApplications登録、シェル拡張のCLSID登録、sparse package(Remove-AppxPackage)。
  • 残すもの: 拡張子キー(.kmrpt)の既定値。自社ProgIDを指したままでも消さないのが公式の推奨です。インストール後に別アプリが既定を取っていないかの判定は困難であり、Windowsは既定値のProgIDが未登録なら単に無視するため、残しても実害がないからです。
  • 掃除の最後にもSHChangeNotify(SHCNE_ASSOCCHANGED)を呼びます。

「アンインストールしたのにメニューに残骸が出る」問題の多くは、この掃除設計の漏れです。

8. トラブルシューティング ── 出ない・二重・重い

8.1. メニューに出ない

順に切り分けます。

  1. どちらのメニューを見ているか: 旧方式の登録はShift+F10の旧メニュー側にしか出ません。まず両方を確認します。
  2. ビット数: 32bitのみのシェル拡張DLLは64bitエクスプローラーに読み込まれません(4.3節)。
  3. 登録先: HKLM/HKCU、Wow6432Nodeの取り違え。reg queryで実際のキーを確認します。
  4. パッケージ登録: 新メニュー向けならGet-AppxPackageで登録の有無、署名証明書の信頼、-ExternalLocationのパスを確認し、エクスプローラーを再起動します。7
  5. 通知漏れ: SHChangeNotify忘れなら、エクスプローラーの再起動で反映されるかで判別できます。

8.2. 二重に出る・消えない

典型的な原因は、旧方式のレジストリ登録とマニフェスト登録の併存、アンインストール掃除の漏れ(7.4節)、旧バージョンのProgIDの残骸です。旧メニューにだけ二重に出るなら残骸系、新旧両方に出るなら併存系、とあたりを付けられます。

8.3. エクスプローラーが重い・落ちる

右クリックが遅い、特定フォルダーでクラッシュする場合は、まずインストール済みシェル拡張の棚卸しです。NirSoftのShellExViewのようなツールで非Microsoft製の拡張を一覧し、疑わしいものを一時無効化して二分探索すると、原因DLLを特定できます。クラッシュならイベントビューアーの「障害が発生しているモジュール」も手がかりになります。自社拡張が原因だった場合は、メニュー構築パスでの同期I/O・ネットワークアクセスを疑ってください(4.2節・5.2節)。

8.4. 検証はWindows Sandboxが便利

シェル統合の検証は「クリーンな環境でインストール→動作→アンインストール→残骸ゼロ」の確認が基本です。ここで便利なのがWindows Sandbox(Pro/Enterprise/Education)で、起動のたびにまっさらな使い捨てWindowsが数秒で立ち上がるため、インストーラの登録・掃除のテストを何度でも回せます。閉じれば全部消えるので、レジストリの残骸調査にも向きます。15

9. まとめ

  • ファイル関連付けは「拡張子キー→ProgID→verb」の3段構造で、HKCRはHKLM/HKCUのClassesの合成ビューです。書き込み先は明示し、%1は必ず引用符で囲みます。
  • 既定アプリはユーザーが選ぶ設計で、プログラムからは変更できません。インストーラの仕事は候補として正しく登録するところまでです。
  • 従来型シェル拡張はエクスプローラーに読み込まれるインプロセスCOM DLLです。クラッシュ・遅延は全体へ波及し、64bit必須、マネージコードは非サポート、実装はネイティブC++が原則です。
  • Windows 11で右クリックメニューは二重化しました。新メニューに独自コマンドを出すにはIExplorerCommand+MSIXマニフェストが必要で、従来型IContextMenuは「その他のオプションを確認」側に退避されます。
  • MSIX化できないアプリはsparse package(外部ロケーション付きMSIX)で識別を得るのが現実解です。
  • 「このアプリで開く」だけなら関連付けと静的verbで今も足ります。最も単純な手段から検討するのが公式の指針でもあります。
  • 登録・変更・削除の後はSHChangeNotifyで通知し、アンインストール時はProgIDを消しても拡張子キーの既定値は残します。検証にはWindows Sandboxが便利です。

Windows 11のPC入れ替えで「メニューが隠れた」と気付いたら、まず6章の判断表で(a)(b)(c)のどれに当たるかを確認してください。対応の規模感がその場で見積もれるはずです。

関連記事

関連する相談領域

合同会社小村ソフトでは、業務アプリのファイル関連付け・右クリックメニュー・シェル拡張の設計と実装、Windows 11の新コンテキストメニューへの対応(IExplorerCommand化、sparse packageの導入)、既存インストーラの登録・掃除の見直し、エクスプローラーが重い・落ちる問題の原因調査を扱っています。「その他のオプションを確認に隠れてしまったメニューをどうするか」の方針決めからで構いません。

参考リンク

  1. Microsoft Learn, File Types. 拡張子キーがProgIDを指す構造、OpenWithProgIds、HKLM/HKCU\Software\Classesへの登録の使い分け、関連付け変更後にSHChangeNotify(SHCNE_ASSOCCHANGED)を呼ぶべきこと、アンインストール時にProgIDは削除しつつ拡張子キーの既定値は残すべきことについて。  2 3 4 5

  2. Microsoft Learn, HKEY_CLASSES_ROOT Key. HKEY_CLASSES_ROOTがHKLM\Software\ClassesとHKCU\Software\Classesを合成したビューであること、ユーザー側の定義がマシン側より優先されること、書き込み時の振り分け規則について。  2

  3. Microsoft Learn, Windows app defaults platform. 既定アプリの変更がシステム設定UIを通じてのみ行える設計であること、ユーザー設定データが難読化されフィルタードライバー(UCPD.sys)で書き込み保護されること、レジストリベースの変更が非サポートであること、管理環境ではグループポリシー/MDMポリシーを使うことについて。  2

  4. Microsoft Learn, Working with Shell Extensions. シェル拡張ハンドラーの種類、拡張がエクスプローラー(およびシェルをホストするプロセス)に読み込まれるインプロセスCOM DLLであり、クラッシュやハングがExplorer全体に波及すること、ThreadingModel=Apartmentでの登録、シェル拡張より単純な代替手段を先に検討すべきことについて。  2 3

  5. Microsoft Learn, Guidance for Implementing In-Process Extensions. マネージコードによるインプロセスのシェル拡張実装をMicrosoftが推奨せずサポート対象外とすること、CLRのバージョン競合・再入・オブジェクト寿命の非決定性などの理由、アウトプロセス拡張(プレビューハンドラーやshell\verb\commandからの起動)ではマネージコードが許容されることについて。  2 3

  6. Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. Windows 11の新コンテキストメニューの設計、IExplorerCommand+アプリ識別による拡張、「開く」「プログラムから開く」の上部配置、複数コマンドのアプリ名付きフライアウトへの集約、従来のIContextMenu拡張が「その他のオプションを確認」(Shift+F10)のWindows 10メニューとして読み込まれることについて。  2 3

  7. Microsoft Learn, Add a File Explorer context menu command to a packaged desktop app. Windows 11の新コンテキストメニューへの登録がIExplorerCommand実装+windows.comServer+desktop4:FileExplorerContextMenusのマニフェスト宣言で行われること、ItemTypeに*・Directory・Directory\Backgroundを指定できること、DLLのアーキテクチャ一致、メニュー構築メソッドを高速に保つこと、sparse packageによる非パッケージアプリの対応、登録反映にエクスプローラー再起動が必要な場合があること、ファイル関連付けは汎用のメニュー拡張ではないことについて。  2 3 4 5 6 7 8

  8. Microsoft Learn, Grant package identity by packaging with external location. 既存インストーラを変えずに外部ロケーション付きパッケージ(sparse package)を登録してパッケージ識別を得られること、Windows 10バージョン2004以降で利用可能なこと、識別が必須のWindows機能(コンテキストメニュー登録・通知等)が使えるようになることについて。  2 3

  9. Microsoft Learn, Choosing a Static or Dynamic Shortcut Menu Method. 要件を満たす最も単純な静的verb方式を選ぶべきこと、IContextMenuが最も強力だが最も複雑で非推奨側に分類されること、IExplorerCommand/IExplorerCommandStateが推奨される方式であることについて。  2

  10. Microsoft Learn, SHChangeNotify function. ファイル関連付けの変更をシステムへ通知するSHCNE_ASSOCCHANGEDイベントの発行方法と、シェルへ変更を認識させるための使い方について。  2

  11. Microsoft Learn, Application Registration. App Pathsサブキーによる実行ファイルの登録が推奨されること、Applicationsサブキーの役割、SystemFileAssociationsによるverb登録、既定アプリ変更時のProgIDと関連情報の優先順位について。 

  12. Microsoft Learn, Creating Shortcut Menu Handlers. 静的verbの登録方法、既定verbの決定順序(既定値→最初のverb→Open→Open With)、標準verbの表示名がOSにより供給されること、Extendedによる拡張verb、DDEコマンドとの関連付けが非推奨(Deprecated)であること、64bit環境でのWOW64リダイレクトの注意について。  2 3

  13. Microsoft Learn, Verbs and File Associations. verbがShellExecuteExでも使われる動詞であること、コマンド文字列でスペースを含み得る要素は引用符で囲む必要があり、”%1”を常に引用符付きで書くべきこと、HKCR\Applications配下への既定手続きの登録について。 

  14. Microsoft Learn, IExplorerCommand interface. GetTitle・GetIcon・GetState・Invoke・EnumSubCommands等のメソッド構成、メソッドがUIスレッドで呼ばれるためネットワーク資源と通信してはならないこと、Windows Vista以降で利用可能であることについて。 

  15. Microsoft Learn, Windows Sandbox. 使い捨ての隔離されたWindows環境を数秒で起動でき、閉じるとすべての変更が破棄されること、ソフトウェアのテストやインストーラの検証に適すること、Pro/Enterprise/Educationで利用できることについて。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

Windows 11で自社アプリの右クリックメニューが「その他のオプションを確認」の中にしか出ないのはなぜですか?
Windows 11ではエクスプローラーの右クリックメニューが新旧2階層に分かれたためです。新しいメニューに項目を出せるのは、IExplorerCommandインターフェースを実装し、MSIXパッケージのマニフェストで登録した(=パッケージ識別を持つ)コマンドだけで、従来型のIContextMenuベースのシェル拡張は「その他のオプションを確認」(Shift+F10)で開く旧メニュー側に退避されました。拡張自体が壊れたわけではないので当面はそのまま動きますが、新メニューに出したい場合はIExplorerCommandへの移行と、MSIX化またはsparse packageによる識別の付与が必要です。
インストーラから自社アプリをファイルの既定アプリ(ダブルクリックで開くアプリ)に設定できますか?
できません。既定アプリの選択はユーザーが行う設計になっており、Windowsはシステム設定UI以外からの既定アプリ変更をサポートしていません。ユーザーごとの選択を保持するUserChoice情報は難読化され、フィルタードライバー(UCPD.sys)によるアプリからの書き込み保護も行われています。インストーラにできるのは、ProgIDとverbを登録し、OpenWithProgIdsに自分を追加して「プログラムから開く」の候補に載り、既定アプリ設定画面へユーザーを誘導するところまでです。既定を奪うのではなく、選ばれる準備を整えるのが正しい実装です。
シェル拡張をC#などのマネージコードで書いてもよいですか?
インプロセスで読み込まれるシェル拡張(コンテキストメニューハンドラーやアイコンハンドラーなど)をマネージコードで書くことは、Microsoftが非推奨かつサポート外と明言しています。拡張はエクスプローラーや共通ファイルダイアログを開く任意のアプリのプロセスに読み込まれるため、CLRのバージョン競合や再入、オブジェクト寿命の非決定性が宿主アプリを不安定にするからです。実装はネイティブC++が原則です。一方、verbのcommandで起動される通常のEXEや、別プロセスで動くプレビューハンドラーのようなアウトプロセス拡張であれば、マネージコードでも問題ありません。
sparse package(外部ロケーション付きMSIX)とは何ですか?
アプリ本体のファイルを含まず、マニフェスト(識別情報)だけを持つ小さなMSIXパッケージです。既存のインストーラ(MSI、Inno Setupなど)で普通にインストールしたアプリに対して、Add-AppxPackageの-ExternalLocationでインストール先フォルダーを指すよう登録すると、そのアプリはパッケージ識別を獲得し、Windows 11の新コンテキストメニューへの登録やトースト通知など、識別が必須の機能を使えるようになります。Windows 10バージョン2004以降で利用でき、パッケージには対象マシンで信頼されるコード署名が必要です。配布方式をMSIXへ全面移行せずに新メニュー対応したい場合の現実的な選択肢です。
右クリックメニューの項目が二重に出る、または消えないときはどうすればよいですか?
まず原因の切り分けとして、新メニューと旧メニュー(その他のオプションを確認)のどちらに出ているかを確認します。二重表示は、旧方式のレジストリ登録とMSIXマニフェスト登録が併存しているか、アンインストール時にProgIDや拡張機能のCLSID登録が掃除されずに残っているのが典型です。関連付けを変更した後はSHChangeNotify(SHCNE_ASSOCCHANGED)による通知漏れ、パッケージ登録直後はエクスプローラーの再起動漏れも疑ってください。それでも解決しない場合は、ShellExViewで非Microsoft製の拡張を一時無効化して二分探索すると、原因のDLLを特定できます。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る