MFC 右键系统菜单

来源:互联网 发布:淘宝上的笛子怎么样 编辑:程序博客网 时间:2024/06/08 18:30

MFC 右键系统菜单

CShellContextMenu类的使用

xxx.h文件

// ShellContextMenu.h: Schnittstelle die Klasse CShellContextMenu.////////////////////////////////////////////////////////////////////////#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// developed by R. Engels 2003/////////////////////////////////////////////////////////////////////class CShellContextMenu  {public:    CMenu * GetMenu ();    void SetObjects (IShellFolder * psfFolder, LPITEMIDLIST pidlItem);    void SetObjects (IShellFolder * psfFolder, LPITEMIDLIST * pidlArray, int nItemCount);    void SetObjects (LPITEMIDLIST pidl);    void SetObjects (CString strObject);    void SetObjects (CStringArray &strArray);    UINT ShowContextMenu (CWnd* pWnd, CPoint pt);    CShellContextMenu();    virtual ~CShellContextMenu();private:    int nItems;    BOOL bDelete;    CMenu * m_Menu;    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_)

.cpp文件

// ShellContextMenu.cpp: Implementierung der Klasse CShellContextMenu.////////////////////////////////////////////////////////////////////////#include "stdafx.h"#include "ShellContextMenu.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Konstruktion/Destruktion//////////////////////////////////////////////////////////////////////#define MIN_ID 1#define MAX_ID 10000IContextMenu2 * g_IContext2 = NULL;IContextMenu3 * g_IContext3 = NULL;CShellContextMenu::CShellContextMenu(){    m_psfFolder = NULL;    m_pidlArray = NULL;    m_Menu = 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_Menu)        delete m_Menu;}// this functions determines which version of IContextMenu is avaibale for those objects (always the highest one)// and returns that interfaceBOOL 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(CWnd *pWnd, CPoint 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))            return (0); // something went wrong    if (!m_Menu)    {        delete m_Menu;        m_Menu = NULL;        m_Menu = new CMenu;        m_Menu->CreatePopupMenu();    }    // lets fill the our popupmenu      pContextMenu->QueryContextMenu (m_Menu->m_hMenu, m_Menu->GetMenuItemCount(), MIN_ID, MAX_ID, CMF_NORMAL | CMF_EXPLORE);    // 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 = m_Menu->TrackPopupMenu ( TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NOANIMATION | TPM_VCENTERALIGN, pt.x, pt.y, pWnd);    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;    }else        idCommand = -1;    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(CString strObject){    // only one object is passed    CStringArray strArray;    strArray.Add (strObject);   // create a CStringArray with one element    SetObjects (strArray);      // and pass it to SetObjects (CStringArray &strArray)                                // for further processing}void CShellContextMenu::SetObjects(CStringArray &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    // 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.GetAt (0).GetLength () + 1, sizeof (OLECHAR));    ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strArray.GetAt(0), -1, olePath, strArray.GetAt(0).GetLength() + 1);       psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL);    free (olePath);#else    psfDesktop->ParseDisplayName (NULL, 0, strArray.GetAt (0).GetBuffer (0), 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.GetSize ();    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), -1, olePath, strArray.GetAt(i).GetLength() + 1);               psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL);        free (olePath);#else        psfDesktop->ParseDisplayName (NULL, 0, strArray.GetAt (i).GetBuffer (0), 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 passedvoid 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 passedvoid 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;}CMenu * CShellContextMenu::GetMenu(){    if (!m_Menu)    {        m_Menu = new CMenu;        m_Menu->CreatePopupMenu();  // create the popupmenu (its empty)    }    return (m_Menu);}// this is workaround function for the Shell API Function SHBindToParent// SHBindToParent is not available under Win95/98HRESULT 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 (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 ((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;}

简单使用 main.cpp

CString filepath="D:\\1.txt";CShellContextMenu* shellMenu = new CShellContextMenu(); shellMenu->SetObjects(filepath);  //设置菜单弹出坐标DWORD dwPos = GetMessagePos();CPoint point( LOWORD(dwPos), HIWORD(dwPos) );UINT cmd=shellMenu->ShowContextMenu(this, point);delete shellMenu;shellMenu=NULL;

运行效果:
这里写图片描述

demo下载地址:
http://download.csdn.net/download/hilary929265053/9995259

原创粉丝点击