⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shellcontextmenu.cpp

📁 TabBars的开源源码
💻 CPP
字号:
/***************************************************************************/
/* NOTE:                                                                   */
/* This document is copyright (c) by Oz Solomonovich.  All non-commercial  */
/* use is allowed, as long as this document not altered in any way, and    */
/* due credit is given.                                                    */
/***************************************************************************/

// ShellContextMenu.cpp : implementation file
//
// Handles the creation of the shell context menu, including the "Send To..."
// sub-menu.  
// Personal note: I think that MS should have made the code to populate and
// handle the "Send To..." sub-menu a part of the shell context menu.  But
// instead they forced us poor saps to write a whole lot of spaghetti COM 
// code to do what should have been a trivial part of the OS.  See the code 
// below and judge for yourself.
//

//some bugs fixed by orbit 
#include "stdafx.h"
#include "BCMenu.h"
#include "ShellContextMenu.h"
#include "ShellTools.h"
#include "PIDL.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define IDM_SHELLCTXFIRST      1
#define IDM_SHELLCTXLAST       29999
#define IDM_SENDTOFIRST        30000
#define IDM_SENDTOLAST         32767

static class CShCMInitializer
{
public:
    CShCMInitializer();
    ~CShCMInitializer();

    static LPSHELLFOLDER   m_sfDesktop;
    static LPSHELLFOLDER   m_sfSendTo;
    static CPIDL           m_pidlSendTo;
} __init;

LPSHELLFOLDER CShCMInitializer::m_sfDesktop  = NULL;
LPSHELLFOLDER CShCMInitializer::m_sfSendTo   = NULL;
CPIDL         CShCMInitializer::m_pidlSendTo;

CShCMInitializer::CShCMInitializer()
{
    HRESULT hr;

    SHGetDesktopFolder(&m_sfDesktop);

    hr = SHGetSpecialFolderLocation(NULL, CSIDL_SENDTO, m_pidlSendTo);
    if (SUCCEEDED(hr)) 
    {
        hr = m_sfDesktop->BindToObject(m_pidlSendTo, NULL, IID_IShellFolder, 
            (LPVOID *)&m_sfSendTo);
        if (!SUCCEEDED(hr)) 
        {
            m_sfSendTo = NULL;
        }
    } 
    else 
    {
        m_sfSendTo = NULL;
    }
}

CShCMInitializer::~CShCMInitializer()
{
    m_sfSendTo->Release();
    m_sfDesktop->Release();
}



CShellContextMenu::CShellContextMenu(HWND hWnd, CString m_cFullPath) : 
    m_hWnd(hWnd), m_cFullPath(m_cFullPath), m_pSendToMenu(NULL)
{
    m_lpcm = NULL;
}

CShellContextMenu::~CShellContextMenu()
{
    if (m_lpcm) m_lpcm->Release();

    if (m_pSendToMenu)
    {
        BCMenuData *pMenuData;
        int i = IDM_SENDTOFIRST;

        pMenuData = m_pSendToMenu->FindMenuListEx(i);
        while (pMenuData)
        {
            CPIDL toFree((LPITEMIDLIST)pMenuData->pContext);
            pMenuData = m_pSendToMenu->FindMenuListEx(++i);
        }
        delete m_pSendToMenu;
    }
}

bool CShellContextMenu::IsMenuCommand(int iCmd) const
{
    return (
            (IDM_SENDTOFIRST   <= iCmd  &&  iCmd <= IDM_SENDTOLAST) ||
            (IDM_SHELLCTXFIRST <= iCmd  &&  iCmd <= IDM_SHELLCTXLAST)
           );
}

void CShellContextMenu::InvokeCommand(int iCmd) const
{
    if (iCmd)
    {
        if (IDM_SENDTOFIRST <= iCmd  &&  iCmd <= IDM_SENDTOLAST)
        {
            // "Send To..." item

            CPIDL        pidlFile(m_cFullPath), pidlDrop;
            LPDROPTARGET pDT;
            LPDATAOBJECT pDO;
            HRESULT hr;

            hr = pidlFile.GetUIObjectOf(IID_IDataObject, (LPVOID *)&pDO, 
                m_hWnd);
            if (SUCCEEDED(hr))
            {
                pidlDrop.Set((LPITEMIDLIST)
                    (m_pSendToMenu->FindMenuListEx(iCmd))->pContext);
                hr = pidlDrop.GetUIObjectOf(IID_IDropTarget, 
                    (LPVOID *)&pDT, m_hWnd);

                if (SUCCEEDED(hr))
                {
                    // do the drop
                    POINTL pt = { 0, 0 };
                    DWORD dwEffect = 
                        DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
                    hr = pDT->DragEnter(pDO, MK_LBUTTON, pt, &dwEffect);

                    if (SUCCEEDED(hr) && dwEffect) 
                    {
                        hr = pDT->Drop(pDO, MK_LBUTTON, pt, &dwEffect);
                    } 
                    else 
                    {
                        hr = pDT->DragLeave();
                    }

                    pDT->Release();
                }

                pidlDrop.m_pidl = NULL;
            }
        }
        else
        {
            // Shell command

            CMINVOKECOMMANDINFO cmi;
            cmi.cbSize       = sizeof(cmi);
            cmi.fMask        = 0;
            cmi.hwnd         = m_hWnd;
            cmi.lpVerb       = MAKEINTRESOURCE(iCmd - 1);
            cmi.lpParameters = NULL;
            cmi.lpDirectory  = NULL;
            cmi.nShow        = SW_SHOWNORMAL;
            cmi.dwHotKey     = 0;
            cmi.hIcon        = NULL;
            m_lpcm->InvokeCommand(&cmi);
        }
    }
}


