openwith.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 33k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 
  4. //
  5. // File: openwith.cpp
  6. //
  7. // This file contains the implementation of COpenWithMenu, a context menu handler
  8. //
  9. // History:
  10. //         2-27-98  by ningz
  11. //         3-SEP-98  ZekeL - rewrote ExtAppList() and moved it here
  12. //------------------------------------------------------------------------
  13. #include "shellprv.h"
  14. #include <fsmenu.h>
  15. #include "ids.h"
  16. #include <shlwapi.h>
  17. #include "openwith.h"
  18. #include "uemapp.h"
  19. #define TF_OPENWITHMENU 0x00004000
  20. #define SZOPENWITHLIST                  TEXT("OpenWithList")
  21. #define REGSTR_PATH_EXPLORER_FILEEXTS   TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts")
  22. #define _OpenWithListMaxItems()         10
  23. typedef struct _OpenWithList {
  24.     HANDLE hMRU;
  25.     HANDLE hMutex;
  26.     HKEY   hkLM;
  27.     HKEY   hkCU;
  28. } OPENWITHLIST, *POPENWITHLIST;
  29. LONG _CreateListMutex(LPCTSTR pszExt, HANDLE *phMutex)
  30. {
  31.     TCHAR szMutex[MAX_PATH];
  32.     wnsprintf(szMutex, SIZECHARS(szMutex), TEXT("%s%s"), SZOPENWITHLIST, pszExt);
  33.     CharLower(szMutex);
  34.     *phMutex = CreateMutex(CreateAllAccessSecurityAttributes(NULL, NULL, NULL), FALSE, szMutex);
  35.     if (!*phMutex) 
  36.         return GetLastError();
  37.     return NOERROR;
  38. }
  39. inline BOOL _GrabMutex(HANDLE hMutex)
  40. {
  41.     DWORD dwResult = WaitForSingleObject(hMutex, INFINITE);
  42.     return (WAIT_OBJECT_0 == dwResult) ;
  43. }
  44. //
  45. //  OpenWithListClose()
  46. //  Frees up resources allocated by OpenWithListOpen().  No return value.
  47. //
  48. void OpenWithListClose(IN HANDLE hList)
  49. {
  50.     POPENWITHLIST pList = (POPENWITHLIST) hList;  
  51.     TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListClose() called", pList);
  52.     if (pList) 
  53.     {
  54.         if (pList->hMutex) 
  55.             CloseHandle(pList->hMutex);
  56.         
  57.         if (pList->hkCU) 
  58.             RegCloseKey(pList->hkCU);
  59.         if (pList->hkLM) 
  60.             RegCloseKey(pList->hkLM);
  61.         if (pList->hMRU) 
  62.             FreeMRUList(pList->hMRU);
  63.         LocalFree(pList);
  64.     } 
  65.     
  66.     return;
  67. }
  68. //
  69. //  OpenWithListOpen() 
  70. //  allocates and initializes the state for the openwithlist 
  71. //
  72. HRESULT OpenWithListOpen(IN LPCTSTR pszExt, HANDLE *phList)
  73. {
  74.     LONG lResult;
  75.     if (!pszExt || !*pszExt || !phList) 
  76.         return E_INVALIDARG;
  77.     *phList = NULL;
  78.     
  79.     POPENWITHLIST pList = (POPENWITHLIST) LocalAlloc(LPTR, sizeof(OPENWITHLIST));
  80.     
  81.     if (!pList) 
  82.         return E_OUTOFMEMORY;
  83.     lResult = _CreateListMutex(pszExt, &pList->hMutex);
  84.     if (NOERROR == lResult)
  85.     {
  86.         TCHAR szSubKey[MAX_PATH];
  87.         //  Build up the subkey string.
  88.         wnsprintf(szSubKey, SIZECHARS(szSubKey), TEXT("%s\%s\%s"), REGSTR_PATH_EXPLORER_FILEEXTS, pszExt, SZOPENWITHLIST);
  89.         MRUINFO mi = {sizeof(mi), _OpenWithListMaxItems(), 0, HKEY_CURRENT_USER, szSubKey, NULL};
  90.         pList->hMRU = CreateMRUList(&mi);
  91.         //  the MRU list initializes the key a specific way so that
  92.         //  it is a real MRU...
  93.         if (pList->hMRU) 
  94.         {
  95.             lResult = RegOpenKeyEx(HKEY_CURRENT_USER, szSubKey, 0L, 
  96.                 MAXIMUM_ALLOWED, &(pList->hkCU));
  97.             if (ERROR_SUCCESS == lResult)
  98.             {
  99.                 //  make different subkey for LM
  100.                 wnsprintf(szSubKey, SIZECHARS(szSubKey), TEXT("%s\%s"), pszExt, SZOPENWITHLIST);
  101.                 RegOpenKeyEx(HKEY_CLASSES_ROOT, szSubKey, 0L,
  102.                     MAXIMUM_ALLOWED, &(pList->hkLM));
  103.             }
  104.         }
  105.         else //  !pList->hMRU
  106.             lResult = ERROR_BADKEY;
  107.     }
  108.     if (NOERROR != lResult)
  109.     {
  110.         OpenWithListClose((HANDLE)pList);
  111.         TraceMsg(TF_OPENWITHMENU, "OpenWithListOpen() failed on %s, err = %d", pszExt, lResult);
  112.         return HRESULT_FROM_WIN32(lResult);
  113.     }
  114.     TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListOpen() created on %s", pList, pszExt);
  115.     *phList = (HANDLE) pList;
  116.     return S_OK;
  117. }
  118. HRESULT _AddItem(HANDLE hMRU, HKEY hkList, LPCTSTR pszName)
  119. {
  120.     HRESULT hr = S_OK;
  121.     if (hMRU)
  122.     {
  123.         int cItems = EnumMRUList(hMRU, -1, NULL, 0L);
  124.         //  just trim us down to make room...
  125.         while (cItems >= _OpenWithListMaxItems())
  126.             DelMRUString(hMRU, --cItems);
  127.             
  128.         if (0 > AddMRUString(hMRU, pszName))
  129.             hr = E_UNEXPECTED;
  130.     }
  131.     
  132.     return hr;
  133. }
  134. void _DeleteItem(POPENWITHLIST pList, LPCTSTR pszName)
  135. {
  136.     int iItem = FindMRUString(pList->hMRU, pszName, NULL);
  137.     if (0 <= iItem) {
  138.         DelMRUString(pList->hMRU, iItem);
  139.     } 
  140.    
  141. }
  142. STDAPI OpenWithListRegister(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszVerb, HKEY hkProgid)
  143. {
  144.     if (!pszExt || !hkProgid)
  145.         return E_INVALIDARG;
  146.     POPENWITHLIST pList;
  147.     //
  148.     //  ----> Peruser entries are stored here
  149.     //  HKCUSoftwareMicrosoftWindowsCurrentVersionExplorerFileExts
  150.     //     .Ext
  151.     //         Application = "foo.exe"
  152.     //         OpenWithList
  153.     //             MRUList = "ab"
  154.     //             a = "App.exe"
  155.     //             b = "foo.exe"
  156.     //
  157.     //  ----> for permanent entries are stored un HKCR
  158.     //  HKCR
  159.     //     .Ext
  160.     //         OpenWithList
  161.     //             app.exe
  162.     //
  163.     //  ----> and applications or the system can write app association here
  164.     //     Applications
  165.     //         APP.EXE
  166.     //             shell...
  167.     //         foo.exe
  168.     //             shell...
  169.     //
  170.     //
  171.     HRESULT hr = OpenWithListOpen(pszExt, (HANDLE *)&pList);
  172.     
  173.     if (SUCCEEDED(hr))
  174.     {
  175.         if (_GrabMutex(pList->hMutex))
  176.         {
  177.             TCHAR szPath[MAX_PATH];
  178.             HRESULT hr = AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, hkProgid, pszVerb, szPath, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szPath)));
  179.             if (SUCCEEDED(hr))
  180.             {
  181.                 LPCTSTR pszExe = PathFindFileName(szPath);
  182.                 if (IsPathInOpenWithKillList(pszExe))
  183.                     hr = E_ACCESSDENIED;
  184.                 else
  185.                     hr = AssocMakeApplicationByKey(ASSOCMAKEF_VERIFY, hkProgid, pszVerb);
  186.             
  187.                 if (SUCCEEDED(hr))
  188.                 {
  189.                     TraceMsg(TF_OPENWITHMENU, "[%X] OpenWithListRegister() adding %s",pList, pszExe);
  190.                     hr = _AddItem(pList->hMRU, pList->hkCU, pszExe);
  191.                 }
  192.                 if (FAILED(hr)) 
  193.                     _DeleteItem(pList, pszExe);
  194.             }
  195.             ReleaseMutex(pList->hMutex);
  196.         }
  197.         else
  198.             hr = E_UNEXPECTED;
  199.         OpenWithListClose(pList);
  200.     }
  201.     return hr;
  202. }
  203. STDAPI_(void) OpenWithListSoftRegisterProcess(DWORD dwFlags, LPCTSTR pszExt)
  204. {
  205.     if (!pszExt || !*pszExt)
  206.         return;
  207.     POPENWITHLIST pList;
  208.     
  209.     if (SUCCEEDED(OpenWithListOpen(pszExt, (HANDLE *)&pList)))
  210.     {
  211.         if (_GrabMutex(pList->hMutex))
  212.         {
  213.             TCHAR szApp[MAX_PATH];  
  214.             if (GetModuleFileName(NULL, szApp, SIZECHARS(szApp))
  215.             && !IsPathInOpenWithKillList(szApp))
  216.                 _AddItem(pList->hMRU, pList->hkCU, PathFindFileName(szApp));
  217.             ReleaseMutex(pList->hMutex);
  218.         }
  219.         OpenWithListClose(pList);
  220.     }
  221. }
  222. typedef struct _OpenWithItem
  223. {
  224.     LPTSTR  pszFriendly;
  225.     LPTSTR  pszVerb;
  226.     HKEY    hKey;
  227. } OPENWITHITEM, *POPENWITHITEM;
  228. BOOL _GetAppKey(LPCTSTR pszApp, HKEY *phkApp)
  229. {
  230.     ASSERT(pszApp && *pszApp);
  231.     TCHAR szKey[MAX_PATH];
  232.     lstrcpy(szKey, TEXT("Applications\"));
  233.     StrCatBuff(szKey, pszApp, SIZECHARS(szKey));
  234.     return (NOERROR == RegOpenKeyEx(
  235.         HKEY_CLASSES_ROOT,
  236.         szKey,
  237.         0L,
  238.         MAXIMUM_ALLOWED,
  239.         phkApp));
  240. }
  241. DWORD _AppListEnumLM(HKEY hkey, HANDLE hMRU, LPCTSTR pszVerb, POPENWITHITEM pItems, ULONG cItems, DWORD index)
  242. {
  243.     for (DWORD iItem = 0; index < cItems; iItem++) 
  244.     {
  245.         TCHAR sz[MAX_PATH];
  246.         DWORD cch;
  247.         HKEY hkItem;
  248.         
  249.         cch = SIZECHARS(sz);
  250.         if (NOERROR != RegEnumKeyEx(hkey, iItem, sz, &cch, 
  251.             NULL, NULL, NULL, NULL))
  252.             break;
  253.         //  if we already tried this from the MRU or
  254.         //  if the APP key doesnt exist here, then 
  255.         //  just continue on to the next item
  256.         if ((0 <= FindMRUString(hMRU, sz, NULL))
  257.         ||  IsPathInOpenWithKillList(sz)
  258.         ||  !_GetAppKey(sz, &hkItem))
  259.             continue;
  260.             
  261.         ASSERT(hkItem);
  262.         //
  263.         //  this will filter out apps that dont support the specified verb
  264.         //  we reuse sz here.
  265.         if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_FRIENDLYAPPNAME, hkItem, pszVerb, sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
  266.         {
  267.             pItems[index].pszFriendly = StrDup(sz);
  268.             pItems[index].pszVerb = pszVerb ? StrDup(pszVerb) : NULL;
  269.             pItems[index].hKey = hkItem;
  270.             hkItem = NULL;
  271.             index++;
  272.         } 
  273.         if (hkItem)
  274.             RegCloseKey(hkItem);
  275.             
  276.     } //  for
  277.     return index;
  278. }
  279. void _ReleaseListItem(POPENWITHITEM pItem)
  280. {
  281.     if (pItem->pszFriendly) 
  282.         LocalFree(pItem->pszFriendly);
  283.     if (pItem->pszVerb)
  284.         LocalFree(pItem->pszVerb);
  285.     if (pItem->hKey) 
  286.         RegCloseKey(pItem->hKey);
  287.     //  since this is called from only one place, 
  288.     //  we dont actually need to clear the values...
  289.     //  ZeroMemory(pItem, SIZEOF(OPENWITHITEM))
  290. }
  291. //
  292. // Must declare an explicit structure to keep Win64 happy.
  293. // We actually hand out an internal pointer to the rgItems array,
  294. // but occasionally we need to peek at the cItems.
  295. //
  296. typedef struct ITEMSLIST {
  297.     DWORD cItems;
  298.     OPENWITHITEM rgItems[1];
  299. } ITEMSLIST, *PITEMSLIST;
  300. void OpenWithListReleaseList(POPENWITHITEM pItems)
  301. {
  302.     if (pItems)
  303.     {
  304.         PITEMSLIST pList = CONTAINING_RECORD(pItems, ITEMSLIST, rgItems);
  305.         
  306.         DWORD cItems = pList->cItems;
  307.         for (DWORD i = 0; i < cItems; i++)
  308.             _ReleaseListItem(&pList->rgItems[i]);
  309.         LocalFree(pList);
  310.     }
  311. }
  312. DWORD _AllocList(POPENWITHLIST pList, POPENWITHITEM *ppItems)
  313. {
  314.     
  315.     DWORD cResult = (DWORD) EnumMRUList(pList->hMRU, -1, NULL, 0L);
  316.     if (cResult == (DWORD)-1)
  317.         cResult = 0;
  318.     if (pList->hkLM)
  319.     {
  320.         DWORD dw = 0;
  321.         RegQueryInfoKey(
  322.             pList->hkLM,
  323.             NULL,
  324.             NULL,
  325.             NULL,
  326.             &dw,
  327.             NULL,
  328.             NULL,
  329.             NULL,
  330.             NULL,
  331.             NULL,
  332.             NULL,
  333.             NULL
  334.         );
  335.         cResult += dw;        
  336.     }
  337.     if (cResult)
  338.     {
  339.         PITEMSLIST pList = (PITEMSLIST)LocalAlloc(LPTR, FIELD_OFFSET(ITEMSLIST, rgItems[cResult]));
  340.         if(pList)
  341.             *ppItems = &pList->rgItems[0];
  342.         else
  343.             cResult = 0;
  344.     }
  345.     return cResult;    
  346. }
  347. DWORD _AppListEnumMRU(HANDLE hMRU, LPCTSTR pszVerb, POPENWITHITEM pItems, ULONG cItems, DWORD index)
  348. {
  349.     for (int iItem = 0; index < cItems; iItem++) 
  350.     {
  351.         TCHAR sz[MAX_PATH];
  352.         HKEY hkItem;
  353.         
  354.         if (0 > EnumMRUList(hMRU, iItem, sz, SIZECHARS(sz)))
  355.             break;
  356.         //  dont quit if we couldnt get this key...
  357.         if (IsPathInOpenWithKillList(sz) || !_GetAppKey(sz, &hkItem))
  358.             continue;
  359.             
  360.         ASSERT(hkItem);
  361.         //
  362.         //  this will filter out apps that dont support the specified verb
  363.         //  we reuse sz here.
  364.         if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_FRIENDLYAPPNAME, hkItem, pszVerb, sz, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(sz)))))
  365.         {
  366.             pItems[index].pszFriendly = StrDup(sz);
  367.             pItems[index].pszVerb = pszVerb ? StrDup(pszVerb) : NULL;
  368.             pItems[index].hKey = hkItem;
  369.             hkItem = NULL;
  370.             index++;
  371.         } 
  372.         if (hkItem)
  373.             RegCloseKey(hkItem);
  374.             
  375.     } //  for
  376.     return index;
  377. }
  378. //
  379. //  OpenWithListGetList()
  380. //
  381. //  Allocates and initializes a list of all the openwithlist items
  382. //  that are associated with the pszExt, and can shellexecute pszVerb.
  383. //  if the pszVerb is NULL, the default verbs are allowed
  384. //
  385. DWORD OpenWithListGetList(LPCTSTR pszExt, LPCTSTR pszVerb, POPENWITHITEM *ppItems)
  386. {
  387.     POPENWITHLIST pList;
  388.     DWORD cResult = 0;
  389.     if (!pszExt || !ppItems)
  390.         return 0;
  391.     *ppItems = NULL;
  392.     
  393.     if (SUCCEEDED(OpenWithListOpen(pszExt, (HANDLE *)&pList)))
  394.     {
  395.         if (_GrabMutex(pList->hMutex))
  396.         {
  397.             POPENWITHITEM pItems;
  398.             LONG cItems = _AllocList(pList, &pItems);
  399.             if (cItems)
  400.             {
  401.                 //  enum the MRU first, and then if there are
  402.                 //  any that were under HKCR, them we will add them too.
  403.                 cResult = _AppListEnumMRU(pList->hMRU, pszVerb, pItems, cItems, 0);
  404.                 if (pList->hkLM)
  405.                     cResult =_AppListEnumLM(pList->hkLM, pList->hMRU, pszVerb, pItems, cItems, cResult);
  406.                 if (cResult)
  407.                 {
  408.                     PITEMSLIST pList = CONTAINING_RECORD(pItems, ITEMSLIST, rgItems);
  409.                     pList->cItems = cResult;
  410.                     *ppItems = pItems;
  411.                 }
  412.                 else
  413.                 {
  414.                     OpenWithListReleaseList(pItems);    
  415.                 }
  416.             }
  417.             ReleaseMutex(pList->hMutex);
  418.         }
  419.         OpenWithListClose((HANDLE)pList);
  420.         
  421.     } 
  422.     return cResult;
  423. }
  424. typedef struct 
  425. {
  426.     TCHAR szMenuText[MAX_PATH];
  427.     int iImage;
  428.     
  429.     DWORD dwFlags;
  430. } OPENWITHMENUITEMINFO, * LPOPENWITHMENUITEMINFO;
  431. // format strings
  432. const TCHAR c_szSShellSCommand[] = TEXT("%s\Shell\%s\Command");
  433. const TCHAR c_szSShell[] = TEXT("%s\Shell");
  434. class COpenWithMenu : public IContextMenu3, IShellExtInit,IObjectWithSite
  435. {
  436.     // IUnknown
  437.     STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  438.     STDMETHOD_(ULONG,AddRef)(void);
  439.     STDMETHOD_(ULONG,Release)(void);
  440.     
  441.     // IContextMenu
  442.     STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  443.     STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  444.     STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax);
  445.     
  446.     // IContextMenu2
  447.     STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam);
  448.     
  449.     // IContextMenu3
  450.     STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult);
  451.     
  452.     // IShellExtInit
  453.     STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID);
  454.     
  455.     //IObjectWithSite
  456.     STDMETHOD(SetSite)(IUnknown*);
  457.     STDMETHOD(GetSite)(REFIID,void**);
  458.     
  459.     int                 _cRef;
  460.     HMENU               _hMenu;
  461.     UINT                _idCmdFirst;
  462.     int                 _nItems;
  463.     UINT                _uFlags;
  464.     POPENWITHITEM       _pItems;
  465.     TCHAR               _szPath[MAX_PATH];
  466.     HIMAGELIST          _himlSystemImageList;
  467.     IDataObject        *_pdtobj;
  468.     IShellView2        *_pShellView2;
  469.     LPCITEMIDLIST       _pidlFolder;
  470.     
  471.     COpenWithMenu();
  472.     ~COpenWithMenu();
  473.     
  474.     friend HRESULT COpenWithMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID *  ppvOut);
  475.     
  476. private:
  477.     //Handle Menu messages submitted to HandleMenuMsg
  478.     void DrawItem(DRAWITEMSTRUCT *lpdi);
  479.     LRESULT MeasureItem(MEASUREITEMSTRUCT *lpmi);
  480.     BOOL InitMenuPopup(HMENU hMenu);
  481.     
  482.     //Internal Helpers
  483.     POPENWITHITEM GetItemData(HMENU hmenu, UINT iItem);
  484. };
  485. COpenWithMenu::COpenWithMenu() : _cRef(1) 
  486. {
  487.     TraceMsg(TF_OPENWITHMENU, "ctor COpenWithMenu %x", this);
  488. }
  489. COpenWithMenu::~COpenWithMenu()
  490. {
  491.     TraceMsg(TF_OPENWITHMENU, "dtor COpenWithMenu %x", this);
  492.     if (_pdtobj)
  493.         _pdtobj->Release();
  494.     if (_pItems)
  495.     {
  496.         OpenWithListReleaseList(_pItems);
  497.     }
  498.     //Safety Net: Release my site in case I manage to get 
  499.     // Released without my site SetSite(NULL) first.
  500.     ATOMICRELEASE(_pShellView2);
  501. }
  502. STDAPI COpenWithMenu_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID *  ppvOut)
  503. {
  504.     HRESULT hr = E_FAIL;
  505.     
  506.     TraceMsg(TF_OPENWITHMENU, "COpenWithMenu_CreateInstance()");
  507.     *ppvOut = NULL;                     
  508.     if (pUnkOuter)
  509.         return CLASS_E_NOAGGREGATION;
  510.     COpenWithMenu * powm = new COpenWithMenu();
  511.     if (!powm)
  512.         return E_OUTOFMEMORY;
  513.     
  514.     hr = powm->QueryInterface(riid, (LPVOID *)ppvOut);
  515.     powm->Release();
  516.     
  517.     return hr;
  518. }
  519. HRESULT COpenWithMenu::QueryInterface(REFIID riid, void **ppvObj)
  520. {
  521.     if (IsEqualIID(riid, IID_IUnknown) || 
  522.         IsEqualIID(riid, IID_IContextMenu) || 
  523.         IsEqualIID(riid, IID_IContextMenu2) ||  
  524.         IsEqualIID(riid, IID_IContextMenu3))
  525.     {
  526.         *ppvObj = SAFECAST(this, IContextMenu3 *);
  527.     }
  528.     else if (IsEqualIID(riid, IID_IShellExtInit))
  529.     {
  530.         *ppvObj = SAFECAST(this, IShellExtInit *);
  531.     }
  532.     else if (IsEqualIID(riid, IID_IObjectWithSite))
  533.     {
  534.         *ppvObj = SAFECAST(this, IObjectWithSite *);
  535.     }
  536.     else 
  537.     {
  538.         *ppvObj = NULL;
  539.         return E_NOINTERFACE;
  540.     }
  541.     
  542.     AddRef();
  543.     return NOERROR;
  544. }
  545. ULONG COpenWithMenu::AddRef()
  546. {
  547.     _cRef++;
  548.     TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::AddRef = %x", _cRef);
  549.     return _cRef;
  550. }
  551. ULONG COpenWithMenu::Release()
  552. {
  553.     _cRef--;
  554.     TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::Release = %x", _cRef);
  555.     
  556.     if (_cRef > 0)
  557.         return _cRef;
  558.     
  559.     delete this;
  560.     return 0;
  561. }
  562. /*
  563.     Purpose:
  564.         Add verb to extension app list
  565. */
  566. VOID AddVerbItem(IQueryAssociations *pqa, HKEY hkeyClass, LPCTSTR pszExt, LPCTSTR pszVerb)
  567. {
  568.     WCHAR wsz[MAX_PATH];
  569.     WCHAR wszVerb[MAX_PATH];
  570.     // HackHack: we don't want to put msohtmed.exe in openwithlist
  571.     if (pszVerb)
  572.         SHTCharToUnicode(pszVerb, wszVerb, SIZECHARS(wszVerb));
  573.         
  574.     if (SUCCEEDED(pqa->GetString(0, ASSOCSTR_EXECUTABLE, pszVerb ? wszVerb : NULL, wsz,(LPDWORD)MAKEINTRESOURCE(SIZECHARS(wsz))))
  575.     && (StrStrIW(wsz, L"msohtmed")))
  576.         return;
  577.     OpenWithListRegister(0, pszExt, pszVerb, hkeyClass);
  578. }
  579. /*
  580.     Purpose:
  581.         Add Open/Edit/Default verb to extension app list
  582. */
  583. HRESULT AddVerbItems(LPCTSTR pszExt)
  584. {
  585.     IQueryAssociations *pqa;
  586.     HRESULT hr = E_FAIL;
  587.     
  588.     if (SUCCEEDED(AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (LPVOID *)&pqa)))
  589.     {
  590.         HKEY hkeyClass;
  591.         WCHAR wszExt[MAX_PATH];
  592.         SHTCharToUnicode(pszExt, wszExt, SIZECHARS(wszExt));
  593.         
  594.         if (SUCCEEDED(pqa->Init(0, wszExt, NULL, NULL))
  595.         && (SUCCEEDED(pqa->GetKey(0, ASSOCKEY_SHELLEXECCLASS, NULL, &hkeyClass))))
  596.         {
  597.             AddVerbItem(pqa, hkeyClass, pszExt, NULL);
  598.             AddVerbItem(pqa, hkeyClass, pszExt, c_szOpen);
  599.             AddVerbItem(pqa, hkeyClass, pszExt, c_szEdit);
  600.             RegCloseKey(hkeyClass);
  601.             hr = S_OK;
  602.         }
  603.         pqa->Release();
  604.     }
  605.     return hr;
  606. }
  607. HRESULT COpenWithMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  608. {
  609.     MENUITEMINFO mii;
  610.     LPTSTR pszExt;
  611.     TCHAR szOpenWithMenu[80];
  612.     
  613.     _idCmdFirst = idCmdFirst;
  614.     _uFlags = uFlags;
  615.     
  616.     if (SUCCEEDED(PathFromDataObject(_pdtobj, _szPath, ARRAYSIZE(_szPath))))
  617.     {
  618.         // No openwith context menu for executables.
  619.         if (PathIsExe(_szPath))
  620.             return NOERROR;
  621.             
  622.         pszExt = PathFindExtension(_szPath);
  623.         if (pszExt && *pszExt)
  624.         {
  625.             // Add Open/Edit/Default verb to extension app list
  626.             if (SUCCEEDED(AddVerbItems(pszExt)))
  627.             {
  628.                 // Do this only if AddVerbItems succeeded; otherwise,
  629.                 // we would create an empty MRU for a nonexisting class,
  630.                 // causing the class to spring into existence and cause
  631.                 // the "Open With" dialog to think we are overriding
  632.                 // rather than creating new.
  633.                 // get extension app list
  634.                 _nItems = OpenWithListGetList(pszExt, NULL, &_pItems);
  635.             }
  636.         }
  637.     }
  638.     // For known file type(there is at least one verb under its progid), 
  639.     // if there is only one item in its openwithlist, don't show open with sub menu
  640.     if (1 == _nItems)
  641.     {
  642.         TCHAR szExe[MAX_PATH];
  643.         DWORD cch = ARRAYSIZE(szExe);
  644.         if (SUCCEEDED(AssocQueryString(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, pszExt, NULL, szExe, &cch)))
  645.         {
  646.             TCHAR szItem[MAX_PATH];
  647.             cch = ARRAYSIZE(szItem);
  648.             
  649.             if (SUCCEEDED(AssocQueryStringByKey(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, _pItems[0].hKey, _pItems[0].pszVerb, szItem, &cch))
  650.             && 0 == StrCmpI(szExe, szItem))
  651.             {
  652.                 OpenWithListReleaseList(_pItems);
  653.                 _pItems = NULL;
  654.                 _nItems = 0;
  655.             }
  656.         }
  657.     }
  658.     LoadString(g_hinst, (_nItems ? IDS_OPENWITH : IDS_OPENWITHNEW), szOpenWithMenu, ARRAYSIZE(szOpenWithMenu));
  659.     
  660.     if (_nItems)
  661.     {
  662.         _hMenu = CreatePopupMenu();
  663.         
  664.         mii.cbSize = sizeof(MENUITEMINFO);
  665.         mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  666.         mii.wID = idCmdFirst+1;
  667.         mii.fType = MFT_STRING;
  668.         mii.dwTypeData = szOpenWithMenu;
  669.         mii.dwItemData = 0;
  670.     
  671.         InsertMenuItem(_hMenu,0,TRUE,&mii);
  672.     
  673.         mii.fMask = MIIM_ID|MIIM_SUBMENU|MIIM_TYPE;
  674.         mii.fType = MFT_STRING;
  675.         mii.wID = idCmdFirst;
  676.         mii.hSubMenu = _hMenu;
  677.         mii.dwTypeData = szOpenWithMenu;
  678.     
  679.         InsertMenuItem(hmenu,indexMenu,TRUE,&mii);
  680.         return ResultFromShort(_nItems + 2);
  681.     }
  682.     else
  683.     {
  684.         _hMenu = hmenu;
  685.         
  686.         mii.cbSize = sizeof(MENUITEMINFO);
  687.         mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  688.         mii.fType = MFT_STRING;
  689.         mii.wID = idCmdFirst;
  690.         mii.dwTypeData = szOpenWithMenu;
  691.         mii.dwItemData = 0;
  692.         
  693.         InsertMenuItem(hmenu,indexMenu,TRUE,&mii);
  694.         return ResultFromShort(1);
  695.     }
  696. }
  697. HRESULT COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  698. {
  699.     HRESULT hres = E_OUTOFMEMORY;
  700.     CMINVOKECOMMANDINFOEX ici;
  701.     LPVOID pvFree;
  702.     //  maybe these two routines should be collapsed into one?
  703.     if ((IS_INTRESOURCE(pici->lpVerb) || 0 == lstrcmpiA(pici->lpVerb, "openas"))
  704.     && SUCCEEDED(ICI2ICIX(pici, &ici, &pvFree)))
  705.     {
  706.         SHELLEXECUTEINFO ei = {0};
  707.         if (SUCCEEDED(ICIX2SEI(&ici, &ei)))
  708.         {
  709.             POPENWITHITEM pItem;
  710.             ei.lpFile = _szPath;
  711.             if (IS_INTRESOURCE(pici->lpVerb))
  712.                 pItem = GetItemData(_hMenu, LOWORD(pici->lpVerb));
  713.             else
  714.                 pItem = NULL;
  715.             if (pItem)
  716.             {
  717.                 //  if pitem is there, this means that we are using
  718.                 //  something that was in the openwith list MRU.
  719.                 
  720.                 ei.lpVerb = pItem->pszVerb;
  721.                 ei.hkeyClass = pItem->hKey;
  722.                 //  make sure to update the MRU
  723.                 OpenWithListRegister(0, PathFindExtension(_szPath), pItem->pszVerb, pItem->hKey);
  724.             }
  725.             else
  726.             {   
  727.                 // use the "Unknown" key so we get the openwith prompt
  728.                 RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Unknown"), 0L, MAXIMUM_ALLOWED, &ei.hkeyClass);
  729.                 if (!(_uFlags & CMF_DEFAULTONLY))
  730.                 {
  731.                     // defview sets CFM_DEFAULTONLY when the user is double-clicking. We check it
  732.                     // here since we want do NOT want to query the class store if the user explicitly
  733.                     // right-clicked on the menu and choo   se openwith.
  734.                     // pop up open with dialog without querying class store
  735.                     ei.fMask |= SEE_MASK_NOQUERYCLASSSTORE;
  736.                 }
  737.             }
  738.             //  if we got the key then we are good to go!
  739.             if (ei.hkeyClass)
  740.             {
  741.                 ei.fMask |= SEE_MASK_CLASSKEY;
  742. #ifdef WINNT
  743.                 // Shrink the shell since the user is about to run an application.
  744.                 ShrinkWorkingSet();
  745. #endif
  746.                 if (FALSE != ShellExecuteEx(&ei)) 
  747.                 {
  748.                     hres = NOERROR;
  749. #ifdef WINNT
  750.                     // Shrink the shell since the user just ran an application.
  751.                     ShrinkWorkingSet();
  752. #endif
  753.                     if (UEMIsLoaded())
  754.                     {
  755.                         // note that we already got a UIBL_DOTASSOC (from
  756.                         // OpenAs_RunDLL or whatever it is that 'Unknown'
  757.                         // runs).  so the Uassist analysis app will have to
  758.                         // subtract it off
  759.                         UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_RUNASSOC, UIBL_DOTNOASSOC);
  760.                     }
  761.                 }
  762.                 else
  763.                 {
  764.                     hres = E_FAIL;
  765.                 }
  766.                 // Close the Unknown key if we opened it
  767.                 if (!pItem && ei.hkeyClass)
  768.                 {
  769.                     RegCloseKey(ei.hkeyClass);
  770.                 }
  771.             }
  772.         }
  773.         if (pvFree)
  774.             LocalFree(pvFree);
  775.     }        
  776.     return hres;
  777. }
  778. HRESULT COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pRes, LPSTR pszName, UINT cchMax)
  779. {
  780.     return E_NOTIMPL;
  781. }
  782. HRESULT COpenWithMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  783. {
  784.     return HandleMenuMsg2(uMsg,wParam,lParam,NULL);
  785. }
  786. HRESULT COpenWithMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam,LRESULT *lResult)
  787. {
  788.     switch (uMsg)
  789.     {
  790.     case WM_INITMENUPOPUP:
  791.         {
  792.             InitMenuPopup(_hMenu);
  793.         }
  794.         break;
  795.     /*    
  796.     case WM_DRAWITEM:
  797.         {
  798.             DRAWITEMSTRUCT * pdi = (DRAWITEMSTRUCT *)lParam;
  799.             
  800.             DrawItem(pdi);
  801.         }
  802.         break;
  803.         
  804.     case WM_MEASUREITEM:
  805.         {
  806.             MEASUREITEMSTRUCT *pmi = (MEASUREITEMSTRUCT *)lParam;
  807.             
  808.             MeasureItem(pmi);
  809.             
  810.         }
  811.         break;
  812.     case WM_MENUCHAR:
  813.         {
  814.             int c = GetMenuItemCount(_hMenu);
  815.             for (int i = 0; i < c; i++) 
  816.             {
  817.                 LPOPENWITHMENUITEMINFO lpomi = GetItemData(_hMenu, i);
  818.                 if(lpomi && _MenuCharMatch(lpomi->szMenuText,(TCHAR)LOWORD(wParam),FALSE))
  819.                 {
  820.                     _lpomiLast = lpomi;
  821.                     if(lResult) *lResult = MAKELONG(i,MNC_EXECUTE);
  822.                     return S_OK;
  823.                 }
  824.             }
  825.             if(lResult) *lResult = MAKELONG(0,MNC_IGNORE);
  826.             return S_FALSE;
  827.             
  828.         }
  829.         
  830.     */
  831.     }
  832.     
  833.     return NOERROR;
  834. }
  835. HRESULT COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  836. {
  837.     if (_pdtobj)
  838.         _pdtobj->Release();
  839.     
  840.     
  841.     _pidlFolder = pidlFolder;
  842.     _pdtobj = pdtobj;
  843.     
  844.     
  845.     if (_pdtobj)
  846.         _pdtobj->AddRef();
  847.     
  848.     return NOERROR;
  849. }
  850. void COpenWithMenu::DrawItem(DRAWITEMSTRUCT *lpdi)
  851. {
  852. /*
  853.     if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
  854.     {
  855.         DWORD dwRop;
  856.         int x, y;
  857.         SIZE sz;
  858.         LPOPENWITHMENUITEMINFO lpomi = (LPOPENWITHMENUITEMINFO)lpdi->itemData;
  859.         
  860.         // Draw the image (if there is one).
  861.         
  862.         GetTextExtentPoint(lpdi->hDC, lpomi->szMenuText, lstrlen(lpomi->szMenuText), &sz);
  863.         
  864.         if (lpdi->itemState & ODS_SELECTED)
  865.         {
  866.             SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
  867.             SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
  868.             // REVIEW HACK - keep track of the last selected item.
  869.             _lpomiLast = lpomi;
  870.             dwRop = SRCSTENCIL;
  871.             FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_HIGHLIGHT));
  872.         }
  873.         else
  874.         {
  875.             dwRop = SRCAND;
  876.             SetTextColor(lpdi->hDC, GetSysColor(COLOR_MENUTEXT));
  877.             FillRect(lpdi->hDC,&lpdi->rcItem,GetSysColorBrush(COLOR_MENU));
  878.         }
  879.         
  880.         RECT rc = lpdi->rcItem;
  881.         rc.left += +2*CXIMAGEGAP+g_cxSmIcon;
  882.         
  883.         
  884.         DrawText(lpdi->hDC,lpomi->szMenuText,lstrlen(lpomi->szMenuText),
  885.             &rc,DT_SINGLELINE|DT_VCENTER);
  886.         if (lpomi->iImage != -1)
  887.         {
  888.             x = lpdi->rcItem.left+CXIMAGEGAP;
  889.             y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
  890.             ImageList_Draw(g_himlSysSmall, lpomi->iImage, lpdi->hDC, x, y, ILD_TRANSPARENT);
  891.         } 
  892.         else 
  893.         {
  894.             x = lpdi->rcItem.left+CXIMAGEGAP;
  895.             y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
  896.         }
  897.     }
  898. */
  899. }
  900. LRESULT COpenWithMenu::MeasureItem(MEASUREITEMSTRUCT *lpmi)
  901. {
  902.     LRESULT lres = FALSE;
  903. /*    
  904.     LPOPENWITHMENUITEMINFO lpomi = (LPOPENWITHMENUITEMINFO)lpmi->itemData;
  905.     if (lpomi)
  906.     {
  907.         // Get the rough height of an item so we can work out when to break the
  908.         // menu. User should really do this for us but that would be useful.
  909.         HDC hdc = GetDC(NULL);
  910.         if (hdc)
  911.         {
  912.             // REVIEW cache out the menu font?
  913.             NONCLIENTMETRICS ncm;
  914.             ncm.cbSize = SIZEOF(NONCLIENTMETRICS);
  915.             if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE))
  916.             {
  917.                 HFONT hfont = CreateFontIndirect(&ncm.lfMenuFont);
  918.                 if (hfont)
  919.                 {
  920.                     SIZE sz;
  921.                     HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
  922.                     GetTextExtentPoint(hdc, lpomi->szMenuText, lstrlen(lpomi->szMenuText), &sz);
  923.                     lpmi->itemHeight = max (g_cySmIcon+CXIMAGEGAP/2, ncm.iMenuHeight);
  924.                     lpmi->itemWidth = g_cxSmIcon + 2*CXIMAGEGAP + sz.cx;
  925.                     //lpmi->itemWidth = 2*CXIMAGEGAP + sz.cx;
  926.                     SelectObject(hdc, hfontOld);
  927.                     DeleteObject(hfont);
  928.                     lres = TRUE;
  929.                 }
  930.             }
  931.             ReleaseDC(NULL, hdc);
  932.         }
  933.     }
  934.     else
  935.     {
  936.         TraceMsg(TF_OPENWITHMENU, TEXT("fm_mi: Filemenu is invalid."));
  937.     }
  938. */    
  939.     return lres;
  940. }
  941. BOOL COpenWithMenu::InitMenuPopup(HMENU hmenu)
  942. {
  943.     TCHAR szMenuText[80];
  944.     MENUITEMINFO mii;
  945.     
  946.     TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::InitMenuPopup");
  947.     if (!_nItems || !_pItems)
  948.         return FALSE;
  949.     if (GetItemData(hmenu, 0))  // already initialized.
  950.         return FALSE;
  951.     // remove the place holder.
  952.     DeleteMenu(hmenu,0,MF_BYPOSITION);
  953.     // add app's in mru list to context menu
  954.     for (int i = 0; i < _nItems; i++)
  955.     {
  956.         mii.cbSize = sizeof(MENUITEMINFO);
  957.         mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  958.         mii.wID = _idCmdFirst + i;
  959.         mii.fType = MFT_STRING;
  960.         mii.dwTypeData = _pItems[i].pszFriendly;
  961.         mii.dwItemData = (DWORD_PTR)&_pItems[i];
  962.         InsertMenuItem(hmenu,GetMenuItemCount(hmenu),TRUE,&mii);
  963.     }
  964.     // add seperator
  965.     AppendMenu(hmenu,MF_SEPARATOR,0,NULL); 
  966.     // add "&Browse..."
  967.     LoadString(g_hinst, IDS_OPENWITHBROWSE, szMenuText, ARRAYSIZE(szMenuText));
  968.     mii.cbSize = sizeof(MENUITEMINFO);
  969.     mii.fMask = MIIM_ID|MIIM_TYPE|MIIM_DATA;
  970.     mii.wID = _idCmdFirst + _nItems + 1;
  971.     mii.fType = MFT_STRING;
  972.     mii.dwTypeData = szMenuText;
  973.     mii.dwItemData = 0;
  974.     InsertMenuItem(hmenu,GetMenuItemCount(hmenu),TRUE,&mii);
  975.     return TRUE;
  976. }
  977. POPENWITHITEM COpenWithMenu::GetItemData(HMENU hmenu, UINT iItem)
  978. {
  979.     MENUITEMINFO mii;
  980.     
  981.     mii.cbSize = SIZEOF(MENUITEMINFO);
  982.     mii.fMask = MIIM_DATA | MIIM_STATE;
  983.     mii.cch = 0;     // just in case...
  984.     
  985.     if (GetMenuItemInfo(hmenu, iItem, TRUE, &mii))
  986.         return (POPENWITHITEM)mii.dwItemData;
  987.     
  988.     return NULL;
  989. }
  990. HRESULT COpenWithMenu::SetSite(IUnknown* pUnk)
  991. {
  992.     ATOMICRELEASE(_pShellView2);
  993.     TraceMsg(TF_OPENWITHMENU, "COpenWithMenu::SetSite = 0x%x", pUnk);
  994.     
  995.     if(pUnk)
  996.         return pUnk->QueryInterface(IID_IShellView2,(void**)&_pShellView2);
  997.     return NOERROR;
  998. }
  999. HRESULT COpenWithMenu::GetSite(REFIID riid,void** ppvObj)
  1000. {
  1001.     if(_pShellView2)
  1002.         return _pShellView2->QueryInterface(riid,ppvObj);
  1003.     else
  1004.     {
  1005.         ASSERT(ppvObj != NULL);
  1006.         *ppvObj = NULL;
  1007.         return E_NOINTERFACE;
  1008.     }
  1009. }