今日的 Windows 殼層整合 ── 右鍵選單、檔案關聯,以及 Windows 11 的變化

· · Windows, 殼層擴充功能, 右鍵選單, 檔案關聯, COM, Windows 11, 檔案總管, MSIX, Windows 開發

更新紀錄(僅初版,2026年08月20日 發布)
初次發布
引用本文(DOI(已登錄存檔): 10.5281/zenodo.22176284)

以下 DOI 指向先前登錄的存檔,內容可能與目前正文不同。引用目前正文時,請使用本頁網址。

Go Komura(2026)。〈今日的 Windows 殼層整合 ── 右鍵選單、檔案關聯,以及 Windows 11 的變化〉。小村軟體有限公司。 https://comcomponent.com/zh-TW/blog/windows-shell-integration-context-menu-file-association/

DOI(已登錄存檔)
10.5281/zenodo.22176284
DOI(上次登錄版本)
10.5281/zenodo.22176285

「換成 Windows 11 之後,找不到自家應用程式的右鍵選單了。」實際確認會發現項目並沒有消失,打開選單最後面的「顯示更多選項」就會出現──這類諮詢背後不是故障,而是 Windows 11 對選單所做的設計變更。在現場,它會變成「多按了一次」「找不到項目」這類詢問。

不過,「讓檔案用這個應用程式開啟」和「把自訂命令放進新的右鍵選單」是兩件不同的事。 不先把兩者分開就動手寫殼層擴充功能 DLL,連原本只要註冊關聯就能滿足的需求也會被做複雜。

本文是寫給中小企業 IT 負責人,以及維護業務應用程式的 Windows 開發者的入門文章。從關聯的基礎,講到新舊選單的差異、實作方式的選擇、安裝程式中的註冊與刪除,再到問題的釐清,串成一條線來說明。

1. 先講結論

關聯與 verb 的底層沒有變,Windows 11 變的是選單的呈現方式,分成了新舊兩套。 先決定「想實現什麼」,再只挑出為此必要的機制。

只要「開啟」的話,先從關聯與靜態 verb 考慮

檔案關聯的底層,是登錄檔中「副檔名機碼→ProgID→verb」這樣的三層結構。副檔名機碼指向 ProgID,ProgID 底下的 shell\<verb>\command 保存要啟動的命令列。如果只是想做到「用這個應用程式開啟」,現在依然靠這套註冊與靜態 verb 就夠了,不需要殼層擴充功能 DLL。Microsoft 也建議先從能滿足需求的最單純靜態 verb 開始考慮。12

註冊位置方面,面向全體使用者就寫 HKLM,以使用者為單位就寫 HKCU。HKCR 是把兩者的 Classes 疊起來的合成檢視,所以寫入時要明確指定 HKLM 或 HKCU,把 HKCR 當成查看用。3

另外,登錄成候選,和被選為預設應用程式是兩回事。 按兩下要開啟的預設應用程式由使用者選擇,作業系統會保護這個選擇。安裝程式不該擅自搶走預設值,把自己正確註冊成候選,才是應用程式這一端的工作。4

要把自訂命令放上新選單,就需要 IExplorerCommand 與套件識別

在 Windows 11 上,傳統的 IContextMenu 擴充功能會被移到以「顯示更多選項」(Shift+F10)開啟的舊選單那一側。要把自訂命令放上新選單,就需要實作 IExplorerCommand 並取得套件識別。56

官方路徑是:把實作 IExplorerCommand 的原生 DLL,以 MSIX 資訊清單(desktop4:FileExplorerContextMenus)註冊。若既有應用程式無法全面改成 MSIX,也可以用 sparse package(帶外部位置的 MSIX)只賦予它識別。67

連 DLL 的安全性與解除安裝時的善後一起考慮

傳統殼層擴充功能是會被載入到檔案總管等處理程序內的 COM DLL。擴充功能當掉或變慢,都會波及整個宿主。64 位元的宿主需要 64 位元 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 的三層結構

這一章依照檔案側的註冊 → 寫入位置 → 應用程式側的註冊 → 使用者的選擇的順序來整理。副檔名指向 ProgID、verb 保存命令列,較複雜的擴充功能則以處理程序內 COM DLL 的形式運作──這套底層二十多年來沒有變過。

2.1. 用一個例子讀懂三層結構

先用一個例子看關聯的基本形。副檔名機碼指向 ProgID,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。

