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.

1045 lines
29 KiB

  1. //
  2. // fassoc.cpp
  3. //
  4. // IQueryAssociations shell implementations
  5. //
  6. // New storage - move this to a simple database if possible
  7. //
  8. // ****************************** User Customizations ********************************
  9. //
  10. // HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts
  11. // |
  12. // |+ ".ext" // the extension that has been customized
  13. // | |- "Application" = "UserNotepad.AnyCo.1"
  14. // | |+ "OpenWithList" // MRU for the Open With ctx menu
  15. // |
  16. // _ ...
  17. //
  18. //
  19. // ****************************** NoRoam Store **************************
  20. //
  21. // HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\NoRoam
  22. // |
  23. // |+ ".ext" (the extension that has been customized)
  24. // | |- Application = "UserNotepad.AnyCo.1"
  25. // |
  26. // _ ...
  27. //
  28. // ***************************** Handlers **************************************
  29. // (store detailed per handler file association info)
  30. //
  31. // HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\NoRoam\Associations
  32. // |
  33. #include "shellprv.h"
  34. #include <shpriv.h>
  35. #include "clsobj.h"
  36. #include <shstr.h>
  37. #include <msi.h>
  38. #include "fassoc.h"
  39. #include <runtask.h>
  40. inline BOOL _PathAppend(LPCTSTR pszBase, LPCTSTR pszAppend, LPTSTR pszOut, DWORD cchOut)
  41. {
  42. return SUCCEEDED(StringCchPrintf(pszOut, cchOut, TEXT("%s\\%s"), pszBase, pszAppend));
  43. }
  44. STDAPI UserAssocSet(UASET set, LPCWSTR pszExt, LPCWSTR pszSet)
  45. {
  46. HKEY hk = SHGetShellKey(SHELLKEY_HKCU_FILEEXTS, pszExt, TRUE);
  47. if (hk)
  48. {
  49. // we should always clear
  50. SHDeleteValue(hk, NULL, L"Application");
  51. SHDeleteValue(hk, NULL, L"Progid");
  52. switch (set)
  53. {
  54. case UASET_APPLICATION:
  55. SHSetValue(hk, NULL, L"Application", REG_SZ, pszSet, CbFromCch(lstrlen(pszSet)+1));
  56. break;
  57. case UASET_PROGID:
  58. SHSetValue(hk, NULL, L"Progid", REG_SZ, pszSet, CbFromCch(lstrlen(pszSet)+1));
  59. break;
  60. }
  61. RegCloseKey(hk);
  62. return S_OK;
  63. }
  64. return HRESULT_FROM_WIN32(GetLastError());
  65. }
  66. void _MakeApplicationsKey(LPCTSTR pszApp, LPTSTR pszKey, DWORD cchKey)
  67. {
  68. if (_PathAppend(TEXT("Applications"), pszApp, pszKey, cchKey))
  69. {
  70. // Currently we will only look up .EXE if an extension is not
  71. // specified
  72. if (*PathFindExtension(pszApp) == 0)
  73. {
  74. StrCatBuff(pszKey, TEXT(".exe"), cchKey);
  75. }
  76. }
  77. }
  78. DWORD _OpenApplicationKey(LPCWSTR pszApp, HKEY *phk, BOOL fCheckCommand = FALSE)
  79. {
  80. // look direct
  81. // then try indirecting
  82. // then try appending .exe
  83. WCHAR sz[MAX_PATH];
  84. _MakeApplicationsKey(pszApp, sz, ARRAYSIZE(sz));
  85. DWORD err = RegOpenKeyEx(HKEY_CLASSES_ROOT, sz, 0, MAXIMUM_ALLOWED, phk);
  86. if (err == ERROR_SUCCESS && fCheckCommand)
  87. {
  88. DWORD cch;
  89. if (ERROR_SUCCESS == SHQueryValueEx(*phk, TEXT("NoOpenWith"), NULL, NULL, NULL, NULL)
  90. || FAILED(AssocQueryStringByKey(0, ASSOCSTR_COMMAND, *phk, NULL, NULL, &cch)))
  91. {
  92. err = ERROR_ACCESS_DENIED;
  93. RegCloseKey(*phk);
  94. *phk = NULL;
  95. }
  96. }
  97. return err;
  98. }
  99. class CVersion
  100. {
  101. public:
  102. CVersion(LPCWSTR psz) : _pVer(0), _hrInit(S_FALSE) { StrCpyNW(_szPath, psz, ARRAYSIZE(_szPath)); }
  103. ~CVersion() { if (_pVer) LocalFree(_pVer); }
  104. private:
  105. WCHAR _szPath[MAX_PATH];
  106. void *_pVer;
  107. HRESULT _hrInit;
  108. };
  109. typedef struct
  110. {
  111. WORD wLanguage;
  112. WORD wCodePage;
  113. } XLATE;
  114. const static XLATE s_px[] =
  115. {
  116. { 0, 0x04B0 }, // MLGetUILanguage, CP_UNICODE
  117. { 0, 0x04E4 }, // MLGetUILanguage, CP_USASCII
  118. { 0, 0x0000 }, // MLGetUILanguage, NULL
  119. { 0x0409, 0x04B0 }, // English, CP_UNICODE
  120. { 0x0409, 0x04E4 }, // English, CP_USASCII
  121. { 0x0409, 0x0000 }, // English, NULL
  122. // { 0x041D, 0x04B0 }, // Swedish, CP_UNICODE
  123. // { 0x0407, 0x04E4 }, // German, CP_USASCII
  124. };
  125. HKEY _OpenSystemFileAssociationsKey(LPCWSTR pszExt)
  126. {
  127. WCHAR sz[MAX_PATH] = L"SystemFileAssociations\\";
  128. StrCatBuff(sz, pszExt, ARRAYSIZE(sz));
  129. HKEY hk = NULL;
  130. if (NOERROR != RegOpenKeyEx(HKEY_CLASSES_ROOT, sz, 0, MAXIMUM_ALLOWED, &hk))
  131. {
  132. DWORD cb = sizeof(sz) - sizeof(L"SystemFileAssociations\\");
  133. if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszExt, L"PerceivedType", NULL, sz+ARRAYSIZE(L"SystemFileAssociations\\")-1, &cb))
  134. {
  135. // if (PerceivedType != System)
  136. RegOpenKeyEx(HKEY_CLASSES_ROOT, sz, 0, MAXIMUM_ALLOWED, &hk);
  137. }
  138. }
  139. return hk;
  140. }
  141. BOOL _IsSystemFileAssociations(LPCWSTR pszExt)
  142. {
  143. HKEY hk = _OpenSystemFileAssociationsKey(pszExt);
  144. if (hk)
  145. RegCloseKey(hk);
  146. return hk != NULL;
  147. }
  148. class CTaskEnumHKCR : public CRunnableTask
  149. {
  150. public:
  151. CTaskEnumHKCR() : CRunnableTask(RTF_DEFAULT) {}
  152. // *** pure virtuals ***
  153. virtual STDMETHODIMP RunInitRT(void);
  154. private:
  155. virtual ~CTaskEnumHKCR() {}
  156. void _AddFromHKCR();
  157. };
  158. void _AddProgidForExt(LPCWSTR pszExt)
  159. {
  160. WCHAR szNew[MAX_PATH];
  161. DWORD cb = sizeof(szNew);
  162. if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, szNew, &cb))
  163. {
  164. WCHAR sz[MAX_PATH];
  165. wnsprintf(sz, ARRAYSIZE(sz), L"%s\\OpenWithProgids", pszExt);
  166. SKSetValue(SHELLKEY_HKCU_FILEEXTS, sz, szNew, REG_NONE, NULL, NULL);
  167. }
  168. }
  169. #define IsExtension(s) (*(s) == TEXT('.'))
  170. void CTaskEnumHKCR::_AddFromHKCR()
  171. {
  172. int i;
  173. TCHAR szClass[MAX_PATH];
  174. BOOL fInExtensions = FALSE;
  175. for (i = 0; RegEnumKey(HKEY_CLASSES_ROOT, i, szClass, ARRAYSIZE(szClass)) == ERROR_SUCCESS; i++)
  176. {
  177. // UNDOCUMENTED feature. the enum is sorted,
  178. // so we can just restrict ourselves to extensions
  179. // for perf and fun!
  180. if (fInExtensions)
  181. {
  182. if (!IsExtension(szClass))
  183. break;
  184. }
  185. else if (IsExtension(szClass))
  186. {
  187. fInExtensions = TRUE;
  188. }
  189. else
  190. continue;
  191. if (_IsSystemFileAssociations(szClass))
  192. {
  193. _AddProgidForExt(szClass);
  194. }
  195. }
  196. }
  197. HRESULT CTaskEnumHKCR::RunInitRT()
  198. {
  199. // delete something??
  200. _AddFromHKCR();
  201. return S_OK;
  202. }
  203. STDAPI CTaskEnumHKCR_Create(IRunnableTask **pptask)
  204. {
  205. CTaskEnumHKCR *pteh = new CTaskEnumHKCR();
  206. if (pteh)
  207. {
  208. HRESULT hr = pteh->QueryInterface(IID_PPV_ARG(IRunnableTask, pptask));
  209. pteh->Release();
  210. return hr;
  211. }
  212. *pptask = NULL;
  213. return E_OUTOFMEMORY;
  214. }
  215. typedef enum
  216. {
  217. AHTYPE_USER_APPLICATION = -2,
  218. AHTYPE_ANY_APPLICATION = -1,
  219. AHTYPE_UNDEFINED = 0,
  220. AHTYPE_CURRENTDEFAULT,
  221. AHTYPE_PROGID,
  222. AHTYPE_APPLICATION,
  223. } AHTYPE;
  224. class CAssocHandler : public IAssocHandler
  225. {
  226. public:
  227. CAssocHandler() : _cRef(1) {}
  228. BOOL Init(AHTYPE type, LPCWSTR pszExt, LPCWSTR pszInit);
  229. // IUnknown methods
  230. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  231. STDMETHODIMP_(ULONG) AddRef(void);
  232. STDMETHODIMP_(ULONG) Release(void);
  233. // IAssocHandler methods
  234. STDMETHODIMP GetName(LPWSTR *ppsz);
  235. STDMETHODIMP GetUIName(LPWSTR *ppsz);
  236. STDMETHODIMP GetIconLocation(LPWSTR *ppszPath, int *pIndex);
  237. STDMETHODIMP IsRecommended() { return _type > AHTYPE_UNDEFINED ? S_OK : S_FALSE; }
  238. STDMETHODIMP MakeDefault(LPCWSTR pszDescription);
  239. STDMETHODIMP Exec(HWND hwnd, LPCWSTR pszFile);
  240. STDMETHODIMP Invoke(void *pici, PCWSTR pszFile);
  241. protected: // methods
  242. ~CAssocHandler();
  243. HRESULT _Exec(SHELLEXECUTEINFO *pei);
  244. BOOL _IsNewAssociation();
  245. void _GenerateAssociateNotify();
  246. HRESULT _InitKey();
  247. void _RegisterOWL();
  248. protected: // members
  249. ULONG _cRef;
  250. IQueryAssociations *_pqa;
  251. HKEY _hk;
  252. ASSOCF _flags;
  253. AHTYPE _type;
  254. LPWSTR _pszExt;
  255. LPWSTR _pszInit;
  256. BOOL _fRegistered;
  257. };
  258. STDAPI CAssocHandler::QueryInterface(REFIID riid, void **ppvObj)
  259. {
  260. static const QITAB qit[] = {
  261. QITABENT(CAssocHandler, IAssocHandler),
  262. };
  263. return QISearch(this, qit, riid, ppvObj);
  264. }
  265. STDAPI_(ULONG) CAssocHandler::AddRef()
  266. {
  267. return ++_cRef;
  268. }
  269. STDAPI_(ULONG) CAssocHandler::Release()
  270. {
  271. if (--_cRef > 0)
  272. return _cRef;
  273. delete this;
  274. return 0;
  275. }
  276. BOOL _InList(LPCWSTR pszList, LPCWSTR pszExt, WORD chDelim)
  277. {
  278. LPCWSTR pszMatch = StrStrI(pszList, pszExt);
  279. while (pszMatch)
  280. {
  281. LPCWSTR pszNext = (pszMatch+lstrlen(pszExt));
  282. if (chDelim == *pszNext || !*pszNext)
  283. return TRUE;
  284. pszMatch = StrStrI(pszNext+1, pszExt);
  285. }
  286. return FALSE;
  287. }
  288. // Create a new class key, and set its shell\open\command
  289. BOOL _CreateApplicationKey(LPCTSTR pszPath)
  290. {
  291. DWORD err = ERROR_FILE_NOT_FOUND;
  292. if (PathFileExistsAndAttributes(pszPath, NULL))
  293. {
  294. WCHAR szKey[MAX_PATH];
  295. WCHAR szCmd[MAX_PATH * 2];
  296. wnsprintf(szKey, ARRAYSIZE(szKey), L"Software\\Classes\\Applications\\%s\\shell\\open\\command", PathFindFileName(pszPath));
  297. // if it is not an LFN app, pass unquoted args.
  298. wnsprintf(szCmd, ARRAYSIZE(szCmd), App_IsLFNAware(pszPath) ? L"\"%s\" \"%%1\"" : L"\"%s\" %%1", pszPath);
  299. err = SHSetValue(HKEY_CURRENT_USER, szKey, NULL, REG_SZ, szCmd, CbFromCchW(lstrlen(szCmd)+1));
  300. }
  301. return ERROR_SUCCESS == err;
  302. }
  303. BOOL CAssocHandler::Init(AHTYPE type, LPCWSTR pszExt, LPCWSTR pszInit)
  304. {
  305. BOOL fRet = FALSE;
  306. _type = type;
  307. _pszExt = StrDup(pszExt);
  308. if (pszInit)
  309. _pszInit = StrDup(PathFindFileName(pszInit));
  310. if (_pszExt && (_pszInit || !pszInit))
  311. {
  312. if (SUCCEEDED(AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &_pqa))))
  313. {
  314. HKEY hk = NULL;
  315. _flags = ASSOCF_IGNOREBASECLASS;
  316. switch (type)
  317. {
  318. case AHTYPE_CURRENTDEFAULT:
  319. _flags |= ASSOCF_NOUSERSETTINGS;
  320. pszInit = pszExt;
  321. break;
  322. case AHTYPE_USER_APPLICATION:
  323. case AHTYPE_APPLICATION:
  324. case AHTYPE_ANY_APPLICATION:
  325. _OpenApplicationKey(_pszInit, &hk, TRUE);
  326. if (hk)
  327. {
  328. if (type == AHTYPE_APPLICATION)
  329. {
  330. // check if this type is supported
  331. HKEY hkTypes;
  332. if (ERROR_SUCCESS == RegOpenKeyEx(hk, TEXT("SupportedTypes"), 0, MAXIMUM_ALLOWED, &hkTypes))
  333. {
  334. // the app only supports specific types
  335. if (ERROR_SUCCESS != SHQueryValueEx(hkTypes, _pszExt, NULL, NULL, NULL, NULL))
  336. {
  337. // this type is not supported
  338. // so it will be relegated to the not recommended list
  339. RegCloseKey(hk);
  340. hk = NULL;
  341. }
  342. RegCloseKey(hkTypes);
  343. }
  344. }
  345. }
  346. else if (type == AHTYPE_USER_APPLICATION)
  347. {
  348. // need to make up a key
  349. if (_CreateApplicationKey(pszInit))
  350. _OpenApplicationKey(_pszInit, &hk);
  351. }
  352. pszInit = NULL;
  353. _flags |= ASSOCF_INIT_BYEXENAME;
  354. break;
  355. case AHTYPE_PROGID:
  356. default:
  357. // _flags |= ...;
  358. break;
  359. }
  360. if (hk || pszInit)
  361. {
  362. if (SUCCEEDED(_pqa->Init(_flags, pszInit , hk, NULL)))
  363. {
  364. WCHAR szExe[MAX_PATH];
  365. DWORD cchExe = ARRAYSIZE(szExe);
  366. // we want to make sure there is something at the other end
  367. fRet = SUCCEEDED(_pqa->GetString(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, NULL, szExe, &cchExe));
  368. // however, if the EXE has been marked as superhidden,
  369. // then the consent decree UI has hidden the app
  370. // and it should not show up under the open with either
  371. if (fRet)
  372. {
  373. fRet = !(IS_SYSTEM_HIDDEN(GetFileAttributes(szExe)));
  374. }
  375. }
  376. }
  377. if (hk)
  378. RegCloseKey(hk);
  379. }
  380. }
  381. return fRet;
  382. }
  383. CAssocHandler::~CAssocHandler()
  384. {
  385. if (_pqa)
  386. _pqa->Release();
  387. if (_pszExt)
  388. LocalFree(_pszExt);
  389. if (_pszInit)
  390. LocalFree(_pszInit);
  391. if (_hk)
  392. RegCloseKey(_hk);
  393. }
  394. HRESULT CAssocHandler::GetName(LPWSTR *ppsz)
  395. {
  396. WCHAR sz[MAX_PATH];
  397. DWORD cch = ARRAYSIZE(sz);
  398. HRESULT hr = _pqa->GetString(_flags | ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, NULL, sz, &cch);
  399. if (SUCCEEDED(hr))
  400. {
  401. hr = SHStrDup(sz, ppsz);
  402. }
  403. return hr;
  404. }
  405. HRESULT CAssocHandler::GetUIName(LPWSTR *ppsz)
  406. {
  407. WCHAR sz[MAX_PATH];
  408. DWORD cch = ARRAYSIZE(sz);
  409. HRESULT hr = _pqa->GetString(_flags | ASSOCF_VERIFY, ASSOCSTR_FRIENDLYAPPNAME, NULL, sz, &cch);
  410. if (SUCCEEDED(hr))
  411. {
  412. hr = SHStrDup(sz, ppsz);
  413. }
  414. return hr;
  415. }
  416. HRESULT CAssocHandler::GetIconLocation(LPWSTR *ppszPath, int *pIndex)
  417. {
  418. // HRESULT hr = _pqa->GetString(0, ASSOCSTR_DEFAULTAPPICON, NULL, psz, &cchT);
  419. // if (FAILED(hr))
  420. WCHAR sz[MAX_PATH];
  421. DWORD cch = ARRAYSIZE(sz);
  422. HRESULT hr = _pqa->GetString(_flags | ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, NULL, sz, &cch);
  423. if (SUCCEEDED(hr))
  424. {
  425. hr = SHStrDup(sz, ppszPath);
  426. if (*ppszPath)
  427. {
  428. *pIndex = PathParseIconLocation(*ppszPath);
  429. }
  430. }
  431. return hr;
  432. }
  433. STDAPI OpenWithListRegister(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszVerb, HKEY hkProgid);
  434. HRESULT CAssocHandler::_InitKey()
  435. {
  436. if (!_hk)
  437. {
  438. return _pqa->GetKey(_flags, ASSOCKEY_SHELLEXECCLASS, NULL, &_hk);
  439. }
  440. return S_OK;
  441. }
  442. void CAssocHandler::_RegisterOWL()
  443. {
  444. if (!_fRegistered && SUCCEEDED(_InitKey()))
  445. {
  446. OpenWithListRegister(0, _pszExt, NULL, _hk);
  447. _fRegistered = TRUE;
  448. }
  449. }
  450. HRESULT CAssocHandler::Exec(HWND hwnd, LPCWSTR pszFile)
  451. {
  452. SHELLEXECUTEINFO ei = {0};
  453. ei.cbSize = sizeof(ei);
  454. ei.hwnd = hwnd;
  455. ei.lpFile = pszFile;
  456. ei.nShow = SW_NORMAL;
  457. return _Exec(&ei);
  458. }
  459. HRESULT CAssocHandler::_Exec(SHELLEXECUTEINFO *pei)
  460. {
  461. HRESULT hr = _InitKey();
  462. if (SUCCEEDED(hr))
  463. {
  464. pei->hkeyClass = _hk;
  465. pei->fMask |= SEE_MASK_CLASSKEY;
  466. if (ShellExecuteEx(pei))
  467. {
  468. _RegisterOWL();
  469. }
  470. else
  471. {
  472. hr = HRESULT_FROM_WIN32(GetLastError());
  473. }
  474. }
  475. return hr;
  476. }
  477. HRESULT CAssocHandler::Invoke(void *pici, PCWSTR pszFile)
  478. {
  479. SHELLEXECUTEINFO ei;
  480. HRESULT hr = ICIX2SEI((CMINVOKECOMMANDINFOEX *)pici, &ei);
  481. ei.lpFile = pszFile;
  482. if (SUCCEEDED(hr))
  483. hr = _Exec(&ei);
  484. return hr;
  485. }
  486. BOOL CAssocHandler::_IsNewAssociation()
  487. {
  488. BOOL fRet = TRUE;
  489. WCHAR szOld[MAX_PATH];
  490. WCHAR szNew[MAX_PATH];
  491. if (SUCCEEDED(AssocQueryString(ASSOCF_VERIFY, ASSOCSTR_EXECUTABLE, _pszExt, NULL, szOld, (LPDWORD)MAKEINTRESOURCE(ARRAYSIZE(szOld))))
  492. && SUCCEEDED(_pqa->GetString(ASSOCF_VERIFY | _flags, ASSOCSTR_EXECUTABLE, NULL, szNew, (LPDWORD)MAKEINTRESOURCE(ARRAYSIZE(szNew))))
  493. && (0 == lstrcmpi(szNew, szOld)))
  494. {
  495. //
  496. // these have the same executable, trust
  497. // that when the exe installed itself, it did
  498. // it correctly, and we dont need to overwrite
  499. // their associations with themselves :)
  500. //
  501. fRet = FALSE;
  502. }
  503. return fRet;
  504. }
  505. //
  506. // This is a real hack, but for now we generate an idlist that looks
  507. // something like: C:\*.ext which is the extension for the IDList.
  508. // We use the simple IDList as to not hit the disk...
  509. //
  510. void CAssocHandler::_GenerateAssociateNotify()
  511. {
  512. TCHAR szFakePath[MAX_PATH];
  513. LPITEMIDLIST pidl;
  514. GetWindowsDirectory(szFakePath, ARRAYSIZE(szFakePath));
  515. szFakePath[3] = L'*';
  516. StrCpyN(szFakePath + 4, _pszExt, ARRAYSIZE(szFakePath) - 4); // "C:\*.foo"
  517. pidl = SHSimpleIDListFromPath(szFakePath);
  518. if (pidl)
  519. {
  520. // Now call off to the notify function.
  521. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, pidl, NULL);
  522. ILFree(pidl);
  523. }
  524. }
  525. // return true if ok to continue
  526. HRESULT CAssocHandler::MakeDefault(LPCWSTR pszDesc)
  527. {
  528. HRESULT hr = E_FAIL;
  529. // if the user is choosing the existing association
  530. // or if we werent able to setup an Application ,
  531. // then we want to leave it alone,
  532. BOOL fForceUserCustomised = (AHTYPE_CURRENTDEFAULT == _type && S_FALSE == _pqa->GetData(0, ASSOCDATA_HASPERUSERASSOC, NULL, NULL, NULL));
  533. if (fForceUserCustomised || _IsNewAssociation())
  534. {
  535. switch (_type)
  536. {
  537. case AHTYPE_CURRENTDEFAULT:
  538. // if it is reverting to the machine default
  539. // then we want to eliminate the user association
  540. if (!fForceUserCustomised || !_pszInit)
  541. {
  542. hr = UserAssocSet(UASET_CLEAR, _pszExt, NULL);
  543. break;
  544. }
  545. // else fall through to AHTYPE_PROGID
  546. // this supports overriding shimgvw's (and others?)
  547. // dynamic contextmenu
  548. case AHTYPE_PROGID:
  549. hr = UserAssocSet(UASET_PROGID, _pszExt, _pszInit);
  550. break;
  551. case AHTYPE_APPLICATION:
  552. case AHTYPE_ANY_APPLICATION:
  553. case AHTYPE_USER_APPLICATION:
  554. // if there is a current association
  555. // then we just customize the user portion
  556. // otherwise we update
  557. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, _pszExt, NULL, NULL, NULL, NULL))
  558. {
  559. // we don't overwrite the existing association under HKCR,
  560. // instead, we put it under HKCU. So now shell knows the new association
  561. // but third party software that mimics shell or does not use ShellExecute
  562. // will still use the old association in HKCR, which may confuse users.
  563. hr = UserAssocSet(UASET_APPLICATION, _pszExt, _pszInit);
  564. }
  565. else
  566. {
  567. if (SUCCEEDED(_InitKey()))
  568. {
  569. // there is no current progid
  570. ASSERT(lstrlen(_pszExt) > 1); // because we always skip the "." below
  571. WCHAR wszProgid[MAX_PATH];
  572. WCHAR szExts[MAX_PATH];
  573. int iLast = StrCatChainW(szExts, ARRAYSIZE(szExts) -1, 0, _pszExt);
  574. // double null term
  575. szExts[++iLast] = 0;
  576. wnsprintfW(wszProgid, ARRAYSIZE(wszProgid), L"%ls_auto_file", _pszExt+1);
  577. HKEY hkDst;
  578. ASSOCPROGID apid = {sizeof(apid), wszProgid, pszDesc, NULL, NULL, szExts};
  579. if (SUCCEEDED(AssocMakeProgid(0, _pszInit, &apid, &hkDst)))
  580. {
  581. hr = AssocCopyVerbs(_hk, hkDst);
  582. RegCloseKey(hkDst);
  583. }
  584. }
  585. }
  586. }
  587. _GenerateAssociateNotify();
  588. _RegisterOWL();
  589. }
  590. // if the application already
  591. // existed, then it will
  592. // return S_FALSE;
  593. return (S_OK == hr);
  594. }
  595. HRESULT _CreateAssocHandler(AHTYPE type, LPCWSTR pszExt, LPCWSTR pszInit, IAssocHandler **ppah)
  596. {
  597. CAssocHandler *pah = new CAssocHandler();
  598. if (pah)
  599. {
  600. if (pah->Init(type, pszExt, pszInit))
  601. {
  602. *ppah = pah;
  603. return S_OK;
  604. }
  605. else
  606. pah->Release();
  607. }
  608. return E_FAIL;
  609. }
  610. STDAPI SHCreateAssocHandler(LPCWSTR pszExt, LPCWSTR pszApp, IAssocHandler **ppah)
  611. {
  612. // path to app/handler
  613. return _CreateAssocHandler(pszApp ? AHTYPE_USER_APPLICATION : AHTYPE_CURRENTDEFAULT, pszExt, pszApp, ppah);
  614. }
  615. #define SZOPENWITHLIST TEXT("OpenWithList")
  616. #define REGSTR_PATH_EXPLORER_FILEEXTS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts")
  617. #define _OpenWithListMaxItems() 10
  618. class CMRUEnumHandlers
  619. {
  620. public:
  621. CMRUEnumHandlers() : _index(0) {}
  622. ~CMRUEnumHandlers() { FreeMRUList(_hmru);}
  623. BOOL Init(LPCWSTR pszExt);
  624. BOOL Next();
  625. LPCWSTR Curr() { return _szHandler;}
  626. protected:
  627. HANDLE _hmru;
  628. int _index;
  629. WCHAR _szHandler[MAX_PATH];
  630. };
  631. BOOL CMRUEnumHandlers::Init(LPCWSTR pszExt)
  632. {
  633. TCHAR szSubKey[MAX_PATH];
  634. // Build up the subkey string.
  635. wnsprintf(szSubKey, SIZECHARS(szSubKey), TEXT("%s\\%s\\%s"), REGSTR_PATH_EXPLORER_FILEEXTS, pszExt, SZOPENWITHLIST);
  636. MRUINFO mi = {sizeof(mi), _OpenWithListMaxItems(), 0, HKEY_CURRENT_USER, szSubKey, NULL};
  637. _hmru = CreateMRUList(&mi);
  638. return (_hmru != NULL);
  639. }
  640. BOOL CMRUEnumHandlers::Next()
  641. {
  642. ASSERT(_hmru);
  643. return (-1 != EnumMRUListW(_hmru, _index++, _szHandler, ARRAYSIZE(_szHandler)));
  644. }
  645. typedef struct OPENWITHLIST
  646. {
  647. HKEY hk;
  648. DWORD dw;
  649. AHTYPE type;
  650. } OWL;
  651. class CEnumHandlers : public IEnumAssocHandlers
  652. {
  653. friend HRESULT SHAssocEnumHandlers(LPCTSTR pszExtra, IEnumAssocHandlers **ppEnumHandler);
  654. public:
  655. // IUnknown methods
  656. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  657. STDMETHODIMP_(ULONG) AddRef(void);
  658. STDMETHODIMP_(ULONG) Release(void);
  659. // IEnumAssocHandlers methods
  660. STDMETHODIMP Next(ULONG celt, IAssocHandler **rgelt, ULONG *pcelt);
  661. protected: // methods
  662. // Constructor & Destructor
  663. CEnumHandlers() : _cRef(1) {}
  664. ~CEnumHandlers();
  665. BOOL Init(LPCWSTR pszExt);
  666. BOOL _NextDefault(IAssocHandler **ppah);
  667. BOOL _NextHandler(HKEY hk, DWORD *pdw, BOOL fOpenWith, IAssocHandler **ppah);
  668. BOOL _NextProgid(HKEY *phk, DWORD *pdw, IAssocHandler **ppah);
  669. BOOL _NextMru(IAssocHandler **ppah);
  670. BOOL _NextOpenWithList(OWL *powl, IAssocHandler **ppah);
  671. protected: // members
  672. int _cRef;
  673. LPWSTR _pszExt;
  674. HKEY _hkProgids;
  675. DWORD _dwProgids;
  676. HKEY _hkUserProgids;
  677. DWORD _dwUserProgids;
  678. CMRUEnumHandlers _mru;
  679. BOOL _fMruReady;
  680. OWL _owlExt;
  681. OWL _owlType;
  682. OWL _owlAny;
  683. BOOL _fCheckedDefault;
  684. };
  685. BOOL CEnumHandlers::Init(LPCWSTR pszExt)
  686. {
  687. _AddProgidForExt(pszExt);
  688. _pszExt = StrDup(pszExt);
  689. if (_pszExt)
  690. {
  691. // known progids
  692. WCHAR szKey[MAX_PATH];
  693. wnsprintf(szKey, ARRAYSIZE(szKey), L"%s\\OpenWithProgids", pszExt);
  694. RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, MAXIMUM_ALLOWED, &_hkProgids);
  695. _hkUserProgids = SHGetShellKey(SHELLKEY_HKCU_FILEEXTS, szKey, FALSE);
  696. // user's MRU
  697. _fMruReady = _mru.Init(pszExt);
  698. // HKCR\.ext\OpenWithList
  699. wnsprintf(szKey, ARRAYSIZE(szKey), L"%s\\OpenWithList", pszExt);
  700. RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, MAXIMUM_ALLOWED, &_owlExt.hk);
  701. _owlExt.type = AHTYPE_APPLICATION;
  702. WCHAR sz[40];
  703. DWORD cb = sizeof(sz);
  704. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, pszExt, L"PerceivedType", NULL, sz, &cb))
  705. {
  706. // HKCR\SystemFileAssociations\type\OpenWithList
  707. wnsprintf(szKey, ARRAYSIZE(szKey), L"SystemFileAssociations\\%s\\OpenWithList", sz);
  708. RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, MAXIMUM_ALLOWED, &_owlType.hk);
  709. }
  710. else
  711. {
  712. ASSERT(_owlType.hk == NULL);
  713. }
  714. _owlType.type = AHTYPE_APPLICATION;
  715. // always append anytype to the end
  716. RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Applications", 0, MAXIMUM_ALLOWED, &_owlAny.hk);
  717. _owlAny.type = AHTYPE_ANY_APPLICATION;
  718. return TRUE;
  719. }
  720. return FALSE;
  721. }
  722. //
  723. // CEnumHandlers implementation
  724. //
  725. CEnumHandlers::~CEnumHandlers()
  726. {
  727. if (_pszExt)
  728. LocalFree(_pszExt);
  729. if (_hkProgids)
  730. RegCloseKey(_hkProgids);
  731. if (_hkUserProgids)
  732. RegCloseKey(_hkUserProgids);
  733. if (_owlExt.hk)
  734. RegCloseKey(_owlExt.hk);
  735. if (_owlType.hk)
  736. RegCloseKey(_owlType.hk);
  737. if (_owlAny.hk)
  738. RegCloseKey(_owlAny.hk);
  739. }
  740. STDAPI CEnumHandlers::QueryInterface(REFIID riid, void **ppvObj)
  741. {
  742. static const QITAB qit[] = {
  743. QITABENT(CEnumHandlers, IEnumAssocHandlers),
  744. };
  745. return QISearch(this, qit, riid, ppvObj);
  746. }
  747. STDAPI_(ULONG) CEnumHandlers::AddRef()
  748. {
  749. return ++_cRef;
  750. }
  751. STDAPI_(ULONG) CEnumHandlers::Release()
  752. {
  753. if (--_cRef > 0)
  754. return _cRef;
  755. delete this;
  756. return 0;
  757. }
  758. BOOL CEnumHandlers::_NextDefault(IAssocHandler **ppah)
  759. {
  760. BOOL fRet = FALSE;
  761. if (!_fCheckedDefault && _pszExt)
  762. {
  763. WCHAR sz[MAX_PATH];
  764. DWORD cb = sizeof(sz);
  765. // pass the progid if we have it
  766. if (ERROR_SUCCESS != SHGetValue(HKEY_CLASSES_ROOT, _pszExt, NULL, NULL, sz, &cb))
  767. *sz = 0;
  768. fRet = SUCCEEDED(_CreateAssocHandler(AHTYPE_CURRENTDEFAULT, _pszExt, *sz ? sz : NULL, ppah));
  769. _fCheckedDefault = TRUE;
  770. }
  771. return fRet;
  772. }
  773. BOOL CEnumHandlers::_NextProgid(HKEY *phk, DWORD *pdw, IAssocHandler **ppah)
  774. {
  775. BOOL fRet = FALSE;
  776. while (*phk && !fRet)
  777. {
  778. TCHAR szProgid[MAX_PATH];
  779. DWORD cchProgid = ARRAYSIZE(szProgid);
  780. DWORD err = RegEnumValue(*phk, *pdw, szProgid, &cchProgid, NULL, NULL, NULL, NULL);
  781. if (ERROR_SUCCESS == err)
  782. {
  783. fRet = SUCCEEDED(_CreateAssocHandler(AHTYPE_PROGID, _pszExt, szProgid, ppah));
  784. (*pdw)++;
  785. }
  786. else
  787. {
  788. RegCloseKey(*phk);
  789. *phk = NULL;
  790. }
  791. }
  792. return fRet;
  793. }
  794. BOOL CEnumHandlers::_NextMru(IAssocHandler **ppah)
  795. {
  796. BOOL fRet = FALSE;
  797. while (_fMruReady && !fRet)
  798. {
  799. if (_mru.Next())
  800. {
  801. fRet = SUCCEEDED(_CreateAssocHandler(AHTYPE_APPLICATION, _pszExt, _mru.Curr(), ppah));
  802. }
  803. else
  804. {
  805. _fMruReady = FALSE;
  806. }
  807. }
  808. return fRet;
  809. }
  810. BOOL CEnumHandlers::_NextOpenWithList(OWL *powl, IAssocHandler **ppah)
  811. {
  812. BOOL fRet = FALSE;
  813. while (powl->hk && !fRet)
  814. {
  815. TCHAR szHandler[MAX_PATH];
  816. DWORD cchHandler = ARRAYSIZE(szHandler);
  817. DWORD err = RegEnumKeyEx(powl->hk, powl->dw, szHandler, &cchHandler, NULL, NULL, NULL, NULL);
  818. if (err == ERROR_SUCCESS)
  819. {
  820. (powl->dw)++;
  821. fRet = SUCCEEDED(_CreateAssocHandler(powl->type, _pszExt, szHandler, ppah));
  822. }
  823. else
  824. {
  825. RegCloseKey(powl->hk);
  826. powl->hk = NULL;
  827. }
  828. }
  829. return fRet;
  830. }
  831. STDAPI CEnumHandlers::Next(ULONG celt, IAssocHandler **rgelt, ULONG *pcelt)
  832. {
  833. UINT cNum = 0;
  834. ZeroMemory(rgelt, sizeof(rgelt[0])*celt);
  835. while (cNum < celt && _NextDefault(&rgelt[cNum]))
  836. {
  837. cNum++;
  838. }
  839. while (cNum < celt && _NextProgid(&_hkProgids, &_dwProgids, &rgelt[cNum]))
  840. {
  841. cNum++;
  842. }
  843. while (cNum < celt && _NextProgid(&_hkUserProgids, &_dwUserProgids, &rgelt[cNum]))
  844. {
  845. cNum++;
  846. }
  847. while (cNum < celt && _NextMru(&rgelt[cNum]))
  848. {
  849. cNum++;
  850. }
  851. while (cNum < celt && _NextOpenWithList(&_owlExt, &rgelt[cNum]))
  852. {
  853. cNum++;
  854. }
  855. while (cNum < celt && _NextOpenWithList(&_owlType, &rgelt[cNum]))
  856. {
  857. cNum++;
  858. }
  859. while (cNum < celt && _NextOpenWithList(&_owlAny, &rgelt[cNum]))
  860. {
  861. cNum++;
  862. }
  863. if (pcelt)
  864. *pcelt = cNum;
  865. return (0 < cNum) ? S_OK: S_FALSE;
  866. }
  867. //
  868. // pszExtra: NULL - enumerate all handlers
  869. // .xxx - enumerate handlers by file extension (we might internally map to content type)
  870. // Others - not currently supported
  871. //
  872. STDAPI SHAssocEnumHandlers(LPCTSTR pszExt, IEnumAssocHandlers **ppenum)
  873. {
  874. HRESULT hr = E_OUTOFMEMORY;
  875. CEnumHandlers *penum = new CEnumHandlers();
  876. *ppenum = NULL;
  877. if (penum)
  878. {
  879. if (penum->Init(pszExt))
  880. {
  881. *ppenum = penum;
  882. hr = S_OK;
  883. }
  884. else
  885. penum->Release();
  886. }
  887. return hr;
  888. }
  889. STDAPI_(BOOL) IsPathInOpenWithKillList(LPCTSTR pszPath)
  890. {
  891. // return TRUE for invalid path
  892. if (!pszPath || !*pszPath)
  893. return TRUE;
  894. // get file name
  895. BOOL fRet = FALSE;
  896. LPCTSTR pchFile = PathFindFileName(pszPath);
  897. HKEY hkey;
  898. // maybe should use full path for better resolution
  899. if (ERROR_SUCCESS == _OpenApplicationKey(pchFile, &hkey))
  900. {
  901. // just check for the existence of the value....
  902. if (ERROR_SUCCESS == SHQueryValueEx(hkey, TEXT("NoOpenWith"), NULL, NULL, NULL, NULL))
  903. {
  904. fRet = TRUE;
  905. }
  906. RegCloseKey(hkey);
  907. }
  908. LPWSTR pszKillList;
  909. if (!fRet && SUCCEEDED(SKAllocValue(SHELLKEY_HKLM_EXPLORER, L"FileAssociation", TEXT("KillList"), NULL, (void **)&pszKillList, NULL)))
  910. {
  911. fRet = _InList(pszKillList, pchFile, L';');
  912. LocalFree(pszKillList);
  913. }
  914. return fRet;
  915. }