使用動態動詞自訂快捷方式功能表

快顯功能表處理常式也稱為操作功能表處理常式或動詞處理常式。 快捷方式功能表處理常式是檔案類型處理常式的類型。

本主題的組織方式如下:

關於靜態和動態動詞

強烈建議您使用其中一個靜態動詞方法來實作快捷方式功能表。 建議您遵循建立操作功能表處理常式 的一節 中提供的指示。 若要在 Windows 7 和更新版本中取得靜態動詞的動態行為,請參閱建立操作功能表處理常式中的 <取得靜態動詞的 動態行為>。 如需靜態動詞實作的詳細資料,以及要避免的動態動詞,請參閱 為快捷方式功能表 選擇靜態或動態動詞。

如果您必須註冊檔案類型的動態動詞來擴充檔案類型的快捷方式功能表,請遵循本主題稍後提供的指示。

注意

註冊在 32 位應用程式內容中運作的處理常式時,有 64 位 Windows 的特殊考慮:當殼層動詞在 32 位應用程式的內容中叫用時,WOW64 子系統會將檔案系統存取重新導向至某些路徑。 如果您的 .exe 處理常式儲存在其中一個路徑中,則無法在此內容中存取。 因此,作為解決方法,請將 .exe 儲存在未重新導向的路徑中,或儲存啟動實際版本的 .exe 存根版本。

 

快顯功能表處理常式如何使用動態動詞

除了 IUnknown 之外,快捷方式功能表處理常式也會匯出下列其他介面,以處理實作擁有者繪製功能表項目所需的傳訊:

如需擁有者繪製功能表項目的詳細資訊,請參閱 使用功能表 建立 擁有者繪製的功能表項目 一節。

Shell 會使用 IShellExtInit 介面來初始化處理常式。 當 Shell 呼叫 IShellExtInit::Initialize 時,它會傳入具有物件名稱的資料物件,以及包含檔案之資料夾之專案識別碼清單 (PIDL) 的指標。 hkeyProgID 參數是登錄快捷方式功能表控制碼所在的登錄位置。 IShellExtInit::Initialize 方法必須從資料物件擷取檔案名,並將名稱和資料夾的指標儲存在專案識別碼清單 (PIDL) 以供稍後使用。 如需處理常式初始化的詳細資訊,請參閱 實作 IShellExtInit

當指令動詞顯示在快捷方式功能表中時,會先探索它們,然後呈現給使用者,最後叫用。 下列清單詳細說明這三個步驟:

  1. Shell 會呼叫 ICoNtextMenu::QueryCoNtextMenu ,其會傳回一組動詞,該動片語可以根據專案或系統的狀態。
  2. 系統會傳入 HMENU 控制碼,方法可用來將專案新增至快捷方式功能表。
  3. 如果使用者按一下其中一個處理常式的專案,Shell 會呼叫 ICoNtextMenu::InvokeCommand 處理常式接著可以執行適當的命令。

避免因無限定動詞名稱而發生衝突

因為動詞是針對每個類型註冊,因此不同的專案上可以使用相同的動詞名稱做為動詞。 這麼做可讓應用程式參考與專案類型無關的常見動詞。 雖然這項功能很有用,但使用不合格的名稱可能會導致與多個選擇相同動詞名稱的獨立軟體廠商(ISV)發生衝突。 若要避免這種情況,請一律使用 ISV 名稱前置詞動詞,如下所示:

ISV_Name.verb

一律使用應用程式特定的 ProgID。 採用將副檔名對應至 ISV 的慣例,提供 ProgID 可避免潛在的衝突。 不過,由於某些專案類型不會使用此對應,因此需要廠商唯一的名稱。 將動詞新增至可能已註冊該動詞的現有 ProgID 時,您必須先移除舊動詞的登錄機碼,再新增自己的動詞。 您必須這樣做,以避免合併兩個動詞動詞的動詞資訊。 無法這麼做會導致無法預期的行為。

使用動態動詞註冊快捷方式功能表處理常式

快捷方式功能表處理常式與檔案類型或資料夾相關聯。 針對檔案類型,處理常式會在下列子機碼下註冊。

HKEY_CLASSES_ROOT
   Program ID
      shellex
         ContextMenuHandlers

若要建立快捷方式功能表處理常式與檔案類型或資料夾的關聯,請先在 CoNtextMenuHandlers 子機碼下 建立子機碼。 為處理常式命名子機碼,並將子機碼的預設值設定為處理常式類別識別碼 (CLSID) GUID 的字串形式。

然後,若要將快捷方式功能表處理常式與不同類型的資料夾產生關聯,請以您對於檔案類型的相同方式註冊處理常式,但在 FolderType 子機碼下 ,如下列範例所示。

HKEY_CLASSES_ROOT
   FolderType
      shellex
         ContextMenuHandlers

如需您可以註冊處理常式之資料夾類型的詳細資訊,請參閱 註冊殼層延伸模組處理常式

如果檔案類型有與其相關聯的快捷方式功能表,則按兩下物件通常會啟動預設命令,而且不會呼叫處理常式的 ICoNtextMenu::QueryCoNtextMenu 方法。 若要指定當按兩下物件時,應該呼叫處理常式的 ICoNtextMenu::QueryCoNtextMenu 方法,請在處理常式的 CLSID 子機碼下建立子機碼,如下所示。

HKEY_CLASSES_ROOT
   CLSID
      {00000000-1111-2222-3333-444444444444}
         shellex
            MayChangeDefaultMenu

按兩下與處理常式相關聯的物件時, 會呼叫 ICoNtextMenu::QueryCoNtextMenu ,並在 uFlags 參數中 設定CMF_DEFAULTONLY 旗標。

只有在可能需要變更快顯功能表的預設動詞時,快捷方式功能表處理常式才應該設定 MayChangeDefaultMenu 子機碼。 設定此子機碼會強制系統在按兩下相關聯的專案時載入處理常式的 DLL。 如果您的處理常式未變更預設動詞,則不應該設定此子機碼,因為這樣做會導致系統不必要地載入 DLL。

下列範例說明啟用 .myp 檔案類型之快捷方式功能表處理常式的登錄專案。 處理常式的 CLSID 子機碼包含 MayChangeDefaultMenu 子機碼,以確保當使用者按兩下相關物件時會呼叫處理常式。

HKEY_CLASSES_ROOT
   .myp
      (Default) = MyProgram.1
   CLSID
      {00000000-1111-2222-3333-444444444444}
         InProcServer32
            (Default) = C:\MyDir\MyCommand.dll
            ThreadingModel = Apartment
         shellex
            MayChangeDefaultMenu
   MyProgram.1
      (Default) = MyProgram Application
      shellex
         ContextMenuHandler
            MyCommand = {00000000-1111-2222-3333-444444444444}

實作 ICoNtextMenu 介面

ICoNtextMenu 是最強大的,也是最複雜的實作方法。 強烈建議您使用其中一個靜態動詞方法實作動詞。 如需詳細資訊,請參閱 為快捷方式功能表 選擇靜態或動態動詞。 ICoNtextMenu 有三種方法: GetCommandString InvokeCommand QueryCoNtextMenu ,這裡會詳細討論。

ICoNtextMenu::GetCommandString 方法

處理常式的 ICoNtextMenu::GetCommandString 方法可用來傳回動詞的正式名稱。 這個方法是一個選擇項目。 在 Windows XP 和舊版 Windows 中,當 Windows 檔案總管具有狀態列時,這個方法會用來擷取功能表項目 [狀態] 列中顯示的解說文字。

idCmd 參數會保存呼叫 ICoNtextMenu::QueryCoNtextMenu 所定義的命令識別碼位移。 如果要求說明字串, uFlags 將會設定為 GCS_HELPTEXTW 。 將說明字串複製到 pszName 緩衝區,並將其 轉換成 PWSTR 。 將 uFlags 設定 GCS_VERBW ,以要求動詞字串。 將適當的字串複製到 pszName ,就像說明字串一樣。 快捷方式 功能表處理常式不會使用GCS_VALIDATEA GCS_VALIDATEW 旗標。