檔案關聯的三層結構副檔名鍵是預設值指名 ProgID 的指標;ProgID 是儲存顯示名稱、圖示與 verb 列表的實體;verb 底下 command 的預設值才是實際啟動的命令列預設值指名 ProgID副檔名鍵 .kmrptProgID KomuraSoft.Report.1verb(shell 底下的 open 等)command 預設值啟動 Report.exe也儲存顯示名稱與 DefaultIcon

圖 1: 副檔名機碼是指標,ProgID 是實體,verb 的 command 才是實際啟動的命令列。

2.2. HKCR 是「合成檢視」── 寫到哪裡,意義就不同

上面的例子是用 HKEY_CLASSES_ROOT(HKCR)表示的,但HKCR 並不是實體的儲存位置,而是把 HKLM\Software\Classes 與 HKCU\Software\Classes 疊起來的合成檢視。同一個機碼若兩邊都有,HKCU 這邊獲勝。3

HKCR 是合併檢視HKCR 是把 HKLM 與 HKCU 的 Classes 疊在一起;兩邊有同一個鍵時 HKCU 獲勝;註冊要明確寫到 HKLM 或 HKCU,把 HKCR 當唯讀HKLM\\Software\\Classes(全體使用者)HKCR(合併檢視)HKCU\\Software\\Classes(單使用者)同一鍵存在時 HKCU 獲勝當唯讀(用來確認)

圖 2: HKCR 是 HKLM 與 HKCU Classes 疊在一起的樣子;寫入目標一律指明其中一邊。

寫入位置 意義 需要的權限
HKLM\Software\Classes 全體使用者共用的註冊 系統管理員權限
HKCU\Software\Classes 只屬於該使用者的註冊 不需要
直接寫入 HKCR 依既有機碼所在的位置決定寫到哪一邊 視情況而定

實務上,安全的做法是一律把註冊明確寫到 HKLM 或 HKCU 其中一邊,並把 HKCR 當成讀取(確認)用。

不要把關聯資料與 COM 註冊的位元數混為一談

在 WOW64 的登錄檔重新導向裡,關聯資料與 COM 註冊的處理方式並不相同。副檔名機碼、ProgID 這類位於 HKLM\Software\Classes 正下方的關聯資料,自 Windows 7 起在 32 位元/64 位元的登錄檔檢視之間是共用的,所以就算由 32 位元安裝程式寫入,也不會跑到 Wow6432Node 那一側。

另一方面,Classes\CLSID 等 COM 註冊相關的部分子機碼則屬於重新導向對象,在後面會談到的殼層擴充功能(處理程序內 COM)註冊上,32 位元/64 位元分開寫就有了意義。細節在「登錄檔的 32bit/64bit 重新導向與虛擬化的陷阱 ── Wow6432Node 與「明明寫入了卻讀不到值」的問題」中討論。

2.3. 應用程式側的註冊 ── App Paths、Applications、RegisteredApplications

與檔案側(副檔名與 ProgID)成對的應用程式側註冊,也有三種。11

  • App Paths(HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths):讓 ShellExecuteEx 只用執行檔名稱就能啟動的註冊。因為不必污染 PATH 環境變數,Microsoft 建議採用這一種。
  • Applications(HKCR\Applications\<應用程式.exe>):定義以「開啟方式」被交付任意檔案時的預設開啟方式,以及應用程式的顯示名稱(FriendlyAppName)。
  • RegisteredApplications + Capabilities:宣告應用程式能處理的副檔名與 MIME 類型,好讓它以候選身分出現在 Windows 的預設應用程式設定畫面。

「我們的應用程式沒有出現在預設應用程式清單裡」這類諮詢,大多是註冊了 ProgID 卻省略了這個 Capabilities 註冊。

應用端的三種註冊應用端註冊有 App Paths、Applications、RegisteredApplications 三種,分別負責只靠檔名啟動、開啟方式的預設開法,以及出現在預設應用設定頁應用端註冊App PathsApplicationsRegisteredApplications只靠檔名啟動開啟方式的預設出現在預設應用頁需要 Capabilities 宣告

圖 3: 應用端註冊有三種,要成為預設應用候選需要 Capabilities 註冊。

2.4. 預設應用程式屬於使用者 ── UserChoice 的保護

就算在副檔名機碼的預設值裡寫上 ProgID,也不一定就會成為預設應用程式。使用者透過「開啟方式」等方式明確選擇的結果,會保存在 HKCU\...\Explorer\FileExts\<副檔名>\UserChoice,而關聯的解析會以這邊為優先。

