Я хочу создать функцию, которая отображает контекстное меню оболочки для заданного списка файлов (в виде строки),
но файлы находятся из другого места в пространстве имен оболочки.
( Это то же самое контекстное меню, которое отображается, когда вы щелкаете правой кнопкой мыши выбранные файлы в области результатов поиска в Windows Explorer )
Я видел, что проблема обсуждалась в нескольких вопросах раньше, но ни один из них не дал решения:
1. Отображение Windows контекстного меню для нескольких элементов
2. Отображение диалогового окна свойств файла для файлов в разных каталогах
3. Как отобразить системное контекстное меню для нескольких файлов в разных папках?
Однако я заметил, что комментарии здесь относятся к этому блогу (2007), написано автором файлового менеджера для windows. (его программное обеспечение файлового менеджера явно показывает, что он это сделал)
Он упоминает переопределение метода IShellFolder::GetUIObjectOf
и реализацию нового интерфейса IDataObject
.
С тех пор прошло 13 лет, так что я думаю с использованием новых функций, введенных в Windows Vista :
1. Я пытаюсь использовать SHCreateDefaultContextMenu
вместо CDefFolderMenu_Create2
.
2. Возможно, вместо этого можно использовать функцию SHCreateDataObject
реализации нового объекта DataObject?
Я не эксперт в области COM, и мне сложно выполнять эти задачи. Надеюсь, кто-то заполнит недостающие блоки.
Я приложил здесь свои усилия.
Он реализован как dll, чтобы другим было проще использовать и делиться.
#include <Windows.h>
#include <shlobj.h>
#define MIN_SHELL_ID 1
#define MAX_SHELL_ID 30000
extern "C"
{
HRESULT ProcessCMCommand(LPCONTEXTMENU pCM, UINT idCmdOffset, HWND hWnd);
// Try to convert pICv1 into IContextMenu2 or IcontextMenu3
// In case of success, release pICv1.
HRESULT UpgradeContextMenu(
LPCONTEXTMENU pICv1, // In: The context menu version 1 to be converted.
void** ppCMout, // Out: The new context menu (or old one in case
// the convertion could not be done)
int* pcmType) // Out: The version number.
{
HRESULT hr;
// Try to get version 3 first.
hr = pICv1->QueryInterface(IID_IContextMenu3, ppCMout);
if (NOERROR == hr)
{
*pcmType = 3;
}
else
{
hr = pICv1->QueryInterface(IID_IContextMenu2, ppCMout);
if (NOERROR == hr)
*pcmType = 2;
}
if (*ppCMout)
{
pICv1->Release(); // free version 1
}
else { // only version 1 is supported
*pcmType = 1;
*ppCMout = pICv1;
hr = NOERROR;
}
return hr;
}
// Global ref to the context menu to be used while
// the window messages are being handled.
LPCONTEXTMENU2 g_pIContext_v2 = NULL;
// Handle window messages.
// The messages: WM_DRAWITEM, WM_MEASUREITEM, WM_INITMENUPOPUP, WM_MENUSELECT
// should be redirected to this function.
__declspec(dllexport) void OnWindowMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (g_pIContext_v2 == NULL) // Version 1 is used.
return;
switch (msg)
{
case WM_DRAWITEM:
case WM_MEASUREITEM:
if (wp)
break;
case WM_INITMENUPOPUP:
g_pIContext_v2->HandleMenuMsg(msg, wp, lp);
break;
case WM_MENUSELECT:
// Get the help text and update the status bar , Not implemented yet.
break;
}
}
// Shows the shell context menu for the given file list at the given position.
// * error checking is not implemented here.
__declspec(dllexport) HRESULT ShowContextMenu(
HWND hWnd,
int xPos,
int yPos,
wchar_t **fullFileNames, // path + file name
size_t fullFileNamesSize
)
{
HRESULT hr;
// absolute pidl of the files.
LPITEMIDLIST *pidlChilds;
pidlChilds = new LPITEMIDLIST[fullFileNamesSize];
// Get the desktop folder.
LPSHELLFOLDER pDesktop;
SHGetDesktopFolder(&pDesktop);
size_t pidlChildsSize = 0;
for (size_t i = 0; i < fullFileNamesSize; i++)
{
PIDLIST_ABSOLUTE pidl_absolute;
UINT attributes;
hr = SHParseDisplayName(fullFileNames[i], NULL, &pidl_absolute, SFGAO_SYSTEM, (SFGAOF*)&attributes);
pidlChilds[pidlChildsSize] = pidl_absolute;
pidlChildsSize++;
}
// Probably I need to use the new class in place of the pDesktop arg.
DEFCONTEXTMENU dcm =
{
hWnd,
NULL, // contextMenuCB
NULL, // pidlFolder,
pDesktop,
pidlChildsSize,
(LPCITEMIDLIST*)pidlChilds,
NULL, // *punkAssociationInfo
NULL, // cKeys
NULL // *aKeys
};
IContextMenu *pCMv1 = NULL; // Version 1
IContextMenu *pCMx = NULL; // Versions 2 or 3
hr = SHCreateDefaultContextMenu(&dcm, IID_IContextMenu, (void**)&pCMv1);
int pcmType = 0;
hr = UpgradeContextMenu(pCMv1, (void**)&pCMx, &pcmType);
if (pcmType > 1) // pCMv1 was feed inside UpgradeContextMenu function.
{
g_pIContext_v2 = (LPCONTEXTMENU2)pCMx;
}
HMENU hMenu = CreatePopupMenu();
hr = pCMx->QueryContextMenu(hMenu, 0, MIN_SHELL_ID, MAX_SHELL_ID, CMF_EXPLORE | CMF_CANRENAME);
UINT cmdID = TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_RETURNCMD,
xPos, yPos, 0, hWnd, 0);
if (cmdID >= MIN_SHELL_ID && cmdID <= MAX_SHELL_ID)
ProcessCMCommand(pCMx,
cmdID - MIN_SHELL_ID, // offset passed
hWnd
);
// Free resources
for (size_t i = 0; i < pidlChildsSize; i++)
CoTaskMemFree(pidlChilds[i]);
pDesktop->Release();
pCMx->Release();
delete[] pidlChilds;
g_pIContext_v2 = NULL;
return hr;
}
HRESULT ProcessCMCommand(LPCONTEXTMENU pCM, UINT idCmdOffset, HWND hWnd)
{
CMINVOKECOMMANDINFOEX ici;
ZeroMemory(&ici, sizeof(ici));
ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
ici.hwnd = hWnd;
ici.fMask = CMIC_MASK_UNICODE;
ici.lpVerb = MAKEINTRESOURCEA(idCmdOffset);
ici.lpVerbW = MAKEINTRESOURCEW(idCmdOffset);
ici.nShow = SW_SHOWNORMAL;
return pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
}
/*
class CIShellFolderSpy : public IShellFolder
{
public:
// constructor
CIShellFolderSpy(LPSHELLFOLDER sf) {
sf->AddRef();
m_iSF = sf;
}
~CIShellFolderSpy() { m_iSF->Release(); }
// IUnknown methods --------
STDMETHOD_(ULONG, AddRef)() {
return m_iSF->AddRef();
}
STDMETHOD_(ULONG, Release)() {
return m_iSF->Release();
}
STDMETHOD_(HRESULT, QueryInterface(REFIID riid, void **ppvObject)) {
return m_iSF->QueryInterface(riid, ppvObject);
}
// IShellFolder methods ----
STDMETHOD_(HRESULT, BindToObject)(
PCUIDLIST_RELATIVE pidl,
IBindCtx *pbc,
REFIID riid,
void **ppv) {
return m_iSF->BindToObject(pidl, pbc, riid, ppv);
}
STDMETHOD_(HRESULT, BindToStorage)(
PCUIDLIST_RELATIVE pidl,
IBindCtx *pbc,
REFIID riid,
void **ppv) {
return m_iSF->BindToStorage(pidl, pbc, riid, ppv);
}
STDMETHOD_(HRESULT, CompareIDs)(
LPARAM lParam,
PCUIDLIST_RELATIVE pidl1,
PCUIDLIST_RELATIVE pidl2) {
return m_iSF->CompareIDs(lParam, pidl1, pidl2);
}
STDMETHOD_(HRESULT, CreateViewObject)(
HWND hwndOwner,
REFIID riid,
void **ppv) {
return m_iSF->CreateViewObject(hwndOwner, riid, ppv);
}
STDMETHOD_(HRESULT, EnumObjects)(
HWND hwnd,
SHCONTF grfFlags,
IEnumIDList **ppenumIDList) {
return m_iSF->EnumObjects(hwnd, grfFlags, ppenumIDList);
}
STDMETHOD_(HRESULT, GetAttributesOf)(
UINT cidl,
PCUITEMID_CHILD_ARRAY apidl,
SFGAOF *rgfInOut) {
return m_iSF->GetAttributesOf(cidl, apidl, rgfInOut);
}
STDMETHOD_(HRESULT, GetDisplayNameOf)(
PCUITEMID_CHILD pidl,
SHGDNF uFlags,
STRRET *pName) {
return m_iSF->GetDisplayNameOf(pidl, uFlags, pName);
}
STDMETHOD_(HRESULT, ParseDisplayName)(
HWND hwnd,
IBindCtx *pbc,
LPWSTR pszDisplayName,
ULONG *pchEaten,
PIDLIST_RELATIVE *ppidl,
ULONG *pdwAttributes
) {
return m_iSF->ParseDisplayName(hwnd,
pbc,
pszDisplayName,
pchEaten,
ppidl,
pdwAttributes);
}
STDMETHOD_(HRESULT, SetNameOf)(
HWND hwnd,
PCUITEMID_CHILD pidl,
LPCWSTR pszName,
SHGDNF uFlags,
PITEMID_CHILD *ppidlOut
) {
return m_iSF->SetNameOf(
hwnd,
pidl,
pszName,
uFlags,
ppidlOut);
}
virtual HRESULT STDMETHODCALLTYPE GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST *apidl,
REFIID riid,
UINT *rgfReserved,
void **ppv)
{
if (InlineIsEqualGUID(riid, IID_IDataObject))
{
// i ignore the pidl array supplied and return the good data object
return m_pMDObj->QueryInterface(riid, ppv);
}
else {
// i've seen IDropTarget requests, let base handle them
return m_iSF->GetUIObjectOf(hwndOwner, cidl, apidl, riid, rgfReserved, ppv);
}
}
protected:
LPSHELLFOLDER m_iSF;
CMultiDataObject* m_pMDObj;
}; // end of class
*/
}