SSMS 殼層命令與 F12 接線
本頁包含 F12 怎麼接到殼層命令、濾鏡的回報要求、命令表幾種看起來正確卻安靜失效的寫法, 以及改完命令表為什麼一定要重新安裝。 平台硬規則見平台護欄。
F12 怎麼接到的
現代編輯器的 ICommandHandler 接不到 F12。 這一點是實測出來的,不是推論: 處理常式建立了、MEF 也沒過期(紀錄檔有「SQL 編輯器已建立」),按 F12 之後紀錄檔 卻連一行都沒有。原因是現代管線只看得到「核心編輯器決定轉進來」的命令,而 SSMS 的 查詢視窗在核心編輯器外面還有自己的文件檢視與舊版語言服務,兩者都排在命令鏈更前面。
而且 SSMS 22 根本沒有把 F12 綁在 Edit.GoToDefinition 上。 這一點也是實測 出來的:濾鏡掛上之後從來沒有收到 VSStd97/GotoDefn,紀錄檔顯示命令是從命令表的 鍵繫結進來的。因此是兩條路:
| 路徑 | 接得到的情況 | 紀錄檔那一行 |
|---|---|---|
命令表的全域 F12 鍵繫結(Menus.vsct) | 預設;F12 實際走的就是這一條 | 移至定義命令抵達 SqlAssist(命令表) |
SqlShellCommandFilter(IVsTextView 上的 IOleCommandTarget 濾鏡) | 使用者自己把 Edit.GoToDefinition 綁到某個鍵 | 移至定義命令抵達 SqlAssist(殼層命令濾鏡) |
兩者互斥——鍵繫結只會解析出一個命令——所以不會執行兩次。SqlDefinitionOpener 的重入防護是最後一道保險。
濾鏡的第二個用途是把按鍵還給自己的 Popup。殼層照作用中的視窗框架(Popup 開著 時仍是查詢視窗)預先解析按鍵,Tab、↑↓、Enter、Delete 就沿著命令鏈進來,不攔就是改到 後面那份 SQL——與焦點在哪個視窗無關,換視窗殼層修不掉。作法與規則見 按鍵交還。
包夾命令兩條路都有:命令表把 Ctrl+Alt+S 綁在自己的命令上,濾鏡再攔 VSStd2K/SURROUNDWITH,接使用者自己綁在 Edit.SurroundWith 上的鍵。命令表不綁 Ctrl+K, Ctrl+S——那條和絃在 SSMS 上解析得到的是內建行為,搶不到(見 片段包夾)。 VSStd2K 也是打字用的 TYPECHAR 那一組,所以那個分支每個按鍵都會走進來, 下一步只剩一次整數比對。
鍵繫結用全域範圍而不是某個編輯器範圍:SSMS 的查詢視窗用的是它自己的編輯器 工廠,猜錯 GUID 的症狀是繫結安靜地不生效。全域一定註冊得上,代價是在物件總管、 結果格線上也綁到,所以命令自己在沒有 SQL 編輯器時回報停用——停用的命令不會被 派送,F12 在那些地方就照常落回殼層。
濾鏡為什麼一定要回報 supported + enabled
QueryStatus 那一段不是形式。殼層在派送 Exec 之前會先問過整條命令鏈, 沒有任何目標認領的命令就是停用的,而停用的命令連 Exec 都不會發出去—— 症狀正好是「按下去完全沒反應,紀錄檔也什麼都沒有」。
濾鏡在最熱的路徑上
QueryStatus 在每一次按鍵、每一次閒置與每一次開選單時都會被呼叫數十次, Exec 則是每打一個字元一次。因此先比命令群組 GUID,不相符立刻原封轉給下一個目標, 中間不配置物件、不取服務、不記錄。唯一的例外是讀一次靜態旗標 ShellKeyCapture.IsActive, 為 false 時連 Guid 複製都不做。
按了某個鍵卻沒反應時
打開 sqlAssist.diagnostics.verboseLogging,紀錄檔會出現兩類新的行。
「殼層命令濾鏡已掛上」 — 沒有這一行就是濾鏡沒掛上(取不到 IVsTextView), 只剩另外兩條路。
「未處理的殼層命令:VSStd97/GotoDefn(925)」 — 濾鏡看到、但本擴充沒有處理的 命令,每一個只記第一次。全部都記的話打字時的 TYPECHAR 會把紀錄檔灌爆, 完全不記就沒辦法回答「按下那個鍵到底送出了什麼命令」,而那正是這一類問題唯一 需要的資訊。
於是流程變成:按下那個鍵,看紀錄檔多了哪一行,要攔的就是它——把它的命令群組與 識別碼加進 SqlShellCommandFilter 即可。這條路以後接任何殼層命令都一樣走。
三種都沒有出現,才是「這個鍵根本沒有繫結」,那要在 Menus.vsct 自己綁。
紀錄檔連「SQL 編輯器已建立」都沒有,是 MEF 快取過期,與命令無關, 見偵錯。
命令表上「看起來正確但安靜失效」的寫法
<Menu> 的 <Parent> 必須是一個 Group(IDG_*),不能是 Menu(IDM_*)。 掛錯層沒有編譯錯誤也沒有執行期例外,pkgdef 與命令處理器照常註冊,整個子選單就是不出現。
命令表只有一種語言(英文,中性語言),選單文字靠 TextChanges 換。 沒標這個旗標,QueryStatus 設的 Text 殼層照收卻不換字;每顆按鈕的文字都在 SqlAssistCommands.MenuLabel,漏一顆就停在命令表的英文;ButtonText 要與 MenuText.en 一致,否則套件載入前後換一次字。tools/Test-CommandTable.ps1 擋下三種。 實測標了之後,工具列提示顯示的是目前的 Text,命令表的 ToolTipText 不再出現,所以不寫它。
<Menu> 上的 <Icon> 會被 VSCT 編譯器整個丟掉。 實測:把那一行刪掉、或移到 <Parent> 之前,編出來的 .cto 與原檔 md5 完全相同;只有補上 IconIsMoniker 才會變, 而換成 moniker 圖示卻不加旗標又回到原值。反編譯 Menus.cto 也看得到 SqlAssistMenu 只剩 Pict/IconAndText 旗標,沒有 <Icon> 元素,同一份檔案的 <Button> 則保留得好好的。 所以工具選單上的 SqlAssist 是純文字,與 VS 內建子選單一致;品牌識別在工具列、右鍵選單 與關於視窗上。tools/Test-CommandTable.ps1 擋得下前者,後者只有這一段記錄得下來。
改了命令表一定要重新安裝
新增命令、選單項目或鍵繫結之後,只部署 DLL 是沒有用的。命令表雖然編譯在 SqlAssist.Ssms22.dll 的資源裡,殼層卻是照 pkgdef 的 Menus.ctmenu, N 那個 N 決定要不要重讀;Deploy-DebugExtension.ps1 不會更新 pkgdef,清快取也救不了。 症狀是新的選單項目不出現、新綁的鍵完全沒反應,而且沒有任何錯誤。
兩件事因此綁在一起:
- 改了
Menus.vsct就把SqlAssistPackage的ProvideMenuResource版號加一。 - 用
Install-Extension.ps1重新安裝,不要用Deploy-DebugExtension.ps1。
第 1 件會讓第 2 件變成強制:部署腳本會比對兩邊的 N,不一致就直接擋下來並要求重裝。