不要把設計做成直接改寫 UserChoice

Windows 並不支援由程式變更預設應用程式。 預設應用程式的設定被設計成由使用者透過系統設定 UI 完成,UserChoice 的資料經過混淆處理,篩選器驅動程式(UCPD.sys)會阻擋應用程式寫入。在受管理的環境中,群組原則/MDM 原則才是官方手段。4

過去之所以會出現 SetUserFTA 這類「模仿雜湊值再改寫」的工具,正是這道保護的反面。

安裝程式要做的是「準備好被選中」

要放進自家應用程式安裝程式的,是下面三件事。

  1. 正確註冊 ProgID 與 verb。
  2. 把自己加進 OpenWithProgIds。
  3. 必要時把使用者引導到預設應用程式的設定畫面。

不是搶走預設值,而是把使用者可以選擇的狀態準備好。

預設應用解析與 UserChoice 保護使用者明確選擇的結果儲存在 UserChoice 並在關聯解析中優先;UCPD.sys 阻止應用改寫,因此安裝程式能做的是註冊成候選並引導到設定頁優先UCPD.sys 阻止UserChoice(使用者的選擇)關聯解析副檔名鍵預設值從應用改寫安裝程式的工作註冊 ProgID 與 verb加到 OpenWithProgIds引導到設定頁

圖 4: 關聯解析優先採用使用者的選擇(UserChoice),作業系統保護它不被應用改寫。

3. 「開啟」以外的 verb ── print、edit、runas 與自訂 verb

3.1. 標準 verb 與自訂 verb

verb 不只有 open。作業系統知道其含意的標準 verb,除了 open 之外還有 edit、print、play、preview 等,而標準 verb 會依作業系統的地區設定自動取得顯示名稱。按兩下時使用的預設 verb,是依 shell 機碼的預設值→登錄檔上的第一個 verb→open→openwith 的順序決定。12

預設 verb 的決定順序按兩下使用的預設 verb,是依 shell 鍵預設值、登錄檔中第一個 verb、open、openwith 的順序找到的第一個若無若無若無shell 鍵預設值登錄檔中第一個 verbopenopenwith

圖 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

命令列的引號事故沒加引號的命令會在空格處被切開,誤當成啟動 My 並帶引數 Program.exe,因此可能含空格的 EXE 路徑、以及代表所選檔案路徑的 %1,都應一律用引號包起來在空格處切開沒加引號的命令被誤當成啟動別的 EXE有引號的命令按預期啟動用引號包住 EXE 路徑%1 也一律用引號包住

圖 6: 沒加引號的命令會在空格處被錯誤切開,因此 EXE 路徑與 %1 都要一律用引號包住。

3.4. 動手寫 DLL 之前,先確認靜態 verb 是否就夠

到這裡為止只靠登錄檔的機制(靜態 verb),一個 DLL 都不用寫就能實現,也沒有讓檔案總管變得不穩定的風險。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。
處理程序內擴充功能的連帶損害結構殼層擴充功能 DLL 不只載入到檔案總管,也載入到開啟檔案對話方塊的任何應用程序,因此擴充功能的崩潰或掛住會波及整個宿主程序處理程序內載入處理程序內載入殼層擴充功能 DLL檔案總管開啟對話方塊的任何應用崩潰或掛住會擴散顯示時不要做慢工作

圖 7: 擴充功能 DLL 在宿主處理程序中執行,當掉或卡住都會波及整個宿主。

調查「打開某個資料夾檔案總管就卡住」「按右鍵要等五秒」這類諮詢時,原因不是自家應用程式而是第三方的殼層擴充功能,其實並不罕見。縮小範圍的做法會在第 8 章說明。

4.3. 位元數要一致 ── 64 位元環境必須用 64 位元 DLL

處理程序內 DLL 的位元數必須和載入它的處理程序一致。64 位元 Windows 的檔案總管是 64 位元處理程序,所以只建置成 32 位元的殼層擴充功能 DLL 根本不會被載入,也完全不會出現在選單上。而且不會有任何錯誤訊息,這是「明明註冊了卻沒出現」的經典原因。

這不代表連應用程式本體都得改成 64 位元

32 位元的應用程式本體搭配 64 位元的殼層擴充功能 DLL 是正當的組態,但要注意 COM 註冊會依位元數分開(Wow6432Node)。另外,從 verb 的 command 啟動的是另一個處理程序的 EXE,因此不受這項限制(維持 32 位元 EXE 也沒問題)。

