Source code of Windows XP (NT5)
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.

773 lines
21 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include <shlwapi.h>
  4. #include "openwith.h"
  5. #include "uemapp.h"
  6. #include "mtpt.h"
  7. #include "fassoc.h"
  8. #include "filetbl.h"
  9. #include "datautil.h"
  10. #include <dpa.h>
  11. #include "defcm.h"
  12. #define TF_OPENWITHMENU 0x00000000
  13. #define SZOPENWITHLIST TEXT("OpenWithList")
  14. #define REGSTR_PATH_EXPLORER_FILEEXTS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts")
  15. #define OPEN_WITH_LIST_MAX_ITEMS 10
  16. //
  17. // OpenWithListOpen()
  18. // allocates and initializes the state for the openwithlist
  19. //
  20. HRESULT OpenWithListOpen(IN LPCTSTR pszExt, HANDLE *phmru)
  21. {
  22. *phmru = 0;
  23. if (pszExt && *pszExt)
  24. {
  25. TCHAR szSubKey[MAX_PATH];
  26. // Build up the subkey string.
  27. wnsprintf(szSubKey, SIZECHARS(szSubKey), TEXT("%s\\%s\\%s"), REGSTR_PATH_EXPLORER_FILEEXTS, pszExt, SZOPENWITHLIST);
  28. MRUINFO mi = {sizeof(mi), OPEN_WITH_LIST_MAX_ITEMS, 0, HKEY_CURRENT_USER, szSubKey, NULL};
  29. *phmru = CreateMRUList(&mi);
  30. }
  31. return *phmru ? S_OK : E_OUTOFMEMORY;
  32. }
  33. HRESULT _AddItem(HANDLE hmru, LPCTSTR pszName)
  34. {
  35. HRESULT hr = S_OK;
  36. if (hmru)
  37. {
  38. int cItems = EnumMRUList(hmru, -1, NULL, 0);
  39. // just trim us down to make room...
  40. while (cItems >= OPEN_WITH_LIST_MAX_ITEMS)
  41. DelMRUString(hmru, --cItems);
  42. if (0 > AddMRUString(hmru, pszName))
  43. hr = E_UNEXPECTED;
  44. }
  45. return hr;
  46. }
  47. void _DeleteItem(HANDLE hmru, LPCTSTR pszName)
  48. {
  49. int iItem = FindMRUString(hmru, pszName, NULL);
  50. if (0 <= iItem)
  51. {
  52. DelMRUString(hmru, iItem);
  53. }
  54. }
  55. void _AddProgidForExt(LPCWSTR pszExt);
  56. STDAPI OpenWithListRegister(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszVerb, HKEY hkProgid)
  57. {
  58. //
  59. // ----> Peruser entries are stored here
  60. // HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts
  61. // \.Ext
  62. // Application = "foo.exe"
  63. // \OpenWithList
  64. // MRUList = "ab"
  65. // a = "App.exe"
  66. // b = "foo.exe"
  67. //
  68. // ----> for permanent entries are stored un HKCR
  69. // HKCR
  70. // \.Ext
  71. // \OpenWithList
  72. // \app.exe
  73. //
  74. // ----> and applications or the system can write app association here
  75. // \Applications
  76. // \APP.EXE
  77. // \shell...
  78. // \foo.exe
  79. // \shell...
  80. //
  81. //
  82. HANDLE hmru;
  83. HRESULT hr = OpenWithListOpen(pszExt, &hmru);
  84. if (SUCCEEDED(hr))
  85. {
  86. TCHAR szPath[MAX_PATH];
  87. hr = AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, hkProgid, pszVerb, szPath, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szPath)));
  88. if (SUCCEEDED(hr))
  89. {
  90. LPCTSTR pszExe = PathFindFileName(szPath);
  91. if (IsPathInOpenWithKillList(pszExe))
  92. hr = E_ACCESSDENIED;
  93. else
  94. hr = AssocMakeApplicationByKey(ASSOCMAKEF_VERIFY, hkProgid, pszVerb);
  95. if (SUCCEEDED(hr))
  96. {
  97. TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListRegister() adding %s",hmru, pszExe);
  98. hr = _AddItem(hmru, pszExe);
  99. }
  100. if (FAILED(hr))
  101. _DeleteItem(hmru, pszExe);
  102. }
  103. FreeMRUList(hmru);
  104. }
  105. _AddProgidForExt(pszExt);
  106. return hr;
  107. }
  108. STDAPI_(void) OpenWithListSoftRegisterProcess(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszProcess)
  109. {
  110. HANDLE hmru;
  111. if (SUCCEEDED(OpenWithListOpen(pszExt, &hmru)))
  112. {
  113. TCHAR szApp[MAX_PATH];
  114. if (!pszProcess)
  115. {
  116. if (GetModuleFileName(NULL, szApp, SIZECHARS(szApp)))
  117. pszProcess = szApp;
  118. }
  119. if (pszProcess && !IsPathInOpenWithKillList(pszProcess))
  120. _AddItem(hmru, PathFindFileName(pszProcess));
  121. FreeMRUList(hmru);
  122. }
  123. }
  124. class COpenWithArray : public CDPA<CAppInfo>
  125. {
  126. public:
  127. ~COpenWithArray();
  128. HRESULT FillArray(PCWSTR pszExt);
  129. private:
  130. static int CALLBACK _DeleteAppInfo(CAppInfo *pai, void *pv)
  131. { if (pai) delete pai; return 1; }
  132. };
  133. COpenWithArray::~COpenWithArray()
  134. {
  135. if ((HDPA)this)
  136. DestroyCallback(_DeleteAppInfo, NULL);
  137. }
  138. HRESULT COpenWithArray::FillArray(PCWSTR pszExt)
  139. {
  140. IEnumAssocHandlers *penum;
  141. HRESULT hr = SHAssocEnumHandlers(pszExt, &penum);
  142. if (SUCCEEDED(hr))
  143. {
  144. IAssocHandler *pah;
  145. while (S_OK == penum->Next(1, &pah, NULL))
  146. {
  147. // we only want the best
  148. if (S_OK == pah->IsRecommended())
  149. {
  150. CAppInfo *pai = new CAppInfo(pah);
  151. if (pai)
  152. {
  153. if (pai->Init())
  154. {
  155. // Trim duplicate items before we add them for other programs
  156. int i = 0;
  157. for (; i < GetPtrCount(); i++)
  158. {
  159. if (0 == lstrcmpi(pai->Name(), GetPtr(i)->Name()))
  160. {
  161. // its a match
  162. break;
  163. }
  164. }
  165. // if we dont add this to the dpa
  166. // then we need to clean it up
  167. if (i == GetPtrCount() && -1 != AppendPtr(pai))
  168. pai = NULL;
  169. }
  170. if (pai)
  171. delete pai;
  172. }
  173. }
  174. pah->Release();
  175. }
  176. penum->Release();
  177. }
  178. return hr;
  179. }
  180. class COpenWithMenu : public IContextMenu3, IShellExtInit
  181. {
  182. // IUnknown
  183. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  184. STDMETHOD_(ULONG,AddRef)(void);
  185. STDMETHOD_(ULONG,Release)(void);
  186. // IContextMenu
  187. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  188. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  189. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  190. // IContextMenu2
  191. STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  192. // IContextMenu3
  193. STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  194. // IShellExtInit
  195. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  196. friend HRESULT COpenWithMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppvOut);
  197. protected: // methods
  198. COpenWithMenu();
  199. ~COpenWithMenu();
  200. //Handle Menu messages submitted to HandleMenuMsg
  201. void DrawItem(DRAWITEMSTRUCT *lpdi);
  202. LRESULT MeasureItem(MEASUREITEMSTRUCT *lpmi);
  203. BOOL InitMenuPopup(HMENU hMenu);
  204. //Internal Helpers
  205. HRESULT _GetHelpText(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL fUnicode);
  206. HRESULT _MatchMenuItem(TCHAR ch, LRESULT* plRes);
  207. protected: // members
  208. LONG _cRef;
  209. HMENU _hMenu;
  210. BOOL _fMenuNeedsInit;
  211. UINT _idCmdFirst;
  212. COpenWithArray _owa;
  213. int _nItems;
  214. UINT _uFlags;
  215. IDataObject *_pdtobj;
  216. TCHAR _szPath[MAX_PATH];
  217. };
  218. COpenWithMenu::COpenWithMenu() : _cRef(1)
  219. {
  220. TraceMsg(TF_OPENWITHMENU, "ctor COpenWithMenu %x", this);
  221. }
  222. COpenWithMenu::~COpenWithMenu()
  223. {
  224. TraceMsg(TF_OPENWITHMENU, "dtor COpenWithMenu %x", this);
  225. if (_pdtobj)
  226. _pdtobj->Release();
  227. if (_hMenu)
  228. {
  229. ASSERT(_nItems);
  230. DestroyMenu(_hMenu);
  231. }
  232. }
  233. STDAPI COpenWithMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppvOut)
  234. {
  235. *ppvOut = NULL;
  236. if (pUnkOuter)
  237. return CLASS_E_NOAGGREGATION;
  238. COpenWithMenu * powm = new COpenWithMenu();
  239. if (!powm)
  240. return E_OUTOFMEMORY;
  241. HRESULT hr = powm->QueryInterface(riid, ppvOut);
  242. powm->Release();
  243. return hr;
  244. }
  245. HRESULT COpenWithMenu::QueryInterface(REFIID riid, void **ppvObj)
  246. {
  247. static const QITAB qit[] = {
  248. QITABENTMULTI(COpenWithMenu, IContextMenu, IContextMenu3),
  249. QITABENTMULTI(COpenWithMenu, IContextMenu2, IContextMenu3),
  250. QITABENT(COpenWithMenu, IContextMenu3),
  251. QITABENT(COpenWithMenu, IShellExtInit),
  252. { 0 },
  253. };
  254. return QISearch(this, qit, riid, ppvObj);
  255. }
  256. ULONG COpenWithMenu::AddRef()
  257. {
  258. return InterlockedIncrement(&_cRef);
  259. }
  260. ULONG COpenWithMenu::Release()
  261. {
  262. if (InterlockedDecrement(&_cRef))
  263. return _cRef;
  264. delete this;
  265. return 0;
  266. }
  267. /*
  268. Purpose:
  269. Add Open/Edit/Default verb to extension app list
  270. */
  271. HRESULT AddVerbItems(LPCTSTR pszExt)
  272. {
  273. IQueryAssociations *pqa;
  274. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  275. if (SUCCEEDED(hr))
  276. {
  277. hr = pqa->Init(0, pszExt, NULL, NULL);
  278. if (SUCCEEDED(hr))
  279. {
  280. HKEY hkeyClass;
  281. hr = pqa->GetKey(0, ASSOCKEY_SHELLEXECCLASS, NULL, &hkeyClass);
  282. if (SUCCEEDED(hr))
  283. {
  284. OpenWithListRegister(0, pszExt, NULL, hkeyClass);
  285. RegCloseKey(hkeyClass);
  286. }
  287. // we add in the editor too
  288. if (SUCCEEDED(pqa->GetKey(0, ASSOCKEY_SHELLEXECCLASS, L"Edit", &hkeyClass)))
  289. {
  290. OpenWithListRegister(0, pszExt, NULL, hkeyClass);
  291. RegCloseKey(hkeyClass);
  292. }
  293. hr = S_OK;
  294. }
  295. pqa->Release();
  296. }
  297. return hr;
  298. }
  299. //
  300. // Our context menu IDs are assigned like this
  301. //
  302. // idCmdFirst = Open With Custom Program (either on main menu or on popup)
  303. // idCmdFirst+1 through idCmdFirst+_nItems = Open With program in OpenWithList
  304. #define OWMENU_BROWSE 0
  305. #define OWMENU_APPFIRST 1
  306. HRESULT COpenWithMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  307. {
  308. MENUITEMINFO mii;
  309. LPTSTR pszExt;
  310. TCHAR szOpenWithMenu[80];
  311. _idCmdFirst = idCmdFirst;
  312. _uFlags = uFlags;
  313. if (SUCCEEDED(PathFromDataObject(_pdtobj, _szPath, ARRAYSIZE(_szPath))))
  314. {
  315. // No openwith context menu for executables.
  316. if (PathIsExe(_szPath))
  317. return S_OK;
  318. pszExt = PathFindExtension(_szPath);
  319. if (pszExt && *pszExt)
  320. {
  321. // Add Open/Edit/Default verb to extension app list
  322. if (SUCCEEDED(AddVerbItems(pszExt)))
  323. {
  324. // Do this only if AddVerbItems succeeded; otherwise,
  325. // we would create an empty MRU for a nonexisting class,
  326. // causing the class to spring into existence and cause
  327. // the "Open With" dialog to think we are overriding
  328. // rather than creating new.
  329. // get extension app list
  330. if (_owa.Create(4) && SUCCEEDED(_owa.FillArray(pszExt)))
  331. {
  332. _nItems = _owa.GetPtrCount();
  333. if (1 == _nItems)
  334. {
  335. // For known file type(there is at least one verb under its progid),
  336. // if there is only one item in its openwithlist, don't show open with sub menu
  337. _nItems = 0;
  338. }
  339. }
  340. }
  341. }
  342. }
  343. LoadString(g_hinst, (_nItems ? IDS_OPENWITH : IDS_OPENWITHNEW), szOpenWithMenu, ARRAYSIZE(szOpenWithMenu));
  344. if (_nItems)
  345. {
  346. // we need to create a submenu
  347. // with all of our goodies
  348. _hMenu = CreatePopupMenu();
  349. if (_hMenu)
  350. {
  351. _fMenuNeedsInit = TRUE;
  352. mii.cbSize = sizeof(MENUITEMINFO);
  353. mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  354. mii.wID = idCmdFirst+OWMENU_APPFIRST;
  355. mii.fType = MFT_STRING;
  356. mii.dwTypeData = szOpenWithMenu;
  357. mii.dwItemData = 0;
  358. InsertMenuItem(_hMenu,0,TRUE,&mii);
  359. mii.fMask = MIIM_ID|MIIM_SUBMENU|MIIM_TYPE;
  360. mii.fType = MFT_STRING;
  361. mii.wID = idCmdFirst+OWMENU_BROWSE;
  362. mii.hSubMenu = _hMenu;
  363. mii.dwTypeData = szOpenWithMenu;
  364. InsertMenuItem(hmenu,indexMenu,TRUE,&mii);
  365. }
  366. }
  367. else
  368. {
  369. mii.cbSize = sizeof(MENUITEMINFO);
  370. mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  371. mii.fType = MFT_STRING;
  372. mii.wID = idCmdFirst+OWMENU_BROWSE;
  373. mii.dwTypeData = szOpenWithMenu;
  374. mii.dwItemData = 0;
  375. InsertMenuItem(hmenu,indexMenu,TRUE,&mii);
  376. }
  377. return ResultFromShort(_nItems + 1);
  378. }
  379. HRESULT COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  380. {
  381. HRESULT hr = E_OUTOFMEMORY;
  382. CMINVOKECOMMANDINFOEX ici;
  383. void * pvFree;
  384. // maybe these two routines should be collapsed into one?
  385. if ((IS_INTRESOURCE(pici->lpVerb) || 0 == lstrcmpiA(pici->lpVerb, "openas"))
  386. && SUCCEEDED(ICI2ICIX(pici, &ici, &pvFree)))
  387. {
  388. BOOL fOpenAs = TRUE;
  389. if (pici->lpVerb && IS_INTRESOURCE(pici->lpVerb))
  390. {
  391. int i = LOWORD(pici->lpVerb) - OWMENU_APPFIRST;
  392. if (i < _owa.GetPtrCount())
  393. {
  394. hr = _owa.GetPtr(i)->Handler()->Invoke(&ici, _szPath);
  395. fOpenAs = FALSE;
  396. }
  397. }
  398. if (fOpenAs)
  399. {
  400. SHELLEXECUTEINFO ei = {0};
  401. hr = ICIX2SEI(&ici, &ei);
  402. if (SUCCEEDED(hr))
  403. {
  404. // use the "Unknown" key so we get the openwith prompt
  405. ei.lpFile = _szPath;
  406. // dont do the zone check before the user picks the app.
  407. // wait until they actually try to invoke the file.
  408. ei.fMask |= SEE_MASK_NOZONECHECKS;
  409. RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Unknown"), 0, MAXIMUM_ALLOWED, &ei.hkeyClass);
  410. if (!(_uFlags & CMF_DEFAULTONLY))
  411. {
  412. // defview sets CFM_DEFAULTONLY when the user is double-clicking. We check it
  413. // here since we want do NOT want to query the class store if the user explicitly
  414. // right-clicked on the menu and choo se openwith.
  415. // pop up open with dialog without querying class store
  416. ei.fMask |= SEE_MASK_NOQUERYCLASSSTORE;
  417. }
  418. if (ei.hkeyClass)
  419. {
  420. ei.fMask |= SEE_MASK_CLASSKEY;
  421. if (ShellExecuteEx(&ei))
  422. {
  423. hr = S_OK;
  424. if (UEMIsLoaded())
  425. {
  426. // note that we already got a UIBL_DOTASSOC (from
  427. // OpenAs_RunDLL or whatever it is that 'Unknown'
  428. // runs). so the Uassist analysis app will have to
  429. // subtract it off
  430. UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_RUNASSOC, UIBL_DOTNOASSOC);
  431. }
  432. }
  433. else
  434. {
  435. hr = HRESULT_FROM_WIN32(GetLastError());
  436. }
  437. RegCloseKey(ei.hkeyClass);
  438. }
  439. else
  440. hr = E_FAIL;
  441. }
  442. }
  443. LocalFree(pvFree); // accepts NULL
  444. }
  445. return hr;
  446. }
  447. HRESULT COpenWithMenu::_GetHelpText(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL fUnicode)
  448. {
  449. UINT ids;
  450. LPCTSTR pszFriendly = NULL;
  451. if (idCmd == OWMENU_BROWSE)
  452. {
  453. ids = IDS_OPENWITHHELP;
  454. pszFriendly = TEXT("");
  455. }
  456. else if ((idCmd-OWMENU_APPFIRST) < (UINT_PTR)_owa.GetPtrCount())
  457. {
  458. ids = IDS_OPENWITHAPPHELP;
  459. pszFriendly = _owa.GetPtr(idCmd-OWMENU_APPFIRST)->UIName();
  460. }
  461. if (!pszFriendly)
  462. return E_FAIL;
  463. if (fUnicode)
  464. {
  465. WCHAR wszFormat[80];
  466. LoadStringW(HINST_THISDLL, ids, wszFormat, ARRAYSIZE(wszFormat));
  467. wnsprintfW((LPWSTR)pszName, cchMax, wszFormat, pszFriendly);
  468. }
  469. else
  470. {
  471. CHAR szFormat[80];
  472. LoadStringA(HINST_THISDLL, ids, szFormat, ARRAYSIZE(szFormat));
  473. wnsprintfA(pszName, cchMax, szFormat, pszFriendly);
  474. }
  475. return S_OK;
  476. }
  477. const ICIVERBTOIDMAP c_sIDVerbMap[] =
  478. {
  479. { L"openas", "openas", OWMENU_BROWSE, OWMENU_BROWSE, },
  480. };
  481. HRESULT COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  482. {
  483. switch (uType)
  484. {
  485. case GCS_VERBA:
  486. case GCS_VERBW:
  487. return SHMapCmdIDToVerb(idCmd, c_sIDVerbMap, ARRAYSIZE(c_sIDVerbMap), pszName, cchMax, GCS_VERBW == uType);
  488. case GCS_HELPTEXTA:
  489. case GCS_HELPTEXTW:
  490. return _GetHelpText(idCmd, pszName, cchMax, uType == GCS_HELPTEXTW);
  491. }
  492. return E_NOTIMPL;
  493. }
  494. HRESULT COpenWithMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  495. {
  496. return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
  497. }
  498. // Defined in fsmenu.cpp
  499. BOOL _MenuCharMatch(LPCTSTR lpsz, TCHAR ch, BOOL fIgnoreAmpersand);
  500. HRESULT COpenWithMenu::_MatchMenuItem(TCHAR ch, LRESULT* plRes)
  501. {
  502. if (plRes == NULL)
  503. return S_FALSE;
  504. int iLastSelectedItem = -1;
  505. int iNextMatch = -1;
  506. BOOL fMoreThanOneMatch = FALSE;
  507. int c = GetMenuItemCount(_hMenu);
  508. // Pass 1: Locate the Selected Item
  509. for (int i = 0; i < c; i++)
  510. {
  511. MENUITEMINFO mii = {0};
  512. mii.cbSize = sizeof(mii);
  513. mii.fMask = MIIM_STATE;
  514. if (GetMenuItemInfo(_hMenu, i, MF_BYPOSITION, &mii))
  515. {
  516. if (mii.fState & MFS_HILITE)
  517. {
  518. iLastSelectedItem = i;
  519. break;
  520. }
  521. }
  522. }
  523. // Pass 2: Starting from the selected item, locate the first item with the matching name.
  524. for (int i = iLastSelectedItem + 1; i < c; i++)
  525. {
  526. if (i < _owa.GetPtrCount()
  527. && _MenuCharMatch(_owa.GetPtr(i)->UIName(), ch, FALSE))
  528. {
  529. if (iNextMatch != -1)
  530. {
  531. fMoreThanOneMatch = TRUE;
  532. break; // We found all the info we need
  533. }
  534. else
  535. {
  536. iNextMatch = i;
  537. }
  538. }
  539. }
  540. // Pass 3: If we did not find a match, or if there was only one match
  541. // Search from the first item, to the Selected Item
  542. if (iNextMatch == -1 || fMoreThanOneMatch == FALSE)
  543. {
  544. for (int i = 0; i <= iLastSelectedItem; i++)
  545. {
  546. if (i < _owa.GetPtrCount()
  547. && _MenuCharMatch(_owa.GetPtr(i)->UIName(), ch, FALSE))
  548. {
  549. if (iNextMatch != -1)
  550. {
  551. fMoreThanOneMatch = TRUE;
  552. break;
  553. }
  554. else
  555. {
  556. iNextMatch = i;
  557. }
  558. }
  559. }
  560. }
  561. if (iNextMatch != -1)
  562. {
  563. *plRes = MAKELONG(iNextMatch, fMoreThanOneMatch? MNC_SELECT : MNC_EXECUTE);
  564. }
  565. else
  566. {
  567. *plRes = MAKELONG(0, MNC_IGNORE);
  568. }
  569. return S_OK;
  570. }
  571. HRESULT COpenWithMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *plResult)
  572. {
  573. LRESULT lResult = 0;
  574. HRESULT hr = S_OK;
  575. switch (uMsg)
  576. {
  577. case WM_INITMENUPOPUP:
  578. InitMenuPopup(_hMenu);
  579. break;
  580. case WM_DRAWITEM:
  581. DrawItem((DRAWITEMSTRUCT *)lParam);
  582. break;
  583. case WM_MEASUREITEM:
  584. lResult = MeasureItem((MEASUREITEMSTRUCT *)lParam);
  585. break;
  586. case WM_MENUCHAR:
  587. hr = _MatchMenuItem((TCHAR)LOWORD(wParam), &lResult);
  588. break;
  589. default:
  590. hr = E_NOTIMPL;
  591. break;
  592. }
  593. if (plResult)
  594. *plResult = lResult;
  595. return hr;
  596. }
  597. HRESULT COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  598. {
  599. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  600. return S_OK;
  601. }
  602. #define CXIMAGEGAP 6
  603. #define SRCSTENCIL 0x00B8074AL
  604. void COpenWithMenu::DrawItem(DRAWITEMSTRUCT *lpdi)
  605. {
  606. CAppInfo *pai = _owa.GetPtr(lpdi->itemData);
  607. DrawMenuItem(lpdi, pai->UIName(), pai->IconIndex());
  608. }
  609. LRESULT COpenWithMenu::MeasureItem(MEASUREITEMSTRUCT *pmi)
  610. {
  611. CAppInfo *pai = _owa.GetPtr(pmi->itemData);
  612. return MeasureMenuItem(pmi, pai->UIName());
  613. }
  614. BOOL COpenWithMenu::InitMenuPopup(HMENU hmenu)
  615. {
  616. TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::InitMenuPopup");
  617. if (_fMenuNeedsInit)
  618. {
  619. TCHAR szMenuText[80];
  620. MENUITEMINFO mii;
  621. // remove the place holder.
  622. DeleteMenu(hmenu,0,MF_BYPOSITION);
  623. // add app's in mru list to context menu
  624. for (int i = 0; i < _owa.GetPtrCount(); i++)
  625. {
  626. mii.cbSize = sizeof(MENUITEMINFO);
  627. mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  628. mii.wID = _idCmdFirst + OWMENU_APPFIRST + i;
  629. mii.fType = MFT_OWNERDRAW;
  630. mii.dwItemData = i;
  631. InsertMenuItem(hmenu,GetMenuItemCount(hmenu),TRUE,&mii);
  632. }
  633. // add seperator
  634. AppendMenu(hmenu,MF_SEPARATOR,0,NULL);
  635. // add "Choose Program..."
  636. LoadString(g_hinst, IDS_OPENWITHBROWSE, szMenuText, ARRAYSIZE(szMenuText));
  637. mii.cbSize = sizeof(MENUITEMINFO);
  638. mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  639. mii.wID = _idCmdFirst + OWMENU_BROWSE;
  640. mii.fType = MFT_STRING;
  641. mii.dwTypeData = szMenuText;
  642. mii.dwItemData = 0;
  643. InsertMenuItem(hmenu,GetMenuItemCount(hmenu),TRUE,&mii);
  644. _fMenuNeedsInit = FALSE;
  645. return TRUE;
  646. }
  647. return FALSE;
  648. }