windows 系统右键菜单

// ShellContextMenu.h
//
//

#if !defined(AFX_SHELLCONTEXTMENU_H__A358AACF_7C7C_410D_AD29_67310B2DDC22__INCLUDED_)
#define AFX_SHELLCONTEXTMENU_H__A358AACF_7C7C_410D_AD29_67310B2DDC22__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000


/
// class to show shell contextmenu of files/folders/shell objects
/

#include
#include

#ifdef _UNICODE
#define xstring std::wstring
#else
#define xstring std::string
#endif

class CShellContextMenu  
{
public:
    HMENU GetMenu();
    void SetObjects(IShellFolder * psfFolder, LPITEMIDLIST pidlItem);
    void SetObjects(IShellFolder * psfFolder, LPITEMIDLIST * pidlArray, int nItemCount);
    void SetObjects(LPITEMIDLIST pidl);
    void SetObjects(xstring strObject);
    void SetObjects(std::vector &strArray);
    UINT ShowContextMenu(HWND hwnd, POINT pt);
    CShellContextMenu();
    virtual ~CShellContextMenu();

private:
    int nItems;
    BOOL bDelete;
    HMENU m_hMenu;
    IShellFolder * m_psfFolder;
    LPITEMIDLIST * m_pidlArray;    

    void InvokeCommand(LPCONTEXTMENU pContextMenu, UINT idCommand);
    BOOL GetContextMenu(void ** ppContextMenu, int & iMenuType);
    HRESULT SHBindToParentEx(LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast);
    static LRESULT CALLBACK HookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    void FreePIDLArray(LPITEMIDLIST * pidlArray);
    LPITEMIDLIST CopyPIDL(LPCITEMIDLIST pidl, int cb = -1);
    UINT GetPIDLSize(LPCITEMIDLIST pidl);
    LPBYTE GetPIDLPos(LPCITEMIDLIST pidl, int nPos);
    int GetPIDLCount(LPCITEMIDLIST pidl);
};

#endif // !defined(AFX_SHELLCONTEXTMENU_H__A358AACF_7C7C_410D_AD29_67310B2DDC22__INCLUDED_)
 

// ShellContextMenu.cpp: Implementierung der Klasse CShellContextMenu.
//
//

#include "stdafx.h"
#include "stdafx.h"
#include "ShellContextMenu.h"

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

//
// Konstruktion/Destruktion
//

#define MIN_ID 1
#define MAX_ID 10000

IContextMenu2 * g_IContext2 = NULL;
IContextMenu3 * g_IContext3 = NULL;

CShellContextMenu::CShellContextMenu()
{
    m_psfFolder = NULL;
    m_pidlArray = NULL;
    m_hMenu = NULL;
}

CShellContextMenu::~CShellContextMenu()
{
    // free all allocated datas
    if (m_psfFolder && bDelete)
        m_psfFolder->Release ();
    m_psfFolder = NULL;
    FreePIDLArray (m_pidlArray);
    m_pidlArray = NULL;

    if (m_hMenu)
        DestroyMenu(m_hMenu);//释放菜单资源
}

// this functions determines which version of IContextMenu is avaibale for those objects (always the highest one)
// and returns that interface
BOOL CShellContextMenu::GetContextMenu (void ** ppContextMenu, int & iMenuType)
{
    *ppContextMenu = NULL;
    LPCONTEXTMENU icm1 = NULL;

    // first we retrieve the normal IContextMenu interface (every object should have it)
    m_psfFolder->GetUIObjectOf (NULL, nItems, (LPCITEMIDLIST *) m_pidlArray, IID_IContextMenu, NULL, (void**) &icm1);

    if (icm1)
    {    // since we got an IContextMenu interface we can now obtain the higher version interfaces via that
        if (icm1->QueryInterface (IID_IContextMenu3, ppContextMenu) == NOERROR)
            iMenuType = 3;
        else if (icm1->QueryInterface (IID_IContextMenu2, ppContextMenu) == NOERROR)
            iMenuType = 2;

        if (*ppContextMenu) 
            icm1->Release(); // we can now release version 1 interface, cause we got a higher one
        else 
        {    
            iMenuType = 1;
            *ppContextMenu = icm1;    // since no higher versions were found
        }                            // redirect ppContextMenu to version 1 interface
    }
    else
        return (FALSE);    // something went wrong

    return (TRUE); // success
}