殼層擴充功能 DLL 的位元數對齊64 位檔案總管能載入的殼層擴充功能 DLL 只有 64 位;僅 32 位的 DLL 不會出現在選單上也沒有錯誤;從 verb command 啟動的 EXE 是獨立程序,不受此限制可載入無法載入獨立程序64 位檔案總管64 位殼層擴充功能 DLL僅 32 位的 DLL沒有錯誤、選單上不出現從 verb 啟動的 EXE維持 32 位沒問題

圖 8: 能被 64 位元檔案總管載入的只有 64 位元 DLL,由 verb 啟動的 EXE 不受此限制。

4.4. 不該用受控程式碼撰寫的理由

我們常被問到「能不能用 C# 寫殼層擴充功能」,但Microsoft 明確表示:以受控程式碼(.NET)撰寫處理程序內的殼層擴充功能並不建議,也不在支援範圍內。9

理由在於擴充功能會被載入任意處理程序這項性質。讓宿主應用程式變得不穩定的因素,主要有下面三個。

  • CLR 的版本衝突。 尤其在 .NET Framework 4 以前會出問題。
  • 重入。 等待鎖時,CLR 會重入訊息迴圈,這是個問題。
  • 物件存留期。 記憶體回收造成的存留期非決定性,會與 COM 的參考計數約定衝突。

其中有些項目在 .NET Framework 4 之後與現代的 .NET 上已經緩解,但官方立場並沒有改變。

實務上的方針很簡單。處理程序內擴充功能用原生 C++ 撰寫;想用受控程式碼,就做成由 verb 的 command 啟動的一般 EXE,或是在另一個處理程序中執行的處理程序外擴充功能(預覽處理常式等)。9

判斷託管程式碼是否允許在檔案總管內執行的處理程序內擴充功能原則上用原生 C++ 編寫;若要用託管程式碼,就做成從 verb command 啟動的普通 EXE,或在獨立程序執行的程序外擴充功能是否在處理程序內執行?用原生 C++ 寫託管程式碼沒問題CLR/重入風險使宿主變得不穩定由 verb 啟動的 EXE程序外預覽

圖 9: 處理程序內擴充功能原則上用原生 C++,受控程式碼只用在另一個處理程序執行的組態。

5. Windows 11 的新內容功能表 ── 選單變成兩層

這裡依照被移到舊選單的原因 → 註冊到新選單 → 保留既有安裝程式的做法的順序說明。只是「用這個應用程式開啟」的關聯,和新增自訂命令之間的差別,會在 5.4 節確認。

5.1. 發生了什麼事

Windows 11 改版了檔案總管的右鍵選單。剪下、複製等變成上方的圖示列,「開啟」「開啟方式」被集中放在上方,應用程式新增的命令則被歸類到殼層標準命令的下方。同一個應用程式若新增多個命令,會收進帶有應用程式名稱的飛出視窗(子選單)。5

而最關鍵的是這一點:傳統以 IContextMenu 為基礎的殼層擴充功能並沒有被移除,而是被移到用「顯示更多選項」(Shift+F10)開啟、原封不動載入 Windows 10 選單的舊選單那一側。5 開頭那則諮詢裡「選單被藏起來了」的真正原因,就是這種兩層化。

Windows 11 拆成兩層的右鍵選單右鍵先開啟的是新選單;能出現在那裡的命令只有以 IExplorerCommand 與套件識別註冊的項;傳統 IContextMenu 擴充功能被移到用顯示更多選項開啟的舊選單顯示更多選項 Shift+F10對檔案右鍵單擊新選單(Windows 11)IExplorerCommand + 識別的命令舊選單(Windows 10 選單)傳統 IContextMenu 擴充功能多個命令收進浮出

圖 10: 新選單上只放 IExplorerCommand + 識別的命令,傳統擴充功能被移到舊選單那一側。

5.2. 放上新選單的官方路徑 ── IExplorerCommand + 資訊清單註冊

要把自訂命令放上新選單,官方路徑是實作 IExplorerCommand 的原生 DLL,加上以 MSIX 資訊清單註冊。6

資訊清單裡要寫兩種宣告

下面的例子,前半宣告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 要和檔案總管的架構(64 位元/ARM64)一致。6

建構選單的方法要快速返回

