|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article explains how to use my self-written Why CShellContextMenuI have a lot of projects in which i work with files/folders. So I wanted to use the common shell contextmenu for those. Microsoft put a wonderful example on how to achieve this in their CShellContextMenu scm; // instantiate class object scm.SetObjects (TEXT ("file:///c://">c:\\")); // we whant shellcontext menu for drive c:\ scm.ShowContextMenu (this, point); // point is a CPoint objects which indicates where the contextmenu should // be shown this refers to a MFC-window class (showcontextmenu needs that // to set the owner window) There's just one other importing thing you have to do. In your // Initialize OLE 2.0 libraries if (!AfxOleInit ()) { AfxMessageBox (TEXT ("Unable to load OLE 2.0 libraries!")); return (FALSE); }and put the following #include statement in your project's stdafx.h file. #include <afxole.h> // for OLE That's all you need to pop-up the shell contextmenu for drive C. How CShellContextMenu worksLet's have an inside look in First take a look at those void SetObjects (CString strObject); // one file system path (file/folder) void SetObjects (CStringArray &strArray); // array of multiple file system paths (files/folders) void SetObjects (LPITEMIDLIST pidl); // full qualified PIDL of shell object void SetObjects (IShellFolder * psfFolder, LPITEMIDLIST pidlItem); // relative PIDL and its parent IShellFolder interface void SetObjects (IShellFolder * psfFolder, LPITEMIDLIST * pidlArray, int nItemCount); // array of multiple relative PIDLs and their parent IShellFolder interface With the 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; // 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(),0, MIN_ID, MAX_ID, CMF_EXPLORE);
// subclass window to handle menurelated messages in CShellContextMenu
WNDPROC OldWndProc;
if (iMenuType > 1) // only version 2 and 3 supports menu messages
{
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 = Menu.TrackPopupMenu (TPM_RETURNCMD | TPM_LEFTALIGN,
pt.x, pt.y, pWnd);
if (OldWndProc) // unsubclass
SetWindowLong (pWnd->m_hWnd, GWL_WNDPROC, (DWORD) OldWndProc);
// see if returned idCommand belongs to shell menu entries
if (idCommand >= MIN_ID && idCommand <= MAX_ID)
{ //executes related command
InvokeCommand (pContextMenu, idCommand - MIN_ID);
idCommand = 0;
}
pContextMenu->Release();
g_IContext2 = NULL;
g_IContext3 = NULL;
return (idCommand);
}
As you can see Let's again take a look at the code. After we have a pointer to the
Here's 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 }
That's the alternative window procedure that is only used while the contextmenu is being showed. 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);
}
This little function is also very important. Without it the shell context menu would also show correctly with all the expected menu items, but it would do just nothing if you'd click on an item. So, all this function does is fill an 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); } SummarySo, that's the whole thing behind the shell contextmenu. Wasn't that hard was it? Shell interfaces are not that difficult like they seem to be on the first look. One problem with them is that they are not well documented in the MSDN. So with a little work and some google-searching everything's possible. Before I began working with that shell context menu I didn't know much about the Shell. I did use a lot of shell functions like I hope the article is good to understand, because English is not my native language. On the other hand, it's my first development related article ever. So hey, I think it's good enough for that. What comes next?I hope the example project covers
HistoryApril 29th, 2003
April 10th, 2003
| ||||||||||||||||||||