Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2207 lines
72 KiB

  1. #include "stdafx.h"
  2. #pragma hdrstop
  3. #include <oleacc.h> // MSAAMENUINFO stuff
  4. #include <runtask.h>
  5. #include "datautil.h"
  6. #include "idlcomm.h"
  7. #include "stgutil.h"
  8. #include <winnls.h>
  9. #include "filetbl.h"
  10. #include "cdburn.h"
  11. #include "mtpt.h"
  12. #ifndef CMF_DVFILE
  13. #define CMF_DVFILE 0x00010000 // "File" pulldown
  14. #endif
  15. class CSendToMenu : public IContextMenu3, IShellExtInit, IOleWindow
  16. {
  17. public:
  18. // IUnknown
  19. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  20. STDMETHOD_(ULONG,AddRef)(void);
  21. STDMETHOD_(ULONG,Release)(void);
  22. // IContextMenu
  23. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  24. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  25. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  26. // IContextMenu2
  27. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  28. // IContextMenu3
  29. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  30. // IShellExtInit
  31. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  32. // IOleWindow
  33. STDMETHOD(GetWindow)(HWND *phwnd);
  34. STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) {return E_NOTIMPL;};
  35. private:
  36. CSendToMenu();
  37. ~CSendToMenu();
  38. LONG _cRef;
  39. HMENU _hmenu;
  40. UINT _idCmdFirst;
  41. BOOL _bFirstTime;
  42. HWND _hwnd;
  43. IDataObject *_pdtobj;
  44. LPITEMIDLIST _pidlLast;
  45. DWORD _GetKeyState(void);
  46. HRESULT _DoDragDrop(HWND hwndParent, IDropTarget *pdrop);
  47. BOOL _CanDrop(IShellFolder *psf, LPCITEMIDLIST pidl);
  48. HRESULT _MenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl);
  49. HRESULT _RemovableDrivesMenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl);
  50. static HRESULT CALLBACK s_MenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl);
  51. static HRESULT CALLBACK s_RemovableDrivesMenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl);
  52. friend HRESULT CSendToMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut);
  53. };
  54. CSendToMenu::CSendToMenu() : _cRef(1)
  55. {
  56. DllAddRef();
  57. }
  58. CSendToMenu::~CSendToMenu()
  59. {
  60. if (_hmenu)
  61. FileMenu_DeleteAllItems(_hmenu);
  62. if (_pdtobj)
  63. _pdtobj->Release();
  64. ILFree(_pidlLast);
  65. DllRelease();
  66. }
  67. HRESULT CSendToMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
  68. {
  69. HRESULT hr = E_OUTOFMEMORY;
  70. CSendToMenu *pstm = new CSendToMenu();
  71. if (pstm)
  72. {
  73. hr = pstm->QueryInterface(riid, ppvOut);
  74. pstm->Release();
  75. }
  76. return hr;
  77. }
  78. HRESULT CSendToMenu::QueryInterface(REFIID riid, void **ppvObj)
  79. {
  80. static const QITAB qit[] = {
  81. QITABENT(CSendToMenu, IShellExtInit), // IID_IShellExtInit
  82. QITABENT(CSendToMenu, IOleWindow), // IID_IOleWindow
  83. QITABENT(CSendToMenu, IContextMenu3), // IID_IContextMenu3
  84. QITABENTMULTI(CSendToMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  85. QITABENTMULTI(CSendToMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
  86. { 0 }
  87. };
  88. return QISearch(this, qit, riid, ppvObj);
  89. }
  90. ULONG CSendToMenu::AddRef()
  91. {
  92. return InterlockedIncrement(&_cRef);
  93. }
  94. ULONG CSendToMenu::Release()
  95. {
  96. ASSERT( 0 != _cRef );
  97. ULONG cRef = InterlockedDecrement(&_cRef);
  98. if ( 0 == cRef )
  99. {
  100. delete this;
  101. }
  102. return cRef;
  103. }
  104. HRESULT CSendToMenu::GetWindow(HWND *phwnd)
  105. {
  106. HRESULT hr = E_INVALIDARG;
  107. if (phwnd)
  108. {
  109. *phwnd = _hwnd;
  110. hr = S_OK;
  111. }
  112. return hr;
  113. }
  114. HRESULT CSendToMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  115. {
  116. // if they want the default menu only (CMF_DEFAULTONLY) OR
  117. // this is being called for a shortcut (CMF_VERBSONLY)
  118. // we don't want to be on the context menu
  119. if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY))
  120. return S_OK;
  121. UINT idMax = idCmdFirst;
  122. _hmenu = CreatePopupMenu();
  123. if (_hmenu)
  124. {
  125. TCHAR szSendLinkTo[80];
  126. TCHAR szSendPageTo[80];
  127. MENUITEMINFO mii;
  128. // add a dummy item so we are identified at WM_INITMENUPOPUP time
  129. LoadString(g_hinst, IDS_SENDLINKTO, szSendLinkTo, ARRAYSIZE(szSendLinkTo));
  130. LoadString(g_hinst, IDS_SENDPAGETO, szSendPageTo, ARRAYSIZE(szSendPageTo));
  131. mii.cbSize = sizeof(MENUITEMINFO);
  132. mii.fMask = MIIM_ID | MIIM_TYPE;
  133. mii.fType = MFT_STRING;
  134. mii.dwTypeData = szSendLinkTo;
  135. mii.wID = idCmdFirst + 1;
  136. if (InsertMenuItem(_hmenu, 0, TRUE, &mii))
  137. {
  138. _idCmdFirst = idCmdFirst + 1; // remember this for later
  139. mii.fType = MFT_STRING;
  140. mii.dwTypeData = szSendLinkTo;
  141. mii.wID = idCmdFirst;
  142. mii.fState = MF_DISABLED | MF_GRAYED;
  143. mii.fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;
  144. mii.hSubMenu = _hmenu;
  145. if (InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
  146. {
  147. idMax += 0x40; // reserve space for this many items
  148. _bFirstTime = TRUE; // fill this at WM_INITMENUPOPUP time
  149. }
  150. else
  151. {
  152. _hmenu = NULL;
  153. }
  154. }
  155. }
  156. _hmenu = NULL;
  157. return ResultFromShort(idMax - idCmdFirst);
  158. }
  159. DWORD CSendToMenu::_GetKeyState(void)
  160. {
  161. DWORD grfKeyState = MK_LBUTTON; // default
  162. if (GetAsyncKeyState(VK_CONTROL) < 0)
  163. grfKeyState |= MK_CONTROL;
  164. if (GetAsyncKeyState(VK_SHIFT) < 0)
  165. grfKeyState |= MK_SHIFT;
  166. if (GetAsyncKeyState(VK_MENU) < 0)
  167. grfKeyState |= MK_ALT; // menu's don't really allow this
  168. return grfKeyState;
  169. }
  170. HRESULT CSendToMenu::_DoDragDrop(HWND hwndParent, IDropTarget *pdrop)
  171. {
  172. DWORD grfKeyState = _GetKeyState();
  173. if (grfKeyState == MK_LBUTTON)
  174. {
  175. // no modifieres, change default to COPY
  176. grfKeyState = MK_LBUTTON | MK_CONTROL;
  177. DataObj_SetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_COPY);
  178. }
  179. _hwnd = hwndParent;
  180. IUnknown_SetSite(pdrop, SAFECAST(this, IOleWindow *)); // Let them have access to our HWND.
  181. HRESULT hr = SHSimulateDrop(pdrop, _pdtobj, grfKeyState, NULL, NULL);
  182. IUnknown_SetSite(pdrop, NULL);
  183. if (hr == S_FALSE)
  184. {
  185. ShellMessageBox(g_hinst, hwndParent,
  186. MAKEINTRESOURCE(IDS_SENDTO_ERRORMSG),
  187. MAKEINTRESOURCE(IDS_CABINET),
  188. MB_OK|MB_ICONEXCLAMATION);
  189. }
  190. return hr;
  191. }
  192. HRESULT CSendToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  193. {
  194. HRESULT hr;
  195. if (_pdtobj && _pidlLast)
  196. {
  197. IDropTarget *pdrop;
  198. hr = SHGetUIObjectFromFullPIDL(_pidlLast, pici->hwnd, IID_PPV_ARG(IDropTarget, &pdrop));
  199. if (SUCCEEDED(hr))
  200. {
  201. hr = _DoDragDrop(pici->hwnd, pdrop);
  202. pdrop->Release();
  203. }
  204. }
  205. else
  206. hr = E_INVALIDARG;
  207. return hr;
  208. }
  209. HRESULT CSendToMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  210. {
  211. return E_NOTIMPL;
  212. }
  213. HRESULT CSendToMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  214. {
  215. return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
  216. }
  217. BOOL CSendToMenu::_CanDrop(IShellFolder *psf, LPCITEMIDLIST pidl)
  218. {
  219. BOOL fCanDrop = FALSE;
  220. IDropTarget *pdt;
  221. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidl, IID_X_PPV_ARG(IDropTarget, 0, &pdt))))
  222. {
  223. POINTL pt = {0};
  224. DWORD dwEffect = DROPEFFECT_COPY;
  225. // Do a drag enter, if they return no drop effect then we can't drop
  226. if (SUCCEEDED(pdt->DragEnter(_pdtobj, _GetKeyState(), pt, &dwEffect)))
  227. {
  228. if (dwEffect != DROPEFFECT_NONE)
  229. fCanDrop = TRUE; // show it!
  230. pdt->DragLeave();
  231. }
  232. pdt->Release();
  233. }
  234. return fCanDrop;
  235. }
  236. HRESULT CSendToMenu::_MenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl)
  237. {
  238. HRESULT hr = S_OK;
  239. switch (fmm)
  240. {
  241. case FMM_ADD:
  242. hr = _CanDrop(psf, pidl) ? S_OK : S_FALSE;
  243. break;
  244. case FMM_SETLASTPIDL:
  245. Pidl_Set(&_pidlLast, pidl);
  246. break;
  247. default:
  248. hr = E_FAIL;
  249. }
  250. return hr;
  251. }
  252. HRESULT CSendToMenu::_RemovableDrivesMenuCallback(UINT fmm, IShellFolder *psf, LPCITEMIDLIST pidl)
  253. {
  254. HRESULT hr = S_OK;
  255. switch (fmm)
  256. {
  257. case FMM_ADD:
  258. hr = S_FALSE; // assume we wont show it
  259. if (_CanDrop(psf, pidl))
  260. {
  261. // now we know it's a removable drive. in general we dont want to display cd-rom drives.
  262. // we know this is the my computer folder so just get the parsing name, we need it for GetDriveType.
  263. WCHAR szDrive[MAX_PATH];
  264. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING, szDrive, ARRAYSIZE(szDrive))))
  265. {
  266. CMountPoint *pmtpt = CMountPoint::GetMountPoint(szDrive);
  267. if (pmtpt)
  268. {
  269. if (pmtpt->IsCDROM())
  270. {
  271. // of all cdroms, only the enabled burning folder is okay to put on sendto
  272. WCHAR szRecorder[4];
  273. if (SUCCEEDED(CDBurn_GetRecorderDriveLetter(szRecorder, ARRAYSIZE(szRecorder))) &&
  274. (lstrcmpiW(szRecorder, szDrive) == 0))
  275. {
  276. hr = S_OK;
  277. }
  278. }
  279. else if (pmtpt->IsFloppy() || pmtpt->IsStrictRemovable() || pmtpt->IsRemovableDevice())
  280. {
  281. // also put on removable devices.
  282. hr = S_OK;
  283. }
  284. pmtpt->Release();
  285. }
  286. else
  287. {
  288. // if this failed it could be a memory condition but its more likely to be that the
  289. // parsing name doesnt map to a mountpoint. in that case fall back to SFGAO_REMOVABLE
  290. // to pick up portable audio devices. if this was because of lowmem its no biggie.
  291. if (SHGetAttributes(psf, pidl, SFGAO_REMOVABLE))
  292. {
  293. hr = S_OK;
  294. }
  295. }
  296. }
  297. }
  298. break;
  299. case FMM_SETLASTPIDL:
  300. Pidl_Set(&_pidlLast, pidl);
  301. break;
  302. default:
  303. hr = E_FAIL;
  304. }
  305. return hr;
  306. }
  307. HRESULT CSendToMenu::s_MenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl)
  308. {
  309. return ((CSendToMenu*)lParam)->_MenuCallback(fmm, psf, pidl);
  310. }
  311. HRESULT CSendToMenu::s_RemovableDrivesMenuCallback(UINT fmm, LPARAM lParam, IShellFolder *psf, LPCITEMIDLIST pidl)
  312. {
  313. return ((CSendToMenu*)lParam)->_RemovableDrivesMenuCallback(fmm, psf, pidl);
  314. }
  315. HRESULT GetFolder(int csidl, IShellFolder **ppsf)
  316. {
  317. LPITEMIDLIST pidl;
  318. HRESULT hr = SHGetFolderLocation(NULL, csidl, NULL, 0, &pidl);
  319. if (SUCCEEDED(hr))
  320. {
  321. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, ppsf));
  322. ILFree(pidl);
  323. }
  324. return hr;
  325. }
  326. HRESULT CSendToMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  327. {
  328. HRESULT hr = S_OK;
  329. LRESULT lRes = 0;
  330. switch (uMsg)
  331. {
  332. case WM_INITMENUPOPUP:
  333. if (_bFirstTime)
  334. {
  335. _bFirstTime = FALSE;
  336. //In case of Shell_MergeMenus
  337. if (_hmenu == NULL)
  338. _hmenu = (HMENU)wParam;
  339. // delete the dummy entry
  340. DeleteMenu(_hmenu, 0, MF_BYPOSITION);
  341. FMCOMPOSE fmc = {0};
  342. if (SUCCEEDED(GetFolder(CSIDL_SENDTO, &fmc.psf)))
  343. {
  344. fmc.idCmd = _idCmdFirst;
  345. fmc.grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  346. fmc.pfnCallback = s_MenuCallback;
  347. fmc.lParam = (LPARAM)this; // not reference counted
  348. FileMenu_Compose(_hmenu, FMCM_REPLACE, &fmc);
  349. fmc.psf->Release();
  350. }
  351. if (SUCCEEDED(GetFolder(CSIDL_DRIVES, &fmc.psf)))
  352. {
  353. fmc.dwMask = FMC_NOEXPAND;
  354. fmc.idCmd = _idCmdFirst;
  355. fmc.grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  356. fmc.pfnCallback = s_RemovableDrivesMenuCallback;
  357. fmc.lParam = (LPARAM)this; // not reference counted
  358. FileMenu_Compose(_hmenu, FMCM_APPEND, &fmc);
  359. fmc.psf->Release();
  360. }
  361. }
  362. else if (_hmenu != (HMENU)wParam)
  363. {
  364. // secondary cascade menu
  365. FileMenu_InitMenuPopup((HMENU)wParam);
  366. }
  367. break;
  368. case WM_DRAWITEM:
  369. {
  370. DRAWITEMSTRUCT *pdi = (DRAWITEMSTRUCT *)lParam;
  371. if (pdi->CtlType == ODT_MENU && pdi->itemID == _idCmdFirst)
  372. {
  373. lRes = FileMenu_DrawItem(NULL, pdi);
  374. }
  375. }
  376. break;
  377. case WM_MEASUREITEM:
  378. {
  379. MEASUREITEMSTRUCT *pmi = (MEASUREITEMSTRUCT *)lParam;
  380. if (pmi->CtlType == ODT_MENU && pmi->itemID == _idCmdFirst)
  381. {
  382. lRes = FileMenu_MeasureItem(NULL, pmi);
  383. }
  384. }
  385. break;
  386. case WM_MENUCHAR:
  387. {
  388. TCHAR ch = (TCHAR)LOWORD(wParam);
  389. HMENU hmenu = (HMENU)lParam;
  390. lRes = FileMenu_HandleMenuChar(hmenu, ch);
  391. }
  392. break;
  393. default:
  394. hr = E_NOTIMPL;
  395. break;
  396. }
  397. if (plResult)
  398. *plResult = lRes;
  399. return hr;
  400. }
  401. HRESULT CSendToMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  402. {
  403. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  404. return S_OK;
  405. }
  406. #define CXIMAGEGAP 6
  407. //This is included by shell32/shellprv.h I'm not sure where this is in shdocvw
  408. #define CCH_KEYMAX 64
  409. typedef struct
  410. {
  411. // Accessibility info must be first
  412. MSAAMENUINFO msaa;
  413. TCHAR chPrefix;
  414. TCHAR szMenuText[CCH_KEYMAX];
  415. TCHAR szExt[MAX_PATH];
  416. TCHAR szClass[CCH_KEYMAX];
  417. DWORD dwFlags;
  418. int iImage;
  419. TCHAR szUserFile[CCH_KEYMAX];
  420. } NEWOBJECTINFO;
  421. typedef struct
  422. {
  423. int type;
  424. void *lpData;
  425. DWORD cbData;
  426. HKEY hkeyNew;
  427. } NEWFILEINFO;
  428. typedef struct
  429. {
  430. ULONG cbStruct;
  431. ULONG ver;
  432. SYSTEMTIME lastupdate;
  433. } SHELLNEW_CACHE_STAMP;
  434. // ShellNew config flags
  435. #define SNCF_DEFAULT 0x0000
  436. #define SNCF_NOEXT 0x0001
  437. #define SNCF_USERFILES 0x0002
  438. #define NEWTYPE_DATA 0x0003
  439. #define NEWTYPE_FILE 0x0004
  440. #define NEWTYPE_NULL 0x0005
  441. #define NEWTYPE_COMMAND 0x0006
  442. #define NEWTYPE_FOLDER 0x0007
  443. #define NEWTYPE_LINK 0x0008
  444. #define NEWITEM_FOLDER 0
  445. #define NEWITEM_LINK 1
  446. #define NEWITEM_MAX 2
  447. class CNewMenu : public CObjectWithSite,
  448. public IContextMenu3,
  449. public IShellExtInit
  450. {
  451. // IUnknown
  452. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  453. STDMETHOD_(ULONG,AddRef)(void);
  454. STDMETHOD_(ULONG,Release)(void);
  455. // IContextMenu
  456. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  457. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  458. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  459. // IContextMenu2
  460. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  461. // IContextMenu3
  462. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  463. // IShellExtInit
  464. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  465. LONG _cRef;
  466. HMENU _hmenu;
  467. UINT _idCmdFirst;
  468. HIMAGELIST _himlSystemImageList;
  469. IDataObject *_pdtobj;
  470. LPITEMIDLIST _pidlFolder;
  471. POINT _ptNewItem; // from the view, point of click
  472. NEWOBJECTINFO *_pnoiLast;
  473. HDPA _hdpaMenuInfo;
  474. CNewMenu();
  475. ~CNewMenu();
  476. friend HRESULT CNewMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut);
  477. private:
  478. //Handle Menu messages submitted to HandleMenuMsg
  479. BOOL DrawItem(DRAWITEMSTRUCT *lpdi);
  480. LRESULT MeasureItem(MEASUREITEMSTRUCT *pmi);
  481. BOOL InitMenuPopup(HMENU hMenu);
  482. //Internal Helpers
  483. NEWOBJECTINFO *GetItemData(HMENU hmenu, UINT iItem);
  484. HRESULT RunCommand(HWND hwnd, LPCTSTR pszPath, LPCTSTR pszRun);
  485. HRESULT CopyTemplate(IStream *pStream, NEWFILEINFO *pnfi);
  486. // Generates it from the Fragment and _pidlFolder
  487. BOOL _GeneratePidlFromName(LPTSTR pszName, LPITEMIDLIST* ppidl);
  488. HRESULT _GetItemName(IUnknown *punkFolder, LPWSTR pszItemName, LPWSTR pszPath, UINT cchPath);
  489. HRESULT _MatchMenuItem(TCHAR ch, LRESULT* plRes);
  490. BOOL _InsertNewMenuItem(HMENU hmenu, UINT idCmd, NEWOBJECTINFO *pnoiClone);
  491. HRESULT ConsolidateMenuItems(BOOL bForce);
  492. HANDLE _hMutex, _hEvent;
  493. };
  494. void GetConfigFlags(HKEY hkey, DWORD * pdwFlags)
  495. {
  496. TCHAR szTemp[MAX_PATH];
  497. DWORD cbData = ARRAYSIZE(szTemp);
  498. *pdwFlags = SNCF_DEFAULT;
  499. if (SHQueryValueEx(hkey, TEXT("NoExtension"), 0, NULL, (BYTE *)szTemp, &cbData) == ERROR_SUCCESS)
  500. {
  501. *pdwFlags |= SNCF_NOEXT;
  502. }
  503. }
  504. BOOL GetNewFileInfoForKey(HKEY hkeyExt, NEWFILEINFO *pnfi, DWORD * pdwFlags)
  505. {
  506. BOOL fRet = FALSE;
  507. HKEY hKey; // this gets the \\.ext\progid key
  508. HKEY hkeyNew;
  509. TCHAR szProgID[80];
  510. DWORD cbProgID = sizeof(szProgID);
  511. // open the Newcommand
  512. if (SHRegGetValue(hkeyExt, NULL, NULL, SRRF_RT_REG_SZ, NULL, szProgID, &cbProgID) != ERROR_SUCCESS)
  513. {
  514. return FALSE;
  515. }
  516. if (ERROR_SUCCESS != RegOpenKeyEx(hkeyExt, szProgID, 0, KEY_QUERY_VALUE, &hKey))
  517. {
  518. hKey = hkeyExt;
  519. }
  520. if (ERROR_SUCCESS == RegOpenKeyEx(hKey, TEXT("ShellNew"), 0, KEY_QUERY_VALUE, &hkeyNew))
  521. {
  522. DWORD dwType, cbData;
  523. TCHAR szTemp[MAX_PATH];
  524. HKEY hkeyConfig;
  525. // Are there any config flags?
  526. if (pdwFlags)
  527. {
  528. if (ERROR_SUCCESS == RegOpenKeyEx(hkeyNew, TEXT("Config"), 0, KEY_QUERY_VALUE, &hkeyConfig))
  529. {
  530. GetConfigFlags(hkeyConfig, pdwFlags);
  531. RegCloseKey(hkeyConfig);
  532. }
  533. else
  534. {
  535. *pdwFlags = 0;
  536. }
  537. }
  538. if (cbData = sizeof(szTemp), (SHQueryValueEx(hkeyNew, TEXT("FileName"), 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS)
  539. && ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)))
  540. {
  541. fRet = TRUE;
  542. if (pnfi)
  543. {
  544. pnfi->type = NEWTYPE_FILE;
  545. pnfi->hkeyNew = hkeyNew; // store this away so we can find out which one held the file easily
  546. ASSERT((LPTSTR*)pnfi->lpData == NULL);
  547. pnfi->lpData = StrDup(szTemp);
  548. hkeyNew = NULL;
  549. }
  550. }
  551. else if (cbData = sizeof(szTemp), (SHQueryValueEx(hkeyNew, TEXT("command"), 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS)
  552. && ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)))
  553. {
  554. fRet = TRUE;
  555. if (pnfi)
  556. {
  557. pnfi->type = NEWTYPE_COMMAND;
  558. pnfi->hkeyNew = hkeyNew; // store this away so we can find out which one held the command easily
  559. ASSERT((LPTSTR*)pnfi->lpData == NULL);
  560. pnfi->lpData = StrDup(szTemp);
  561. hkeyNew = NULL;
  562. }
  563. }
  564. else if ((SHQueryValueEx(hkeyNew, TEXT("Data"), 0, &dwType, NULL, &cbData) == ERROR_SUCCESS) && cbData)
  565. {
  566. // yes! the data for a new file is stored in the registry
  567. fRet = TRUE;
  568. // do they want the data?
  569. if (pnfi)
  570. {
  571. pnfi->type = NEWTYPE_DATA;
  572. pnfi->cbData = cbData;
  573. pnfi->lpData = (void*)LocalAlloc(LPTR, cbData);
  574. if (pnfi->lpData)
  575. {
  576. if (dwType == REG_SZ)
  577. {
  578. // Get the Unicode data from the registry.
  579. LPWSTR pszTemp = (LPWSTR)LocalAlloc(LPTR, cbData);
  580. if (pszTemp)
  581. {
  582. SHQueryValueEx(hkeyNew, TEXT("Data"), 0, &dwType, (LPBYTE)pszTemp, &cbData);
  583. pnfi->cbData = SHUnicodeToAnsi(pszTemp, (LPSTR)pnfi->lpData, cbData);
  584. if (pnfi->cbData == 0)
  585. {
  586. LocalFree(pnfi->lpData);
  587. pnfi->lpData = NULL;
  588. }
  589. LocalFree(pszTemp);
  590. }
  591. else
  592. {
  593. LocalFree(pnfi->lpData);
  594. pnfi->lpData = NULL;
  595. }
  596. }
  597. else
  598. {
  599. SHQueryValueEx(hkeyNew, TEXT("Data"), 0, &dwType, (BYTE*)pnfi->lpData, &cbData);
  600. }
  601. }
  602. }
  603. }
  604. else if (cbData = sizeof(szTemp), (SHQueryValueEx(hkeyNew, TEXT("NullFile"), 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS))
  605. {
  606. fRet = TRUE;
  607. if (pnfi)
  608. {
  609. pnfi->type = NEWTYPE_NULL;
  610. pnfi->cbData = 0;
  611. pnfi->lpData = NULL;
  612. }
  613. }
  614. if (hkeyNew)
  615. RegCloseKey(hkeyNew);
  616. }
  617. if (hKey != hkeyExt)
  618. {
  619. RegCloseKey(hKey);
  620. }
  621. return fRet;
  622. }
  623. BOOL GetNewFileInfoForExtension(NEWOBJECTINFO *pnoi, NEWFILEINFO *pnfi, HKEY* phKey, LPINT piIndex)
  624. {
  625. TCHAR szValue[80];
  626. DWORD lSize = sizeof(szValue);
  627. HKEY hkeyNew;
  628. BOOL fRet = FALSE;;
  629. if (phKey && ((*phKey) == (HKEY)-1))
  630. {
  631. // we're done
  632. return FALSE;
  633. }
  634. // do the ShellNew key stuff if there's no phKey passed in (which means
  635. // use the info in pnoi to get THE one) and there's no UserFile specified.
  636. //
  637. // if there IS a UserFile specified, then it's a file, and that szUserFile points to it..
  638. if (!phKey && !pnoi->szUserFile[0] ||
  639. (phKey && !*phKey))
  640. {
  641. // check the new keys under the class id (if any)
  642. TCHAR szSubKey[128];
  643. HRESULT hr;
  644. hr = StringCchPrintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("%s\\CLSID"), pnoi->szClass);
  645. if (SUCCEEDED(hr))
  646. {
  647. lSize = sizeof(szValue);
  648. if (SHRegGetValue(HKEY_CLASSES_ROOT, szSubKey, NULL, SRRF_RT_REG_SZ, NULL, szValue, &lSize) == ERROR_SUCCESS)
  649. {
  650. hr = StringCchPrintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("CLSID\\%s"), szValue);
  651. if (SUCCEEDED(hr))
  652. {
  653. lSize = sizeof(szValue);
  654. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szSubKey, 0, KEY_QUERY_VALUE, &hkeyNew))
  655. {
  656. fRet = GetNewFileInfoForKey(hkeyNew, pnfi, &pnoi->dwFlags);
  657. RegCloseKey(hkeyNew);
  658. }
  659. }
  660. }
  661. }
  662. // otherwise check under the type extension... do the extension, not the type
  663. // so that multi-ext to 1 type will work right
  664. if (!fRet && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, pnoi->szExt, 0, KEY_QUERY_VALUE, &hkeyNew)))
  665. {
  666. fRet = GetNewFileInfoForKey(hkeyNew, pnfi, &pnoi->dwFlags);
  667. RegCloseKey(hkeyNew);
  668. }
  669. if (phKey)
  670. {
  671. // if we're iterating, then we've got to open the key now...
  672. hr = StringCchPrintf(szSubKey, ARRAYSIZE(szSubKey), TEXT("%s\\%s\\ShellNew\\FileName"), pnoi->szExt, pnoi->szClass);
  673. if (SUCCEEDED(hr))
  674. {
  675. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szSubKey, 0, KEY_QUERY_VALUE, phKey))
  676. {
  677. *piIndex = 0;
  678. // if we didn't find one of the default ones above,
  679. // try it now
  680. // otherwise just return success or failure on fRet
  681. if (!fRet)
  682. {
  683. goto Iterate;
  684. }
  685. }
  686. else
  687. {
  688. *phKey = (HKEY)-1;
  689. }
  690. }
  691. else
  692. {
  693. *phKey = (HKEY)-1;
  694. }
  695. }
  696. }
  697. else if (!phKey && pnoi->szUserFile[0])
  698. {
  699. // there's no key, so just return info about szUserFile
  700. pnfi->type = NEWTYPE_FILE;
  701. pnfi->lpData = StrDup(pnoi->szUserFile);
  702. pnfi->hkeyNew = NULL;
  703. fRet = TRUE;
  704. }
  705. else if (phKey)
  706. {
  707. DWORD dwSize;
  708. DWORD dwData;
  709. DWORD dwType;
  710. // we're iterating through...
  711. Iterate:
  712. dwSize = ARRAYSIZE(pnoi->szUserFile);
  713. dwData = ARRAYSIZE(pnoi->szMenuText);
  714. if (RegEnumValue(*phKey, *piIndex, pnoi->szUserFile, &dwSize, NULL,
  715. &dwType, (LPBYTE)pnoi->szMenuText, &dwData) == ERROR_SUCCESS)
  716. {
  717. (*piIndex)++;
  718. // if there's something more than the null..
  719. if (dwData <= 1)
  720. {
  721. HRESULT hr = StringCchCopy(pnoi->szMenuText, ARRAYSIZE(pnoi->szMenuText), PathFindFileName(pnoi->szUserFile));
  722. if (SUCCEEDED(hr))
  723. {
  724. PathRemoveExtension(pnoi->szMenuText);
  725. }
  726. else
  727. {
  728. pnoi->szMenuText[0] = TEXT('\0');
  729. }
  730. }
  731. fRet = TRUE;
  732. }
  733. else
  734. {
  735. RegCloseKey(*phKey);
  736. *phKey = (HKEY)-1;
  737. fRet = FALSE;
  738. }
  739. }
  740. return fRet;
  741. }
  742. #define SHELLNEW_CONSOLIDATION_MUTEX TEXT("ShellNewConsolidationMutex")
  743. #define SHELLNEW_CONSOLIDATION_EVENT TEXT("ShellNewConsolidationEvent")
  744. CNewMenu::CNewMenu() :
  745. _cRef(1),
  746. _hMutex(CreateMutex(NULL, FALSE, SHELLNEW_CONSOLIDATION_MUTEX)),
  747. _hEvent(CreateEvent(NULL, FALSE, FALSE, SHELLNEW_CONSOLIDATION_EVENT))
  748. {
  749. DllAddRef();
  750. ASSERT(_pnoiLast == NULL);
  751. }
  752. CNewMenu::~CNewMenu()
  753. {
  754. if (_hdpaMenuInfo)
  755. {
  756. // we dont own the lifetime of _hmenu, and it gets destroyed before the destructor
  757. // is called. thus maintain the lifetime of our NEWOBJECTINFO data in a dpa.
  758. for (int i = 0; i < DPA_GetPtrCount(_hdpaMenuInfo); i++)
  759. {
  760. NEWOBJECTINFO *pNewObjInfo = (NEWOBJECTINFO *)DPA_GetPtr(_hdpaMenuInfo, i);
  761. LocalFree(pNewObjInfo);
  762. }
  763. DPA_Destroy(_hdpaMenuInfo);
  764. }
  765. ILFree(_pidlFolder);
  766. if (_pdtobj)
  767. _pdtobj->Release();
  768. if (_hMutex)
  769. {
  770. CloseHandle(_hMutex);
  771. }
  772. if (_hEvent)
  773. {
  774. CloseHandle(_hEvent);
  775. }
  776. DllRelease();
  777. }
  778. HRESULT CNewMenu_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvOut)
  779. {
  780. // aggregation checking is handled in class factory
  781. *ppvOut = NULL;
  782. HRESULT hr = E_OUTOFMEMORY;
  783. CNewMenu * pShellNew = new CNewMenu();
  784. if (pShellNew)
  785. {
  786. if (!pShellNew->_hMutex || !pShellNew->_hEvent)
  787. {
  788. hr = E_OUTOFMEMORY;
  789. }
  790. else
  791. {
  792. hr = pShellNew->QueryInterface(riid, ppvOut);
  793. }
  794. pShellNew->Release();
  795. }
  796. return hr;
  797. }
  798. HRESULT CNewMenu::QueryInterface(REFIID riid, void **ppvObj)
  799. {
  800. static const QITAB qit[] = {
  801. QITABENT(CNewMenu, IShellExtInit), // IID_IShellExtInit
  802. QITABENT(CNewMenu, IContextMenu3), // IID_IContextMenu3
  803. QITABENTMULTI(CNewMenu, IContextMenu2, IContextMenu3), // IID_IContextMenu2
  804. QITABENTMULTI(CNewMenu, IContextMenu, IContextMenu3), // IID_IContextMenu
  805. QITABENT(CNewMenu, IObjectWithSite), // IID_IObjectWithSite
  806. { 0 }
  807. };
  808. return QISearch(this, qit, riid, ppvObj);
  809. }
  810. ULONG CNewMenu::AddRef()
  811. {
  812. return InterlockedIncrement(&_cRef);
  813. }
  814. ULONG CNewMenu::Release()
  815. {
  816. ASSERT( 0 != _cRef );
  817. ULONG cRef = InterlockedDecrement(&_cRef);
  818. if ( 0 == cRef )
  819. {
  820. delete this;
  821. }
  822. return cRef;
  823. }
  824. HRESULT CNewMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  825. {
  826. // if they want the default menu only (CMF_DEFAULTONLY) OR
  827. // this is being called for a shortcut (CMF_VERBSONLY)
  828. // we don't want to be on the context menu
  829. MENUITEMINFO mfi = {0};
  830. if (uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY))
  831. return S_OK;
  832. ConsolidateMenuItems(FALSE);
  833. _idCmdFirst = idCmdFirst+2;
  834. TCHAR szNewMenu[80];
  835. LoadString(g_hinst, IDS_NEWMENU, szNewMenu, ARRAYSIZE(szNewMenu));
  836. // HACK: I assume that they are querying during a WM_INITMENUPOPUP or equivalent
  837. GetCursorPos(&_ptNewItem);
  838. _hmenu = CreatePopupMenu();
  839. mfi.cbSize = sizeof(MENUITEMINFO);
  840. mfi.fMask = MIIM_ID | MIIM_TYPE;
  841. mfi.wID = idCmdFirst+1;
  842. mfi.fType = MFT_STRING;
  843. mfi.dwTypeData = szNewMenu;
  844. InsertMenuItem(_hmenu, 0, TRUE, &mfi);
  845. ZeroMemory(&mfi, sizeof (mfi));
  846. mfi.cbSize = sizeof(MENUITEMINFO);
  847. mfi.fMask = MIIM_ID | MIIM_SUBMENU | MIIM_TYPE | MIIM_DATA;
  848. mfi.fType = MFT_STRING;
  849. mfi.wID = idCmdFirst;
  850. mfi.hSubMenu = _hmenu;
  851. mfi.dwTypeData = szNewMenu;
  852. mfi.dwItemData = 0;
  853. InsertMenuItem(hmenu, indexMenu, TRUE, &mfi);
  854. _hmenu = NULL;
  855. return ResultFromShort(_idCmdFirst - idCmdFirst + 1);
  856. }
  857. // This is almost the same as ILCreatePidlFromPath, but
  858. // uses only the filename from the full path pszPath and
  859. // the _pidlFolder to generate the pidl. This is used because
  860. // when creating an item in Desktop\My Documents, it used to create a
  861. // full pidl c:\documents and Settings\lamadio\My Documents\New folder
  862. // instead of the pidl desktop\my documents\New Folder.
  863. BOOL CNewMenu::_GeneratePidlFromName(LPTSTR pszFile, LPITEMIDLIST* ppidl)
  864. {
  865. *ppidl = NULL; // Out param
  866. IShellFolder* psf;
  867. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, _pidlFolder, &psf))))
  868. {
  869. LPITEMIDLIST pidlItem;
  870. if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, pszFile, NULL, &pidlItem, NULL)))
  871. {
  872. *ppidl = ILCombine(_pidlFolder, pidlItem);
  873. ILFree(pidlItem);
  874. }
  875. psf->Release();
  876. }
  877. return BOOLFROMPTR(*ppidl);
  878. }
  879. HRESULT CNewMenu::_GetItemName(IUnknown *punkFolder, LPWSTR pszItemName, LPWSTR pszPath, UINT cchPath)
  880. {
  881. // we need to pick up the name by asking the folder about the item,
  882. // not by pathappending to the folder's path.
  883. IShellFolder *psf;
  884. HRESULT hr = punkFolder->QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  885. if (SUCCEEDED(hr))
  886. {
  887. LPITEMIDLIST pidlFile;
  888. hr = psf->ParseDisplayName(NULL, NULL, pszItemName, NULL, &pidlFile, NULL);
  889. if (SUCCEEDED(hr))
  890. {
  891. hr = DisplayNameOf(psf, pidlFile, SHGDN_FORPARSING, pszPath, cchPath);
  892. ILFree(pidlFile);
  893. }
  894. psf->Release();
  895. }
  896. return hr;
  897. }
  898. const ICIVERBTOIDMAP c_IDMap[] =
  899. {
  900. { L"NewFolder", "NewFolder", NEWITEM_FOLDER, NEWITEM_FOLDER, },
  901. { L"link", "link", NEWITEM_LINK, NEWITEM_LINK, },
  902. };
  903. HRESULT CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  904. {
  905. HRESULT hr = E_FAIL;
  906. DWORD dwFlags;
  907. if (IS_INTRESOURCE(pici->lpVerb) && _pnoiLast)
  908. dwFlags = _pnoiLast->dwFlags;
  909. else
  910. {
  911. UINT uID;
  912. if (SUCCEEDED(SHMapICIVerbToCmdID(pici, c_IDMap, ARRAYSIZE(c_IDMap), &uID)))
  913. {
  914. switch (uID)
  915. {
  916. case NEWITEM_FOLDER:
  917. dwFlags = NEWTYPE_FOLDER;
  918. break;
  919. case NEWITEM_LINK:
  920. dwFlags = NEWTYPE_LINK;
  921. break;
  922. default:
  923. ASSERTMSG(0, "should not get what we don't put on the menu");
  924. return E_FAIL;
  925. }
  926. }
  927. }
  928. TCHAR szFileSpec[MAX_PATH+80]; // Add some slop incase we overflow
  929. TCHAR szTemp[MAX_PATH+80]; // Add some slop incase we overflow
  930. //See if the pidl is folder shortcut and if so get the target path.
  931. SHGetTargetFolderPath(_pidlFolder, szTemp, ARRAYSIZE(szTemp));
  932. BOOL fLFN = IsLFNDrive(szTemp);
  933. NEWFILEINFO nfi;
  934. DWORD dwErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  935. nfi.lpData = NULL;
  936. nfi.hkeyNew = NULL;
  937. switch (dwFlags)
  938. {
  939. case NEWTYPE_FOLDER:
  940. LoadString(g_hinst, fLFN ? IDS_FOLDERLONGPLATE : IDS_FOLDERTEMPLATE, szFileSpec, ARRAYSIZE(szFileSpec));
  941. break;
  942. case NEWTYPE_LINK:
  943. LoadString(g_hinst, IDS_NEWLINKTEMPLATE, szFileSpec, ARRAYSIZE(szFileSpec));
  944. break;
  945. default:
  946. LoadString(g_hinst, IDS_NEWFILEPREFIX, szFileSpec, ARRAYSIZE(szFileSpec));
  947. //
  948. // If we are running on a mirrored BiDi localized system,
  949. // then flip the order of concatenation so that the
  950. // string is read properly for Arabic. [samera]
  951. //
  952. if (IS_BIDI_LOCALIZED_SYSTEM())
  953. {
  954. StringCchCopy(szTemp, ARRAYSIZE(szTemp), szFileSpec); // ok to truncate, for display only
  955. StringCchPrintf(szFileSpec, ARRAYSIZE(szFileSpec), TEXT("%s %s"), _pnoiLast->szMenuText, szTemp); // ok to truncate, for display only
  956. }
  957. else
  958. {
  959. StringCchCat(szFileSpec, ARRAYSIZE(szFileSpec), _pnoiLast->szMenuText); // ok to truncate, for display only
  960. }
  961. SHStripMneumonic(szFileSpec);
  962. if (!(dwFlags & SNCF_NOEXT))
  963. {
  964. StringCchCat(szFileSpec, ARRAYSIZE(szFileSpec), _pnoiLast->szExt); // ok to truncate, for display only
  965. }
  966. break;
  967. }
  968. BOOL fCreateStorage = (dwFlags == NEWTYPE_FOLDER);
  969. //See if the pidl is folder shortcut and if so get the target pidl.
  970. LPITEMIDLIST pidlTarget;
  971. hr = SHGetTargetFolderIDList(_pidlFolder, &pidlTarget);
  972. if (SUCCEEDED(hr))
  973. {
  974. IStorage * pStorage;
  975. hr = StgBindToObject(pidlTarget, STGM_READWRITE, IID_PPV_ARG(IStorage, &pStorage));
  976. if (SUCCEEDED(hr))
  977. {
  978. IStream *pStreamCreated = NULL;
  979. IStorage *pStorageCreated = NULL;
  980. STATSTG statstg = { 0 };
  981. if (fCreateStorage)
  982. {
  983. hr = StgMakeUniqueName(pStorage, szFileSpec, IID_PPV_ARG(IStorage, &pStorageCreated));
  984. if (SUCCEEDED(hr))
  985. pStorageCreated->Stat(&statstg, STATFLAG_DEFAULT);
  986. }
  987. else
  988. {
  989. hr = StgMakeUniqueName(pStorage, szFileSpec, IID_PPV_ARG(IStream, &pStreamCreated));
  990. if (SUCCEEDED(hr))
  991. pStreamCreated->Stat(&statstg, STATFLAG_DEFAULT);
  992. }
  993. if (SUCCEEDED(hr))
  994. {
  995. switch (dwFlags)
  996. {
  997. case NEWTYPE_FOLDER:
  998. // we're already done.
  999. break;
  1000. case NEWTYPE_LINK:
  1001. if (statstg.pwcsName)
  1002. {
  1003. // Lookup Command in Registry under key HKCR/.lnk/ShellNew/Command
  1004. TCHAR szCommand[MAX_PATH];
  1005. DWORD dwLength = sizeof(szCommand);
  1006. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT,
  1007. TEXT(".lnk\\ShellNew"), TEXT("Command"), NULL, szCommand, &dwLength))
  1008. {
  1009. TCHAR szPath[MAX_PATH];
  1010. hr = _GetItemName(SAFECAST(pStorage, IUnknown*), statstg.pwcsName, szPath, ARRAYSIZE(szPath));
  1011. if (SUCCEEDED(hr))
  1012. {
  1013. hr = RunCommand(pici->hwnd, szPath, szCommand);
  1014. }
  1015. }
  1016. }
  1017. break;
  1018. default:
  1019. if (GetNewFileInfoForExtension(_pnoiLast, &nfi, NULL, NULL))
  1020. {
  1021. switch (nfi.type)
  1022. {
  1023. case NEWTYPE_FILE:
  1024. hr = CopyTemplate(pStreamCreated, &nfi);
  1025. break;
  1026. case NEWTYPE_NULL:
  1027. // already created a zero-length file.
  1028. break;
  1029. case NEWTYPE_DATA:
  1030. ULONG ulWritten;
  1031. hr = pStreamCreated->Write(nfi.lpData, nfi.cbData, &ulWritten);
  1032. if (SUCCEEDED(hr))
  1033. {
  1034. hr = pStreamCreated->Commit(STGC_DEFAULT);
  1035. }
  1036. break;
  1037. case NEWTYPE_COMMAND:
  1038. if (statstg.pwcsName)
  1039. {
  1040. TCHAR szPath[MAX_PATH];
  1041. hr = _GetItemName(SAFECAST(pStorage, IUnknown*), statstg.pwcsName, szPath, ARRAYSIZE(szPath));
  1042. if (SUCCEEDED(hr))
  1043. {
  1044. // oops, we already created the stream, but we actually
  1045. // just wanted the filename for the RunCommand, so we
  1046. // have to delete it first.
  1047. ATOMICRELEASE(pStreamCreated);
  1048. hr = pStorage->DestroyElement(statstg.pwcsName);
  1049. // flush out any notifications from the destroy (the
  1050. // destroy causes notifications).
  1051. SHChangeNotifyHandleEvents();
  1052. if (SUCCEEDED(hr))
  1053. {
  1054. hr = RunCommand(pici->hwnd, szPath, (LPTSTR)nfi.lpData);
  1055. if (hr == S_FALSE)
  1056. hr = S_OK;
  1057. }
  1058. }
  1059. }
  1060. break;
  1061. default:
  1062. hr = E_FAIL;
  1063. break;
  1064. }
  1065. }
  1066. else
  1067. {
  1068. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  1069. }
  1070. break;
  1071. }
  1072. // these have to be released before _GeneratePidlFromName, since that opens
  1073. // the storage in exclusive mode for other reasons. but we can't release
  1074. // them earlier because CopyTemplate might need them.
  1075. if (pStorageCreated)
  1076. pStorageCreated->Release();
  1077. if (pStreamCreated)
  1078. pStreamCreated->Release();
  1079. if (SUCCEEDED(hr))
  1080. hr = pStorage->Commit(STGC_DEFAULT);
  1081. pStorage->Release();
  1082. LPITEMIDLIST pidlCreatedItem;
  1083. if (SUCCEEDED(hr) &&
  1084. _GeneratePidlFromName(statstg.pwcsName, &pidlCreatedItem))
  1085. {
  1086. SHChangeNotifyHandleEvents();
  1087. IShellView2 *psv2;
  1088. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellView2, &psv2))))
  1089. {
  1090. DWORD dwFlagsSelFlags = SVSI_SELECT | SVSI_POSITIONITEM;
  1091. if (!(dwFlags & NEWTYPE_LINK))
  1092. dwFlagsSelFlags |= SVSI_EDIT;
  1093. psv2->SelectAndPositionItem(ILFindLastID(pidlCreatedItem), dwFlagsSelFlags, NULL);
  1094. psv2->Release();
  1095. }
  1096. ILFree(pidlCreatedItem);
  1097. }
  1098. CoTaskMemFree(statstg.pwcsName);
  1099. }
  1100. else
  1101. {
  1102. pStorage->Release();
  1103. }
  1104. }
  1105. ILFree(pidlTarget);
  1106. }
  1107. if (nfi.lpData)
  1108. LocalFree((HLOCAL)nfi.lpData);
  1109. if (nfi.hkeyNew)
  1110. RegCloseKey(nfi.hkeyNew);
  1111. if (FAILED_AND_NOT_CANCELED(hr) && !(pici->fMask & CMIC_MASK_FLAG_NO_UI))
  1112. {
  1113. TCHAR szTitle[MAX_PATH];
  1114. LoadString(g_hinst, (fCreateStorage ? IDS_DIRCREATEFAILED_TITLE : IDS_FILECREATEFAILED_TITLE), szTitle, ARRAYSIZE(szTitle));
  1115. SHSysErrorMessageBox(pici->hwnd, szTitle, fCreateStorage ? IDS_CANNOTCREATEFOLDER : IDS_CANNOTCREATEFILE,
  1116. HRESULT_CODE(hr), szFileSpec, MB_OK | MB_ICONEXCLAMATION);
  1117. }
  1118. SetErrorMode(dwErrorMode);
  1119. return hr;
  1120. }
  1121. HRESULT CNewMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  1122. {
  1123. switch (uType)
  1124. {
  1125. case GCS_HELPTEXT:
  1126. if (idCmd < NEWITEM_MAX)
  1127. {
  1128. LoadString(g_hinst, (UINT)(IDS_NEWHELP_FIRST + idCmd), (LPTSTR)pszName, cchMax);
  1129. return S_OK;
  1130. }
  1131. break;
  1132. case GCS_HELPTEXTA:
  1133. if (idCmd < NEWITEM_MAX)
  1134. {
  1135. LoadStringA(g_hinst, (UINT)(IDS_NEWHELP_FIRST + idCmd), pszName, cchMax);
  1136. return S_OK;
  1137. }
  1138. break;
  1139. case GCS_VERBW:
  1140. case GCS_VERBA:
  1141. return SHMapCmdIDToVerb(idCmd, c_IDMap, ARRAYSIZE(c_IDMap), pszName, cchMax, (GCS_VERBW == uType));
  1142. }
  1143. return E_NOTIMPL;
  1144. }
  1145. //Defined in fsmenu.obj
  1146. BOOL _MenuCharMatch(LPCTSTR lpsz, TCHAR ch, BOOL fIgnoreAmpersand);
  1147. HRESULT CNewMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1148. {
  1149. return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
  1150. }
  1151. HRESULT CNewMenu::_MatchMenuItem(TCHAR ch, LRESULT* plRes)
  1152. {
  1153. // If plRes is NULL we're being called on HandleMenuMsg() which
  1154. // doesn't support returning an LRESULT, which is needed for WM_MENUCHAR...
  1155. if (plRes == NULL)
  1156. return S_FALSE;
  1157. int iLastSelectedItem = -1;
  1158. int iNextMatch = -1;
  1159. BOOL fMoreThanOneMatch = FALSE;
  1160. int c = GetMenuItemCount(_hmenu);
  1161. // Pass 1: Locate the Selected Item
  1162. for (int i = 0; i < c; i++)
  1163. {
  1164. MENUITEMINFO mii = {0};
  1165. mii.cbSize = sizeof(mii);
  1166. mii.fMask = MIIM_STATE;
  1167. if (GetMenuItemInfo(_hmenu, i, MF_BYPOSITION, &mii))
  1168. {
  1169. if (mii.fState & MFS_HILITE)
  1170. {
  1171. iLastSelectedItem = i;
  1172. break;
  1173. }
  1174. }
  1175. }
  1176. // Pass 2: Starting from the selected item, locate the first item with the matching name.
  1177. for (i = iLastSelectedItem + 1; i < c; i++)
  1178. {
  1179. MENUITEMINFO mii = {0};
  1180. mii.cbSize = sizeof(mii);
  1181. mii.fMask = MIIM_DATA | MIIM_STATE;
  1182. if (GetMenuItemInfo(_hmenu, i, MF_BYPOSITION, &mii))
  1183. {
  1184. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)mii.dwItemData;
  1185. if (pnoi && _MenuCharMatch(pnoi->szMenuText, ch, FALSE))
  1186. {
  1187. _pnoiLast = pnoi;
  1188. if (iNextMatch != -1)
  1189. {
  1190. fMoreThanOneMatch = TRUE;
  1191. break; // We found all the info we need
  1192. }
  1193. else
  1194. {
  1195. iNextMatch = i;
  1196. }
  1197. }
  1198. }
  1199. }
  1200. // Pass 3: If we did not find a match, or if there was only one match
  1201. // Search from the first item, to the Selected Item
  1202. if (iNextMatch == -1 || fMoreThanOneMatch == FALSE)
  1203. {
  1204. for (i = 0; i <= iLastSelectedItem; i++)
  1205. {
  1206. MENUITEMINFO mii = {0};
  1207. mii.cbSize = sizeof(mii);
  1208. mii.fMask = MIIM_DATA | MIIM_STATE;
  1209. if (GetMenuItemInfo(_hmenu, i, MF_BYPOSITION, &mii))
  1210. {
  1211. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)mii.dwItemData;
  1212. if (pnoi && _MenuCharMatch(pnoi->szMenuText, ch, FALSE))
  1213. {
  1214. _pnoiLast = pnoi;
  1215. if (iNextMatch != -1)
  1216. {
  1217. fMoreThanOneMatch = TRUE;
  1218. break;
  1219. }
  1220. else
  1221. {
  1222. iNextMatch = i;
  1223. }
  1224. }
  1225. }
  1226. }
  1227. }
  1228. if (iNextMatch != -1)
  1229. {
  1230. *plRes = MAKELONG(iNextMatch, fMoreThanOneMatch? MNC_SELECT : MNC_EXECUTE);
  1231. }
  1232. else
  1233. {
  1234. *plRes = MAKELONG(0, MNC_IGNORE);
  1235. }
  1236. return S_OK;
  1237. }
  1238. HRESULT CNewMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *plResult)
  1239. {
  1240. HRESULT hr = S_OK;
  1241. LRESULT lRes = 0;
  1242. switch (uMsg)
  1243. {
  1244. case WM_INITMENUPOPUP:
  1245. if (_hmenu == NULL)
  1246. {
  1247. _hmenu = (HMENU)wParam;
  1248. }
  1249. InitMenuPopup(_hmenu);
  1250. break;
  1251. case WM_DRAWITEM:
  1252. DrawItem((DRAWITEMSTRUCT *)lParam);
  1253. break;
  1254. case WM_MEASUREITEM:
  1255. lRes = MeasureItem((MEASUREITEMSTRUCT *)lParam);
  1256. break;
  1257. case WM_MENUCHAR:
  1258. hr = _MatchMenuItem((TCHAR)LOWORD(wParam), &lRes);
  1259. break;
  1260. default:
  1261. hr = E_NOTIMPL;
  1262. break;
  1263. }
  1264. if (plResult)
  1265. *plResult = lRes;
  1266. return hr;
  1267. }
  1268. HRESULT CNewMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  1269. {
  1270. ASSERT(_pidlFolder == NULL);
  1271. _pidlFolder = ILClone(pidlFolder);
  1272. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  1273. return S_OK;
  1274. }
  1275. BOOL CNewMenu::DrawItem(DRAWITEMSTRUCT *lpdi)
  1276. {
  1277. BOOL fFlatMenu = FALSE;
  1278. BOOL fFrameRect = FALSE;
  1279. SystemParametersInfo(SPI_GETFLATMENU, 0, (PVOID)&fFlatMenu, 0);
  1280. if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
  1281. {
  1282. int x, y;
  1283. SIZE sz;
  1284. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)lpdi->itemData;
  1285. // Draw the image (if there is one).
  1286. GetTextExtentPoint(lpdi->hDC, pnoi->szMenuText, lstrlen(pnoi->szMenuText), &sz);
  1287. if (lpdi->itemState & ODS_SELECTED)
  1288. {
  1289. // REVIEW HACK - keep track of the last selected item.
  1290. _pnoiLast = pnoi;
  1291. if (fFlatMenu)
  1292. {
  1293. fFrameRect = TRUE;
  1294. SetBkColor(lpdi->hDC, GetSysColor(COLOR_MENUHILIGHT));
  1295. SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
  1296. FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENUHILIGHT));
  1297. }
  1298. else
  1299. {
  1300. SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
  1301. SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  1302. FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
  1303. }
  1304. }
  1305. else
  1306. {
  1307. SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
  1308. FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENU));
  1309. }
  1310. RECT rc = lpdi->rcItem;
  1311. rc.left += +2*CXIMAGEGAP+g_cxSmIcon;
  1312. DrawText(lpdi->hDC,pnoi->szMenuText,lstrlen(pnoi->szMenuText),
  1313. &rc,DT_SINGLELINE|DT_VCENTER);
  1314. if (pnoi->iImage != -1)
  1315. {
  1316. x = lpdi->rcItem.left+CXIMAGEGAP;
  1317. y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
  1318. HIMAGELIST himlSmall;
  1319. Shell_GetImageLists(NULL, &himlSmall);
  1320. ImageList_Draw(himlSmall, pnoi->iImage, lpdi->hDC, x, y, ILD_TRANSPARENT);
  1321. }
  1322. else
  1323. {
  1324. x = lpdi->rcItem.left+CXIMAGEGAP;
  1325. y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
  1326. }
  1327. if (fFrameRect)
  1328. {
  1329. HBRUSH hbrFill = (HBRUSH)GetSysColorBrush(COLOR_HIGHLIGHT);
  1330. HBRUSH hbrSave = (HBRUSH)SelectObject(lpdi->hDC, hbrFill);
  1331. int x = lpdi->rcItem.left;
  1332. int y = lpdi->rcItem.top;
  1333. int cx = lpdi->rcItem.right - x - 1;
  1334. int cy = lpdi->rcItem.bottom - y - 1;
  1335. PatBlt(lpdi->hDC, x, y, 1, cy, PATCOPY);
  1336. PatBlt(lpdi->hDC, x + 1, y, cx, 1, PATCOPY);
  1337. PatBlt(lpdi->hDC, x, y + cy, cx, 1, PATCOPY);
  1338. PatBlt(lpdi->hDC, x + cx, y + 1, 1, cy, PATCOPY);
  1339. SelectObject(lpdi->hDC, hbrSave);
  1340. }
  1341. return TRUE;
  1342. }
  1343. return FALSE;
  1344. }
  1345. LRESULT CNewMenu::MeasureItem(MEASUREITEMSTRUCT *pmi)
  1346. {
  1347. LRESULT lres = FALSE;
  1348. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)pmi->itemData;
  1349. if (pnoi)
  1350. {
  1351. // Get the rough height of an item so we can work out when to break the
  1352. // menu. User should really do this for us but that would be useful.
  1353. HDC hdc = GetDC(NULL);
  1354. if (hdc)
  1355. {
  1356. // REVIEW cache out the menu font?
  1357. NONCLIENTMETRICS ncm;
  1358. ncm.cbSize = sizeof(ncm);
  1359. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
  1360. {
  1361. HFONT hfont = CreateFontIndirect(&ncm.lfMenuFont);
  1362. if (hfont)
  1363. {
  1364. SIZE sz;
  1365. HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
  1366. GetTextExtentPoint(hdc, pnoi->szMenuText, lstrlen(pnoi->szMenuText), &sz);
  1367. pmi->itemHeight = max (g_cySmIcon+CXIMAGEGAP/2, ncm.iMenuHeight);
  1368. pmi->itemWidth = g_cxSmIcon + 2*CXIMAGEGAP + sz.cx;
  1369. //pmi->itemWidth = 2*CXIMAGEGAP + sz.cx;
  1370. SelectObject(hdc, hfontOld);
  1371. DeleteObject(hfont);
  1372. lres = TRUE;
  1373. }
  1374. }
  1375. ReleaseDC(NULL, hdc);
  1376. }
  1377. }
  1378. return lres;
  1379. }
  1380. BOOL GetClassDisplayName(LPTSTR pszClass,LPTSTR pszDisplayName,DWORD cchDisplayName)
  1381. {
  1382. DWORD cch;
  1383. return SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, pszClass, TEXT("open"), NULL, &cch)) &&
  1384. SUCCEEDED(AssocQueryString(0, ASSOCSTR_FRIENDLYDOCNAME, pszClass, NULL, pszDisplayName, &cchDisplayName));
  1385. }
  1386. // New Menu item consolidation worker task
  1387. class CNewMenuConsolidator : public CRunnableTask
  1388. {
  1389. public:
  1390. virtual STDMETHODIMP RunInitRT(void);
  1391. static const GUID _taskid;
  1392. static HRESULT CreateInstance(REFIID riid, void **ppv);
  1393. private:
  1394. CNewMenuConsolidator();
  1395. ~CNewMenuConsolidator();
  1396. HANDLE _hMutex, _hEvent;
  1397. };
  1398. CNewMenuConsolidator::CNewMenuConsolidator() :
  1399. CRunnableTask(RTF_DEFAULT),
  1400. _hMutex(CreateMutex(NULL, FALSE, SHELLNEW_CONSOLIDATION_MUTEX)),
  1401. _hEvent(CreateEvent(NULL, FALSE, FALSE, SHELLNEW_CONSOLIDATION_EVENT))
  1402. {
  1403. DllAddRef();
  1404. }
  1405. CNewMenuConsolidator::~CNewMenuConsolidator()
  1406. {
  1407. if (_hMutex)
  1408. {
  1409. CloseHandle(_hMutex);
  1410. }
  1411. if (_hEvent)
  1412. {
  1413. CloseHandle(_hEvent);
  1414. }
  1415. DllRelease();
  1416. }
  1417. //
  1418. // Instance generator.
  1419. //
  1420. HRESULT CNewMenuConsolidator::CreateInstance(REFIID riid, void **ppv)
  1421. {
  1422. HRESULT hr = E_OUTOFMEMORY;
  1423. CNewMenuConsolidator *pnmc = new CNewMenuConsolidator();
  1424. if (pnmc)
  1425. {
  1426. if (!pnmc->_hMutex || !pnmc->_hEvent)
  1427. {
  1428. hr = E_FAIL;
  1429. }
  1430. else
  1431. {
  1432. hr = pnmc->QueryInterface(riid, ppv);
  1433. }
  1434. pnmc->Release();
  1435. }
  1436. return hr;
  1437. }
  1438. const GUID CNewMenuConsolidator::_taskid =
  1439. { 0xf87a1f28, 0xc7f, 0x11d2, { 0xbe, 0x1d, 0x0, 0xa0, 0xc9, 0xa8, 0x3d, 0xa1 } };
  1440. #define REGSTR_SESSION_SHELLNEW STRREG_DISCARDABLE STRREG_POSTSETUP TEXT("\\ShellNew")
  1441. #define REGVAL_SESSION_SHELLNEW_TIMESTAMP TEXT("~reserved~")
  1442. #define REGVAL_SESSION_SHELLNEW_LANG TEXT("Language")
  1443. #define SHELLNEW_CACHE_CURRENTVERSION MAKELONG(1, 1)
  1444. // Constructs a current New submenu cache stamp.
  1445. void CNewMenu_MakeCacheStamp(SHELLNEW_CACHE_STAMP* pStamp)
  1446. {
  1447. pStamp->cbStruct = sizeof(*pStamp);
  1448. pStamp->ver = SHELLNEW_CACHE_CURRENTVERSION;
  1449. GetLocalTime(&pStamp->lastupdate);
  1450. }
  1451. // Determines whether the New submenu cache needs to be rebuilt.
  1452. BOOL CNewMenu_ShouldUpdateCache(SHELLNEW_CACHE_STAMP* pStamp)
  1453. {
  1454. // Correct version?
  1455. return !(sizeof(*pStamp) == pStamp->cbStruct &&
  1456. SHELLNEW_CACHE_CURRENTVERSION == pStamp->ver);
  1457. }
  1458. // Gathers up shellnew entries from HKCR into a distinct registry location
  1459. // for faster enumeration of the New submenu items.
  1460. //
  1461. // We'll do a first time cache initialization only if we have to before showing
  1462. // the menu, but will always rebuild the cache following display of the menu.
  1463. HRESULT CNewMenu::ConsolidateMenuItems(BOOL bForce)
  1464. {
  1465. HKEY hkeyShellNew = NULL;
  1466. BOOL bUpdate = TRUE; // unless we discover otherwise
  1467. HRESULT hr = S_OK;
  1468. // make sure that a worker thread isnt currently slamming the registry info we're inspecting.
  1469. // if we timeout, then do nothing, since the worker thread is already working on it.
  1470. if (WAIT_OBJECT_0 == WaitForSingleObject(_hMutex, 0))
  1471. {
  1472. // If we're not being told to unconditionally update the cache and
  1473. // we validate that we've already established one, then we get out of doing any
  1474. // work.
  1475. if (!bForce &&
  1476. ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW,
  1477. 0, KEY_QUERY_VALUE, &hkeyShellNew))
  1478. {
  1479. SHELLNEW_CACHE_STAMP stamp;
  1480. ULONG cbVal = sizeof(stamp);
  1481. if (ERROR_SUCCESS == SHQueryValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_TIMESTAMP, NULL,
  1482. NULL, (LPBYTE)&stamp, &cbVal) &&
  1483. sizeof(stamp) == cbVal)
  1484. {
  1485. bUpdate = CNewMenu_ShouldUpdateCache(&stamp);
  1486. }
  1487. LCID lcid;
  1488. ULONG cblcid = sizeof(lcid);
  1489. if (!bUpdate &&
  1490. ERROR_SUCCESS == SHQueryValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_LANG, NULL,
  1491. NULL, (LPBYTE)&lcid, &cblcid) &&
  1492. sizeof(lcid) == cblcid)
  1493. {
  1494. bUpdate = (GetUserDefaultUILanguage() != lcid); // if the languages are different, then update
  1495. }
  1496. RegCloseKey(hkeyShellNew);
  1497. }
  1498. // end synchronize
  1499. ReleaseMutex(_hMutex);
  1500. if (bUpdate)
  1501. {
  1502. IShellTaskScheduler* pScheduler;
  1503. hr = CoCreateInstance(CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC,
  1504. IID_PPV_ARG(IShellTaskScheduler, &pScheduler));
  1505. if (SUCCEEDED(hr))
  1506. {
  1507. IRunnableTask *pTask;
  1508. hr = CNewMenuConsolidator::CreateInstance(IID_PPV_ARG(IRunnableTask, &pTask));
  1509. if (SUCCEEDED(hr))
  1510. {
  1511. // the background task will set _hEvent for us when it's done.
  1512. hr = pScheduler->AddTask(pTask, CNewMenuConsolidator::_taskid, NULL, ITSAT_DEFAULT_PRIORITY);
  1513. pTask->Release();
  1514. }
  1515. pScheduler->Release();
  1516. }
  1517. }
  1518. if (!bUpdate || FAILED(hr))
  1519. {
  1520. // if the scheduler wont activate the event, we do it ourselves.
  1521. SetEvent(_hEvent);
  1522. }
  1523. }
  1524. return hr;
  1525. }
  1526. // Consolidation worker.
  1527. STDMETHODIMP CNewMenuConsolidator::RunInitRT()
  1528. {
  1529. ULONG dwErr = ERROR_SUCCESS;
  1530. // the possible owners of the mutex are
  1531. // - nobody, we'll own it
  1532. // - other worker threads like this one
  1533. // - the guy who checks to see if the cached info is in the registry.
  1534. // if there's another worker thread which owns this mutex, then bail, since that one
  1535. // will do all the work we're going to do.
  1536. // if the guy who checks the cached info has it, then bail, since it'll spawn
  1537. // another one of these soon enough.
  1538. // so use a 0 timeout.
  1539. if (WAIT_OBJECT_0 == WaitForSingleObject(_hMutex, 0))
  1540. {
  1541. HKEY hkeyShellNew = NULL;
  1542. TCHAR szExt[MAX_PATH];
  1543. ULONG dwDisposition;
  1544. // Delete the existing cache; we'll build it from scratch each time.
  1545. while (ERROR_SUCCESS == (dwErr = RegCreateKeyEx(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW,
  1546. 0, NULL, 0, KEY_SET_VALUE, NULL,
  1547. &hkeyShellNew, &dwDisposition)) &&
  1548. REG_CREATED_NEW_KEY != dwDisposition)
  1549. {
  1550. // Key already existed, so delete it, and loop to reopen.
  1551. RegCloseKey(hkeyShellNew);
  1552. SHDeleteKey(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW);
  1553. hkeyShellNew = NULL;
  1554. }
  1555. if (ERROR_SUCCESS == dwErr)
  1556. {
  1557. // Enumerate each subkey of HKCR, looking for New menu items.
  1558. for (int i = 0; RegEnumKey(HKEY_CLASSES_ROOT, i, szExt, ARRAYSIZE(szExt)) == ERROR_SUCCESS; i++)
  1559. {
  1560. TCHAR szClass[CCH_KEYMAX];
  1561. TCHAR szDisplayName[CCH_KEYMAX];
  1562. DWORD cbVal = sizeof(szClass);
  1563. // find .ext that have proper class descriptions with them.
  1564. if ((szExt[0] == TEXT('.')) &&
  1565. SHRegGetValue(HKEY_CLASSES_ROOT, szExt, NULL, SRRF_RT_REG_SZ, NULL, szClass, &cbVal) == ERROR_SUCCESS
  1566. && GetClassDisplayName(szClass, szDisplayName, ARRAYSIZE(szDisplayName)))
  1567. {
  1568. NEWOBJECTINFO noi = {0};
  1569. HKEY hkeyIterate = NULL;
  1570. int iIndex = 0;
  1571. HRESULT hr;
  1572. BOOL fOk = TRUE;
  1573. hr = StringCchCopy(noi.szExt, ARRAYSIZE(noi.szExt), szExt);
  1574. if (FAILED(hr))
  1575. {
  1576. fOk = FALSE;
  1577. }
  1578. hr = StringCchCopy(noi.szClass, ARRAYSIZE(noi.szClass), szClass);
  1579. if (FAILED(hr))
  1580. {
  1581. fOk = FALSE;
  1582. }
  1583. hr = StringCchCopy(noi.szMenuText, ARRAYSIZE(noi.szMenuText), szDisplayName);
  1584. if (FAILED(hr))
  1585. {
  1586. fOk = FALSE;
  1587. }
  1588. noi.iImage = -1;
  1589. if (fOk)
  1590. {
  1591. // Retrieve all additional information for the key.
  1592. while (GetNewFileInfoForExtension(&noi, NULL, &hkeyIterate, &iIndex))
  1593. {
  1594. // Stick it in the cache.
  1595. RegSetValueEx(hkeyShellNew, noi.szMenuText, NULL, REG_BINARY,
  1596. (LPBYTE)&noi, sizeof(noi));
  1597. }
  1598. }
  1599. }
  1600. }
  1601. // Stamp the cache.
  1602. SHELLNEW_CACHE_STAMP stamp;
  1603. CNewMenu_MakeCacheStamp(&stamp);
  1604. RegSetValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_TIMESTAMP,
  1605. NULL, REG_BINARY, (LPBYTE)&stamp, sizeof(stamp));
  1606. LCID lcid = GetUserDefaultUILanguage();
  1607. RegSetValueEx(hkeyShellNew, REGVAL_SESSION_SHELLNEW_LANG,
  1608. NULL, REG_DWORD, (LPBYTE)&lcid, sizeof(lcid));
  1609. }
  1610. if (NULL != hkeyShellNew)
  1611. RegCloseKey(hkeyShellNew);
  1612. // signal the event so InitMenuPopup can proceed.
  1613. SetEvent(_hEvent);
  1614. ReleaseMutex(_hMutex);
  1615. }
  1616. return HRESULT_FROM_WIN32(dwErr);
  1617. }
  1618. BOOL CNewMenu::_InsertNewMenuItem(HMENU hmenu, UINT idCmd, NEWOBJECTINFO *pnoiClone)
  1619. {
  1620. if (pnoiClone->szMenuText[0])
  1621. {
  1622. NEWOBJECTINFO *pnoi = (NEWOBJECTINFO *)LocalAlloc(LPTR, sizeof(NEWOBJECTINFO));
  1623. if (pnoi)
  1624. {
  1625. *pnoi = *pnoiClone;
  1626. pnoi->msaa.dwMSAASignature = MSAA_MENU_SIG;
  1627. if (StrChr(pnoi->szMenuText, TEXT('&')) == NULL)
  1628. {
  1629. pnoi->chPrefix = TEXT('&');
  1630. pnoi->msaa.pszWText = &pnoi->chPrefix;
  1631. }
  1632. else
  1633. {
  1634. pnoi->msaa.pszWText = pnoi->szMenuText;
  1635. }
  1636. pnoi->msaa.cchWText = lstrlen(pnoi->msaa.pszWText);
  1637. MENUITEMINFO mii = {0};
  1638. mii.cbSize = sizeof(mii);
  1639. mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_ID;
  1640. mii.fType = MFT_OWNERDRAW;
  1641. mii.fState = MFS_ENABLED;
  1642. mii.wID = idCmd;
  1643. mii.dwItemData = (DWORD_PTR)pnoi;
  1644. mii.dwTypeData = (LPTSTR)pnoi;
  1645. if (-1 != DPA_AppendPtr(_hdpaMenuInfo, pnoi))
  1646. {
  1647. InsertMenuItem(hmenu, -1, TRUE, &mii);
  1648. }
  1649. else
  1650. {
  1651. LocalFree(pnoi);
  1652. return FALSE;
  1653. }
  1654. }
  1655. }
  1656. return TRUE;
  1657. }
  1658. // WM_INITMENUPOPUP handler
  1659. BOOL CNewMenu::InitMenuPopup(HMENU hmenu)
  1660. {
  1661. UINT iStart = 3;
  1662. NEWOBJECTINFO noi = {0};
  1663. if (GetItemData(hmenu, iStart)) //Position 0 is New Folder, 1 shortcut, 2 sep
  1664. return FALSE; //already initialized. No need to do anything
  1665. _hdpaMenuInfo = DPA_Create(4);
  1666. if (!_hdpaMenuInfo)
  1667. return FALSE;
  1668. //Remove the place holder.
  1669. DeleteMenu(hmenu,0,MF_BYPOSITION);
  1670. //Insert New Folder menu item
  1671. LoadString(g_hinst, IDS_NEWFOLDER, noi.szMenuText, ARRAYSIZE(noi.szMenuText));
  1672. noi.dwFlags = NEWTYPE_FOLDER;
  1673. noi.iImage = Shell_GetCachedImageIndex(TEXT("shell32.dll"), II_FOLDER, 0); //Shange to indicate Folder
  1674. _InsertNewMenuItem(hmenu, _idCmdFirst-NEWITEM_MAX+NEWITEM_FOLDER, &noi);
  1675. TCHAR szTemp[MAX_PATH+80]; // Add some slop incase we overflow
  1676. //See if the pidl is folder shortcut and if so get the target path.
  1677. SHGetTargetFolderPath(_pidlFolder, szTemp, ARRAYSIZE(szTemp));
  1678. if (IsLFNDrive(szTemp)) //for short filename servers we don't support anything but new folder
  1679. {
  1680. //Insert New Shortcut menu item
  1681. LoadString(g_hinst, IDS_NEWLINK, noi.szMenuText, ARRAYSIZE(noi.szMenuText));
  1682. noi.iImage = Shell_GetCachedImageIndex(TEXT("shell32.dll"), II_LINK, 0); //Shange to indicate Link
  1683. noi.dwFlags = NEWTYPE_LINK;
  1684. _InsertNewMenuItem(hmenu, _idCmdFirst-NEWITEM_MAX+NEWITEM_LINK, &noi);
  1685. //Insert menu item separator
  1686. AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
  1687. // This may take a while, so put up the hourglass
  1688. DECLAREWAITCURSOR;
  1689. SetWaitCursor();
  1690. // Retrieve extension menu items from cache:
  1691. // begin synchronize.
  1692. //
  1693. if (WAIT_OBJECT_0 == WaitForSingleObject(_hEvent, INFINITE))
  1694. {
  1695. HKEY hkeyShellNew;
  1696. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_SESSION_SHELLNEW,
  1697. 0, KEY_QUERY_VALUE, &hkeyShellNew))
  1698. {
  1699. TCHAR szVal[CCH_KEYMAX];
  1700. ULONG cbVal = ARRAYSIZE(szVal);
  1701. ULONG cbData = sizeof(noi);
  1702. ULONG dwType = REG_BINARY;
  1703. for (int i = 0;
  1704. ERROR_SUCCESS == RegEnumValue(hkeyShellNew, i, szVal, &cbVal, 0,
  1705. &dwType, (LPBYTE)&noi, &cbData);
  1706. i++)
  1707. {
  1708. if (lstrcmp(szVal, REGVAL_SESSION_SHELLNEW_TIMESTAMP) != 0 &&
  1709. sizeof(noi) == cbData &&
  1710. REG_BINARY == dwType)
  1711. {
  1712. SHFILEINFO sfi;
  1713. _himlSystemImageList = (HIMAGELIST)SHGetFileInfo(noi.szExt, FILE_ATTRIBUTE_NORMAL,
  1714. &sfi, sizeof(SHFILEINFO),
  1715. SHGFI_USEFILEATTRIBUTES |
  1716. SHGFI_SYSICONINDEX |
  1717. SHGFI_SMALLICON);
  1718. if (_himlSystemImageList)
  1719. {
  1720. //pnoi->himlSmallIcons = sfi.hIcon;
  1721. noi.iImage = sfi.iIcon;
  1722. }
  1723. else
  1724. {
  1725. //pnoi->himlSmallIcons = INVALID_HANDLE_VALUE;
  1726. noi.iImage = -1;
  1727. }
  1728. _InsertNewMenuItem(hmenu, _idCmdFirst, &noi);
  1729. }
  1730. cbVal = ARRAYSIZE(szVal);
  1731. cbData = sizeof(noi);
  1732. dwType = REG_BINARY;
  1733. }
  1734. RegCloseKey(hkeyShellNew);
  1735. }
  1736. // consolidate menu items following display.
  1737. ConsolidateMenuItems(TRUE);
  1738. }
  1739. ResetWaitCursor();
  1740. }
  1741. return TRUE;
  1742. }
  1743. NEWOBJECTINFO *CNewMenu::GetItemData(HMENU hmenu, UINT iItem)
  1744. {
  1745. MENUITEMINFO mii;
  1746. mii.cbSize = sizeof(MENUITEMINFO);
  1747. mii.fMask = MIIM_DATA | MIIM_STATE;
  1748. mii.cch = 0; // just in case...
  1749. if (GetMenuItemInfo(hmenu, iItem, TRUE, &mii))
  1750. return (NEWOBJECTINFO *)mii.dwItemData;
  1751. return NULL;
  1752. }
  1753. LPTSTR ProcessArgs(LPTSTR szArgs,...)
  1754. {
  1755. LPTSTR szRet;
  1756. va_list ArgList;
  1757. va_start(ArgList,szArgs);
  1758. if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
  1759. szArgs, 0, 0, (LPTSTR)&szRet, 0, &ArgList))
  1760. {
  1761. return NULL;
  1762. }
  1763. va_end(ArgList);
  1764. return szRet;
  1765. }
  1766. HRESULT CNewMenu::RunCommand(HWND hwnd, LPCTSTR pszPath, LPCTSTR pszRun)
  1767. {
  1768. SHELLEXECUTEINFO ei = { 0 };
  1769. TCHAR szCommand[MAX_PATH];
  1770. TCHAR szRun[MAX_PATH];
  1771. HRESULT hr;
  1772. SHExpandEnvironmentStrings(pszRun, szCommand, ARRAYSIZE(szCommand));
  1773. hr = StringCchCopy(szRun, ARRAYSIZE(szRun), szCommand);
  1774. if (SUCCEEDED(hr))
  1775. {
  1776. PathRemoveArgs(szCommand);
  1777. //
  1778. // Mondo hackitude-o-rama.
  1779. //
  1780. // Win95, IE3, SDK: %1 - filename
  1781. //
  1782. // IE4: %1 - hwnd, %2 = filename
  1783. //
  1784. // So IE4 broken Win95 compat and broke compat with the SDK.
  1785. // For IE5 we restore compat with Win95 and the SDK, while
  1786. // still generating an IE4-style command if we detect that the
  1787. // registry key owner tested with IE4 rather than following the
  1788. // instructions in the SDK.
  1789. //
  1790. // The algorithm is like this:
  1791. //
  1792. // If we see a "%2", then use %1 - hwnd, %2 - filename
  1793. // Otherwise, use %1 - filename, %2 - hwnd
  1794. //
  1795. LPTSTR pszArgs = PathGetArgs(szRun);
  1796. LPTSTR ptszPercent2 = StrStr(pszArgs, TEXT("%2"));
  1797. if (ptszPercent2 && ptszPercent2[2] != TEXT('!'))
  1798. {
  1799. // App wants %1 = hwnd and %2 = filename
  1800. pszArgs = ProcessArgs(pszArgs, (DWORD_PTR)hwnd, pszPath);
  1801. }
  1802. else
  1803. {
  1804. // App wants %2 = hwnd and %1 = filename
  1805. pszArgs = ProcessArgs(pszArgs, pszPath, (DWORD_PTR)hwnd);
  1806. }
  1807. if (pszArgs)
  1808. {
  1809. HMONITOR hMon = MonitorFromPoint(_ptNewItem, MONITOR_DEFAULTTONEAREST);
  1810. if (hMon)
  1811. {
  1812. ei.fMask |= SEE_MASK_HMONITOR;
  1813. ei.hMonitor = (HANDLE)hMon;
  1814. }
  1815. ei.hwnd = hwnd;
  1816. ei.lpFile = szCommand;
  1817. ei.lpParameters = pszArgs;
  1818. ei.nShow = SW_SHOWNORMAL;
  1819. ei.cbSize = sizeof(ei);
  1820. if (ShellExecuteEx(&ei))
  1821. hr = S_FALSE; // Return S_FALSE because ShellExecuteEx is not atomic
  1822. else
  1823. hr = E_FAIL;
  1824. LocalFree(pszArgs);
  1825. }
  1826. else
  1827. hr = E_OUTOFMEMORY;
  1828. }
  1829. return hr;
  1830. }
  1831. HRESULT CNewMenu::CopyTemplate(IStream *pStream, NEWFILEINFO *pnfi)
  1832. {
  1833. TCHAR szSrcFolder[MAX_PATH], szSrc[MAX_PATH];
  1834. szSrc[0] = 0;
  1835. // failure here is OK, we will try CSIDL_COMMON_TEMPLATES too.
  1836. if (SHGetSpecialFolderPath(NULL, szSrcFolder, CSIDL_TEMPLATES, FALSE))
  1837. {
  1838. if (PathCombine(szSrc, szSrcFolder, (LPTSTR)pnfi->lpData))
  1839. {
  1840. if (!PathFileExistsAndAttributes(szSrc, NULL))
  1841. szSrc[0] = 0;
  1842. }
  1843. else
  1844. {
  1845. szSrc[0] = TEXT('\0');
  1846. }
  1847. }
  1848. if (szSrc[0] == 0)
  1849. {
  1850. if (SHGetSpecialFolderPath(NULL, szSrcFolder, CSIDL_COMMON_TEMPLATES, FALSE))
  1851. {
  1852. if (PathCombine(szSrc, szSrcFolder, (LPTSTR)pnfi->lpData))
  1853. {
  1854. if (!PathFileExistsAndAttributes(szSrc, NULL))
  1855. szSrc[0] = 0;
  1856. }
  1857. else
  1858. {
  1859. szSrc[0] = TEXT('\0');
  1860. }
  1861. }
  1862. }
  1863. if (szSrc[0] == 0)
  1864. {
  1865. // work around CSIDL_TEMPLATES not being setup right or
  1866. // templates that are left in the old %windir%\shellnew location
  1867. UINT cch = GetWindowsDirectory(szSrcFolder, ARRAYSIZE(szSrcFolder));
  1868. if (cch != 0 && cch < ARRAYSIZE(szSrcFolder))
  1869. {
  1870. if (PathAppend(szSrcFolder, TEXT("ShellNew")))
  1871. {
  1872. // note: if the file spec is fully qualified szSrcFolder is ignored
  1873. if (PathCombine(szSrc, szSrcFolder, (LPTSTR)pnfi->lpData))
  1874. {
  1875. // groovy
  1876. }
  1877. else
  1878. {
  1879. szSrc[0] = TEXT('\0');
  1880. }
  1881. }
  1882. else
  1883. {
  1884. szSrc[0] = TEXT('\0');
  1885. }
  1886. }
  1887. else
  1888. {
  1889. szSrc[0] = TEXT('\0');
  1890. }
  1891. }
  1892. //
  1893. // we just allow a null file to be created when the copy fails.
  1894. // this is for appcompat with office97. they fail to copy winword8.doc
  1895. // anywhere on the system. on win2k we succeed anyway with an empty file.
  1896. //
  1897. return SUCCEEDED(StgCopyFileToStream(szSrc, pStream)) ? S_OK : S_FALSE;
  1898. }