IExplorerCommand 本身是從 Windows 7 時代就有的介面,要實作標題(GetTitle)、圖示(GetIcon)、啟用/停用/隱藏的狀態(GetState)與執行(Invoke)。方法是從 UI 執行緒呼叫的,因此禁止存取網路資源,建構選單這類方法必須快速返回。繁重的處理要放到 Invoke 之後再做。146

新選單註冊的清單結構MSIX 清單的 COM 伺服器宣告把 CLSID 對映到 DLL,右鍵選單擴充功能宣告用 ItemType 與 Verb 把目標與實現綁在一起,於是自訂命令出現在新選單上把 CLSID 對映到 DLL用 ItemType 與 Verb 指定MSIX 清單COM 伺服器宣告選單擴充功能宣告IExplorerCommand 實現 DLL命令出現在新選單目標是副檔名、所有檔案等

圖 11: 資訊清單裡的兩個宣告把實作 DLL 與對象綁在一起,命令才會出現在新選單上。

5.3. 未封裝應用程式的選項 ── 用 sparse package 只取得識別

「我們的應用程式只能用 MSI 發布,改成 MSIX 不可行」時的退路,就是 sparse package(帶外部位置的 MSIX)。做一個不含應用程式本體、只有資訊清單的小型 MSIX 並加以簽章,在既有安裝程式的最後註冊它。這樣應用程式就取得了套件識別,也就能進行上述的資訊清單註冊(=顯示在新選單上)。

可用的版本是 Windows 10 版本 2004 以後,而且套件需要以目標電腦所信任的憑證簽章。7 註冊與解除的順序,以及「它是以使用者為單位註冊」這件事該注意什麼,會在 7.3 節說明。

用 sparse package 取得識別的流程現有安裝程式放好應用本體後,以外部位置註冊只有清單的 sparse package,應用就取得套件識別,並能做新選單的清單註冊現有安裝程式放置應用本體sparse package只有清單,沒有本體以外部位置註冊取得套件識別新選單註冊成為可能需要受信任的簽章

圖 12: 把不含本體的 sparse package 以外部位置註冊後,應用程式就取得套件識別。

最大的優點是不必替換安裝程式,對於握有既有 MSI/EXE 安裝程式資產的應用程式來說,這是務實的解法。與全面移轉到 MSIX 的比較,也可參閱「Windows 應用發布方式怎麼選 - MSI / MSIX / ClickOnce / xcopy / 自訂 updater 的判斷表」。

5.4. 關聯的 verb 在新選單上長什麼樣

這點很容易被誤解:第 2〜3 章的關聯(ProgID 與 verb)在新選單上依然有效。 按兩下的預設 verb、「開啟」與「開啟方式」的候選,都是從關聯解析出來的,並顯示在新選單的上方。也就是說,若只是「想讓檔案能用這個應用程式開啟」,在 Windows 11 上不需要任何額外處理。

另一方面,關聯並不是通用的選單擴充機制,所以想把任意自訂命令放到新選單的第一層,就需要 IExplorerCommand 加上套件識別——這就是兩者的分工。6

關聯與新選單的角色分工ProgID 與 verb 關聯在新選單上仍用來解析預設 verb、開啟、開啟方式,並顯示在上方;要把任意自訂命令放到新選單第一層,需要 IExplorerCommand 與識別關聯(ProgID + verb)解析預設/開啟新選單上方Win11 不需額外工作自訂命令IExplorerCommand+識別新選單第一層

圖 13: 關聯在新選單上仍負責「開啟」這一類的解析,只有自訂命令需要 IExplorerCommand + 識別。

6. 實務判斷表 ── 三個選項要選哪一個

先確認 (a) 是否就夠;如果需要把自訂命令顯示在新選單上,就選 (b)。 暫時維持既有的傳統擴充功能則是 (c)。

想實現的事 建議的做法 在 Windows 11 上的呈現 需要的工作與成本
(a) 想用按兩下或「開啟」啟動自家應用程式 關聯 + 靜態 verb(只做登錄檔註冊) 整合顯示在新選單的「開啟」與「開啟方式」 只需安裝程式做登錄檔註冊。不需要 DLL,也沒有額外的簽章要求
(b) 想把針對所選檔案/資料夾的自訂命令放上新選單 實作 IExplorerCommand + MSIX 資訊清單註冊。未封裝的應用程式用 sparse package 賦予識別 新選單的第一層(多個命令會收進以應用程式名稱命名的飛出視窗) 原生 C++ DLL + 套件識別 + 程式碼簽章
(c) 繼續使用既有的傳統 IContextMenu 擴充功能 暫時原樣維持(新開發不要選它) 只出現在「顯示更多選項」(Shift+F10)的舊選單那一側 維持 64 位元建置與 COM 註冊。未來要規劃移轉到 (b)