LRESULT CALLBACK CShellContextMenu::HookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    { 
    case WM_MENUCHAR:    // only supported by IContextMenu3
        if (g_IContext3)
        {
            LRESULT lResult = 0;
            g_IContext3->HandleMenuMsg2 (message, wParam, lParam, &lResult);
            return (lResult);
        }
        break;

    case WM_DRAWITEM:
    case WM_MEASUREITEM:
        if (wParam) 
            break; // if wParam != 0 then the message is not menu-related

    case WM_INITMENUPOPUP:
        if (g_IContext2)
            g_IContext2->HandleMenuMsg (message, wParam, lParam);
        else    // version 3
            g_IContext3->HandleMenuMsg (message, wParam, lParam);
        return (message == WM_INITMENUPOPUP ? 0 : TRUE); // inform caller that we handled WM_INITPOPUPMENU by ourself
        break;

    default:
        break;
    }

    // call original WndProc of window to prevent undefined bevhaviour of window
    return ::CallWindowProc ((WNDPROC) GetProp ( hWnd, TEXT ("OldWndProc")), hWnd, message, wParam, lParam);
}


UINT CShellContextMenu::ShowContextMenu(HWND hWnd, POINT pt)
{
    int iMenuType = 0;    // to know which version of IContextMenu is supported
    LPCONTEXTMENU pContextMenu;    // common pointer to IContextMenu and higher version interface

    if (!GetContextMenu ((void**) &pContextMenu, iMenuType) || !pContextMenu)    
        return (0);    // something went wrong

    if (!m_hMenu)
    {
        m_hMenu=CreatePopupMenu();
    }

    // lets fill the our popupmenu  
    pContextMenu->QueryContextMenu (m_hMenu, GetMenuItemCount(m_hMenu), MIN_ID, MAX_ID, CMF_NORMAL | CMF_EXPLORE| CMF_CANRENAME);
    int iMenuCnt = GetMenuItemCount(m_hMenu);
    TCHAR chItem[MAX_PATH]={0};
    for (int i = 0;i < iMenuCnt;i++)
    {
        memset(chItem, 0, sizeof(chItem));
        GetMenuString(m_hMenu, i, chItem, 100, MF_BYPOSITION);
        xstring strMenuItem = chItem;

        if (strMenuItem==(_T("打开(&O)")))
        {
        }
    }

    // subclass window to handle menurelated messages in CShellContextMenu 
    //WNDPROC OldWndProc;
    //if (iMenuType > 1)    // only subclass if its version 2 or 3
    //{
    //    OldWndProc = (WNDPROC) SetWindowLong (pWnd->m_hWnd, GWL_WNDPROC, (DWORD) HookWndProc);
    //    if (iMenuType == 2)
    //        g_IContext2 = (LPCONTEXTMENU2) pContextMenu;
    //    else    // version 3
    //        g_IContext3 = (LPCONTEXTMENU3) pContextMenu;
    //}
    //else
    //    OldWndProc = NULL;

    UINT idCommand = TrackPopupMenu(m_hMenu,TPM_RETURNCMD | TPM_LEFTALIGN, pt.x, pt.y,0,hWnd,0);

    //if (OldWndProc) // unsubclass
    //    SetWindowLong (pWnd->m_hWnd, GWL_WNDPROC, (DWORD) OldWndProc);

    if (idCommand >= MIN_ID && idCommand <= MAX_ID)    // see if returned idCommand belongs to shell menu entries
    {
        InvokeCommand (pContextMenu, idCommand - MIN_ID);    // execute related command
        idCommand = 0;
    }

    pContextMenu->Release();
    g_IContext2 = NULL;
    g_IContext3 = NULL;

    return (idCommand);
}


void CShellContextMenu::InvokeCommand (LPCONTEXTMENU pContextMenu, UINT idCommand)
{
    CMINVOKECOMMANDINFO cmi = {0};
    cmi.cbSize = sizeof (CMINVOKECOMMANDINFO);
    cmi.lpVerb = (LPSTR) MAKEINTRESOURCE (idCommand);
    cmi.nShow = SW_SHOWNORMAL;

    pContextMenu->InvokeCommand (&cmi);
}


void CShellContextMenu::SetObjects(xstring strObject)
{
    // only one object is passed
    std::vector strArray;
    strArray.push_back(strObject);    // create a xstring with one element

    SetObjects (strArray);        // and pass it to SetObjects (CStringArray &strArray)
    // for further processing
}


void CShellContextMenu::SetObjects(std::vector &strArray)
{
    // free all allocated datas
    if (m_psfFolder && bDelete)
        m_psfFolder->Release ();
    m_psfFolder = NULL;
    FreePIDLArray (m_pidlArray);
    m_pidlArray = NULL;

    // get IShellFolder interface of Desktop (root of shell namespace)
    IShellFolder * psfDesktop = NULL;
    SHGetDesktopFolder (&psfDesktop);    // needed to obtain full qualified pidl
    if (NULL == psfDesktop)
    {
        return;
    }

    // ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface
    // but since we use the Desktop as our interface and the Desktop is the namespace root
    // that means that it's a fully qualified PIDL, which is what we need
    LPITEMIDLIST pidl = NULL;

#ifndef _UNICODE
    OLECHAR * olePath = NULL;
    olePath = (OLECHAR *) calloc (strArray.at(0).length() + 1, sizeof (OLECHAR));
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strArray.at(0).c_str(), -1, olePath, strArray.at(0).length() + 1);    
    psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL);
    free (olePath);
