更新履歴(7件・最終更新 2026年09月07日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 技術的な主張と条件はそのままに、関連付けと新旧の右クリックメニューの違いを冒頭で示し、目的別の読み方、静的verbの選択、DLLの制約、パッケージのユーザー単位登録、後始末と不具合の切り分けを小見出しで追いやすく整理した。
- 知識マップの関係を総点検し、説明文と食い違っていた関係(向きの逆転・過剰な一般化・述語の取り違え)を修正しました。本文の説明は変えていません。
- 知識マップの関係を総点検し、説明文と食い違っていた関係(向きの逆転・過剰な一般化・述語の取り違え)を修正しました。本文の説明は変えていません。
- 知識マップの関係を総点検し、説明文と食い違っていた関係(向きの逆転・過剰な一般化・述語の取り違え)を修正しました。本文の説明は変えていません。
- 知識マップの関係を総点検し、説明文と食い違っていた関係(向きの逆転・過剰な一般化・述語の取り違え)を修正しました。本文の説明は変えていません。
- 知識マップの「IContextMenu拡張はWOW6432Nodeを必要とする」という関係を、「ホストプロセスとのビット数一致を必要とする」に改めました。本文の説明は変えていません。
- 仕組みの説明を図で追えるよう、Mermaid図を19点追加しました。ファイル関連付けの3段構造、HKCRの合成ビュー、アプリ側の3種類の登録、UserChoiceの保護、既定verbの決定順序、コマンドラインの引用符の事故、インプロセス拡張の巻き添え構造・ビット数・マネージコード可否、Windows 11で二重化したメニューの構造、新メニュー登録のマニフェスト構造、sparse packageの流れと登録順序、関連付けと新メニューの役割分担、実装方式の3択の選び方、アンインストール時の掃除の設計、メニューに出ないときの切り分け順序、二重表示の切り分け、原因DLLの特定手順を図にしています。本文の文章とコード、参考リンクは変えていません。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.22054257)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Windowsシェル統合の今 ── 右クリックメニュー・ファイル関連付け・Windows 11の変化」合同会社小村ソフト. https://doi.org/10.5281/zenodo.22054257 https://comcomponent.com/blog/windows-shell-integration-context-menu-file-association/
- DOI(最新版)
- 10.5281/zenodo.22054257
- DOI(この版)
- 10.5281/zenodo.22556341
「Windows 11に入れ替えたら、自社アプリの右クリックメニューが見つからない」。確認すると、項目は消えておらず、メニュー末尾の「その他のオプションを確認」を開けば現れる──この相談で起きているのは故障ではなく、Windows 11によるメニューの設計変更です。現場では「クリックが1回増えた」「項目が見つからない」という問い合わせにつながります。
ただし、「ファイルをこのアプリで開くこと」と「独自コマンドを新しい右クリックメニューに出すこと」は、別の対応です。 ここを分けずにシェル拡張DLLを書き始めると、関連付けの登録だけで済む要件まで複雑にしてしまいます。
本記事は、中小企業の情シス担当者と業務アプリを預かるWindows開発者に向けた入門です。関連付けの土台から新旧メニューの違い、実装方式の選択、インストーラでの登録と削除、不具合の切り分けまでをつなげて説明します。
1. まず結論
関連付けとverbの土台は変わらず、Windows 11ではメニューの見せ方が新旧に分かれました。 まず「何を実現したいか」を決め、そのために必要な仕組みだけを選びます。
「開く」だけなら、関連付けと静的verbから考える
ファイル関連付けの土台は、レジストリの「拡張子キー→ProgID→verb」という3段構造です。拡張子キーはProgIDを指し、ProgID配下のshell\<verb>\commandが起動するコマンドラインを持ちます。単に「このアプリで開く」を実現したいだけなら、今もこの登録と静的verbで足り、シェル拡張DLLは不要です。Microsoftも、要件を満たす最も単純な静的verbから検討するよう案内しています。12
登録先は、全ユーザー向けならHKLM、ユーザー単位ならHKCUです。HKCRは両者のClassesを重ねた合成ビューなので、書き込み先はHKLMかHKCUを明示し、HKCRは確認用と考えます。3
なお、候補への登録と、既定アプリへの選択は別です。 ダブルクリックで開く既定アプリはユーザーが選び、OSがその選択を保護します。インストーラが勝手に既定を奪うのではなく、候補として正しく登録するところまでがアプリ側の仕事です。4
独自コマンドを新メニューに出すなら、IExplorerCommandとパッケージ識別が必要
Windows 11では、従来型のIContextMenu拡張は「その他のオプションを確認」(Shift+F10)で開く旧メニュー側に退避されます。新メニューへ独自コマンドを載せるには、IExplorerCommandの実装とパッケージ識別が必要です。56
公式ルートは、IExplorerCommandを実装したネイティブDLLをMSIXマニフェスト(desktop4:FileExplorerContextMenus)で登録する方法です。既存アプリを全面的にMSIX化できない場合は、sparse package(外部ロケーション付きMSIX)で識別だけを与えられます。67
DLLの安全性と、アンインストール時の後始末まで考える
従来型シェル拡張は、エクスプローラーなどのプロセス内に読み込まれるCOM DLLです。拡張のクラッシュや遅延はホスト全体へ波及します。64bitのホストには64bit DLLが必要で、インプロセス拡張をマネージコードで実装することはサポートされません。89
関連付けの登録・変更・削除後はSHChangeNotify(SHCNE_ASSOCCHANGED)で通知します。アンインストールでは自社のProgID等を削除し、拡張子キーの既定値は残すのが公式ガイドです。メニューを出すだけでなく、変更の反映と後始末までがシェル統合です。110
目的別の読み方
| 知りたいこと・起きている症状 | 読むところ |
|---|---|
| どの実装方式を選ぶべきか | 6章の判断表。関連付けだけか、新メニューへの独自コマンド追加かを分けます |
| ダブルクリックや「プログラムから開く」に対応したい | 2章の関連付けと3章の静的verb |
| 登録したのに既定アプリにならない | 2.4節のUserChoice。候補登録とユーザーの選択を分けます |
| Windows 11でメニューが一段奥に隠れた | 5章の新旧メニュー。対応方法は5.2節、MSIX化できない場合は5.3節 |
| インストーラで何を登録・解除すればよいか | 7章。特に7.3節のユーザー単位の登録と7.4節の後始末 |
| メニューが出ない・二重に出る・エクスプローラーが重い | 8章の切り分け。DLLの制約は4章 |
仕組みから学ぶ場合は2章から順に、既存アプリの対応方針を決める場合は6章から読めます。
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全16件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. ファイル関連付けの仕組み ── 拡張子キー→ProgID→verbの3段構造
この章では、ファイル側の登録 → 書き込み先 → アプリ側の登録 → ユーザーの選択の順に整理します。拡張子がProgIDを指し、verbがコマンドラインを持ち、込み入った拡張はインプロセスCOM DLLとして動く、という土台は20年以上変わっていません。
2.1. 3段構造を1つの例で読む
まず、関連付けの基本形を1つの例で見ます。拡張子キーがProgIDを指し、その中のverbが起動するコマンドを持ちます。1 ユーザーが既定アプリを選んでいる場合の優先関係は、2.4節で説明します。
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を差し替えたりできます。
flowchart TB
accTitle: ファイル関連付けの3段構造
accDescr: 拡張子キーは既定値でProgIDを指すだけのポインターで、ProgIDが表示名やアイコンとverb一覧を持つ実体であり、verb配下のcommandの既定値が実際に起動されるコマンドラインになる
ext["拡張子キー .kmrpt"] -->|既定値でProgIDを指す| pid["ProgID KomuraSoft.Report.1"]
pid --> vb["verb(shell配下のopenなど)"]
vb --> cmd["commandの既定値"]
cmd --> exe["Report.exeが起動される"]
pid -.-> attr["表示名・DefaultIconも持つ"]
図1: 拡張子キーはポインター、ProgIDが実体、verbのcommandが実際に起動されるコマンドライン。
2.2. HKCRは「合成ビュー」── どこに書くかで意味が変わる
上の例はHKEY_CLASSES_ROOT(HKCR)で示しましたが、HKCRは物理的な保存場所ではなく、HKLM\Software\ClassesとHKCU\Software\Classesを重ねた合成ビューです。同じキーが両方にあればHKCU側が勝ちます。3
flowchart TB
accTitle: HKCRは合成ビュー
accDescr: HKLMとHKCUそれぞれのClassesを重ねたものがHKCRで、同じキーが両方にあればHKCU側が優先され、登録の書き込みはHKLMかHKCUを明示してHKCRは読み取り用と割り切る
hklm["HKLM\Software\Classes(全ユーザー)"] --> hkcr["HKCR(合成ビュー)"]
hkcu["HKCU\Software\Classes(ユーザー単位)"] --> hkcr
hkcu -.-> win["同じキーがあればHKCU側が勝つ"]
hkcr -.-> ro["読み取り(確認)用と割り切る"]
図2: HKCRはHKLMとHKCUのClassesを重ねた見え方で、書き込み先は必ずどちらかを明示する。
| 書き込み先 | 意味 | 必要な権限 |
|---|---|---|
HKLM\Software\Classes |
全ユーザー共通の登録 | 管理者権限 |
HKCU\Software\Classes |
そのユーザーだけの登録 | 不要 |
HKCRへ直接書く |
既存キーの場所に依存して振り分けられる | 場合による |
実務では、登録は必ずHKLMかHKCUのどちらかを明示して書き、HKCRは読み取り(確認)用と割り切るのが安全です。
関連付けデータと、COM登録のビット数を混同しない
WOW64のレジストリリダイレクトでは、関連付けデータとCOM登録で扱いが違います。拡張子キーや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登録を省略しているケースです。
flowchart TB
accTitle: アプリ側の3種類の登録
accDescr: アプリ側の登録にはApp PathsとApplicationsとRegisteredApplicationsの3種類があり、それぞれ実行ファイル名だけでの起動、プログラムから開くときの既定の開き方、既定アプリ設定画面への掲載という役割を担う
app["アプリ側の登録"] --> ap["App Paths"]
app --> apps["Applications"]
app --> ra["RegisteredApplications"]
ap --> r1["ファイル名だけで起動"]
apps --> r2["プログラムから開く既定"]
ra --> r3["既定アプリ画面に載る"]
r3 -.-> cap["Capabilitiesで宣言が条件"]
図3: アプリ側の登録は3種類あり、既定アプリ画面の候補に載るにはCapabilities登録が必要になる。
2.4. 既定アプリはユーザーのもの ── UserChoiceの保護
拡張子キーの既定値にProgIDを書いても、それだけで既定アプリになるとは限りません。ユーザーが「プログラムから開く」等で明示的に選んだ結果は、HKCU\...\Explorer\FileExts\<拡張子>\UserChoiceに保持され、関連付けの解決ではこちらが優先されます。
UserChoiceを直接書き換える設計にはしない
Windowsは既定アプリのプログラムによる変更をサポートしていません。 既定アプリの設定はシステム設定UIを通じてユーザーが行う設計で、UserChoiceのデータは難読化され、フィルタードライバー(UCPD.sys)がアプリからの書き込みをブロックします。管理環境ではグループポリシー/MDMポリシーが公式の手段です。4
かつてSetUserFTAのような「ハッシュを模倣して書き換える」ツールが使われてきたのは、この保護の裏返しです。
インストーラは「選ばれる準備」をする
自社アプリのインストーラに組み込むのは、次の3つです。
- ProgIDとverbを正しく登録する。
- OpenWithProgIdsへ追加する。
- 必要なら既定アプリの設定画面へユーザーを誘導する。
既定を奪うのではなく、ユーザーが選べる状態を整えます。
flowchart TB
accTitle: 既定アプリの解決とUserChoiceの保護
accDescr: ユーザーが明示的に選んだ結果はUserChoiceに保持されて関連付けの解決で優先され、アプリからの書き換えはUCPD.sysが遮断するため、インストーラにできるのは候補としての登録と設定画面への誘導までになる
uc["UserChoice(ユーザーの選択)"] -->|優先| res["関連付けの解決"]
ext["拡張子キーの既定値"] --> res
wr["アプリからの書き換え"] -.->|UCPD.sysが遮断| uc
res ~~~ inst["インストーラの仕事"]
inst --> a1["ProgIDとverbの登録"]
inst --> a2["OpenWithProgIdsへ追加"]
inst --> a3["設定画面への誘導"]
図4: 関連付けの解決ではユーザーの選択(UserChoice)が優先され、アプリからの書き換えはOSが保護する。
3. 「開く」以外のverb ── print・edit・runas・カスタムverb
3.1. 標準verbとカスタムverb
verbはopenだけではありません。OSが意味を知っている標準verbにはopenのほかedit、print、play、previewなどがあり、標準verbはOSのロケールに応じた表示名が自動で付きます。ダブルクリック時に使われる既定verbは、shellキーの既定値→レジストリ上の最初のverb→open→openwithの順で決まります。12
flowchart TB
accTitle: 既定verbの決定順序
accDescr: ダブルクリック時に使われる既定verbはshellキーの既定値、レジストリ上の最初のverb、open、openwithの順で最初に見つかったものに決まる
s1["shellキーの既定値"] -->|無ければ| s2["レジストリ上の最初のverb"]
s2 -->|無ければ| s3["open"]
s3 -->|無ければ| s4["openwith"]
図5: ダブルクリック時の既定verbは、この順で最初に見つかったものが使われる。
独自の動詞を足したいときはカスタム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.2. 昇格・Shift時の表示・古いDDEを区別する
verbには、次のような指定もあります。
- runasというverbを登録すると「管理者として実行」に相当する昇格起動を定義でき、
ShellExecute系APIからrunasを指定した起動にも使われます。 - verbキーに
Extendedという空の値を置くと、Shiftキーを押しながら右クリックしたときだけ表示される拡張verbになります。めったに使わない危険な操作を隠すのに便利です。12 - 古いアプリの関連付けにはDDE(
ddeexecキー)で既存プロセスへ文書を送り込む構成が残っていることがありますが、DDEによるverb起動はすでに非推奨(Deprecated)の遺産です。新規に書く理由はありません。12
3.3. EXEパスと選択ファイルのパスを引用符で囲む
事故が多いのがコマンドラインの引用符です。コマンド文字列の要素にスペースが入り得るなら、必ず引用符で囲む必要があります。C:\Program Files\...のようなEXEパスはもちろん、%1(選択ファイルのパス)は常に"%1"と書くべきです。ユーザーのファイルパスにスペースが含まれないことは保証できないからです。引用符のないMy Program.exeは「MyをProgram.exeという引数付きで起動する」と解釈されます。13
flowchart TB
accTitle: コマンドラインの引用符の事故
accDescr: 引用符のないコマンドはスペースの位置で区切られてMyをProgram.exeという引数付きで起動すると誤解釈されるため、スペースを含み得るEXEパスと選択ファイルのパスを表す%1は常に引用符で囲む
c1["引用符なしのcommand"] -->|スペースで分割| bad["別のEXEの起動と誤解釈"]
c2["引用符ありのcommand"] --> good["意図どおりに起動"]
c2 -.-> q1["EXEパスを引用符で囲む"]
q1 -.-> q2["%1も常に引用符で囲む"]
図6: 引用符のないコマンドはスペースで誤って区切られるため、EXEパスと%1は常に引用符で囲む。
3.4. DLLを書く前に、静的verbで足りるかを確認する
ここまでのレジストリだけの仕組み(静的verb)は、DLLを1つも書かずに実現でき、エクスプローラーを不安定にするリスクもありません。Microsoft自身が「シェル拡張を書く前に、要件を満たす最も単純な静的verbで済まないか検討せよ」と繰り返し述べています。2
4. 従来型シェル拡張 ── エクスプローラーの中で動くDLL
この章で押さえるのは、従来型の拡張DLLは、エクスプローラー等のプロセス内で動くという点です。動作場所を理解すると、安定性・ビット数・実装言語の制約がつながります。
4.1. シェル拡張の種類
静的verbでは足りない「選択内容に応じてメニューを動的に変える」「アイコンやプロパティ画面を差し替える」といった要件には、シェル拡張ハンドラーを使います。代表的な種類は次のとおりです。8
| ハンドラー | 主なインターフェース | できること |
|---|---|---|
| コンテキストメニューハンドラー | IContextMenu + IShellExtInit | メニュー項目を動的に追加・制御 |
| アイコンハンドラー / アイコンオーバーレイ | IExtractIcon / IShellIconOverlayIdentifier | ファイルごとのアイコン・重ね表示 |
| プロパティシートハンドラー | IShellPropSheetExt | プロパティ画面へのタブ追加 |
| サムネイル / インフォチップ | IThumbnailProvider / IQueryInfo | 縮小表示・ホバー時の説明 |
| ドラッグ&ドロップ / コピーフックハンドラー | IDropTarget / ICopyHook | ドロップ時・コピー移動時の介入 |
これらはいずれもCOMのクラスとして実装し、CLSIDをレジストリに登録します。COM自体の考え方は「COM / ActiveX / OCX とは何か」を参照してください。
4.2. インプロセスCOMサーバーであることの意味
従来型シェル拡張の本質は、エクスプローラー(や共通ファイルダイアログを開いた任意のアプリ)のプロセス内に読み込まれるインプロセスCOMサーバー(DLL)だということです。ここからすべての注意点が導かれます。8
- 拡張がクラッシュすれば、エクスプローラーが巻き添えでクラッシュします。ハングすれば右クリックが数秒固まります。しかも被害はエクスプローラーに限らず、ファイルを開くダイアログを表示したすべてのアプリに及びます。
- メニュー構築はUIスレッドで行われるため、ネットワークアクセスやファイルI/Oのような遅い処理をメニュー表示時に行ってはいけません。
- スレッディングモデルは
Apartmentで登録するのが原則です。
flowchart TB
accTitle: インプロセス拡張の巻き添え構造
accDescr: シェル拡張DLLはエクスプローラーだけでなくファイルダイアログを開いた任意のアプリのプロセスにも読み込まれるため、拡張のクラッシュやハングは宿主プロセス全体に波及する
dll["シェル拡張DLL"] -->|プロセス内に読込| exp["エクスプローラー"]
dll -->|プロセス内に読込| any["ダイアログを開く任意アプリ"]
exp --> dmg["クラッシュやハングが波及"]
any --> dmg
dmg -.-> rule["遅い処理を表示時にしない"]
図7: 拡張DLLは宿主プロセスの中で動くため、クラッシュやハングは宿主全体へ波及する。
「特定のフォルダーを開くとエクスプローラーが固まる」「右クリックに5秒かかる」という相談を調査すると、原因が自社アプリではなくサードパーティ製のシェル拡張だった、ということは珍しくありません。切り分け方法は8章で扱います。
4.3. ビット数の一致 ── 64bit環境では64bit DLLが必須
インプロセスDLLは、読み込むプロセスとビット数が一致しなければなりません。64bit Windowsのエクスプローラーは64bitプロセスなので、32bitでしかビルドしていないシェル拡張DLLは、そもそも読み込まれず、メニューに一切現れません。エラーも出ないため「登録したのに出ない」原因の定番です。
アプリ本体まで64bit化する必要があるわけではない
32bitアプリ本体と64bitシェル拡張DLLの組み合わせは正当な構成ですが、COM登録がbitnessごとに分かれる(Wow6432Node)ことに注意が必要です。なお、verbのcommandから起動するのは別プロセスのEXEなので、この制約を受けません(32bit EXEのままで問題ありません)。
flowchart TB
accTitle: シェル拡張DLLのビット数一致
accDescr: 64bitエクスプローラーに読み込めるのは64bitのシェル拡張DLLだけで、32bitのみのDLLはエラーも出ずにメニューへ現れず、verbのcommandから起動するEXEは別プロセスなので制約を受けない
exp["64bitエクスプローラー"] -->|読み込める| d64["64bitシェル拡張DLL"]
exp -.->|読み込めない| d32["32bitのみのDLL"]
d32 -.-> sym["エラーなくメニューに出ない"]
exe["verbで起動するEXE"] -->|別プロセス| ok32["32bitのままで問題なし"]
図8: 64bitエクスプローラーに読み込まれるのは64bit DLLだけで、verbで起動するEXEはこの制約を受けない。
4.4. マネージコードで書いてはいけない理由
「C#でシェル拡張を書けないか」という質問をよく受けますが、Microsoftはインプロセスのシェル拡張をマネージコード(.NET)で書くことを非推奨とし、サポート対象外と明言しています。9
理由は、拡張が任意のプロセスに読み込まれるという性質にあります。宿主アプリを不安定にする要因は、主に次の3つです。
- CLRのバージョン競合。 特に.NET Framework 4未満で問題になります。
- 再入。 ロック待ちでCLRがメッセージループへ再入する問題があります。
- オブジェクト寿命。 ガベージコレクションによる寿命の非決定性が、COMの参照カウント契約と衝突します。
.NET Framework 4以降やモダンな.NETで緩和された項目もありますが、公式の立場は変わっていません。
実務の指針はシンプルです。インプロセス拡張はネイティブC++で書く。マネージコードを使いたければ、verbのcommandで起動する通常のEXEにするか、別プロセスで動くアウトプロセス拡張(プレビューハンドラー等)にする。9
flowchart TB
accTitle: マネージコード可否の判断
accDescr: エクスプローラーのプロセス内で動くインプロセス拡張はネイティブC++で書くのが原則で、マネージコードを使いたい場合はverbのcommandで起動する通常のEXEか別プロセスで動くアウトプロセス拡張にする
q1{"プロセス内で動く拡張?"} -->|はい| cpp["ネイティブC++で書く"]
q1 -->|いいえ| mg["マネージコードでも可"]
cpp -.-> why["CLR競合や再入で宿主が不安定"]
mg --> e1["verbで起動する通常のEXE"]
mg --> e2["プレビュー等の別プロセス拡張"]
図9: インプロセス拡張はネイティブC++が原則で、マネージコードは別プロセスで動く構成に限る。
5. Windows 11の新コンテキストメニュー ── メニューの二重化
ここでは、旧メニューへ移った理由 → 新メニューへの登録 → 既存インストーラを維持する方法の順に説明します。「このアプリで開く」だけの関連付けと、独自コマンドの追加との違いは5.4節で確認します。
5.1. 何が起きたか
Windows 11は、エクスプローラーの右クリックメニューを刷新しました。切り取り・コピー等が上部のアイコン列になり、「開く」「プログラムから開く」がまとめて上部に配置され、アプリが追加するコマンドはシェル標準コマンドの下にグループ化されます。1つのアプリが複数のコマンドを追加する場合は、アプリ名付きのフライアウト(サブメニュー)に集約されます。5
そして肝心の点がこれです。従来型のIContextMenuベースのシェル拡張は削除されたのではなく、「その他のオプションを確認」(Shift+F10)で開く、Windows 10のメニューをそのまま読み込んだ旧メニュー側に退避されました。5 冒頭の相談の「メニューが隠れた」の正体はこの二重化です。
flowchart TB
accTitle: Windows 11で二重化した右クリックメニュー
accDescr: 右クリックでまず開くのは新メニューで、そこに載るのはIExplorerCommandとパッケージ識別で登録したコマンドだけであり、従来型IContextMenu拡張はその他のオプションを確認で開く旧メニュー側に退避される
rc["ファイルを右クリック"] --> newm["新メニュー(Windows 11)"]
newm --> newi["IExplorerCommand+識別のコマンド"]
newm -->|その他のオプションを確認 Shift+F10| oldm["旧メニュー(Windows 10のメニュー)"]
oldm --> oldi["従来型IContextMenu拡張"]
newi -.-> fly["複数コマンドはフライアウトに集約"]
図10: 新メニューに載るのはIExplorerCommand+識別のコマンドだけで、従来型拡張は旧メニュー側に退避される。
5.2. 新メニューに載せる公式ルート ── IExplorerCommand+マニフェスト登録
新メニューに独自コマンドを出す公式ルートは、IExplorerCommandを実装したネイティブDLLと、MSIXマニフェストでの登録です。6
マニフェストには2種類の宣言を書く
以下の例は、前半でCOMサーバー(CLSIDと実装DLL)を、後半でコンテキストメニュー拡張(対象とコマンド)を宣言しています。両方を結び付けるのが同じCLSIDです。
<!-- パッケージマニフェスト(抜粋) -->
<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>
ItemTypeのTypeには特定の拡張子のほか、*(すべてのファイル)、Directory(フォルダー)、Directory\Background(フォルダーの背景)を指定できます。DLLはエクスプローラーのアーキテクチャ(64bit/ARM64)に一致させます。6
メニュー構築メソッドは高速に返す
IExplorerCommand自体はWindows 7時代からあるインターフェースで、タイトル(GetTitle)、アイコン(GetIcon)、有効/無効/非表示の状態(GetState)、実行(Invoke)を実装します。メソッドはUIスレッドから呼ばれるため、ネットワーク資源へのアクセスは禁止で、メニュー構築系のメソッドは高速に返す必要があります。重い処理はInvoke後に行います。146
flowchart TB
accTitle: 新メニュー登録のマニフェスト構造
accDescr: MSIXマニフェストのCOMサーバー宣言がCLSIDとDLLを対応付け、コンテキストメニュー拡張の宣言がItemTypeとVerbで対象と実装を結び付けることで、独自コマンドが新メニューに表示される
man["MSIXマニフェスト"] --> com["COMサーバー宣言"]
man --> ctx["メニュー拡張の宣言"]
com -->|CLSIDとDLLを対応付け| impl["IExplorerCommand実装DLL"]
ctx -->|ItemTypeとVerbで指定| impl
impl --> shown["新メニューにコマンド表示"]
ctx -.-> tgt["対象は拡張子や全ファイル等"]
図11: マニフェストの2つの宣言が実装DLLと対象を結び付けて、新メニューにコマンドが載る。
5.3. 非パッケージアプリの選択肢 ── sparse packageで識別だけを得る
「うちのアプリはMSIじゃないと配れない。MSIX化は無理」という場合の逃げ道が、sparse package(外部ロケーション付きMSIX)です。アプリ本体を含まない、マニフェストだけの小さなMSIXを署名して作り、既存インストーラの最後に登録します。これでアプリはパッケージ識別を獲得し、上記のマニフェスト登録(=新メニューへの表示)が可能になります。
利用できるのはWindows 10バージョン2004以降で、対象マシンで信頼される証明書によるパッケージの署名が必要です。7 登録・解除の順序と、ユーザー単位の登録であることへの注意は7.3節で扱います。
flowchart TB
accTitle: sparse packageで識別を得る流れ
accDescr: 既存インストーラでアプリ本体を配置した後、マニフェストだけのsparse packageを外部ロケーション付きで登録するとアプリはパッケージ識別を獲得し、新メニューへのマニフェスト登録が可能になる
inst["既存インストーラ"] --> files["アプリ本体を配置"]
sp["sparse package"] -.-> only["マニフェストだけで本体なし"]
files --> reg["外部ロケーション付きで登録"]
sp --> reg
reg --> id["パッケージ識別を獲得"]
id --> ok["新メニュー登録が可能に"]
sp -.-> sign["信頼される署名が必要"]
図12: 本体を含まないsparse packageを外部ロケーション付きで登録すると、アプリはパッケージ識別を獲得する。
インストーラを差し替えずに済むのが最大の利点で、既存のMSI/EXEインストーラ資産を持つアプリの現実解です。MSIXへの全面移行との比較は「Windowsアプリ配布方式の選び方」も参照してください。
5.4. 関連付けのverbは新メニューでどう見えるか
誤解されやすい点ですが、2〜3章の関連付け(ProgIDとverb)は、新メニューでも生きています。ダブルクリックの既定verb、「開く」、「プログラムから開く」の候補は関連付けから解決され、新メニューの上部に表示されます。つまり「このアプリで開けるようにしたい」だけなら、Windows 11でも何も追加対応は要りません。
一方、関連付けは汎用のメニュー拡張ではないため、任意のカスタムコマンドを新メニューの第一階層に出したければIExplorerCommand+識別が必要、という役割分担です。6
flowchart TB
accTitle: 関連付けと新メニューの役割分担
accDescr: ProgIDとverbの関連付けは新メニューでもダブルクリックの既定verbや開くとプログラムから開くの解決に使われて上部に表示され、任意のカスタムコマンドを新メニューの第一階層に出すにはIExplorerCommandと識別が必要になる
assoc["関連付け(ProgIDとverb)"] --> sol["既定verbと開くの解決"]
sol --> top["新メニュー上部に表示"]
assoc -.-> keep["Windows 11でも追加対応不要"]
cmd["任意のカスタムコマンド"] --> need["IExplorerCommand+識別"]
need --> first["新メニュー第一階層に表示"]
図13: 関連付けは新メニューでも「開く」系の解決を担い、カスタムコマンドだけがIExplorerCommand+識別を要する。
6. 実務の判断表 ── 3つの選択肢のどれを取るか
まず(a)で足りるかを確認し、独自コマンドの新メニュー表示が必要なら(b)を選びます。 既存の従来型拡張を当面維持するのが(c)です。
| 実現したいこと | 推奨する手段 | Windows 11での見え方 | 必要な作業・コスト |
|---|---|---|---|
| (a) ダブルクリックや「開く」で自社アプリを起動したい | 関連付け+静的verb(レジストリ登録のみ) | 新メニューの「開く」「プログラムから開く」に統合表示 | インストーラのレジストリ登録のみ。DLL不要・署名の追加要件なし |
| (b) 選択したファイル/フォルダーへの独自コマンドを新メニューに出したい | IExplorerCommand実装+MSIXマニフェスト登録。非パッケージアプリはsparse packageで識別を付与 | 新メニューの第一階層(複数コマンドはアプリ名フライアウトに集約) | ネイティブC++ DLL+パッケージ識別+コード署名 |
| (c) 既存の従来型IContextMenu拡張を使い続ける | 当面そのまま維持(新規開発には選ばない) | 「その他のオプションを確認」(Shift+F10)の旧メニュー側のみ | 64bitビルドとCOM登録の維持。将来は(b)への移行を計画 |
判断1: 要件を満たす最も単純な方法を選ぶ
(a)で済む要件に(b)や(c)を持ち出さないことが第一です。シェル拡張は書いた瞬間から、エクスプローラーの安定性に対する責任を負います。
判断2: 旧メニューの維持と、使い勝手の改善を分けて考える
(c)は「壊れてはいない」だけで、ユーザー体験としては一段劣る位置に置かれ続けます。日常操作で使う頻度が高いコマンドほど、(b)への移行の投資対効果が大きくなります。
flowchart TB
accTitle: 3つの選択肢の選び方
accDescr: ダブルクリックや開くで起動したいだけなら関連付けと静的verbで足り、新メニューに独自コマンドを出すならIExplorerCommandとMSIXマニフェスト登録で、MSIX化できない場合はsparse packageで識別を付与し、既存の従来型IContextMenu拡張は旧メニュー側で当面維持する
q1{"開くだけでよい?"} -->|はい| pa["関連付け+静的verb"]
q1 -->|いいえ| q2{"新メニューに独自コマンド?"}
q2 -->|はい| q3{"MSIX化できる?"}
q3 -->|はい| pb1["IExplorerCommand+MSIX"]
q3 -->|いいえ| pb2["sparse packageで識別"]
q2 -->|いいえ| pc["従来型を当面維持"]
pc -.-> old["旧メニュー側のみに表示"]
pa -.-> dllfree["DLL不要でリスク小"]
図14: 要件に応じて静的verb、IExplorerCommand+識別、従来型維持の3択から選ぶ。
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の登録・解除はインストーラの仕事です。登録はファイル配置の後、解除はファイル削除の前に行います。7
# インストール時: ファイル配置後に、インストール先を外部ロケーションとして登録
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台を複数ユーザーで使う場合、他のユーザーや後から作られたユーザーにはパッケージ識別がなく、新メニューにコマンドが出ません。
全ユーザーで使わせるなら、アプリの初回起動時に自分のパッケージ登録を確認し、未登録なら登録する、といったユーザーごとの登録を用意します。アンインストール時も、登録済みの各ユーザーからの解除を計画に含めます。
登録後にメニューへ反映されない場合
マニフェスト登録の反映には、エクスプローラーの再起動(またはサインアウト)が必要な場合があります。6
flowchart TB
accTitle: sparse packageの登録と解除の順序
accDescr: インストール時はファイル配置の後にsparse packageを登録し、アンインストール時はファイル削除の前に登録を解除するが、登録は実行したユーザーだけに有効な点に注意する
i1["インストール"] --> i2["ファイルを配置"]
i2 --> i3["sparse packageを登録"]
u1["アンインストール"] --> u2["パッケージ登録を解除"]
u2 --> u3["ファイルを削除"]
i3 -.-> pu["登録は実行ユーザーだけに有効"]
図15: 登録はファイル配置の後、解除はファイル削除の前に行い、登録が実行ユーザー単位である点に注意する。
7.4. アンインストール時の掃除 ── 消すものと残すもの
アンインストール時の掃除には、公式ガイドに明確な指針があります。1
- 消すもの: 自社のProgIDキー一式、Capabilities/RegisteredApplications登録、シェル拡張のCLSID登録、sparse package(Remove-AppxPackage)。
- 残すもの: 拡張子キー(
.kmrpt)の既定値。自社ProgIDを指したままでも消さないのが公式の推奨です。インストール後に別アプリが既定を取っていないかの判定は困難であり、Windowsは既定値のProgIDが未登録なら単に無視するため、残しても実害がないからです。 - 掃除の最後にもSHChangeNotify(SHCNE_ASSOCCHANGED)を呼びます。
「アンインストールしたのにメニューに残骸が出る」問題の多くは、この掃除設計の漏れです。
flowchart TB
accTitle: アンインストール時の掃除の設計
accDescr: アンインストールでは自社のProgIDキーやCLSID登録とsparse packageは消し、拡張子キーの既定値は未登録なら無視されるため残し、掃除の最後にSHChangeNotifyで変更を通知する
un["アンインストール"] --> del["消すもの"]
un --> keep["残すもの"]
del --> d1["ProgIDやCLSID登録"]
del --> d2["sparse package"]
keep --> k1["拡張子キーの既定値"]
k1 -.-> why["未登録ProgIDは無視される"]
d1 --> fin["最後にSHChangeNotifyで通知"]
k1 --> fin
図16: 自社の登録は消し、拡張子キーの既定値は残し、掃除の最後に変更を通知する。
8. トラブルシューティング ── 出ない・二重・重い
不具合は「出ない」「二重に出る・消えない」「重い・落ちる」に分けて調べます。最後に、登録と後始末をクリーンな環境で確かめる方法を説明します。
8.1. メニューに出ない
最初に、新旧どちらのメニューを見ているかを確認します。 そのうえで、次の順に切り分けます。
- どちらのメニューを見ているか: 旧方式の登録はShift+F10の旧メニュー側にしか出ません。まず両方を確認します。
- ビット数: 32bitのみのシェル拡張DLLは64bitエクスプローラーに読み込まれません(4.3節)。
- 登録先: HKLM/HKCU、Wow6432Nodeの取り違え。
reg queryで実際のキーを確認します。 - パッケージ登録: 新メニュー向けなら
Get-AppxPackageで登録の有無、署名証明書の信頼、-ExternalLocationのパスを確認し、エクスプローラーを再起動します。6 - 通知漏れ: SHChangeNotify忘れなら、エクスプローラーの再起動で反映されるかで判別できます。
flowchart TB
accTitle: メニューに出ないときの切り分け順序
accDescr: どちらのメニューを見ているかの確認から始め、DLLのビット数、レジストリの登録先、パッケージ登録と署名、SHChangeNotifyの通知漏れの順に切り分ける
c1["新旧どちらのメニューか確認"] --> c2["DLLのビット数を確認"]
c2 --> c3["HKLMとHKCUの登録先を確認"]
c3 --> c4["パッケージ登録と署名を確認"]
c4 --> c5["通知漏れを再起動で判別"]
図17: 「出ない」ときは、見ているメニュー・ビット数・登録先・パッケージ登録・通知漏れの順で切り分ける。
8.2. 二重に出る・消えない
まず、どちらのメニューに重複しているかであたりを付けます。
- 旧メニューにだけ二重に出る: アンインストール時の掃除漏れ(7.4節)や、旧バージョンのProgIDの残骸を疑います。
- 新旧両方に出る: 旧方式のレジストリ登録と、MSIXマニフェスト登録の併存を疑います。
いずれも典型的な原因の切り分けであり、実際の登録内容を確認して判断します。
flowchart TB
accTitle: 二重表示の切り分け
accDescr: 旧メニューにだけ二重に出るなら掃除漏れや旧ProgIDの残骸系、新旧両方に出るなら旧方式のレジストリ登録とマニフェスト登録の併存系とあたりを付ける
q{"どちらに二重に出るか"} -->|旧メニューだけ| zan["残骸系"]
q -->|新旧両方| hei["併存系"]
zan -.-> z1["掃除漏れや旧ProgIDの残り"]
hei -.-> h1["旧レジストリ登録と新登録の併存"]
図18: 旧メニューだけに二重なら残骸系、新旧両方に出るなら併存系とあたりを付ける。
8.3. エクスプローラーが重い・落ちる
右クリックが遅い、特定フォルダーでクラッシュする場合は、まずインストール済みシェル拡張を棚卸しします。
- 拡張を一覧する。 NirSoftのShellExViewのようなツールで、非Microsoft製の拡張を確認します。
- 一時無効化して絞り込む。 疑わしいものを二分探索し、原因DLLを特定します。クラッシュなら、イベントビューアーの「障害が発生しているモジュール」も手がかりです。
- 自社拡張なら、メニュー構築パスを調べる。 同期I/Oやネットワークアクセスを疑います(4.2節・5.2節)。
flowchart TB
accTitle: 重い・落ちるときの原因DLL特定
accDescr: ShellExViewで非Microsoft製のシェル拡張を一覧し、疑わしいものを一時無効化しながら二分探索して原因DLLを特定し、クラッシュの場合はイベントビューアーの障害モジュールも手がかりにする
s1["シェル拡張の棚卸し"] --> s2["非Microsoft製を一覧"]
s2 --> s3["一時無効化して二分探索"]
s3 --> s4["原因DLLを特定"]
crash["クラッシュの場合"] -.-> ev["障害モジュールを確認"]
ev -.-> s4
図19: 非Microsoft製拡張を一時無効化しながら二分探索し、クラッシュ時はイベントビューアーも併用する。
8.4. 検証はWindows Sandboxが便利
シェル統合の検証は「クリーンな環境でインストール→動作→アンインストール→残骸ゼロ」の確認が基本です。ここで便利なのがWindows Sandbox(Pro/Enterprise/Education)で、起動のたびにまっさらな使い捨てWindowsが数秒で立ち上がるため、インストーラの登録・掃除のテストを何度でも回せます。閉じれば全部消えるので、レジストリの残骸調査にも向きます。15
9. まとめ
Windows 11への入れ替えで「メニューが隠れた」と言われたら、まず6章の判断表で、何を実現したいのかを確認してください。考える順序は、次の3段階です。
1. 関連付けで足りるかを判断する
「このアプリで開く」だけなら、関連付けと静的verbで今も足ります。土台は拡張子キー→ProgID→verbで、HKCRはHKLM/HKCUのClassesを重ねた確認用のビューです。書き込み先を明示し、%1は必ず引用符で囲みます。
ただし、既定アプリを選ぶのはユーザーです。インストーラは既定を奪うのではなく、候補として正しく登録します。
2. 独自コマンドを、どちらのメニューに出すかを決める
従来型IContextMenu拡張は、「その他のオプションを確認」で開く旧メニュー側で動きます。新メニューへ独自コマンドを出すには、IExplorerCommandとMSIXマニフェストでの登録が必要です。全面的にMSIX化できないアプリは、sparse packageでパッケージ識別を得るのが現実解です。
従来型拡張のDLLは、エクスプローラーなどのプロセス内で動きます。クラッシュや遅延はホスト全体へ波及し、64bitホストには64bit DLLが必要です。インプロセス拡張をマネージコードで書くことはサポートされず、ネイティブC++での実装が原則です。
3. 登録・変更通知・解除を一組で検証する
関連付けの登録・変更・削除後はSHChangeNotifyで通知します。アンインストールでは自社のProgID等を消し、拡張子キーの既定値は残します。sparse packageはユーザー単位の登録なので、複数ユーザー環境では各ユーザーへの登録と解除も計画に含めます。
Windows Sandboxを使い、クリーンな環境でインストール→動作→アンインストール→残骸の確認を繰り返します。
最も単純な手段から選び、必要な場合だけ新メニューへの独自コマンド追加へ進む。この順番にすると、対応の規模と、維持すべき登録・実装の範囲を整理できます。
関連記事
- COM / ActiveX / OCX とは何か - 違いと関係をまとめて解説
- Reg-Free COMとは - 登録不要でCOMを使う仕組み
- レジストリの32bit/64bitリダイレクトと仮想化の落とし穴 ── Wow6432Nodeと「書いたはずの値がない」問題
- Windowsアプリ配布方式の選び方 - MSI/MSIX/ClickOnce/xcopy/独自更新
- DLL・COMインターフェースの後方互換性 ── どの変更が呼び出し側を壊すのかの判断表
- Windowsアプリ互換の仕組み ── 互換モード・シム・Compatibility Administratorで古いアプリを延命する
関連する相談領域
合同会社小村ソフトでは、業務アプリのファイル関連付け・右クリックメニュー・シェル拡張の設計と実装、Windows 11の新コンテキストメニューへの対応(IExplorerCommand化、sparse packageの導入)、既存インストーラの登録・掃除の見直し、エクスプローラーが重い・落ちる問題の原因調査を扱っています。「その他のオプションを確認に隠れてしまったメニューをどうするか」の方針決めからで構いません。
参考リンク
-
Microsoft Learn, File Types. 拡張子キーがProgIDを指す構造、OpenWithProgIds、HKLM/HKCU\Software\Classesへの登録の使い分け、関連付け変更後にSHChangeNotify(SHCNE_ASSOCCHANGED)を呼ぶべきこと、アンインストール時にProgIDは削除しつつ拡張子キーの既定値は残すべきことについて。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Choosing a Static or Dynamic Shortcut Menu Method. 要件を満たす最も単純な静的verb方式を選ぶべきこと、IContextMenuが最も強力だが最も複雑で非推奨側に分類されること、IExplorerCommand/IExplorerCommandStateが推奨される方式であることについて。 ↩ ↩2
-
Microsoft Learn, HKEY_CLASSES_ROOT Key. HKEY_CLASSES_ROOTがHKLM\Software\ClassesとHKCU\Software\Classesを合成したビューであること、ユーザー側の定義がマシン側より優先されること、書き込み時の振り分け規則について。 ↩ ↩2
-
Microsoft Learn, Windows app defaults platform. 既定アプリの変更がシステム設定UIを通じてのみ行える設計であること、ユーザー設定データが難読化されフィルタードライバー(UCPD.sys)で書き込み保護されること、レジストリベースの変更が非サポートであること、管理環境ではグループポリシー/MDMポリシーを使うことについて。 ↩ ↩2
-
Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. Windows 11の新コンテキストメニューの設計、IExplorerCommand+アプリ識別による拡張、「開く」「プログラムから開く」の上部配置、複数コマンドのアプリ名付きフライアウトへの集約、従来のIContextMenu拡張が「その他のオプションを確認」(Shift+F10)のWindows 10メニューとして読み込まれることについて。 ↩ ↩2 ↩3
-
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
-
Microsoft Learn, Grant package identity by packaging with external location. 既存インストーラを変えずに外部ロケーション付きパッケージ(sparse package)を登録してパッケージ識別を得られること、Windows 10バージョン2004以降で利用可能なこと、識別が必須のWindows機能(コンテキストメニュー登録・通知等)が使えるようになることについて。 ↩ ↩2 ↩3
-
Microsoft Learn, Working with Shell Extensions. シェル拡張ハンドラーの種類、拡張がエクスプローラー(およびシェルをホストするプロセス)に読み込まれるインプロセスCOM DLLであり、クラッシュやハングがExplorer全体に波及すること、ThreadingModel=Apartmentでの登録、シェル拡張より単純な代替手段を先に検討すべきことについて。 ↩ ↩2 ↩3
-
Microsoft Learn, Guidance for Implementing In-Process Extensions. マネージコードによるインプロセスのシェル拡張実装をMicrosoftが推奨せずサポート対象外とすること、CLRのバージョン競合・再入・オブジェクト寿命の非決定性などの理由、アウトプロセス拡張(プレビューハンドラーやshell\verb\commandからの起動)ではマネージコードが許容されることについて。 ↩ ↩2 ↩3
-
Microsoft Learn, SHChangeNotify function. ファイル関連付けの変更をシステムへ通知するSHCNE_ASSOCCHANGEDイベントの発行方法と、シェルへ変更を認識させるための使い方について。 ↩ ↩2
-
Microsoft Learn, Application Registration. App Pathsサブキーによる実行ファイルの登録が推奨されること、Applicationsサブキーの役割、SystemFileAssociationsによるverb登録、既定アプリ変更時のProgIDと関連情報の優先順位について。 ↩
-
Microsoft Learn, Verbs and File Associations. verbがShellExecuteExでも使われる動詞であること、コマンド文字列でスペースを含み得る要素は引用符で囲む必要があり、”%1”を常に引用符付きで書くべきこと、HKCR\Applications配下への既定手続きの登録について。 ↩
-
Microsoft Learn, IExplorerCommand interface. GetTitle・GetIcon・GetState・Invoke・EnumSubCommands等のメソッド構成、メソッドがUIスレッドで呼ばれるためネットワーク資源と通信してはならないこと、Windows Vista以降で利用可能であることについて。 ↩
-
Microsoft Learn, Windows Sandbox. 使い捨ての隔離されたWindows環境を数秒で起動でき、閉じるとすべての変更が破棄されること、ソフトウェアのテストやインストーラの検証に適すること、Pro/Enterprise/Educationで利用できることについて。 ↩
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windowsのプリンタードライバー終了 ── 業務アプリの帳票・ラベル印刷はどう備えるか
Microsoftはv3/v4プリンタードライバーの提供終了を段階的に進めており、2026年7月からはIPPクラスドライバーが優先されます。Windows protected print modeで何が消えるか、業務アプリの帳票・ラベル印刷の依存箇所の棚卸しと備え方を判断表...
WinRTはCOMである ── IInspectable・.winmd・言語プロジェクション、そしてWinUIが今もバイナリ契約の上に乗っている理由
WinRTはマネージドランタイムではなく、COMにメタデータ(.winmd)と言語プロジェクションを足したABIです。IUnknownとIInspectableの関係から、デスクトップアプリでのHWND初期化やpackage identityの詰まりどころまで解説します。
OLEオブジェクトとは何か ── 埋め込み・リンクの仕組みと業務文書の落とし穴
WordにExcelの表を埋め込む機能の正体がOLEオブジェクトです。埋め込みとリンクの違い、複合ファイルと構造化ストレージ、In-Place Activationの仕組みから、リンク切れ・肥大化・セキュリティ対策まで実務目線で解説します。
クリップボードとドラッグ&ドロップの仕組み ── OLEデータ転送を業務アプリで正しく扱う
Excelの表を貼ると崩れる・コピー元を閉じると貼れない――正体は同じ内容を複数形式で置くクリップボードの仕組みです。標準形式・遅延レンダリング・OLEドラッグ&ドロップから履歴とクラウド同期のポリシーまで解説します。
Windowsアプリ 外注・受託開発を依頼する前に整理したいこと
Windowsアプリの外注・受託開発を依頼する前に、既存ソフト改修、装置連携、COM/ActiveX、配布・更新、保守の整理ポイントを解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
ActiveX / 移行テーマ
COM / ActiveX / OCX を残すか、包むか、置き換えるかを整理するトピックです。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
既存Windowsソフトの改修・保守
既存 Windows ソフトの機能追加、保守、段階的モダナイゼーションを支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- 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を特定できます。