判斷 1:選擇能滿足需求的最單純做法

第一原則是:(a) 就能解決的需求,不要搬出 (b) 或 (c)。 殼層擴充功能從寫下它的那一刻起,就要為檔案總管的穩定性負責。

判斷 2:把維持舊選單與改善易用性分開考慮

(c) 只是「沒有壞掉」而已,就使用者體驗而言,它會一直被放在較差的位置。日常操作中使用頻率越高的命令,移轉到 (b) 的投資報酬就越大。

三個選項怎麼選若只想按兩下或開啟就啟動,關聯與靜態 verb 就夠;要把自訂命令放到新選單就用 IExplorerCommand 與 MSIX 清單註冊;無法改成 MSIX 就用 sparse package 賦予識別;既有傳統 IContextMenu 擴充功能暫時留在舊選單那一側是否是是否否開啟就夠了?關聯 + 靜態 verb新選單上的自訂命令?能改成 MSIX?IExplorerCommand+MSIXsparse-pkg 識別暫時維持傳統僅舊選單那一側沒有 DLL,風險小

圖 14: 依需求從靜態 verb、IExplorerCommand + 識別、維持傳統這三者中選一個。

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 - 免註冊使用 COM 的機制,以及合用與不合用的情境」)。

7.2. 改完要發出通知 ── SHChangeNotify

註冊、變更或刪除關聯之後,要用 SHChangeNotify 送出 SHCNE_ASSOCCHANGED 事件通知。省略它的話,變更有可能直到重新啟動為止都不會被檔案總管認得。110

// 在安裝程式的自訂動作等處,於變更關聯之後呼叫一次
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)執行的組態。

不過,就算用模擬註冊,拿到註冊的也只有執行那次安裝的使用者。 一台電腦由多位使用者共用時,其他使用者以及之後才建立的使用者並沒有套件識別,新選單上就不會出現命令。

若要讓全體使用者都能用,就要準備以使用者為單位的註冊:例如在應用程式首次啟動時檢查自己的套件註冊,未註冊就註冊。解除安裝時,也要把「從每一位已註冊的使用者解除」納入計畫。

註冊之後選單上沒有生效時

資訊清單註冊要生效,有時需要重新啟動檔案總管(或登出)。6

註冊與移除 sparse package 的順序安裝時在放好檔案後註冊 sparse package;解除安裝時在刪檔案之前先移除註冊;注意註冊只對執行它的使用者有效安裝放置檔案註冊 sparse package解除安裝移除包註冊刪除檔案註冊只對執行中的使用者有效

圖 15: 註冊在放好檔案之後、解除在刪除檔案之前,並注意註冊是以使用者為單位。

7.4. 解除安裝時的清理 ── 該刪什麼、該留什麼

關於解除安裝時的清理,官方指南有明確的方針。1

  • 該刪的:自家的整組 ProgID 機碼、Capabilities/RegisteredApplications 註冊、殼層擴充功能的 CLSID 註冊,以及 sparse package(Remove-AppxPackage)。
  • 該留的:副檔名機碼(.kmrpt)的預設值。即使它仍指著自家的 ProgID,官方建議也是不要刪掉。 因為安裝之後很難判斷是否已有別的應用程式接手了預設值,而且 Windows 遇到預設值所指的 ProgID 未註冊時只會單純忽略,留著並不會造成實質損害。
  • 清理的最後也要呼叫 SHChangeNotify(SHCNE_ASSOCCHANGED)。

「明明解除安裝了,選單上還是留著殘骸」這類問題,多半是這套清理設計有疏漏。

解除安裝時的清理設計解除安裝時刪除內部 ProgID 鍵、CLSID 註冊與 sparse package;留下副檔名鍵預設值,因為未註冊的 ProgID 會被忽略;清理結束時用 SHChangeNotify 通知更改解除安裝刪除留下ProgID 與 CLSID 註冊sparse package副檔名鍵預設值未註冊的 ProgID 會被忽略結束時用 SHChangeNotify 通知

圖 16: 刪掉自家的註冊、留下副檔名機碼的預設值,並在清理的最後送出變更通知。

8. 疑難排解 ── 不出現、出現兩次、變慢

問題分成「不出現」「出現兩次或消不掉」「變慢或當掉」三類來查。最後說明如何在乾淨的環境中確認註冊與善後。