#else
    psfDesktop->ParseDisplayName (NULL, 0, (LPTSTR)strArray.at(0).c_str(), NULL, &pidl, NULL);
#endif

    // now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface
    LPITEMIDLIST pidlItem = NULL;    // relative pidl
    SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL);
    free (pidlItem);
    // get interface to IMalloc (need to free the PIDLs allocated by the shell functions)
    LPMALLOC lpMalloc = NULL;
    SHGetMalloc (&lpMalloc);
    lpMalloc->Free (pidl);

    // now we have the IShellFolder interface to the parent folder specified in the first element in strArray
    // since we assume that all objects are in the same folder (as it's stated in the MSDN)
    // we now have the IShellFolder interface to every objects parent folder

    IShellFolder * psfFolder = NULL;
    nItems = strArray.size();
    for (int i = 0; i < nItems; i++)
    {
#ifndef _UNICODE
        //olePath = (OLECHAR *) calloc (strArray.GetAt (i).GetLength () + 1, sizeof (OLECHAR));
        //MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strArray.GetAt (i).GetBuffer (0), -1, olePath, strArray.GetAt (i).GetLength () + 1);    
        olePath = (OLECHAR *) calloc (strArray.at(i).length() + 1, sizeof (OLECHAR));
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strArray.at(i).c_str(), -1, olePath, strArray.at(i).length() + 1);    
        psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL);
        free (olePath);
#else
        psfDesktop->ParseDisplayName (NULL, 0, (LPTSTR)strArray.at(i).c_str(), NULL, &pidl, NULL);
#endif
        m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST));
        // get relative pidl via SHBindToParent
        SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem);
        m_pidlArray[i] = CopyPIDL (pidlItem);    // copy relative pidl to pidlArray
        free (pidlItem);
        lpMalloc->Free (pidl);        // free pidl allocated by ParseDisplayName
        psfFolder->Release ();
    }
    lpMalloc->Release ();
    psfDesktop->Release ();

    bDelete = TRUE;    // indicates that m_psfFolder should be deleted by CShellContextMenu
}


// only one full qualified PIDL has been passed
void CShellContextMenu::SetObjects(LPITEMIDLIST pidl)
{
    // free all allocated datas
    if (m_psfFolder && bDelete)
        m_psfFolder->Release ();
    m_psfFolder = NULL;
    FreePIDLArray (m_pidlArray);
    m_pidlArray = NULL;

    // full qualified PIDL is passed so we need
    // its parent IShellFolder interface and its relative PIDL to that
    LPITEMIDLIST pidlItem = NULL;
    SHBindToParent ((LPCITEMIDLIST) pidl, IID_IShellFolder, (void **) &m_psfFolder, (LPCITEMIDLIST *) &pidlItem);    

    m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST));    // allocate ony for one elemnt
    m_pidlArray[0] = CopyPIDL (pidlItem);


    // now free pidlItem via IMalloc interface (but not m_psfFolder, that we need later
    LPMALLOC lpMalloc = NULL;
    SHGetMalloc (&lpMalloc);
    lpMalloc->Free (pidlItem);
    lpMalloc->Release();

    nItems = 1;
    bDelete = TRUE;    // indicates that m_psfFolder should be deleted by CShellContextMenu
}


// IShellFolder interface with a relative pidl has been passed
void CShellContextMenu::SetObjects(IShellFolder *psfFolder, LPITEMIDLIST pidlItem)
{
    // free all allocated datas
    if (m_psfFolder && bDelete)
        m_psfFolder->Release ();
    m_psfFolder = NULL;
    FreePIDLArray (m_pidlArray);
    m_pidlArray = NULL;

    m_psfFolder = psfFolder;

    m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST));
    m_pidlArray[0] = CopyPIDL (pidlItem);

    nItems = 1;
    bDelete = FALSE;    // indicates wheter m_psfFolder should be deleted by CShellContextMenu
}

void CShellContextMenu::SetObjects(IShellFolder * psfFolder, LPITEMIDLIST *pidlArray, int nItemCount)
{
    // free all allocated datas
    if (m_psfFolder && bDelete)
        m_psfFolder->Release ();
    m_psfFolder = NULL;
    FreePIDLArray (m_pidlArray);
    m_pidlArray = NULL;

    m_psfFolder = psfFolder;

    m_pidlArray = (LPITEMIDLIST *) malloc (nItemCount * sizeof (LPITEMIDLIST));

    for (int i = 0; i < nItemCount; i++)
        m_pidlArray[i] = CopyPIDL (pidlArray[i]);

    nItems = nItemCount;
    bDelete = FALSE;    // indicates wheter m_psfFolder should be deleted by CShellContextMenu
}


