ShellContextMenu.cpp
上传用户:yatsl7111
上传日期:2007-01-08
资源大小:1433k
文件大小:19k
源码类别:

图形图象

开发平台:

Visual C++

  1. /***************************************************************************/
  2. /* NOTE:                                                                   */
  3. /* This document is copyright (c) by Oz Solomonovich.  All non-commercial  */
  4. /* use is allowed, as long as this document is not altered in any way, and */
  5. /* due credit is given.                                                    */
  6. /***************************************************************************/
  7. // ShellContextMenu.cpp : implementation file
  8. //
  9. // Handles the creation of the shell context menu, including the "Send To..."
  10. // sub-menu.  
  11. // Personal note: I think that MS should have made the code to populate and
  12. // handle the "Send To..." sub-menu a part of the shell context menu code.  
  13. // But they didn't, so now we're forced to write a whole lot of spaghetti COM 
  14. // code to do what should have been a trivial part of the OS.  See the code 
  15. // below and judge for yourself.
  16. //
  17. // ==========================================================================  
  18. // HISTORY:   
  19. // ==========================================================================
  20. //    1.01  7 Jul 2000 - Philip Oldaker [philip@masmex.com] - Fixed a problem
  21. //                with the SendTo menu now checking for menu id. 
  22. //                Added support for the new OpenWith menu. 
  23. //                The SendTo Menu is now sorted.
  24. #include "stdafx.h"
  25. #include "ShellContextMenu.h"
  26. #include "UICoolMenu.h"
  27. #include <atlbase.h>
  28. #include "PIDL.h"
  29. #ifdef _DEBUG
  30. #define new DEBUG_NEW
  31. #undef THIS_FILE
  32. static char THIS_FILE[] = __FILE__;
  33. #endif
  34. #define IDM_SHELLCTXFIRST      20000
  35. #define IDM_SHELLCTXLAST       29999
  36. #define IDM_SENDTOFIRST        30000
  37. #define IDM_SENDTOLAST         32000
  38. #define IDM_OPENWITHFIRST      32001
  39. #define IDM_OPENWITHLAST       32767
  40. #define IDM_SENDTOID           20028
  41. #define IDM_OPENWITHID1        20127
  42. #define IDM_OPENWITHID2        20128
  43. LPSHELLFOLDER is_less_than_pidl::psf;
  44. LPSHELLFOLDER is_greater_than_pidl::psf;
  45. // OpenWith menu registry keys
  46. static LPCTSTR szAppKey = _T("Applications");
  47. static LPCTSTR szOpenWithListKey = _T("OpenWithList");
  48. static LPCTSTR szShellKey = _T("shell");
  49. static LPCTSTR szCommandKey = _T("command");
  50. static LPCTSTR szFileExtKey = _T("Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts");
  51. // OpenWith menu entries
  52. static LPCTSTR szMRUListEntry = _T("MRUList");
  53. static LPCTSTR szDisplayNameEntry = _T("FriendlyCache");
  54. static class CShCMInitializer
  55. {
  56. public:
  57.     CShCMInitializer();
  58.     ~CShCMInitializer();
  59.     static LPSHELLFOLDER   m_sfDesktop;
  60.     static LPSHELLFOLDER   m_sfSendTo;
  61.     static CImageList *    m_pShellImageList;
  62.     static CPIDL           m_pidlSendTo;
  63. } stat_data;
  64. LPSHELLFOLDER CShCMInitializer::m_sfDesktop       = NULL;
  65. LPSHELLFOLDER CShCMInitializer::m_sfSendTo        = NULL;
  66. CImageList *  CShCMInitializer::m_pShellImageList = NULL;
  67. CPIDL         CShCMInitializer::m_pidlSendTo;
  68. CShCMInitializer::CShCMInitializer()
  69. {
  70.     HRESULT     hr;
  71.     SHFILEINFO  sfi;
  72.     SHGetDesktopFolder(&m_sfDesktop);
  73.     hr = SHGetSpecialFolderLocation(NULL, CSIDL_SENDTO, m_pidlSendTo);
  74.     if (SUCCEEDED(hr)) 
  75.     {
  76.         hr = m_sfDesktop->BindToObject(m_pidlSendTo, NULL, IID_IShellFolder, 
  77.             (LPVOID *)&m_sfSendTo);
  78.         if (!SUCCEEDED(hr)) 
  79.         {
  80.             m_sfSendTo = NULL;
  81.         }
  82.     } 
  83.     else 
  84.     {
  85.         m_sfSendTo = NULL;
  86.     }
  87.     m_pShellImageList = CImageList::FromHandle((HIMAGELIST)
  88.         SHGetFileInfo((LPCTSTR)_T("C:\"), 0, &sfi, sizeof(SHFILEINFO), 
  89.         SHGFI_SYSICONINDEX | SHGFI_SMALLICON));
  90. }
  91. CShCMInitializer::~CShCMInitializer()
  92. {
  93.     m_sfSendTo->Release();
  94.     m_sfDesktop->Release();
  95. }
  96. CShellContextMenu::CShellContextMenu(HWND hWnd,const CString &sAbsPath, LPITEMIDLIST *ppidl, UINT cidl, LPSHELLFOLDER psfParent) 
  97. :   m_hWnd(hWnd), 
  98. m_sAbsPath(sAbsPath),
  99. m_psfParent(psfParent),
  100. m_ppidl(ppidl),
  101. m_cidl(cidl),
  102. m_pSendToMenu(NULL)
  103. {
  104.     m_lpcm = NULL;
  105. m_pidl = *ppidl;
  106. // Initialize button sizes
  107. m_szOldButtonSize = g_CoolMenuManager.SetButtonSize();
  108. }
  109. CShellContextMenu::~CShellContextMenu()
  110. {
  111.     if (m_lpcm) m_lpcm->Release();
  112.     if (m_pSendToMenu)
  113.     {
  114.         int     i = IDM_SENDTOFIRST;
  115.         void *  pData;
  116.         pData = CCoolMenuManager::GetItemData(*m_pSendToMenu, i);
  117.         while (pData)
  118.         {
  119.             CPIDL toFree((LPITEMIDLIST)pData);
  120.             pData = CCoolMenuManager::GetItemData(*m_pSendToMenu, ++i);
  121.         }
  122.         g_CoolMenuManager.UnconvertMenu(m_pSendToMenu);
  123.         m_pSendToMenu = NULL;
  124.     }
  125. // Clean up owner draw menus
  126. STL_FOR_ITERATOR(vecODMenu,m_OwnerDrawMenus)
  127. {
  128. g_CoolMenuManager.UnconvertMenu(STL_GET_CURRENT(m_OwnerDrawMenus));
  129. }
  130. STL_ERASE_ALL(m_OwnerDrawMenus);
  131. g_CoolMenuManager.SetButtonSize(m_szOldButtonSize);
  132. //////////////////////////////
  133. }
  134. /////////////////////////////
  135. // Addition: Philip Oldaker
  136. /////////////////////////////
  137. CString CShellContextMenu::GetExt(const CString &sPath) const
  138. {
  139. TCHAR szDir[_MAX_PATH];
  140. TCHAR szDrive[_MAX_DRIVE];
  141. TCHAR szFileName[_MAX_FNAME];
  142. TCHAR szExt[_MAX_EXT];
  143. szDir[0] = szDrive[0] = szFileName[0] = szExt[0] = 0;
  144. _tsplitpath(sPath,szDrive,szDir,szFileName,szExt);
  145. return szExt;
  146. }
  147. // A bunch of registry nonsense to return all relevant details
  148. // but especially the application icon
  149. void CShellContextMenu::GetAppDetails(const CString &sAppName,CString &sDisplayName,CString &sCommand,HICON &hIconApp) const
  150. {
  151. hIconApp = NULL;
  152. sDisplayName.Empty();
  153. sCommand.Empty();
  154. CString sAppKey(szAppKey);
  155. AddKey(sAppKey,sAppName);
  156. AddKey(sAppKey,szShellKey);
  157. HKEY hKey;
  158. if (RegOpenKeyEx(HKEY_CLASSES_ROOT,sAppKey,0,KEY_READ,&hKey) != ERROR_SUCCESS)
  159. return;
  160. BYTE szDisplayName[_MAX_PATH];
  161. DWORD dwSize=sizeof(szDisplayName);
  162. DWORD dwType=REG_SZ;
  163. LONG nRet = RegQueryValueEx(hKey,szDisplayNameEntry,NULL,&dwType,szDisplayName,&dwSize);
  164. if (nRet != ERROR_SUCCESS)
  165. {
  166. RegCloseKey(hKey);
  167. return;
  168. }
  169. sDisplayName = szDisplayName;
  170. TCHAR szSubKey[_MAX_PATH];
  171. TCHAR szClass[_MAX_PATH];
  172. DWORD dwSizeSubKey = sizeof(szSubKey)-1;
  173. DWORD dwSizeClass = sizeof(szClass)-1;
  174. ZeroMemory(szSubKey,sizeof(szSubKey));
  175. ZeroMemory(szClass,sizeof(szClass));
  176. FILETIME ftLastWriteTime;
  177. HKEY hCmdKey=NULL;
  178. CString sCmdKey;
  179. // Search for a key that contains the "command" key as it may not be under "open"
  180. for(DWORD dwIndex=0;RegEnumKeyEx(hKey,dwIndex, 
  181.   szSubKey, 
  182.   &dwSizeSubKey,
  183.   NULL,
  184.   szClass,
  185.   &dwSizeClass,
  186.   &ftLastWriteTime) == ERROR_SUCCESS;dwIndex++)
  187. {
  188. sCmdKey = szSubKey;
  189. AddKey(sCmdKey,szCommandKey);
  190. if (RegOpenKeyEx(hKey,sCmdKey,0,KEY_READ,&hCmdKey) == ERROR_SUCCESS)
  191. {
  192. break;
  193. }
  194. dwSizeSubKey = sizeof(szSubKey)-1;
  195. dwSizeClass = sizeof(szClass)-1;
  196. }
  197. RegCloseKey(hKey);
  198. hKey = NULL;
  199. if (hCmdKey == NULL)
  200. return;
  201. dwType=REG_SZ | REG_EXPAND_SZ;
  202. BYTE szCommand[_MAX_PATH];
  203. dwSize=sizeof(szCommand);
  204. nRet = RegQueryValueEx(hCmdKey,NULL,NULL,&dwType,szCommand,&dwSize);
  205. if (nRet != ERROR_SUCCESS)
  206. return;
  207. TCHAR szPath[MAX_PATH];
  208. sCommand = szCommand;
  209. if (ExpandEnvironmentStrings(sCommand,szPath,sizeof(szPath)))
  210. sCommand = szPath;
  211. CString sIconPath(sCommand);
  212. sIconPath.MakeLower();
  213. // Only extract icons from exe's at the moment
  214. int nPos = sIconPath.Find(_T(".exe"));
  215. if (nPos == -1)
  216. return;
  217. sIconPath = sIconPath.Left(nPos+4);
  218. // Remove auy quotes
  219. if (sIconPath.Left(1) == '"')
  220. sIconPath = sIconPath.Right(sIconPath.GetLength()-1);
  221. if (sIconPath.Right(1) == '"')
  222. sIconPath = sIconPath.Left(sIconPath.GetLength()-1);
  223. ExtractIconEx(sIconPath, 0, NULL, &hIconApp, 1);
  224. RegCloseKey(hCmdKey);
  225. }
  226. // reg helper
  227. void CShellContextMenu::AddKey(CString &sDestKey,const CString &sSrcKey) const
  228. {
  229. if (sDestKey.Right(1) != '\')
  230. sDestKey += '\';
  231. sDestKey += sSrcKey;
  232. }
  233. ////////////////////////////////////////
  234. bool CShellContextMenu::IsMenuCommand(int iCmd) const
  235. {
  236.     return (
  237.             (IDM_SENDTOFIRST   <= iCmd  &&  iCmd <= IDM_SENDTOLAST) ||
  238.             (IDM_SHELLCTXFIRST <= iCmd  &&  iCmd <= IDM_SHELLCTXLAST)
  239.            );
  240. }
  241. void CShellContextMenu::InvokeCommand(int iCmd) const
  242. {
  243. if (!iCmd)
  244. return;
  245. USES_CONVERSION;
  246. /////////////////////////////
  247.         if (IDM_SENDTOFIRST <= iCmd  &&  iCmd <= IDM_SENDTOLAST)
  248.         {
  249.             // "Send To..." item
  250.             CPIDL        pidlFile(m_sAbsPath), pidlDrop;
  251.             LPDROPTARGET pDT;
  252.             LPDATAOBJECT pDO;
  253.             HRESULT hr;
  254.             hr = pidlFile.GetUIObjectOf(IID_IDataObject, (LPVOID *)&pDO, 
  255.                 m_hWnd);
  256.             if (SUCCEEDED(hr))
  257.             {
  258.                 pidlDrop.Set((LPITEMIDLIST)
  259.                     CCoolMenuManager::GetItemData(*m_pSendToMenu, iCmd));
  260.                 hr = pidlDrop.GetUIObjectOf(IID_IDropTarget, 
  261.                     (LPVOID *)&pDT, m_hWnd);
  262.                 if (SUCCEEDED(hr))
  263.                 {
  264.                     // do the drop
  265.                     POINTL pt = { 0, 0 };
  266.                     DWORD dwEffect = 
  267.                         DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
  268.                     hr = pDT->DragEnter(pDO, MK_LBUTTON, pt, &dwEffect);
  269.                     if (SUCCEEDED(hr) && dwEffect) 
  270.                     {
  271.                         hr = pDT->Drop(pDO, MK_LBUTTON, pt, &dwEffect);
  272.                     } 
  273.                     else 
  274.                     {
  275.                         hr = pDT->DragLeave();
  276.                     }
  277.                     pDT->Release();
  278.                 }
  279.                 pidlDrop.m_pidl = NULL;
  280.             }
  281.         }
  282.         else
  283.         {
  284. DWORD dwItemData=0;
  285. CMenu *pMenu=NULL;
  286. STL_FOR_ITERATOR(vecODMenu,m_OwnerDrawMenus)
  287. {
  288. pMenu = STL_GET_CURRENT(m_OwnerDrawMenus);
  289. if (pMenu && pMenu->GetMenuItemCount() > 0)
  290. {
  291. // switch to original item data
  292. if ((UINT)iCmd >= pMenu->GetMenuItemID(0) && (UINT)iCmd <= pMenu->GetMenuItemID(pMenu->GetMenuItemCount()-1))
  293. {
  294. dwItemData = g_CoolMenuManager.SwitchContextItemData(pMenu,iCmd,0,FALSE);
  295. break;
  296. }
  297. }
  298. }
  299. CWaitCursor w;
  300.             // Shell command
  301.             CMINVOKECOMMANDINFO cmi;
  302.             cmi.cbSize       = sizeof(cmi);
  303.             cmi.fMask        = 0;
  304.             cmi.hwnd         = m_hWnd;
  305.             cmi.lpVerb       = T2CA(MAKEINTRESOURCE(iCmd - IDM_SHELLCTXFIRST));
  306.             cmi.lpParameters = NULL;
  307.             cmi.lpDirectory  = NULL;
  308.             cmi.nShow        = SW_SHOWNORMAL;
  309.             cmi.dwHotKey     = 0;
  310.             cmi.hIcon        = NULL;
  311.             m_lpcm->InvokeCommand(&cmi);
  312. // switch back to our item data
  313. if (dwItemData)
  314. {
  315. g_CoolMenuManager.SwitchContextItemData(pMenu,iCmd,dwItemData,FALSE);
  316. }
  317.         }
  318. }
  319. // retrieves the shell context menu for a file
  320. void CShellContextMenu::SetMenu(CMenu *pMenu)
  321. {
  322.     if (m_sAbsPath.IsEmpty())
  323. return;
  324. ASSERT(m_psfParent);
  325.     HRESULT hr = m_psfParent->GetUIObjectOf(m_hWnd, m_cidl, (LPCITEMIDLIST*)m_ppidl, IID_IContextMenu, NULL, (LPVOID *)&m_lpcm);
  326. // NOTE: Slight change to return here but change it back if you want: PO
  327.     if (FAILED(hr))
  328. return;
  329.     g_CoolMenuManager.UnconvertMenu(pMenu);
  330. g_CoolMenuManager.SetShellContextMenu(m_lpcm,IDM_SHELLCTXFIRST,IDM_SHELLCTXLAST);
  331.     pMenu->DeleteMenu(0, MF_BYPOSITION);
  332.     hr = m_lpcm->QueryContextMenu(*pMenu, 0, IDM_SHELLCTXFIRST, 
  333.         IDM_SHELLCTXLAST, CMF_EXPLORE);
  334. /////////////////////////////
  335. // Additions Philip Oldaker 
  336. // Check for IContextMenu2 used by owner draw menus
  337.     CMenu *pSubMenu;
  338.     int count = pMenu->GetMenuItemCount();
  339.     for (int i = 0; i < count; i++)
  340.     {
  341. CMenuItemInfo mii;
  342. mii.fMask = MIIM_TYPE | MIIM_ID;
  343. pMenu->GetMenuItemInfo(i,&mii,TRUE);
  344. if (mii.fType & MFT_OWNERDRAW)
  345. {
  346. TRACE(_T("OwnerDraw menu item found in CShellContextMenun"));
  347.             g_CoolMenuManager.AddShellContextMenu(pMenu,m_lpcm,i);
  348. STL_ADD_ITEM(m_OwnerDrawMenus,pMenu);
  349. }
  350.         pSubMenu = pMenu->GetSubMenu(i);
  351. /////////////////////////////
  352. // Additions Philip Oldaker 
  353. // Search for owner draw menus and add the ICM2 pointer
  354. if (pSubMenu)
  355. {
  356. // NOTE: Remove the TRACE statements if not needed
  357. UINT nSubCount = pSubMenu->GetMenuItemCount();
  358. TRACE(_T("Count=%un"),nSubCount);
  359. for(UINT m=0;m < nSubCount;m++)
  360. {
  361. CMenuItemInfo mii;
  362. mii.fMask = MIIM_TYPE;
  363. pSubMenu->GetMenuItemInfo(m,&mii,TRUE);
  364. if (mii.fType & MFT_OWNERDRAW)
  365. {
  366. TRACE(_T("OwnerDraw submenu found in CShellContextMenun"));
  367.                     g_CoolMenuManager.AddShellContextMenu(pSubMenu,m_lpcm,m);
  368. STL_ADD_ITEM(m_OwnerDrawMenus,pSubMenu);
  369. }
  370. }
  371. }
  372. /////////////////////////////
  373.     // find the "Send To" and the new "Open With" submenu: look for a defined menu item id
  374.         if (pSubMenu && pSubMenu->GetMenuItemCount() == 1)
  375.         {
  376.     CString str;
  377. pSubMenu->GetMenuString(0,str,MF_BYPOSITION);
  378.             UINT idmFirst = pSubMenu->GetMenuItemID(0);
  379. TRACE(_T("Menu Item %s=%dn"),str,idmFirst);
  380. /////////////////////////////
  381. // Additions Philip Oldaker 
  382. // Populate the OpenWith menu
  383. if (idmFirst == IDM_OPENWITHID1 || idmFirst == IDM_OPENWITHID2)
  384. {
  385. // ok - found it.  now populate it
  386. IContextMenu2 *lpcm2=NULL;
  387. HRESULT hr = m_lpcm->QueryInterface(IID_IContextMenu2,(LPVOID*)&lpcm2);
  388. if (SUCCEEDED(hr))
  389. {
  390. hr = lpcm2->HandleMenuMsg(WM_INITMENUPOPUP,(WPARAM)pSubMenu->GetSafeHmenu(),0);
  391. lpcm2->Release();
  392. // We need the extension to search the registry for valid applications
  393. CString sExt(GetExt(m_sAbsPath));
  394. // No extension then there's nothing to do
  395. if (sExt.IsEmpty())
  396. continue;
  397. FillOpenWithMenu(pSubMenu,sExt);
  398. STL_ADD_ITEM(m_OwnerDrawMenus,pSubMenu);
  399. }
  400. }
  401.             else if (idmFirst == IDM_SENDTOID)
  402.             {
  403.             UINT idm = IDM_SENDTOFIRST;
  404.                 // ok - found it.  now populate it
  405.                 m_pSendToMenu = CMenu::FromHandle(::CreatePopupMenu());
  406.                 ASSERT_VALID(m_pSendToMenu);
  407.                 pSubMenu->DestroyMenu();
  408.                 pMenu->ModifyMenu(i, MF_BYPOSITION | MF_POPUP, 
  409.                     (UINT)m_pSendToMenu->m_hMenu, str);
  410.                 FillSendToMenu(m_pSendToMenu, stat_data.m_sfSendTo, idm);
  411.             }
  412. /////////////////////////////
  413.         }
  414.     }
  415. // Not needed I think :PO
  416.     return;
  417. }
  418. ////////////////////////////
  419. // Additions: Philip Oldaker
  420. // Scan the registry looking for a OpenWithList key in this format
  421. // Entry MRUList contains the alphabetical order eg. afgrde for each entry
  422. // each of these entries points to the application name which in turn
  423. // has the command and display name
  424. ////////////////////////////
  425. void CShellContextMenu::FillOpenWithMenu(CMenu *pMenu,const CString &sExt)
  426. {
  427. HKEY hKey;
  428. CString sFileExtKey(szFileExtKey);
  429. AddKey(sFileExtKey,sExt);
  430. AddKey(sFileExtKey,szOpenWithListKey);
  431. if (RegOpenKeyEx(HKEY_CURRENT_USER,sFileExtKey,0,KEY_READ,&hKey) != ERROR_SUCCESS)
  432. return;
  433. BYTE szMRUList[_MAX_PATH];
  434. szMRUList[0] = 0;
  435. DWORD dwSize=sizeof(szMRUList);
  436. DWORD dwType=REG_SZ;
  437. RegQueryValueEx(hKey,szMRUListEntry,NULL,&dwType,szMRUList,&dwSize);
  438. int nLen = _tcslen((LPCTSTR)szMRUList);
  439. TCHAR szOpenWithItemEntry[sizeof(TCHAR)*2];
  440. ZeroMemory(szOpenWithItemEntry,sizeof(szOpenWithItemEntry));
  441. BYTE szOpenWithItem[_MAX_PATH];
  442. CString sDisplayName;
  443. HICON hIconApp;
  444. CString sCommand;
  445. CString sMenuText;
  446. for(int i=0;i < nLen;i++)
  447. {
  448. szOpenWithItemEntry[0] = szMRUList[i];
  449. dwSize = sizeof(szOpenWithItem);
  450. RegQueryValueEx(hKey,szOpenWithItemEntry,NULL,&dwType,szOpenWithItem,&dwSize);
  451. GetAppDetails(szOpenWithItem, sDisplayName, sCommand, hIconApp);
  452. if (!sCommand.IsEmpty())
  453. {
  454. for(UINT idm=0;idm < pMenu->GetMenuItemCount();idm++)
  455. {
  456. pMenu->GetMenuString(idm,sMenuText,MF_BYPOSITION);
  457. if (sMenuText == sDisplayName)
  458. {
  459. g_CoolMenuManager.ConvertMenuItem(pMenu, idm);
  460. CCoolMenuManager::SetItemIcon(*pMenu, hIconApp, idm, TRUE);
  461. break;
  462. }
  463. }
  464. }
  465. }
  466. RegCloseKey(hKey);
  467. }
  468. void CShellContextMenu::FillSendToMenu(CMenu *pMenu, LPSHELLFOLDER pSF, 
  469.                                        UINT &idm)
  470. {
  471.     if (pSF == NULL) 
  472. return;
  473.     USES_CONVERSION;
  474.     CPIDL           pidl, abspidl;
  475.     LPENUMIDLIST    peidl;
  476.     HRESULT         hr;
  477.     STRRET          str;
  478.     UINT            idmStart = idm;
  479.     LPSHELLFOLDER   pSubSF;
  480.     SHFILEINFO      sfi;
  481. vecCMSort vMenuItems;
  482.     int idx_folder = 0; // folder insertion index
  483.     hr = pSF->EnumObjects(m_hWnd, 
  484.         SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &peidl);
  485.     if (FAILED(hr))
  486. return;
  487.     g_CoolMenuManager.ConvertMenu(pMenu);
  488.     while (peidl->Next(1, pidl, NULL) == S_OK  &&
  489.            idm < IDM_SENDTOLAST) 
  490.     {
  491.         hr = pSF->GetDisplayNameOf(pidl, SHGDN_NORMAL, &str);
  492.         if (SUCCEEDED(hr)) 
  493.         {
  494.             ULONG ulAttrs = (unsigned)-1;
  495.             pSF->GetAttributesOf(1, pidl, &ulAttrs);
  496.             abspidl.MakeAbsPIDLOf(pSF, pidl);
  497.             SHGetFileInfo((LPCTSTR)abspidl.m_pidl, 0, &sfi, sizeof(sfi),
  498.                 SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON);
  499.             pidl.ExtractCStr(str);
  500.             if (ulAttrs & SFGAO_FOLDER) // folder?
  501.             {
  502.                 // create new submenu & recurse
  503.                 HMENU hSubMenu = ::CreateMenu();
  504.                 pMenu->InsertMenu(idx_folder, 
  505.                     MF_POPUP | MF_BYPOSITION | MF_STRING, 
  506.                     (UINT)hSubMenu, A2T(str.cStr));
  507.                 g_CoolMenuManager.ConvertMenuItem(pMenu, 
  508.                     idx_folder);
  509.                 CCoolMenuManager::SetItemIcon(*pMenu, 
  510.                     sfi.hIcon, idx_folder, TRUE);
  511.                 idx_folder++;
  512.                 hr = pSF->BindToObject(pidl, NULL, 
  513.                     IID_IShellFolder, (LPVOID *)&pSubSF);
  514.                 if (!SUCCEEDED(hr)) pSubSF = NULL;
  515.                 FillSendToMenu(CMenu::FromHandle(hSubMenu), pSubSF, 
  516.                     idm);
  517.                 if (pSubSF) pSubSF->Release();
  518.                 abspidl.Free();
  519.             }
  520.             else
  521.             {
  522. CShCMSort *pSMI = new CShCMSort(idm,pidl.m_pidl,sfi.hIcon,A2T(str.cStr),(DWORD)abspidl.m_pidl);
  523. STL_ADD_ITEM(vMenuItems,pSMI);
  524.                 idm++;
  525.             }
  526.             abspidl.m_pidl = NULL;
  527.         }
  528.     }
  529.     peidl->Release();
  530. // Addition: Philip Oldaker
  531. // To keep it familiar the SendTo menu is sorted
  532. STL_SORT(vMenuItems,pSF,STL_SORT_FUNC);
  533. CShCMSort *pItem=NULL;
  534. STL_FOR_ITERATOR(vecCMSort,vMenuItems)
  535. {
  536. pItem = STL_GET_CURRENT(vMenuItems);
  537. pMenu->AppendMenu(MF_STRING, pItem->GetItemID(), pItem->GetText());
  538. g_CoolMenuManager.ConvertMenuItem(pMenu, 
  539. pMenu->GetMenuItemCount() - 1);
  540. CCoolMenuManager::SetItemIcon(*pMenu, 
  541. pItem->GetIcon(), pItem->GetItemID());
  542. CCoolMenuManager::SetItemData(*pMenu, 
  543. (void*)pItem->GetItemData(), pItem->GetItemID());
  544.         CPIDL toFree(pItem->GetPidl());
  545. delete pItem;
  546. }
  547. STL_ERASE_ALL(vMenuItems);
  548. //////////////////////////////////
  549.     // If the menu is still empty (the user has an empty SendTo folder),
  550.     // then add a disabled "(empty)" item so we have at least something
  551.     // to display.
  552.     if (idm == idmStart) 
  553.     {
  554.         pMenu->AppendMenu(MF_GRAYED | MF_DISABLED | MF_STRING, idm, 
  555.             _T("(empty)"));
  556.         idm++;
  557.     }
  558. }