下列範例示範 ICoNtextMenu::GetCommandString 簡單實作,其對應于 本主題的 ICoNtextMenu::QueryCoNtextMenu 方法一節中 提供的 ICoNtextMenu ::QueryCoNtextMenu 範例。 因為處理常式只會新增一個功能表項目,因此只能傳回一組字串。 方法會測試 idCmd 是否 有效,如果為 ,則會傳回要求的字串。

StringCchCopy 函式可用來將要求的字串 複製到 pszName,以確保複製的字串不會超過 cchName 指定的緩衝區大小。 此範例只會實作 uFlags 的 Unicode 值支援,因為只有自 Windows 2000 以來已在 Windows 檔案總管中使用那些值

IFACEMETHODIMP CMenuExtension::GetCommandString(UINT idCommand, 
                                                UINT uFlags, 
                                                UINT *pReserved, 
                                                PSTR pszName, 
                                                UINT cchName)
{
    HRESULT hr = E_INVALIDARG;

    if (idCommand == IDM_DISPLAY)
    {
        switch (uFlags)
        {
            case GCS_HELPTEXTW:
                // Only useful for pre-Vista versions of Windows that 
                // have a Status bar.
                hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), 
                                    cchName, 
                                    L"Display File Name");
                break; 

            case GCS_VERBW:
                // GCS_VERBW is an optional feature that enables a caller
                // to discover the canonical name for the verb passed in
                // through idCommand.
                hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName), 
                                    cchName, 
                                    L"DisplayFileName");
                break; 
        }
    }
    return hr;
}

ICoNtextMenu::InvokeCommand 方法

當使用者按一下功能表項目,告訴處理常式執行相關聯的命令時,會呼叫這個方法。 pici 參數指向包含所需資訊的結構。

雖然 pici 在 Shlobj.h 中宣告為 CMINVOKECOMMANDINFO 結構,但實際上通常會指向 CMINVOKECOMMANDINFOEX 結構。 此結構是 CMINVOKECOMMANDINFO 擴充版本,而且有數個額外的成員,可讓您傳遞 Unicode 字串。

檢查 pici cbSize 成員,以判斷傳入哪一個結構。 如果它是 CMINVOKECOMMANDINFOEX 結構,且 fMask 成員已 設定CMIC_MASK_UNICODE 旗標,請將 pici 轉換成 CMINVOKECOMMANDINFOEX 。 這可讓應用程式使用 結構最後五個成員中包含的 Unicode 資訊。

結構的 lpVerb lpVerbW 成員用來識別要執行的命令。 下列兩種方式之一會識別命令:

  • 依命令的動詞字串
  • 依命令的識別碼位移

若要區分這兩個案例,請檢查 ANSI 案例的 lpVerb 高階字或 Unicode 案例 的 lpVerbW 。 如果高序字不是零字, lpVerb lpVerbW 會保留動詞字串。 如果高序字為零,則命令位移為 lpVerb 的低序字

下列範例示範 ICoNtextMenu::InvokeCommand 簡單實作,其對應至 本節前後提供的 ICoNtextMenu::QueryCoNtextMenu ::GetCommandString 範例。 方法會先決定傳入哪一個結構。 然後,它會判斷命令是由它的位移或動詞所識別。 如果 lpVerb lpVerbW 保留有效的動詞或位移,則方法會顯示訊息方塊。

STDMETHODIMP CShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
    BOOL fEx = FALSE;
    BOOL fUnicode = FALSE;

    if(lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
    {
        fEx = TRUE;
        if((lpcmi->fMask & CMIC_MASK_UNICODE))
        {
            fUnicode = TRUE;
        }
    }

    if( !fUnicode && HIWORD(lpcmi->lpVerb))
    {
        if(StrCmpIA(lpcmi->lpVerb, m_pszVerb))
        {
            return E_FAIL;
        }
    }

    else if( fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpcmi)->lpVerbW))
    {
        if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpcmi)->lpVerbW, m_pwszVerb))
        {
            return E_FAIL;
        }
    }

    else if(LOWORD(lpcmi->lpVerb) != IDM_DISPLAY)
    {
        return E_FAIL;
    }

    else
    {
        MessageBox(lpcmi->hwnd,
                   "The File Name",
                   "File Name",
                   MB_OK|MB_ICONINFORMATION);
    }

    return S_OK;
}