8.1. 選單上不出現

首先要確認你看的是新選單還是舊選單。 在這之上,再依下面的順序縮小範圍。

  1. 你看的是哪一邊的選單:舊做法的註冊只會出現在 Shift+F10 的舊選單那一側。先兩邊都確認。
  2. 位元數:只有 32 位元的殼層擴充功能 DLL 不會被 64 位元的檔案總管載入(4.3 節)。
  3. 註冊位置:HKLM/HKCU 或 Wow6432Node 弄錯了。用 reg query 確認實際的機碼。
  4. 套件註冊:若是針對新選單,就用 Get-AppxPackage 確認有沒有註冊、簽章憑證是否受信任,以及 -ExternalLocation 的路徑,然後重新啟動檔案總管。6
  5. 漏發通知:如果是忘了 SHChangeNotify,可以用「重新啟動檔案總管後會不會生效」來判別。
選單上不出現時的隔離順序先確認你看的是哪一面選單,再依序隔離 DLL 位元數、登錄檔註冊目標、包註冊與簽章,以及漏掉的 SHChangeNotify確認看的是舊或新選單確認 DLL 位元數確認 HKLM 與 HKCU 註冊目標確認包註冊與簽章用重啟判斷是否漏通知

圖 17: 「不出現」時,依看的是哪邊選單、位元數、註冊位置、套件註冊、漏發通知的順序釐清。

8.2. 出現兩次或消不掉

先從重複出現在哪一邊的選單來推測方向。

  • 只在舊選單出現兩次: 懷疑是解除安裝時漏了清理(7.4 節),或是舊版 ProgID 的殘骸。
  • 新舊兩邊都出現: 懷疑是舊做法的登錄檔註冊與 MSIX 資訊清單註冊同時存在。

這兩者都只是典型原因的初步推測,最後仍要確認實際的註冊內容再下判斷。

隔離雙重顯示只在舊選單出現兩次指向清理漏洞或舊 ProgID 等殘留;新舊兩邊都出現兩次指向傳統登錄檔註冊與清單註冊並存僅舊選單新舊兩邊在哪裡出現兩次?殘留並存清理漏洞或留下的舊 ProgID傳統登錄檔註冊與新註冊並存

圖 18: 只在舊選單重複就偏向殘骸類,新舊兩邊都出現就偏向並存類。

8.3. 檔案總管變慢或當掉

按右鍵反應慢、或在特定資料夾當掉時,先盤點已安裝的殼層擴充功能。

  1. 列出擴充功能。 用 NirSoft 的 ShellExView 之類的工具,確認非 Microsoft 出品的擴充功能。
  2. 暫時停用以縮小範圍。 對可疑的項目用二分法逐步排除,找出問題來源的 DLL。若是當掉,事件檢視器裡的「發生錯誤的模組」也是線索。
  3. 若是自家的擴充功能,就檢查建構選單的路徑。 懷疑同步 I/O 或網路存取(4.2 節、5.2 節)。
變重或崩潰時找出元兇 DLL在 ShellExView 列出非 Microsoft 殼層擴充功能,暫時禁用可疑項並用二分法找出元兇 DLL;崩潰時事件檢視器的 faulting module 也是線索盤點殼層擴充功能列出非 Microsoft 的暫時禁用並二分法找出元兇 DLL崩潰時檢查 faulting module

圖 19: 一邊暫時停用非 Microsoft 擴充功能一邊用二分法排除,當掉時併用事件檢視器。

8.4. 驗證時 Windows Sandbox 很方便

驗證殼層整合,基本上就是確認「在乾淨的環境安裝→執行→解除安裝→零殘骸」。這時好用的是 Windows Sandbox(Pro/Enterprise/Education),每次啟動都會在數秒內開出一個全新的拋棄式 Windows,因此安裝程式的註冊與清理測試可以反覆跑很多次。關掉之後一切都會消失,也很適合用來調查登錄檔的殘骸。15

9. 總結

如果換成 Windows 11 之後被反映「選單被藏起來了」,請先用第 6 章的判斷表確認你想實現的到底是什麼。思考的順序分成下面三個階段。

1. 判斷關聯是否就夠

若只是「用這個應用程式開啟」,現在靠關聯與靜態 verb 依然足夠。底層是副檔名機碼→ProgID→verb,而 HKCR 是把 HKLM/HKCU 的 Classes 疊起來、供確認用的檢視。寫入位置要寫明,%1 一定要用引號括起來。

不過,選擇預設應用程式的是使用者。安裝程式不該搶走預設值,而要把自己正確註冊成候選。

2. 決定把自訂命令放到哪一邊的選單

傳統的 IContextMenu 擴充功能,是在以「顯示更多選項」開啟的舊選單那一側運作。要把自訂命令放到新選單,就需要 IExplorerCommand 以及 MSIX 資訊清單註冊。無法全面改成 MSIX 的應用程式,用 sparse package 取得套件識別是務實的解法。

傳統擴充功能的 DLL 是在檔案總管等處理程序內執行的。當掉或延遲都會波及整個宿主,64 位元的宿主需要 64 位元 DLL。以受控程式碼撰寫處理程序內擴充功能並不受支援,原則上要用原生 C++ 實作。

3. 把註冊、變更通知與解除當成一組來驗證

註冊、變更或刪除關聯之後要用 SHChangeNotify 發出通知。解除安裝時刪掉自家的 ProgID 等項目,保留副檔名機碼的預設值。sparse package 是以使用者為單位註冊的,所以在多使用者環境中,也要把每位使用者的註冊與解除納入計畫。

用 Windows Sandbox 反覆執行在乾淨環境安裝→執行→解除安裝→確認殘骸這一輪。

從最單純的手段選起,只有在必要時才進到把自訂命令加入新選單。照這個順序走,就能理清因應的規模,以及該維護的註冊與實作範圍。

相關文章

相關諮詢領域

小村軟體有限公司承接業務應用程式的檔案關聯、右鍵選單與殼層擴充功能的設計與實作,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, Choosing a Static or Dynamic Shortcut Menu Method. 關於應選擇能滿足需求的最單純靜態 verb 做法、IContextMenu 最強大但也最複雜並被歸在不建議的那一側,以及 IExplorerCommand/IExplorerCommandState 才是建議的做法。 ↩ ↩2

  3. Microsoft Learn, HKEY_CLASSES_ROOT Key. 關於 HKEY_CLASSES_ROOT 是 HKLM\Software\Classes 與 HKCU\Software\Classes 的合成檢視、使用者端的定義優先於電腦端,以及寫入時的分派規則。 ↩ ↩2

  4. Microsoft Learn, Windows app defaults platform. 關於變更預設應用程式被設計成只能透過系統設定 UI 進行、使用者設定資料經過混淆並由篩選器驅動程式(UCPD.sys)防寫、以登錄檔為基礎的變更不受支援,以及在受管理環境中使用群組原則/MDM 原則。 ↩ ↩2

  5. Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. 關於 Windows 11 新內容功能表的設計、以 IExplorerCommand 加上應用程式識別進行擴充、把「開啟」與「開啟方式」放在上方、把多個命令收進帶有應用程式名稱的飛出視窗,以及傳統 IContextMenu 擴充功能會以「顯示更多選項」(Shift+F10)的 Windows 10 選單形式載入。 ↩ ↩2 ↩3

  6. 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

  7. Microsoft Learn, Grant package identity by packaging with external location. 關於不更動既有安裝程式,就能以註冊帶外部位置的套件(sparse package)取得套件識別、自 Windows 10 版本 2004 起可用,以及讓必須具備識別的 Windows 功能(內容功能表註冊、通知等)變得可用。 ↩ ↩2 ↩3

  8. Microsoft Learn, Working with Shell Extensions. 關於殼層擴充處理常式的種類、擴充功能是被載入檔案總管(以及承載殼層的處理程序)的處理程序內 COM DLL,因此當掉或卡住會波及整個檔案總管、以 ThreadingModel=Apartment 註冊,以及在撰寫殼層擴充功能之前應先考慮更單純的替代做法。 ↩ ↩2 ↩3

  9. Microsoft Learn, Guidance for Implementing In-Process Extensions. 關於 Microsoft 不建議也不支援用受控程式碼實作處理程序內的殼層擴充功能、理由包括 CLR 版本衝突、重入與物件存留期的非決定性,以及處理程序外擴充功能(預覽處理常式,或從 shell\verb\command 啟動)可以使用受控程式碼。 ↩ ↩2 ↩3

  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 的顯示名稱由作業系統提供、以 Extended 設定的擴充 verb、以 DDE 命令做關聯已屬不建議使用(Deprecated),以及 64 位元環境中 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 把檔案總管的右鍵選單拆成了新舊兩層。能把項目放上新選單的,只有實作 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。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