void CShellContextMenu::FreePIDLArray(LPITEMIDLIST *pidlArray)
{
    if (!pidlArray)
        return;

    int iSize = _msize (pidlArray) / sizeof (LPITEMIDLIST);

    for (int i = 0; i < iSize; i++)
        free (pidlArray[i]);
    free (pidlArray);
}


LPITEMIDLIST CShellContextMenu::CopyPIDL (LPCITEMIDLIST pidl, int cb)
{
    if (cb == -1)
        cb = GetPIDLSize (pidl); // Calculate size of list.

    LPITEMIDLIST pidlRet = (LPITEMIDLIST) calloc (cb + sizeof (USHORT), sizeof (BYTE));
    if (pidlRet)
        CopyMemory(pidlRet, pidl, cb);

    return (pidlRet);
}


UINT CShellContextMenu::GetPIDLSize (LPCITEMIDLIST pidl)
{  
    if (!pidl) 
        return 0;
    int nSize = 0;
    LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;
    while (pidlTemp->mkid.cb)
    {
        nSize += pidlTemp->mkid.cb;
        pidlTemp = (LPITEMIDLIST) (((LPBYTE) pidlTemp) + pidlTemp->mkid.cb);
    }
    return nSize;
}

HMENU CShellContextMenu::GetMenu()
{
    if (!m_hMenu)
    {
        m_hMenu=CreatePopupMenu();    // create the popupmenu (its empty)
    }
    return (m_hMenu);
}


// this is workaround function for the Shell API Function SHBindToParent
// SHBindToParent is not available under Win95/98
HRESULT CShellContextMenu::SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast)
{
    HRESULT hr = 0;
    if (!pidl || !ppv)
        return E_POINTER;

    int nCount = GetPIDLCount (pidl);
    if (nCount == 0)    // desktop pidl of invalid pidl
        return E_POINTER;

    IShellFolder * psfDesktop = NULL;
    SHGetDesktopFolder (&psfDesktop);
if (NULL == psfDesktop)
        {
            return E_POINTER;
        }
    if (nCount == 1)    // desktop pidl
    {
        if ((hr = psfDesktop->QueryInterface(riid, ppv)) == S_OK)
        {
            if (ppidlLast) 
                *ppidlLast = CopyPIDL (pidl);
        }
        psfDesktop->Release ();
        return hr;
    }

    LPBYTE pRel = GetPIDLPos (pidl, nCount - 1);
    LPITEMIDLIST pidlParent = NULL;
    pidlParent = CopyPIDL (pidl, pRel - (LPBYTE) pidl);
    IShellFolder * psfFolder = NULL;

    if ((hr = psfDesktop->BindToObject (pidlParent, NULL, __uuidof (psfFolder), (void **) &psfFolder)) != S_OK)
    {
        free (pidlParent);
        psfDesktop->Release ();
        return hr;
    }
        if (NULL == psfFolder)
        {
            return E_POINTER;
        }
    if ((hr = psfFolder->QueryInterface (riid, ppv)) == S_OK)
    {
        if (ppidlLast)
            *ppidlLast = CopyPIDL ((LPCITEMIDLIST) pRel);
    }
    free (pidlParent);
    psfFolder->Release ();
    psfDesktop->Release ();
    return hr;
}


LPBYTE CShellContextMenu::GetPIDLPos (LPCITEMIDLIST pidl, int nPos)
{
    if (!pidl)
        return 0;
    int nCount = 0;

    BYTE * pCur = (BYTE *) pidl;
    while (((LPCITEMIDLIST) pCur)->mkid.cb)
    {
        if (nCount == nPos)
            return pCur;
        nCount++;
        pCur += ((LPCITEMIDLIST) pCur)->mkid.cb;    // + sizeof(pidl->mkid.cb);
    }
    if (nCount == nPos) 
        return pCur;
    return NULL;
}


int CShellContextMenu::GetPIDLCount (LPCITEMIDLIST pidl)
{
    if (!pidl)
        return 0;

    int nCount = 0;
    BYTE*  pCur = (BYTE *) pidl;
    while (((LPCITEMIDLIST) pCur)->mkid.cb)
    {
        nCount++;
        pCur += ((LPCITEMIDLIST) pCur)->mkid.cb;
    }
    return nCount;
}

你可能感兴趣的:(c/c++,windows,api,windows,c++)