還是先亮源碼下載地址: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}'
}
}
}
}
註冊與反註冊及刪除均和圖標擴展文章中所講的一模一樣,這裏就不再贅述。
下面是運行效果圖。