ICoNtextMenu::QueryCoNtextMenu 方法

Shell 會呼叫 ICoNtextMenu::QueryCoNtextMenu ,讓快捷方式功能表處理常式將其功能表項目新增至功能表。 它會在 hmenu 參數中 傳入 HMENU 控制碼。 indexMenu 參數會設定為要用於要加入之第一個功能表項目的索引。

處理常式所新增的任何功能表項目都必須有 idCmdFirst idCmdLast 參數中 值之間的識別碼。 一般而言,第一個命令識別碼會設定為 idCmdFirst ,這會針對每個額外的命令遞增一 (1)。 這種做法可協助您避免超過 idCmdLast ,並在殼層呼叫多個處理常式時最大化可用的識別碼數目。

專案識別碼的 命令位移 是識別碼與 idCmdFirst 值之間的差異。 儲存處理常式新增至快捷方式功能表的每個專案的位移,因為 Shell 可能會在後續呼叫 ICoNtextMenu::GetCommandString ICoNtextMenu::InvokeCommand 時使用它來識別專案。

您也應該將動詞 指派 給您新增的每個命令。 動詞命令是字串,可在呼叫 ICoNtextMenu::InvokeCommand ,用來識別命令的位移。 ShellExecuteEx 函式也會使用它來執行快捷方式功能表命令。

有三個旗標可以透過 與快捷方式功能表處理常式相關的 uFlags 參數傳入。 下表說明它們。

旗標 描述
CMF_DEFAULTONLY 使用者已選取預設命令,通常是按兩下 物件。 ICoNtextMenu::QueryCoNtextMenu 應該將控制項傳回殼層,而不需修改功能表。
CMF_NODEFAULT 功能表中沒有專案應該是預設專案。 方法應該將其命令新增至功能表。
CMF_NORMAL 快捷方式功能表通常會顯示。 方法應該將其命令新增至功能表。

 

使用 InsertMenu 或 InsertMenuItem 將功能表項目新增至清單。 然後傳 回 HRESULT 值,其嚴重性設定為 SEVERITY_SUCCESS 。 將程式碼值設定為指派的最大命令識別碼位移,加上一個 (1)。 例如,假設 idCmdFirst 設定為 5,而且您會將三個專案新增至功能表,且命令識別碼為 5、7 和 8。 傳回值應該是 MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1)

下列範例示範插入單一命令的 ICoNtextMenu::QueryCoNtextMenu 簡單實作。 命令的識別碼位移IDM_DISPLAY,設定為零。 m_pszVerb 和m_pwszVerb 變數是私用變數,用來以 ANSI 和 Unicode 格式儲存相關聯的語言獨立動詞字串。

#define IDM_DISPLAY 0

STDMETHODIMP CMenuExtension::QueryContextMenu(HMENU hMenu,
                                              UINT indexMenu,
                                              UINT idCmdFirst,
                                              UINT idCmdLast,
                                              UINT uFlags)
{
    HRESULT hr;
    
    if(!(CMF_DEFAULTONLY & uFlags))
    {
        InsertMenu(hMenu, 
                   indexMenu, 
                   MF_STRING | MF_BYPOSITION, 
                   idCmdFirst + IDM_DISPLAY, 
                   "&Display File Name");

    
        
        hr = StringCbCopyA(m_pszVerb, sizeof(m_pszVerb), "display");
        hr = StringCbCopyW(m_pwszVerb, sizeof(m_pwszVerb), L"display");

        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY + 1));
    }

    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

如需其他動詞實作 工作,請參閱 建立操作功能表處理常式。

快速鍵 (操作) 功能表和快捷方式功能表處理常式

動詞和檔案關聯

為您的快捷方式功能表選擇靜態或動態動詞

快顯功能表處理常式和多個選取動詞的最佳做法

建立快捷方式功能表處理常式

快捷方式功能表參考