[ShellExtension]上下文擴展-IContextMenu實現

還是先亮源碼下載地址:https://git.oschina.net/xiangmu110/template_IContextMenuExt

以下所講與提供下載的源碼不同,但都是一個模子出來的。

下面這段程序實現的是在所有類型文件的右鍵擴展菜單中添加“獲取文件大小”和“顯示全路徑”兩個菜單,並實現其功能並支持多文件。

首先創建一個名爲“ContextMenu”的工程,並添加一個簡稱爲“CalcFileSizeExt”的“ATL簡單對象”,如果不清楚的請看該文章:
[ShellExtension]圖標擴展-IShellIconlayIdentifier實現
這篇文章的開頭講解了,我就不再贅述了。

現在開始講解如何繼承實現“IContextMenu”和“IShellExtInit”接口來達成目的。

// CalcFileSizeExt.h : CCalcFileSizeExt 的聲明

#pragma once
#include "resource.h"       // 主符號

#include "ContextMenu_i.h"

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Windows CE 平臺(如不提供完全 DCOM 支持的 Windows Mobile 平臺)上無法正確支持單線程 COM 對象。定義 _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可強制 ATL 支持創建單線程 COM 對象實現並允許使用其單線程 COM 對象實現。rgs 文件中的線程模型已被設置爲“Free”,原因是該模型是非 DCOM Windows CE 平臺支持的唯一線程模型。"
#endif

# include <vector>//引入頭文件,會使用的vector

using namespace ATL;

// CCalcFileSizeExt

class ATL_NO_VTABLE CCalcFileSizeExt :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CCalcFileSizeExt, &CLSID_CalcFileSizeExt>,
    public IDispatchImpl<ICalcFileSizeExt, &IID_ICalcFileSizeExt, &LIBID_ContextMenuLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IShellExtInit,//接收explorer傳遞的信息
    public IContextMenu//實現右鍵擴展
{
public:
    CCalcFileSizeExt()
    {
    }

    DECLARE_REGISTRY_RESOURCEID(IDR_CALCFILESIZEEXT)

    BEGIN_COM_MAP(CCalcFileSizeExt)
        COM_INTERFACE_ENTRY(ICalcFileSizeExt)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IShellExtInit)//添加入口點
        COM_INTERFACE_ENTRY(IContextMenu)//添加入口點
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

    //用於保存文件路徑
    std::vector<std::wstring> m_vtFilePath;

    //IShellExtInit的接口實現
    virtual HRESULT STDMETHODCALLTYPE Initialize(_In_opt_ PCIDLIST_ABSOLUTE pidlFolder, _In_opt_ IDataObject *pdtobj, _In_opt_ HKEY hkeyProgID);

    //IContextMenu的接口實現
    virtual HRESULT STDMETHODCALLTYPE QueryContextMenu(_In_ HMENU hmenu, _In_ UINT indexMenu, _In_ UINT idCmdFirst, _In_ UINT idCmdLast, _In_ UINT uFlags);

    virtual HRESULT STDMETHODCALLTYPE InvokeCommand(_In_ CMINVOKECOMMANDINFO *pici);

    virtual HRESULT STDMETHODCALLTYPE GetCommandString(_In_ UINT_PTR idCmd, _In_ UINT uType, _Reserved_ UINT *pReserved, _Out_writes_bytes_((uType & GCS_UNICODE) ? (cchMax * sizeof(wchar_t)) : cchMax) _When_(!(uType & (GCS_VALIDATEA | GCS_VALIDATEW)), _Null_terminated_) CHAR *pszName, _In_ UINT cchMax);

    //獲取所有文件大小
    void ShowAllFileSIze();

    //顯示所有文件路徑
    void ShowAllFilePath();
};

OBJECT_ENTRY_AUTO(__uuidof(CalcFileSizeExt), CCalcFileSizeExt)
// CalcFileSizeExt.cpp : CCalcFileSizeExt 的實現

#include "stdafx.h"
#include "CalcFileSizeExt.h"

// CCalcFileSizeExt

HRESULT STDMETHODCALLTYPE CCalcFileSizeExt::Initialize(_In_opt_ PCIDLIST_ABSOLUTE pidlFolder, _In_opt_ IDataObject *pdtobj, _In_opt_ HKEY hkeyProgID)
{
    FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    STGMEDIUM stg = { TYMED_HGLOBAL };
    HDROP hDrop;

    //在數據對象中查找CF_HDROP類型數據
    if (FAILED(pdtobj->GetData(&fmt, &stg)))
    {//沒有該數據
        return E_INVALIDARG;
    }

    //獲取指向實際數據的指針
    hDrop = (HDROP)GlobalLock(stg.hGlobal);

    //檢查
    if (NULL == hDrop)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    do
    {
        //有效性檢查,保證最少有一個文件名
        UINT uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
        if (0 == uNumFiles)
        {
            hr = E_INVALIDARG;
            break;
        }

        WCHAR tFilePath[MAX_PATH];
        //取得所有所有文件的文件名
        for (UINT i = 0; i < uNumFiles; i++)
        {
            if (0 != DragQueryFileW(hDrop, i, tFilePath, MAX_PATH))
            {
                //存放如文件列表中
                m_vtFilePath.push_back(tFilePath);
            }
            else
            {//有錯誤
                hr = E_INVALIDARG;
                break;
            }
        }
    } while (0);

    //釋放資源
    GlobalUnlock(stg.hGlobal);
    ReleaseStgMedium(&stg);
    return hr;
}

HRESULT STDMETHODCALLTYPE CCalcFileSizeExt::QueryContextMenu(_In_ HMENU hmenu, _In_ UINT indexMenu, _In_ UINT idCmdFirst, _In_ UINT idCmdLast, _In_ UINT uFlags)
{
    if (uFlags & CMF_DEFAULTONLY)
    {//此時不做任何處理
        return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
    }

    BOOL bRet = false;

    int nCmdId = idCmdFirst;
    //添加按鈕
    bRet = InsertMenu(hmenu, indexMenu, MF_STRING | MF_BYPOSITION, nCmdId, _T("獲取文件大小"));
    //設置按鈕的圖標
    HBITMAP hBitmap = LoadBitmap(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCE(IDB_BITMAP1));
    bRet = SetMenuItemBitmaps(hmenu, indexMenu, MF_BYPOSITION, hBitmap, NULL);

    ++indexMenu;//序號遞增
    ++nCmdId;//命令ID遞增
    bRet = InsertMenu(hmenu, indexMenu, MF_STRING | MF_BYPOSITION, nCmdId, _T("顯示全路徑"));

    //最後一個參數爲創建的菜單的個數
    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 2);
}

//對加入的菜單的事件響應
HRESULT STDMETHODCALLTYPE CCalcFileSizeExt::InvokeCommand(_In_ CMINVOKECOMMANDINFO *pici)
{
    //如果lpVerb指向一個字符串,忽略此次調用
    if (0 != HIWORD(pici->lpVerb))
    {
        return E_INVALIDARG;
    }

    //檢查lpVerb是不是添加的命令,並進行響應
    switch (LOWORD(pici->lpVerb))//爲創建菜單時的indexMenu數值
    {
    case 0:
        ShowAllFileSIze();
        break;
    case 1:
        ShowAllFilePath();
        break;
    default:
        return E_INVALIDARG;
    }

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CCalcFileSizeExt::GetCommandString(_In_ UINT_PTR idCmd, _In_ UINT uType, _Reserved_ UINT *pReserved, _Out_writes_bytes_((uType & GCS_UNICODE) ? (cchMax * sizeof(wchar_t)) : cchMax) _When_(!(uType & (GCS_VALIDATEA | GCS_VALIDATEW)), _Null_terminated_) CHAR *pszName, _In_ UINT cchMax)
{
    //是否是獲取提示,設置幫助字符串
    if (uType & GCS_HELPTEXT)
    {
        //選擇內容
        LPCTSTR szPrompt = NULL;
        switch (idCmd)
        {
        case 0:
            szPrompt = _T("Help:顯示獲取選取文件的總大小。");
            break;
        case 1:
            szPrompt = _T("Help:顯示選擇文件的全路徑。");
            break;
        default:
            return E_INVALIDARG;
        }

        //字符編碼
        USES_CONVERSION;
        if (uType & GCS_UNICODE)
        {
            lstrcpynW((LPWSTR)pszName, T2CW(szPrompt), cchMax);
        }
        else
        {
            lstrcpynA(pszName, T2CA(szPrompt), cchMax);
        }
    }
    return S_OK;
}

//顯示文件大小
void CCalcFileSizeExt::ShowAllFileSIze()
{
    struct _stat status;
    _off_t allSize = 0;
    std::wstring msg;
    std::wstring errorMsg = L"獲取以下文件大小失敗:";
    bool isError = false;
    for each (std::wstring var in m_vtFilePath)
    {
        //獲取文件信息
        if (0 == _wstat(var.c_str(), &status))
        {
            allSize += status.st_size;
        }
        else
        {
            isError = true;
            errorMsg += var;
            errorMsg += L"\n";
        }
    }

    WCHAR buff[10];
    msg += L"文件個數:";
    _itow_s((int)m_vtFilePath.size(), buff, 10);
    msg += buff;
    msg += L"\n文件大小爲(byte):";
    _itow_s(allSize, buff, 10);
    msg += buff;
    if (isError)
    {
        msg += errorMsg;
    }

    MessageBoxW(NULL, msg.c_str(), L"獲取文件大小", MB_OK);
}

//顯示文件路徑
void CCalcFileSizeExt::ShowAllFilePath()
{
    std::wstring msg;
    for each (std::wstring var in m_vtFilePath)
    {
        msg += var;
        msg += L"\n";
    }
    MessageBoxW(NULL, msg.c_str(), L"獲取文件路徑", MB_OK);
}

圖標擴展文章中所講的一樣,需要將這個程序註冊到註冊表中去。這次不是註冊到資源管理器下,而是註冊到對應的文件下。下面這段代碼,是將其註冊到所有類型的文件的上下文菜單中。

HKCR
{
    NoRemove * 
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove CalcFileSizeExt = s '{3E9A1A8A-1311-48F1-9843-879C758E49A0}' 
            }
        }
    }
}

註冊與反註冊及刪除均和圖標擴展文章中所講的一模一樣,這裏就不再贅述。
下面是運行效果圖。
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

這裏寫圖片描述

發佈了48 篇原創文章 · 獲贊 9 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章