首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ Builder 開發 Shell 擴充:右鍵選單擴充處理器完整教學

     編輯:關於C++

什麼是內容選單擴充處理器

當使用者在總管中右鍵點擊檔案/資料夾時,Shell 會顯示內容選單。預設包含「剪下」、「複製」等標準項目。透過實作 Context Menu Handler(一種 In-process COM 物件),可動態為特定檔案類型添加自訂選單項目,實現「以特定程式開啟」、「檔案加密」、「上傳雲端」等功能。

COM 介面架構

除 IUnknown 外,必須實作:

  • IShellExtInit:Shell 初始化處理器,傳遞選取項目資訊
  • IContextMenu:核心介面,提供選單項目增減、說明文字、命令執行
  • IContextMenu2/3(選擇性):支援擁有者繪製選單項目

三大核心方法詳解

1. IShellExtInit::Initialize

從 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;
}

2. IContextMenu::QueryContextMenu

將自訂項目插入選單,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);
}

3. IContextMenu::InvokeCommand

使用者點選自訂項目時觸發,此處彈出檔案選擇對話框,以選定程式開啟目標檔案:

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;
}

4. IContextMenu::GetCommandString(可選)

提供狀態列說明文字,簡單實作直接回傳 S_OK。

註冊機制

檔案類型關聯

HKEY_CLASSES_ROOT
  \ (如 .txt -> txtfile)
    \ShellEx
      \ContextMenuHandlers
        \ (預設值 = {Your-CLSID})

資料夾關聯

HKEY_CLASSES_ROOT
  \Folder (或 Directory)
    \ShellEx
      \ContextMenuHandlers
        \ (預設值 = {Your-CLSID})

雙擊觸發 QueryContextMenu(可選)

在 Handler 的 CLSID 下建立 ShellEx\MayChangeDefaultMenu 子鍵,雙擊檔案時 Shell 會以 CMF_DEFAULTONLY 呼叫 QueryContextMenu,允許處理器改變預設動作。

C++ Builder 專案建立步驟

  1. File → New → ActiveX Library,專案命名 MyContextMenu
  2. File → New → COM Object,CoClass 命名 AddContextMenu,執行緒模型 Apartment
  3. 於產生的類別 TAddContextMenuImpl 加入介面:
#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()
};
  1. 實作上述三個核心方法
  2. 編譯為 DLL
  3. Run → Register ActiveX Server 註冊 COM 伺服器
  4. 手動匯入註冊表:BCB 無自動產生 .rgs 腳本,需自行在 HKEY_CLASSES_ROOT\txtfile\ShellEx\ContextMenuHandlers 建立子鍵,值設為 {CLSID_AddContextMenu}

測試驗證

總管中右鍵任意 .txt 檔 → 出現「選擇開啟方式...」 → 選取程式 → 以該程式開啟檔案。

BCB 開發遺憾與替代方案

  • BCB 產生檔案龐雜,註冊腳本需手工維護
  • 建議改用 Visual C++ (ATL) 或 Delphi 開發 Shell 擴充,工具鏈成熟、除錯容易
  • 若必須用 BCB,可參考 TUpdateRegistry 覆寫註冊邏輯,但實測該函數不被呼叫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved