// 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
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.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
{
// 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;
}