// retrieves the shell context menu for a file
void CShellContextMenu::SetMenu(BCMenu *pMenu, BCMenu *pMenu2)
{
    HRESULT         hr;
    CPIDL           pidl;

    m_lpcm = NULL;

    if (m_cFullPath.GetLength() == 0)
    {
        return;
    }

    if (SUCCEEDED(hr = pidl.Set(m_cFullPath)))
    {
        hr = pidl.GetUIObjectOf(IID_IContextMenu, (LPVOID *)&m_lpcm, m_hWnd);
    }

    if (SUCCEEDED(hr))
    {
        pMenu->DeleteMenu(0, MF_BYPOSITION);
        hr = m_lpcm->QueryContextMenu(*pMenu, 0, IDM_SHELLCTXFIRST, 
            IDM_SHELLCTXLAST, CMF_NODEFAULT | CMF_EXPLORE);

        // find the "Send To" submenu: look for a one item submenu with
        // the same name as it's parent menu
        CMenu *pSubMenu;
        int count = pMenu->GetMenuItemCount();
        CString str1, str2;
        for (int i = 0; i < count; i++)
        {
            pSubMenu = pMenu->GetSubMenu(i);
            if (pSubMenu  &&  pSubMenu->GetMenuItemCount() == 1)
            {
                pMenu->GetMenuString(i, str1, MF_BYPOSITION);
                pSubMenu->GetMenuString(0, str2, MF_BYPOSITION);
                UINT idm = IDM_SENDTOFIRST;
                if (str1 == str2)
                {
                    // ok - found it.  now populate it
                    m_pSendToMenu = new BCMenu();
                    m_pSendToMenu->CreateMenu();
                    pMenu->ModifyMenu(i, MF_BYPOSITION | MF_POPUP, 
                        (UINT)m_pSendToMenu->GetSafeHmenu(), str1);
                    FillSendToMenu(m_pSendToMenu, 
                        CShCMInitializer::m_sfSendTo, idm);
                    pSubMenu->DestroyMenu();
                    break;
                }
            }
        }
    }

    return;
}


void CShellContextMenu::FillSendToMenu(BCMenu *pMenu, 
    LPSHELLFOLDER pSF, UINT &idm)
{
    USES_CONVERSION;
    CPIDL        pidl, pidl2;
    LPENUMIDLIST peidl;
    HRESULT hr;
    STRRET str;
    HICON hIcon;
    UINT idmStart = idm;
    BCMenu *pSubMenu;
    LPSHELLFOLDER pSubSF;

    if (pSF) 
    {
        pMenu->m_bDynIcons = TRUE;
        for (int i = 0; i < 2; i++)
        {
            // i == 0  -->  folders
            // i == 1  -->  other files

            hr = pSF->EnumObjects(m_hWnd, 
                SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &peidl);

            if (!SUCCEEDED(hr)) 
                continue;

            while (peidl->Next(1, pidl, NULL) == S_OK &&
                  idm < IDM_SENDTOLAST) 
            {
                hr = pSF->GetDisplayNameOf(pidl, SHGDN_NORMAL, &str);
                if (SUCCEEDED(hr)) 
                {
                    ULONG ulAttrs = (unsigned)-1;
                    pSF->GetAttributesOf(1, pidl, &ulAttrs);
                    pidl2.MakeAbsPIDLOf(pSF, pidl);
                    hIcon = GetFileIcon((LPCTSTR)pidl2.m_pidl, SHGFI_PIDL);
                    pidl.ExtractCStr(str);
                    if (ulAttrs & SFGAO_FOLDER) // folder?
                    {
                        if (i == 0)
                        {
                            // create new submenu & recurse
                            pSubMenu = new BCMenu();
                            pSubMenu->CreateMenu();
                            pMenu->AppendODMenuA(str.cStr, 
                                MF_ENABLED | MF_OWNERDRAW | MF_POPUP, 
                                pSubMenu, (int)hIcon);
                            hr = pSF->BindToObject(pidl, NULL, 
                                IID_IShellFolder, (LPVOID *)&pSubSF);
                            if (!SUCCEEDED(hr)) pSubSF = NULL;
                            FillSendToMenu(pSubMenu, pSubSF, idm);
                            if (pSubSF) pSubSF->Release();
                            pidl2.Free();
                        }
                    }
                    else
                    {
                        if (i == 1)
                        {
                            pMenu->AppendODMenuA(str.cStr, 
                                MF_ENABLED | MF_OWNERDRAW, idm++, 
                                (int)hIcon);
                            pMenu->FindMenuOption(
                                A2W((const char *)&str.cStr))->pContext = 
                                (void *)pidl2.m_pidl;
                        }
                    }
                    pidl2.m_pidl = NULL;
                    pidl.Free();
                }
            }
            peidl->Release();
        }
    }

    // If the menu is still empty (the user has an empty SendTo folder),
    // then add a disabled "(empty)" item so we have at least something
    // to display.
    if (idm == idmStart) 
    {
        pMenu->AppendMenu(MF_GRAYED | MF_DISABLED | MF_STRING, idm, 
            "(empty)");
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -