當使用者在總管中右鍵點擊檔案/資料夾時,Shell 會顯示內容選單。預設包含「剪下」、「複製」等標準項目。透過實作 Context Menu Handler(一種 In-process COM 物件),可動態為特定檔案類型添加自訂選單項目,實現「以特定程式開啟」、「檔案加密」、「上傳雲端」等功能。
除 IUnknown 外,必須實作:
IShellExtInit:Shell 初始化處理器,傳遞選取項目資訊IContextMenu:核心介面,提供選單項目增減、說明文字、命令執行IContextMenu2/3(選擇性):支援擁有者繪製選單項目從 LPDATAOBJECT 取出使用者選取的檔案路徑(假設單選):
HRESULT __stdcall TAddContextMenuImpl::Initialize(
LPCITEMIDLIST pidlFolder, LPDATAOBJECT lpdobj, HKEY hkeyProgID) {
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
if (FAILED(lpdobj->GetData(&fmt, &stg))) return E_FAIL;
hDrop = (HDROP)GlobalLock(stg.hGlobal);
if (!hDrop) { ReleaseStgMedium(&stg); return E_OUTOFMEMORY; }
DragQueryFile(hDrop, 0, g_szFilePath, MAX_PATH); // 取第一個檔案
GlobalUnlock(stg.hGlobal);
ReleaseStgMedium(&stg);
return S_OK;
}
將自訂項目插入選單,ID 必須落在 idCmdFirst ~ idCmdLast 區間:
HRESULT __stdcall TAddContextMenuImpl::QueryContextMenu(
HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) {
if (!(CMF_DEFAULTONLY & uFlags)) { // 非僅雙擊預設命令時
InsertMenu(hmenu, indexMenu, MF_STRING | MF_BYPOSITION,
idCmdFirst, _T("選擇開啟方式..."));
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1); // 新增 1 項
}
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
}
使用者點選自訂項目時觸發,此處彈出檔案選擇對話框,以選定程式開啟目標檔案:
HRESULT __stdcall TAddContextMenuImpl::InvokeCommand(LPCMINVOKECOMMANDINFO pici) {
if (HIWORD(pici->lpVerb) == 0) { // 以 ID 呼叫
if (LOWORD(pici->lpVerb) == 0) { // 第一個項目 (idCmdFirst)
TOpenDialog* Dlg = new TOpenDialog(NULL);
Dlg->Title = "開啟 \"" + String(g_szFilePath) + "\"";
Dlg->Options << ofFileMustExist << ofPathMustExist << ofNoChangeDir;
if (Dlg->Execute()) {
ShellExecute(pici->hwnd, "open", Dlg->FileName.c_str(),
g_szFilePath, NULL, SW_SHOW);
}
delete Dlg;
return S_OK;
}
}
return S_FALSE;
}
提供狀態列說明文字,簡單實作直接回傳 S_OK。
HKEY_CLASSES_ROOT \(如 .txt -> txtfile) \ShellEx \ContextMenuHandlers \ (預設值 = {Your-CLSID})
HKEY_CLASSES_ROOT
\Folder (或 Directory)
\ShellEx
\ContextMenuHandlers
\ (預設值 = {Your-CLSID})
在 Handler 的 CLSID 下建立 ShellEx\MayChangeDefaultMenu 子鍵,雙擊檔案時 Shell 會以 CMF_DEFAULTONLY 呼叫 QueryContextMenu,允許處理器改變預設動作。
MyContextMenuAddContextMenu,執行緒模型 ApartmentTAddContextMenuImpl 加入介面:#include // IShellExtInit, IContextMenu 定義
class ATL_NO_VTABLE TAddContextMenuImpl :
public CComObjectRootEx,
public CComCoClass,
public IShellExtInit,
public IContextMenu,
public IAddContextMenu // 專案自動產生的介面
{
private:
char g_szFilePath[MAX_PATH];
public:
// IShellExtInit
STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY);
// IContextMenu
STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO);
STDMETHOD(GetCommandString)(UINT, UINT, UINT*, LPSTR, UINT);
BEGIN_COM_MAP(TAddContextMenuImpl)
COM_INTERFACE_ENTRY(IAddContextMenu)
COM_INTERFACE_ENTRY(IContextMenu)
COM_INTERFACE_ENTRY(IShellExtInit)
END_COM_MAP()
};
HKEY_CLASSES_ROOT\txtfile\ShellEx\ContextMenuHandlers 建立子鍵,值設為 {CLSID_AddContextMenu}總管中右鍵任意 .txt 檔 → 出現「選擇開啟方式...」 → 選取程式 → 以該程式開啟檔案。
TUpdateRegistry 覆寫註冊邏輯,但實測該函數不被呼叫