Как использовать SHCreateDefaultContextMenu для отображения контекстного меню оболочки для нескольких файлов из разных папок - PullRequest
0 голосов
/ 05 мая 2020

Я хочу создать функцию, которая отображает контекстное меню оболочки для заданного списка файлов (в виде строки),
но файлы находятся из другого места в пространстве имен оболочки.
( Это то же самое контекстное меню, которое отображается, когда вы щелкаете правой кнопкой мыши выбранные файлы в области результатов поиска в 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

    */
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...