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.

1835 lines
51 KiB

  1. //
  2. //
  3. // assocapi.cpp
  4. //
  5. // Association APIs
  6. //
  7. //
  8. //
  9. #include "priv.h"
  10. #include "apithk.h"
  11. #include <shstr.h>
  12. #include <w95wraps.h>
  13. #include <msi.h>
  14. #include "ids.h"
  15. #include "assoc.h"
  16. #define ISEXTENSION(psz) (TEXT('.') == *(psz))
  17. #define ISGUID(psz) (TEXT('{') == *(psz))
  18. inline BOOL IsEmptyStr(SHSTR &str)
  19. {
  20. return (!*(LPCTSTR)str);
  21. }
  22. HRESULT _AssocGetRegString(HKEY hk, LPCTSTR pszSub, LPCTSTR pszVal, SHSTR &strOut)
  23. {
  24. if (!hk)
  25. {
  26. return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  27. }
  28. DWORD cbOut = CbFromCch(strOut.GetSize());
  29. DWORD err = SHGetValue(hk, pszSub, pszVal, NULL, strOut.GetInplaceStr(), &cbOut);
  30. if (err == ERROR_SUCCESS)
  31. return S_OK;
  32. // else try to resize the buffer
  33. if (cbOut > CbFromCch(strOut.GetSize()))
  34. {
  35. strOut.SetSize(cbOut / sizeof(TCHAR));
  36. err = SHGetValue(hk, pszSub, pszVal, NULL, strOut.GetInplaceStr(), &cbOut);
  37. }
  38. return HRESULT_FROM_WIN32(err);
  39. }
  40. HRESULT _AssocGetRegUIString(HKEY hk, LPCTSTR pszSub, LPCTSTR pszVal, SHSTR &strOut)
  41. {
  42. if (!hk)
  43. return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  44. HKEY hkSub;
  45. DWORD err;
  46. HRESULT hres;
  47. err = RegOpenKeyEx(hk, pszSub, 0, MAXIMUM_ALLOWED, &hkSub);
  48. if (err == ERROR_SUCCESS)
  49. {
  50. // Unfortunately, SHLoadRegUIString doesn't have a way to query the
  51. // buffer size, so we just have to assume INFOTIPSIZE is good enough.
  52. LPTSTR pszOut = strOut.GetModifyableStr(INFOTIPSIZE);
  53. if (pszOut == NULL)
  54. pszOut = strOut.GetInplaceStr();
  55. hres = SHLoadRegUIString(hkSub, pszVal, pszOut, strOut.GetSize());
  56. RegCloseKey(hkSub);
  57. }
  58. else
  59. {
  60. hres = HRESULT_FROM_WIN32(err);
  61. }
  62. return hres;
  63. }
  64. HRESULT _AssocGetRegData(HKEY hk, LPCTSTR pszSubKey, LPCTSTR pszValue, LPDWORD pdwType, LPBYTE pbOut, LPDWORD pcbOut)
  65. {
  66. if (!hk)
  67. return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  68. DWORD err;
  69. if (pszSubKey || pbOut || pcbOut || pdwType)
  70. err = SHGetValue(hk, pszSubKey, pszValue, pdwType, pbOut, pcbOut);
  71. else
  72. err = RegQueryValueEx(hk, pszValue, NULL, NULL, NULL, NULL);
  73. return HRESULT_FROM_WIN32(err);
  74. }
  75. BOOL _GetAppPath(LPCTSTR pszApp, SHSTR& strPath)
  76. {
  77. TCHAR sz[MAX_PATH];
  78. _MakeAppPathKey(pszApp, sz, SIZECHARS(sz));
  79. return SUCCEEDED(_AssocGetRegString(HKEY_LOCAL_MACHINE, sz, NULL, strPath));
  80. }
  81. //
  82. // THE NEW WAY!
  83. //
  84. class CAssocW2k : public IQueryAssociations
  85. {
  86. public:
  87. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  88. STDMETHODIMP_(ULONG) AddRef () ;
  89. STDMETHODIMP_(ULONG) Release ();
  90. // IQueryAssociations methods
  91. STDMETHODIMP Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd);
  92. STDMETHODIMP GetString(ASSOCF flags, ASSOCSTR str, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut);
  93. STDMETHODIMP GetKey(ASSOCF flags, ASSOCKEY, LPCWSTR pszExtra, HKEY *phkeyOut);
  94. STDMETHODIMP GetData(ASSOCF flags, ASSOCDATA data, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut);
  95. STDMETHODIMP GetEnum(ASSOCF flags, ASSOCENUM assocenum, LPCWSTR pszExtra, REFIID riid, LPVOID *ppvOut);
  96. CAssocW2k();
  97. protected:
  98. virtual ~CAssocW2k();
  99. // static methods
  100. static HRESULT _CopyOut(BOOL fNoTruncate, SHSTR& str, LPTSTR psz, DWORD *pcch);
  101. static void _DefaultShellVerb(HKEY hk, LPTSTR pszVerb, DWORD cchVerb, HKEY *phkOut);
  102. typedef enum {
  103. KEYCACHE_INVALID = 0,
  104. KEYCACHE_HKCU = 1,
  105. KEYCACHE_HKLM,
  106. KEYCACHE_APP,
  107. KEYCACHE_FIXED,
  108. PSZCACHE_BASE,
  109. PSZCACHE_HKCU = PSZCACHE_BASE + KEYCACHE_HKCU,
  110. PSZCACHE_HKLM,
  111. PSZCACHE_APP,
  112. PSZCACHE_FIXED,
  113. } KEYCACHETYPE;
  114. typedef struct {
  115. LPTSTR pszCache;
  116. HKEY hkCache;
  117. LPTSTR psz;
  118. KEYCACHETYPE type;
  119. } KEYCACHE;
  120. static BOOL _CanUseCache(KEYCACHE &kc, LPCTSTR psz, KEYCACHETYPE type);
  121. static void _CacheFree(KEYCACHE &kc);
  122. static void _CacheKey(KEYCACHE &kc, HKEY hkCache, LPCTSTR pszName, KEYCACHETYPE type);
  123. static void _CacheString(KEYCACHE &kc, LPCTSTR pszCache, LPCTSTR pszName, KEYCACHETYPE type);
  124. void _Reset(void);
  125. BOOL _UseBaseClass(void);
  126. //
  127. // retrieve the appropriate cached keys
  128. //
  129. HKEY _RootKey(BOOL fForceLM);
  130. HKEY _AppKey(LPCTSTR pszApp, BOOL fCreate = FALSE);
  131. HKEY _ExtensionKey(BOOL fForceLM);
  132. HKEY _OpenProgidKey(LPCTSTR pszProgid);
  133. HKEY _ProgidKey(BOOL fDefaultToExtension);
  134. HKEY _UserProgidKey(void);
  135. HKEY _ClassKey(BOOL fForceLM);
  136. HKEY _ShellVerbKey(HKEY hkey, KEYCACHETYPE type, LPCTSTR pszVerb);
  137. HKEY _ShellVerbKey(BOOL fForceLM, LPCTSTR pszVerb);
  138. HKEY _ShellNewKey(HKEY hkExt);
  139. HKEY _ShellNewKey(BOOL fForceLM);
  140. HKEY _DDEKey(BOOL fForceLM, LPCTSTR pszVerb);
  141. //
  142. // actual worker routines
  143. //
  144. HRESULT _GetCommandString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut);
  145. HRESULT _ParseCommand(ASSOCF flags, LPCTSTR pszCommand, SHSTR& strExe, PSHSTR pstrArgs);
  146. HRESULT _GetExeString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut);
  147. HRESULT _GetFriendlyAppByVerb(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut);
  148. HRESULT _GetFriendlyAppByApp(LPCTSTR pszApp, ASSOCF flags, SHSTR& strOut);
  149. HRESULT _GetFriendlyAppString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut);
  150. HRESULT _GetTipString(LPCWSTR pwszValueName, BOOL fForceLM, SHSTR& strOut);
  151. HRESULT _GetInfoTipString(BOOL fForceLM, SHSTR& strOut);
  152. HRESULT _GetQuickTipString(BOOL fForceLM, SHSTR& strOut);
  153. HRESULT _GetTileInfoString(BOOL fForceLM, SHSTR& strOut);
  154. HRESULT _GetWebViewDisplayPropsString(BOOL fForceLM, SHSTR& strOut);
  155. HRESULT _GetShellNewValueString(BOOL fForceLM, BOOL fQueryOnly, LPCTSTR pszValue, SHSTR& strOut);
  156. HRESULT _GetDDEApplication(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut);
  157. HRESULT _GetDDETopic(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut);
  158. HRESULT _GetContentType(SHSTR& strOut);
  159. HRESULT _GetMSIDescriptor(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, LPBYTE pbOut, LPDWORD pcbOut);
  160. HRESULT _GetShellExecKey(ASSOCF flags, BOOL fForceLM, LPCWSTR pszVerb, HKEY *phkey);
  161. HRESULT _CloneKey(HKEY hk, HKEY *phkey);
  162. HRESULT _GetShellExtension(ASSOCF flags, BOOL fForceLM, LPCTSTR pszShellEx, SHSTR& strOut);
  163. HRESULT _GetFriendlyDocName(SHSTR& strOut);
  164. //
  165. // Members
  166. //
  167. LONG _cRef;
  168. TCHAR _szInit[MAX_PATH];
  169. ASSOCF _assocfBaseClass;
  170. HWND _hwndInit;
  171. BITBOOL _fInited:1;
  172. BITBOOL _fAppOnly:1;
  173. BITBOOL _fAppPath:1;
  174. BITBOOL _fInitedBaseClass:1;
  175. BITBOOL _fIsClsid:1;
  176. BITBOOL _fNoRemapClsid:1;
  177. BITBOOL _fBaseClassOnly:1;
  178. HKEY _hkFileExtsCU;
  179. HKEY _hkExtensionCU;
  180. HKEY _hkExtensionLM;
  181. KEYCACHE _kcProgid;
  182. KEYCACHE _kcShellVerb;
  183. KEYCACHE _kcApp;
  184. KEYCACHE _kcCommand;
  185. KEYCACHE _kcExecutable;
  186. KEYCACHE _kcShellNew;
  187. KEYCACHE _kcDDE;
  188. IQueryAssociations *_pqaBaseClass;
  189. };
  190. CAssocW2k::CAssocW2k() : _cRef(1)
  191. {
  192. };
  193. HRESULT CAssocW2k::Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd)
  194. {
  195. //
  196. // pszAssoc can be:
  197. // .Ext // Detectable
  198. // {Clsid} // Detectable
  199. // Progid // Ambiguous
  200. // Default!
  201. // ExeName // Ambiguous
  202. // Requires ASSOCF_OPEN_BYEXENAME
  203. // MimeType // Ambiguous
  204. // NOT IMPLEMENTED...
  205. //
  206. if (!pszAssoc && !hkProgid)
  207. return E_INVALIDARG;
  208. HKEY hk = NULL;
  209. if (_fInited)
  210. _Reset();
  211. if (pszAssoc)
  212. {
  213. _fAppOnly = BOOLIFY(flags & ASSOCF_OPEN_BYEXENAME);
  214. if (StrChr(pszAssoc, TEXT('\\')))
  215. {
  216. // this is a path
  217. if (_fAppOnly)
  218. _fAppPath = TRUE;
  219. else
  220. {
  221. // we need the extension
  222. pszAssoc = PathFindExtension(pszAssoc);
  223. if (!*pszAssoc)
  224. pszAssoc = NULL;
  225. }
  226. }
  227. if (pszAssoc && *pszAssoc)
  228. {
  229. if (ISGUID(pszAssoc))
  230. {
  231. _PathAppend(TEXT("CLSID"), pszAssoc, _szInit, SIZECHARS(_szInit));
  232. _fIsClsid = TRUE;
  233. // for legacy reasons we dont always
  234. // want to remap the clsid.
  235. if (flags & ASSOCF_INIT_NOREMAPCLSID)
  236. _fNoRemapClsid = TRUE;
  237. }
  238. else
  239. {
  240. StrCpyN(_szInit , pszAssoc, SIZECHARS(_szInit));
  241. // if we initializing to folder dont default to folder.
  242. if (0 == StrCmpI(_szInit, TEXT("Folder")))
  243. flags &= ~ASSOCF_INIT_DEFAULTTOFOLDER;
  244. }
  245. hk = _ClassKey(FALSE);
  246. }
  247. else if (flags & ASSOCF_INIT_DEFAULTTOSTAR)
  248. {
  249. // this is a file without an extension
  250. // but we still allow file association on HKCR\.
  251. _szInit[0] = '.';
  252. _szInit[1] = 0;
  253. hk = _ClassKey(FALSE);
  254. }
  255. }
  256. else
  257. {
  258. ASSERT(hkProgid);
  259. hk = SHRegDuplicateHKey(hkProgid);
  260. if (hk)
  261. _CacheKey(_kcProgid, hk, NULL, KEYCACHE_FIXED);
  262. }
  263. _assocfBaseClass = (flags & (ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_DEFAULTTOSTAR));
  264. //
  265. // NOTE - we can actually do some work even if
  266. // we were unable to create the applications
  267. // key. so we want to succeed in the case
  268. // of an app only association.
  269. //
  270. if (hk || _fAppOnly)
  271. {
  272. _fInited = TRUE;
  273. return S_OK;
  274. }
  275. else if (_UseBaseClass())
  276. {
  277. _fBaseClassOnly = TRUE;
  278. _fInited = TRUE;
  279. return S_OK;
  280. }
  281. return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  282. }
  283. CAssocW2k::~CAssocW2k()
  284. {
  285. _Reset();
  286. }
  287. #define REGFREE(hk) if (hk) { RegCloseKey(hk); (hk) = NULL; } else { }
  288. void CAssocW2k::_Reset(void)
  289. {
  290. _CacheFree(_kcProgid);
  291. _CacheFree(_kcApp);
  292. _CacheFree(_kcShellVerb);
  293. _CacheFree(_kcCommand);
  294. _CacheFree(_kcExecutable);
  295. _CacheFree(_kcShellNew);
  296. _CacheFree(_kcDDE);
  297. REGFREE(_hkFileExtsCU);
  298. REGFREE(_hkExtensionCU);
  299. REGFREE(_hkExtensionLM);
  300. *_szInit = 0;
  301. _assocfBaseClass = 0;
  302. _hwndInit = NULL;
  303. _fInited = FALSE;
  304. _fAppOnly = FALSE;
  305. _fAppPath = FALSE;
  306. _fInitedBaseClass = FALSE;
  307. _fIsClsid = FALSE;
  308. _fBaseClassOnly = FALSE;
  309. ATOMICRELEASE(_pqaBaseClass);
  310. }
  311. STDMETHODIMP CAssocW2k::QueryInterface(REFIID riid, void **ppv)
  312. {
  313. static const QITAB qit[] =
  314. {
  315. QITABENT(CAssocW2k, IQueryAssociations),
  316. { 0 },
  317. };
  318. return QISearch(this, qit, riid, ppv);
  319. }
  320. STDMETHODIMP_(ULONG) CAssocW2k::AddRef()
  321. {
  322. return InterlockedIncrement(&_cRef);
  323. }
  324. STDMETHODIMP_(ULONG) CAssocW2k::Release()
  325. {
  326. if (InterlockedDecrement(&_cRef))
  327. return _cRef;
  328. delete this;
  329. return 0;
  330. }
  331. BOOL CAssocW2k::_UseBaseClass(void)
  332. {
  333. if (!_pqaBaseClass && !_fInitedBaseClass)
  334. {
  335. // try to init the base class
  336. IQueryAssociations *pqa;
  337. AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  338. if (pqa)
  339. {
  340. SHSTR strBase;
  341. if (_fInited && SUCCEEDED(_AssocGetRegString(_ClassKey(TRUE), NULL, TEXT("BaseClass"), strBase)))
  342. {
  343. if (SUCCEEDED(pqa->Init(_assocfBaseClass, strBase, NULL, _hwndInit)))
  344. _pqaBaseClass = pqa;
  345. }
  346. if (!_pqaBaseClass)
  347. {
  348. if ((_assocfBaseClass & ASSOCF_INIT_DEFAULTTOFOLDER)
  349. && (SUCCEEDED(pqa->Init(0, L"Folder", NULL, _hwndInit))))
  350. _pqaBaseClass = pqa;
  351. else if ((_assocfBaseClass & ASSOCF_INIT_DEFAULTTOSTAR)
  352. && (SUCCEEDED(pqa->Init(0, L"*", NULL, _hwndInit))))
  353. _pqaBaseClass = pqa;
  354. }
  355. // if we couldnt init the BaseClass, then kill the pqa
  356. if (!_pqaBaseClass)
  357. pqa->Release();
  358. }
  359. _fInitedBaseClass = TRUE;
  360. }
  361. return (_pqaBaseClass != NULL);
  362. }
  363. HRESULT CAssocW2k::_CopyOut(BOOL fNoTruncate, SHSTR& str, LPTSTR psz, DWORD *pcch)
  364. {
  365. // if caller doesnt want any return size,
  366. // the incoming pointer is actually the size of the buffer
  367. ASSERT(pcch);
  368. ASSERT(psz || !IS_INTRESOURCE(pcch));
  369. HRESULT hr;
  370. DWORD cch = IS_INTRESOURCE(pcch) ? PtrToUlong(pcch) : *pcch;
  371. DWORD cchStr = str.GetLen();
  372. if (psz)
  373. {
  374. if (!fNoTruncate || cch > cchStr)
  375. {
  376. StrCpyN(psz, str, cch);
  377. hr = S_OK;
  378. }
  379. else
  380. hr = E_POINTER;
  381. }
  382. else
  383. hr = S_FALSE;
  384. // return the number of chars written/required
  385. if (!IS_INTRESOURCE(pcch))
  386. *pcch = (hr == S_OK) ? lstrlen(psz) + 1 : cchStr + 1;
  387. return hr;
  388. }
  389. BOOL CAssocW2k::_CanUseCache(KEYCACHE &kc, LPCTSTR psz, KEYCACHETYPE type)
  390. {
  391. if (KEYCACHE_FIXED == kc.type)
  392. return TRUE;
  393. if (KEYCACHE_INVALID != kc.type && type == kc.type)
  394. {
  395. return ((!psz && !kc.psz)
  396. || (psz && kc.psz && 0 == StrCmpC(psz, kc.psz)));
  397. }
  398. return FALSE;
  399. }
  400. void CAssocW2k::_CacheFree(KEYCACHE &kc)
  401. {
  402. if (kc.pszCache)
  403. LocalFree(kc.pszCache);
  404. if (kc.hkCache)
  405. RegCloseKey(kc.hkCache);
  406. if (kc.psz)
  407. LocalFree(kc.psz);
  408. ZeroMemory(&kc, sizeof(kc));
  409. }
  410. void CAssocW2k::_CacheKey(KEYCACHE &kc, HKEY hkCache, LPCTSTR pszName, KEYCACHETYPE type)
  411. {
  412. _CacheFree(kc);
  413. ASSERT(hkCache);
  414. kc.hkCache = hkCache;
  415. if (pszName)
  416. kc.psz = StrDup(pszName);
  417. if (!pszName || kc.psz)
  418. kc.type = type;
  419. }
  420. void CAssocW2k::_CacheString(KEYCACHE &kc, LPCTSTR pszCache, LPCTSTR pszName, KEYCACHETYPE type)
  421. {
  422. _CacheFree(kc);
  423. ASSERT(pszCache && *pszCache);
  424. kc.pszCache = StrDup(pszCache);
  425. if (kc.pszCache)
  426. {
  427. if (pszName)
  428. kc.psz = StrDup(pszName);
  429. if (!pszName || kc.psz)
  430. kc.type = type;
  431. }
  432. }
  433. void CAssocW2k::_DefaultShellVerb(HKEY hk, LPTSTR pszVerb, DWORD cchVerb, HKEY *phkOut)
  434. {
  435. // default to "open"
  436. BOOL fDefaultSpecified = FALSE;
  437. TCHAR sz[MAX_PATH];
  438. DWORD cb = sizeof(sz);
  439. *phkOut = NULL;
  440. // see if something is specified...
  441. if (ERROR_SUCCESS == SHGetValue(hk, TEXT("shell"), NULL, NULL, (LPVOID)sz, &cb) && *sz)
  442. fDefaultSpecified = TRUE;
  443. else
  444. StrCpy(sz, TEXT("open"));
  445. HKEY hkShell;
  446. if (SUCCEEDED(_AssocOpenRegKey(hk, TEXT("shell"), &hkShell)))
  447. {
  448. HKEY hkVerb;
  449. if (FAILED(_AssocOpenRegKey(hkShell, sz, &hkVerb)))
  450. {
  451. if (fDefaultSpecified)
  452. {
  453. // try to find one of the ordered verbs
  454. int c = StrCSpn(sz, TEXT(" ,"));
  455. sz[c] = 0;
  456. _AssocOpenRegKey(hkShell, sz, &hkVerb);
  457. }
  458. else
  459. {
  460. // otherwise just use the first key we find....
  461. cb = SIZECHARS(sz);
  462. if (ERROR_SUCCESS == RegEnumKeyEx(hkShell, 0, sz, &cb, NULL, NULL, NULL, NULL))
  463. _AssocOpenRegKey(hkShell, sz, &hkVerb);
  464. }
  465. }
  466. if (hkVerb)
  467. {
  468. if (phkOut)
  469. *phkOut = hkVerb;
  470. else
  471. RegCloseKey(hkVerb);
  472. }
  473. RegCloseKey(hkShell);
  474. }
  475. if (pszVerb)
  476. StrCpyN(pszVerb, sz, cchVerb);
  477. }
  478. HKEY CAssocW2k::_RootKey(BOOL fForceLM)
  479. {
  480. //
  481. // this is one of the few places where there is no fallback to LM
  482. // if there is no CU, then we return NULL
  483. // we need to use a local for CU, but we can use a global for LM
  484. //
  485. if (!fForceLM)
  486. {
  487. if (!_hkFileExtsCU)
  488. {
  489. _AssocOpenRegKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts"), &_hkFileExtsCU);
  490. }
  491. return _hkFileExtsCU;
  492. }
  493. return HKEY_CLASSES_ROOT;
  494. }
  495. HKEY CAssocW2k::_AppKey(LPCTSTR pszApp, BOOL fCreate)
  496. {
  497. // right now we should only get NULL for the pszApp
  498. // when we are initialized with an EXE.
  499. ASSERT(_fAppOnly || pszApp);
  500. // else if (!_fAppOnly) TODO: handle getting it from default verb...or not
  501. if (!pszApp)
  502. pszApp = _fAppPath ? PathFindFileName(_szInit) : _szInit;
  503. if (_CanUseCache(_kcApp, pszApp, KEYCACHE_APP))
  504. return _kcApp.hkCache;
  505. else
  506. {
  507. HKEY hk;
  508. TCHAR sz[MAX_PATH];
  509. _MakeApplicationsKey(pszApp, sz, SIZECHARS(sz));
  510. _AssocOpenRegKey(HKEY_CLASSES_ROOT, sz, &hk, fCreate);
  511. if (hk)
  512. {
  513. _CacheKey(_kcApp, hk, pszApp, KEYCACHE_APP);
  514. }
  515. return hk;
  516. }
  517. }
  518. HKEY CAssocW2k::_ExtensionKey(BOOL fForceLM)
  519. {
  520. if (_fAppOnly)
  521. return _AppKey(NULL);
  522. if (!ISEXTENSION(_szInit) && !_fIsClsid)
  523. return NULL;
  524. if (!fForceLM)
  525. {
  526. if (!_hkExtensionCU)
  527. _AssocOpenRegKey(_RootKey(FALSE), _szInit, &_hkExtensionCU);
  528. // NOTE there is no fallback here
  529. return _hkExtensionCU;
  530. }
  531. if (!_hkExtensionLM)
  532. _AssocOpenRegKey(_RootKey(TRUE), _szInit, &_hkExtensionLM);
  533. return _hkExtensionLM;
  534. }
  535. HKEY CAssocW2k::_OpenProgidKey(LPCTSTR pszProgid)
  536. {
  537. HKEY hkOut;
  538. if (SUCCEEDED(_AssocOpenRegKey(_RootKey(TRUE), pszProgid, &hkOut)))
  539. {
  540. // Check for a newer version of the ProgID
  541. TCHAR sz[MAX_PATH];
  542. DWORD cb = sizeof(sz);
  543. //
  544. // APPCOMPAT LEGACY - Quattro Pro 2000 and Excel 2000 dont get along - ZekeL - 7-MAR-2000
  545. // mill bug #129525. the problem is if Quattro is installed
  546. // first, then excel picks up quattro's CurVer key for some
  547. // reason. then we end up using Quattro.Worksheet as the current
  548. // version of the Excel.Sheet. this is bug in both of their code.
  549. // since quattro cant even open the file when we give it to them,
  550. // they never should take the assoc in the first place, and when excel
  551. // takes over it shouldnt have preserved the CurVer key from the
  552. // previous association. we could add some code to insure that the
  553. // CurVer key follows the OLE progid naming conventions and that it must
  554. // be derived from the same app name as the progid in order to take
  555. // precedence but for now we will block CurVer from working whenever
  556. // the progid is excel.sheet.8 (excel 2000)
  557. //
  558. if (StrCmpI(TEXT("Excel.Sheet.8"), pszProgid)
  559. && ERROR_SUCCESS == SHGetValue(hkOut, TEXT("CurVer"), NULL, NULL, sz, &cb)
  560. && (cb > sizeof(TCHAR)))
  561. {
  562. // cache this bubby
  563. HKEY hkTemp = hkOut;
  564. if (SUCCEEDED(_AssocOpenRegKey(_RootKey(TRUE), sz, &hkOut)))
  565. {
  566. //
  567. // APPCOMPAT LEGACY - order of preference - ZekeL - 22-JUL-99
  568. // this is to support associations that installed empty curver
  569. // keys, like microsoft project.
  570. //
  571. // 1. curver with shell subkey
  572. // 2. progid with shell subkey
  573. // 3. curver without shell subkey
  574. // 4. progid without shell subkey
  575. //
  576. HKEY hkShell;
  577. if (SUCCEEDED(_AssocOpenRegKey(hkOut, TEXT("shell"), &hkShell)))
  578. {
  579. RegCloseKey(hkShell);
  580. RegCloseKey(hkTemp); // close old ProgID key
  581. }
  582. else if (SUCCEEDED(_AssocOpenRegKey(hkTemp, TEXT("shell"), &hkShell)))
  583. {
  584. RegCloseKey(hkShell);
  585. RegCloseKey(hkOut);
  586. hkOut = hkTemp;
  587. }
  588. else
  589. RegCloseKey(hkTemp);
  590. }
  591. else // reset!
  592. hkOut = hkTemp;
  593. }
  594. }
  595. return hkOut;
  596. }
  597. // we only need to build this once, so build it for
  598. // the lowest common denominator...
  599. HKEY CAssocW2k::_ProgidKey(BOOL fDefaultToExtension)
  600. {
  601. HKEY hkExt = _ExtensionKey(TRUE);
  602. TCHAR sz[MAX_PATH];
  603. ULONG cb = sizeof(sz);
  604. LPCTSTR psz;
  605. HKEY hkRet = NULL;
  606. if (!hkExt && !ISEXTENSION(_szInit))
  607. {
  608. psz = _szInit;
  609. }
  610. else if (!_fNoRemapClsid && hkExt && (ERROR_SUCCESS == SHGetValue(hkExt, _fIsClsid ? TEXT("ProgID") : NULL, NULL, NULL, sz, &cb))
  611. && (cb > sizeof(TCHAR)))
  612. {
  613. psz = sz;
  614. }
  615. else
  616. psz = NULL;
  617. if (psz && *psz)
  618. {
  619. hkRet = _OpenProgidKey(psz);
  620. }
  621. if (!hkRet && fDefaultToExtension && hkExt)
  622. hkRet = SHRegDuplicateHKey(hkExt);
  623. return hkRet;
  624. }
  625. HKEY CAssocW2k::_UserProgidKey(void)
  626. {
  627. SHSTR strApp;
  628. if (SUCCEEDED(_AssocGetRegString(_ExtensionKey(FALSE), NULL, TEXT("Application"), strApp)))
  629. {
  630. HKEY hkRet = _AppKey(strApp);
  631. if (hkRet)
  632. return SHRegDuplicateHKey(hkRet);
  633. }
  634. return NULL;
  635. }
  636. HKEY CAssocW2k::_ClassKey(BOOL fForceLM)
  637. {
  638. // REARCHITECT - we are not supporting clsids correctly here.
  639. HKEY hkRet = NULL;
  640. if (_fAppOnly)
  641. return _AppKey(NULL);
  642. else
  643. {
  644. KEYCACHETYPE type;
  645. if (!fForceLM)
  646. type = KEYCACHE_HKCU;
  647. else
  648. type = KEYCACHE_HKLM;
  649. if (_CanUseCache(_kcProgid, NULL, type))
  650. hkRet = _kcProgid.hkCache;
  651. else
  652. {
  653. if (!fForceLM)
  654. hkRet = _UserProgidKey();
  655. if (!hkRet)
  656. hkRet = _ProgidKey(TRUE);
  657. // cache the value off
  658. if (hkRet)
  659. _CacheKey(_kcProgid, hkRet, NULL, type);
  660. }
  661. }
  662. return hkRet;
  663. }
  664. HKEY CAssocW2k::_ShellVerbKey(HKEY hkey, KEYCACHETYPE type, LPCTSTR pszVerb)
  665. {
  666. HKEY hkRet = NULL;
  667. // check our cache
  668. if (_CanUseCache(_kcShellVerb, pszVerb, type))
  669. hkRet = _kcShellVerb.hkCache;
  670. else if (hkey)
  671. {
  672. // NO cache hit
  673. if (!pszVerb)
  674. _DefaultShellVerb(hkey, NULL, 0, &hkRet);
  675. else
  676. {
  677. TCHAR szKey[MAX_PATH];
  678. _PathAppend(TEXT("shell"), pszVerb, szKey, SIZECHARS(szKey));
  679. _AssocOpenRegKey(hkey, szKey, &hkRet);
  680. }
  681. // only replace the cache if we got something
  682. if (hkRet)
  683. _CacheKey(_kcShellVerb, hkRet, pszVerb, type);
  684. }
  685. return hkRet;
  686. }
  687. HKEY CAssocW2k::_ShellVerbKey(BOOL fForceLM, LPCTSTR pszVerb)
  688. {
  689. HKEY hk = NULL;
  690. if (!fForceLM)
  691. {
  692. hk = _ShellVerbKey(_ClassKey(FALSE), KEYCACHE_HKCU, pszVerb);
  693. if (!hk && _szInit[0]) // szInit[0] = NULL, if Iqa is inited by key.
  694. hk = _ShellVerbKey(_ExtensionKey(FALSE), KEYCACHE_HKCU, pszVerb);
  695. }
  696. if (!hk)
  697. {
  698. KEYCACHETYPE type = (_fAppOnly) ? KEYCACHE_APP : KEYCACHE_HKLM;
  699. hk = _ShellVerbKey(_ClassKey(TRUE), type, pszVerb);
  700. if (!hk && _szInit[0]) // szInit[0] = NULL, if Iqa is inited by key.
  701. hk = _ShellVerbKey(_ExtensionKey(TRUE), type, pszVerb);
  702. }
  703. return hk;
  704. }
  705. HKEY CAssocW2k::_ShellNewKey(HKEY hkExt)
  706. {
  707. //
  708. // shellnew keys look like this
  709. // \.ext
  710. // @ = "progid"
  711. // \progid
  712. // \shellnew
  713. // -- OR --
  714. // \.ext
  715. // \shellnew
  716. //
  717. HKEY hk = NULL;
  718. SHSTR strProgid;
  719. if (SUCCEEDED(_AssocGetRegString(hkExt, NULL, NULL, strProgid)))
  720. {
  721. strProgid.Append(TEXT("\\shellnew"));
  722. _AssocOpenRegKey(hkExt, TEXT("shellnew"), &hk);
  723. }
  724. if (!hk)
  725. _AssocOpenRegKey(hkExt, TEXT("shellnew"), &hk);
  726. return hk;
  727. }
  728. HKEY CAssocW2k::_ShellNewKey(BOOL fForceLM)
  729. {
  730. ASSERT(!_fAppOnly);
  731. if (_CanUseCache(_kcShellNew, NULL, KEYCACHE_HKLM))
  732. return _kcShellNew.hkCache;
  733. HKEY hk = _ShellNewKey(_ExtensionKey(TRUE));
  734. if (hk)
  735. _CacheKey(_kcShellNew, hk, NULL, KEYCACHE_HKLM);
  736. return hk;
  737. }
  738. HRESULT CAssocW2k::_GetCommandString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut)
  739. {
  740. HRESULT hr = E_INVALIDARG;
  741. KEYCACHETYPE type;
  742. if (!fForceLM)
  743. type = PSZCACHE_HKCU;
  744. else if (_fAppOnly)
  745. type = PSZCACHE_APP;
  746. else
  747. type = PSZCACHE_HKLM;
  748. if (pszVerb && !*pszVerb)
  749. pszVerb = NULL;
  750. if (_CanUseCache(_kcCommand, pszVerb, type))
  751. {
  752. hr = strOut.SetStr(_kcCommand.pszCache);
  753. }
  754. if (FAILED(hr))
  755. {
  756. hr = _AssocGetRegString(_ShellVerbKey(fForceLM, pszVerb), TEXT("command"), NULL, strOut);
  757. if (SUCCEEDED(hr))
  758. {
  759. _CacheString(_kcCommand, strOut, pszVerb, type);
  760. }
  761. }
  762. return hr;
  763. }
  764. BOOL _PathIsFile(LPCTSTR pszPath)
  765. {
  766. DWORD attrs = GetFileAttributes(pszPath);
  767. return ((DWORD)-1 != attrs && !(attrs & FILE_ATTRIBUTE_DIRECTORY));
  768. }
  769. HRESULT CAssocW2k::_ParseCommand(ASSOCF flags, LPCTSTR pszCommand, SHSTR& strExe, PSHSTR pstrArgs)
  770. {
  771. // we just need to find where the params begin, and the exe ends...
  772. LPTSTR pch = PathGetArgs(pszCommand);
  773. if (*pch)
  774. *(--pch) = TEXT('\0');
  775. else
  776. pch = NULL;
  777. HRESULT hr = strExe.SetStr(pszCommand);
  778. // to prevent brace proliferation
  779. if (S_OK != hr)
  780. goto quit;
  781. strExe.Trim();
  782. PathUnquoteSpaces(strExe.GetInplaceStr());
  783. //
  784. // WARNING: Expensive disk hits all over!
  785. //
  786. // We check for %1 since it is what appears under (for example) HKCR\exefile\shell\open\command
  787. // This will save us a chain of 35 calls to _PathIsFile("%1") when launching or getting a
  788. // context menu on a shortcut to an .exe or .bat file.
  789. if ((ASSOCF_VERIFY & flags)
  790. && (0 != StrCmp(strExe, TEXT("%1")))
  791. && (!_PathIsFile(strExe)) )
  792. {
  793. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  794. if (PathIsFileSpec(strExe))
  795. {
  796. if (_GetAppPath(strExe, strExe))
  797. {
  798. if (_PathIsFile(strExe))
  799. hr = S_OK;
  800. }
  801. else
  802. {
  803. LPTSTR pszTemp = strExe.GetModifyableStr(MAX_PATH);
  804. if (pszTemp == NULL)
  805. hr = E_OUTOFMEMORY;
  806. else
  807. {
  808. if (PathFindOnPathEx(pszTemp, NULL, PFOPEX_DEFAULT | PFOPEX_OPTIONAL))
  809. {
  810. // the find does a disk check for us...
  811. hr = S_OK;
  812. }
  813. }
  814. }
  815. }
  816. else
  817. {
  818. //
  819. // sometimes the path is not properly quoted.
  820. // these keys will still work because of the
  821. // way CreateProcess works, but we need to do
  822. // some fiddling to figure that out.
  823. //
  824. // if we found args, put them back...
  825. // and try some different args
  826. while (pch)
  827. {
  828. *pch++ = TEXT(' ');
  829. if (pch = StrChr(pch, TEXT(' ')))
  830. *pch = TEXT('\0');
  831. if (S_OK == strExe.SetStr(pszCommand))
  832. {
  833. strExe.Trim();
  834. if (_PathIsFile(strExe))
  835. {
  836. hr = S_FALSE;
  837. // this means that we found something
  838. // but the command line was kinda screwed
  839. break;
  840. }
  841. // this is where we loop again
  842. }
  843. else
  844. {
  845. hr = E_OUTOFMEMORY;
  846. break;
  847. }
  848. }// while (pch)
  849. }
  850. }
  851. if (SUCCEEDED(hr) && pch)
  852. {
  853. // currently right before the args, on a NULL terminator
  854. ASSERT(!*pch);
  855. pch++;
  856. if ((ASSOCF_REMAPRUNDLL & flags)
  857. && 0 == StrCmpNIW(PathFindFileName(strExe), TEXT("rundll"), SIZECHARS(TEXT("rundll")) -1))
  858. {
  859. LPTSTR pchComma = StrChr(pch, TEXT(','));
  860. // make the comma the beginning of the args
  861. if (pchComma)
  862. *pchComma = TEXT('\0');
  863. if (!*(PathFindExtension(pch))
  864. && lstrlen(++pchComma) > SIZECHARS(TEXT(".dll")))
  865. StrCat(pch, TEXT(".dll"));
  866. // recurse :P
  867. hr = _ParseCommand(flags, pch, strExe, pstrArgs);
  868. }
  869. // set the args if we got'em
  870. else if (pstrArgs)
  871. pstrArgs->SetStr(pch);
  872. }
  873. quit:
  874. return hr;
  875. }
  876. HRESULT CAssocW2k::_GetFriendlyDocName(SHSTR& strOut)
  877. {
  878. HRESULT hr = E_FAIL;
  879. HKEY hkProgId = _ProgidKey(_fIsClsid);
  880. if (hkProgId)
  881. {
  882. // first try the MUI friendly version of the string
  883. // if that fails fall back to the default value of the progid.
  884. hr = _AssocGetRegUIString(hkProgId, NULL, L"FriendlyTypeName", strOut);
  885. if (FAILED(hr))
  886. hr = _AssocGetRegString(hkProgId, NULL, NULL, strOut);
  887. RegCloseKey(hkProgId);
  888. }
  889. if (FAILED(hr) || IsEmptyStr(strOut))
  890. {
  891. hr = E_FAIL;
  892. if (!_fIsClsid)
  893. {
  894. // fallback code
  895. TCHAR szDesc[MAX_PATH];
  896. *szDesc = 0;
  897. if (_assocfBaseClass & ASSOCF_INIT_DEFAULTTOFOLDER || 0 == StrCmpIW(L"Folder", _szInit))
  898. {
  899. // load the folder description "Folder"
  900. LoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, szDesc, ARRAYSIZE(szDesc));
  901. }
  902. else if (ISEXTENSION(_szInit) && _szInit[1])
  903. {
  904. TCHAR szTemplate[128]; // "%s File"
  905. CharUpper(_szInit);
  906. LoadString(HINST_THISDLL, IDS_EXTTYPETEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
  907. wnsprintf(szDesc, ARRAYSIZE(szDesc), szTemplate, _szInit + 1);
  908. }
  909. else if (_assocfBaseClass & ASSOCF_INIT_DEFAULTTOSTAR)
  910. {
  911. // load the file description "File"
  912. LoadString(HINST_THISDLL, IDS_FILETYPENAME, szDesc, ARRAYSIZE(szDesc));
  913. }
  914. else if (_szInit[0])
  915. {
  916. StrCpyN(szDesc, _szInit, ARRAYSIZE(szDesc));
  917. CharUpper(szDesc);
  918. }
  919. if (*szDesc)
  920. hr = strOut.SetStr(szDesc);
  921. }
  922. }
  923. return hr;
  924. }
  925. HRESULT CAssocW2k::_GetExeString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut)
  926. {
  927. HRESULT hr = E_FAIL;
  928. KEYCACHETYPE type;
  929. if (!fForceLM)
  930. type = PSZCACHE_HKCU;
  931. else if (_fAppOnly)
  932. type = PSZCACHE_APP;
  933. else
  934. type = PSZCACHE_HKLM;
  935. if (_CanUseCache(_kcExecutable, pszVerb, type))
  936. hr = strOut.SetStr(_kcExecutable.pszCache);
  937. if (FAILED(hr))
  938. {
  939. SHSTR strCommand;
  940. hr = _GetCommandString(flags, fForceLM, pszVerb, strCommand);
  941. if (S_OK == hr)
  942. {
  943. SHSTR strArgs;
  944. strCommand.Trim();
  945. hr = _ParseCommand(flags | ASSOCF_REMAPRUNDLL, strCommand, strOut, &strArgs);
  946. if (S_FALSE == hr)
  947. {
  948. hr = S_OK;
  949. // if (!ASSOCF_NOFIXUPS & flags)
  950. // AssocSetCommandByKey(ASSOCF_SET_SUBSTENV, hkey, pszVerb, strExe.GetStr(), strArgs.GetStr());
  951. }
  952. }
  953. if (SUCCEEDED(hr))
  954. _CacheString(_kcExecutable, strOut, pszVerb, type);
  955. }
  956. return hr;
  957. }
  958. HRESULT _AssocGetDarwinProductString(LPCTSTR pszDarwinCommand, SHSTR& strOut)
  959. {
  960. DWORD cch = strOut.GetSize();
  961. UINT err = MsiGetProductInfo(pszDarwinCommand, INSTALLPROPERTY_PRODUCTNAME, strOut.GetInplaceStr(), &cch);
  962. if (err == ERROR_MORE_DATA && cch > strOut.GetSize())
  963. {
  964. if (SUCCEEDED(strOut.SetSize(cch)))
  965. err = MsiGetProductInfo(pszDarwinCommand, INSTALLPROPERTY_PRODUCTNAME, strOut.GetInplaceStr(), &cch);
  966. else
  967. return E_OUTOFMEMORY;
  968. }
  969. if (err)
  970. return HRESULT_FROM_WIN32(err);
  971. return ERROR_SUCCESS;
  972. }
  973. HRESULT CAssocW2k::_GetFriendlyAppByVerb(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut)
  974. {
  975. if (pszVerb && !*pszVerb)
  976. pszVerb = NULL;
  977. HKEY hk = _ShellVerbKey(fForceLM, pszVerb);
  978. if (hk)
  979. {
  980. HRESULT hr = _AssocGetRegUIString(hk, NULL, TEXT("FriendlyAppName"), strOut);
  981. if (FAILED(hr))
  982. {
  983. SHSTR str;
  984. // check the appkey, for this executeables friendly
  985. // name. this should be the most common case
  986. hr = _GetExeString(flags, fForceLM, pszVerb, str);
  987. if (SUCCEEDED(hr))
  988. {
  989. hr = _GetFriendlyAppByApp(str, flags, strOut);
  990. }
  991. // if the EXE isnt on the System, then Darwin might
  992. // be able to tell us something about it...
  993. if (FAILED(hr))
  994. {
  995. hr = _AssocGetRegString(hk, TEXT("command"), TEXT("command"), str);
  996. if (SUCCEEDED(hr))
  997. {
  998. hr = _AssocGetDarwinProductString(str, strOut);
  999. }
  1000. }
  1001. }
  1002. return hr;
  1003. }
  1004. return E_FAIL;
  1005. }
  1006. HRESULT _GetFriendlyAppByCache(HKEY hkApp, LPCTSTR pszApp, BOOL fVerifyCache, BOOL fNoFixUps, SHSTR& strOut)
  1007. {
  1008. HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1009. if (pszApp)
  1010. {
  1011. FILETIME ftCurr;
  1012. if (MyGetLastWriteTime(pszApp, &ftCurr))
  1013. {
  1014. if (fVerifyCache)
  1015. {
  1016. FILETIME ftCache = {0};
  1017. DWORD cbCache = sizeof(ftCache);
  1018. SHGetValue(hkApp, TEXT("shell"), TEXT("FriendlyCacheCTime"), NULL, &ftCache, &cbCache);
  1019. if (0 == CompareFileTime(&ftCurr, &ftCache))
  1020. hr = S_OK;
  1021. }
  1022. if (FAILED(hr))
  1023. {
  1024. // need to get this from the file itself
  1025. LPTSTR pszOut = strOut.GetModifyableStr(MAX_PATH); // How big is big enough?
  1026. UINT cch = strOut.GetSize();
  1027. if (pszOut == NULL)
  1028. pszOut = strOut.GetInplaceStr();
  1029. if (SHGetFileDescription(pszApp, NULL, NULL, pszOut, &cch))
  1030. hr = S_OK;
  1031. if (SUCCEEDED(hr) && !(fNoFixUps))
  1032. {
  1033. SHSetValue(hkApp, TEXT("shell"), TEXT("FriendlyCache"), REG_SZ, strOut, CbFromCch(strOut.GetLen() +1));
  1034. SHSetValue(hkApp, TEXT("shell"), TEXT("FriendlyCacheCTime"), REG_BINARY, &ftCurr, sizeof(ftCurr));
  1035. }
  1036. }
  1037. }
  1038. }
  1039. return hr;
  1040. }
  1041. HRESULT CAssocW2k::_GetFriendlyAppByApp(LPCTSTR pszApp, ASSOCF flags, SHSTR& strOut)
  1042. {
  1043. HKEY hk = _AppKey(pszApp ? PathFindFileName(pszApp) : NULL, TRUE);
  1044. HRESULT hr = _AssocGetRegUIString(hk, NULL, TEXT("FriendlyAppName"), strOut);
  1045. ASSERT(hk == _kcApp.hkCache);
  1046. if (FAILED(hr))
  1047. {
  1048. // we have now tried the default
  1049. // we need to try our private cache
  1050. hr = _AssocGetRegUIString(hk, TEXT("shell"), TEXT("FriendlyCache"), strOut);
  1051. if (flags & ASSOCF_VERIFY)
  1052. {
  1053. SHSTR strExe;
  1054. if (!pszApp)
  1055. {
  1056. ASSERT(_fAppOnly);
  1057. if (_fAppPath)
  1058. {
  1059. pszApp = _szInit;
  1060. }
  1061. else if (SUCCEEDED(_GetExeString(flags, FALSE, NULL, strExe)))
  1062. {
  1063. pszApp = strExe;
  1064. }
  1065. }
  1066. hr = _GetFriendlyAppByCache(hk, pszApp, SUCCEEDED(hr), (flags & ASSOCF_NOFIXUPS), strOut);
  1067. }
  1068. }
  1069. return hr;
  1070. }
  1071. HRESULT CAssocW2k::_GetFriendlyAppString(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut)
  1072. {
  1073. // Algorithm:
  1074. // if there is a named value "friendly", return its value;
  1075. // if it is a darwin app, return darwin product name;
  1076. // if there is app key, return its named value "friendly"
  1077. // o/w, get friendly name from the exe, and cache it in its app key
  1078. // check the verb first. that overrides the
  1079. // general exe case
  1080. HRESULT hr;
  1081. if (_fAppOnly)
  1082. {
  1083. hr = _GetFriendlyAppByApp(NULL, flags, strOut);
  1084. }
  1085. else
  1086. {
  1087. hr = _GetFriendlyAppByVerb(flags, fForceLM, pszVerb, strOut);
  1088. }
  1089. return hr;
  1090. }
  1091. HRESULT CAssocW2k::_GetShellExtension(ASSOCF flags, BOOL fForceLM, LPCTSTR pszShellEx, SHSTR& strOut)
  1092. {
  1093. HRESULT hr = E_FAIL;
  1094. if (pszShellEx && *pszShellEx)
  1095. {
  1096. // Try to get the extension handler under ProgID first.
  1097. HKEY hk = _ClassKey(fForceLM);
  1098. TCHAR szHandler[140] = TEXT("ShellEx\\");
  1099. StrCatBuff(szHandler, pszShellEx, ARRAYSIZE(szHandler));
  1100. if (hk)
  1101. {
  1102. hr = _AssocGetRegString(hk, szHandler, NULL, strOut);
  1103. }
  1104. // Else try to get the extension handler under file extension.
  1105. if (FAILED(hr) && _szInit[0]) // szInit[0] = NULL, if Iqa is inited by key.
  1106. {
  1107. // reuse hk here
  1108. hk = _ExtensionKey(fForceLM);
  1109. if (hk)
  1110. {
  1111. hr = _AssocGetRegString(hk, szHandler, NULL, strOut);
  1112. }
  1113. }
  1114. }
  1115. return hr;
  1116. }
  1117. HRESULT CAssocW2k::_GetTipString(LPCWSTR pwszValueName, BOOL fForceLM, SHSTR& strOut)
  1118. {
  1119. HRESULT hr = _AssocGetRegUIString(_ClassKey(fForceLM), NULL, pwszValueName, strOut);
  1120. if (FAILED(hr))
  1121. hr = _AssocGetRegUIString(_ExtensionKey(fForceLM), NULL, pwszValueName, strOut);
  1122. if (FAILED(hr) && !fForceLM)
  1123. hr = _AssocGetRegUIString(_ExtensionKey(TRUE), NULL, pwszValueName, strOut);
  1124. return hr;
  1125. }
  1126. HRESULT CAssocW2k::_GetInfoTipString(BOOL fForceLM, SHSTR& strOut)
  1127. {
  1128. return _GetTipString(L"InfoTip", fForceLM, strOut);
  1129. }
  1130. HRESULT CAssocW2k::_GetQuickTipString(BOOL fForceLM, SHSTR& strOut)
  1131. {
  1132. return _GetTipString(L"QuickTip", fForceLM, strOut);
  1133. }
  1134. HRESULT CAssocW2k::_GetTileInfoString(BOOL fForceLM, SHSTR& strOut)
  1135. {
  1136. return _GetTipString(L"TileInfo", fForceLM, strOut);
  1137. }
  1138. HRESULT CAssocW2k::_GetWebViewDisplayPropsString(BOOL fForceLM, SHSTR& strOut)
  1139. {
  1140. return _GetTipString(L"WebViewDisplayProperties", fForceLM, strOut);
  1141. }
  1142. HRESULT CAssocW2k::_GetShellNewValueString(BOOL fForceLM, BOOL fQueryOnly, LPCTSTR pszValue, SHSTR& strOut)
  1143. {
  1144. HRESULT hr = E_FAIL;
  1145. HKEY hk = _ShellNewKey(fForceLM);
  1146. if (hk)
  1147. {
  1148. TCHAR sz[MAX_PATH];
  1149. if (!pszValue)
  1150. {
  1151. // get the default value....
  1152. DWORD cch = SIZECHARS(sz);
  1153. // we want a pszValue....
  1154. if (ERROR_SUCCESS == RegEnumValue(hk, 0, sz, &cch, NULL, NULL, NULL, NULL))
  1155. pszValue = sz;
  1156. }
  1157. hr = _AssocGetRegString(hk, NULL, pszValue, strOut);
  1158. }
  1159. return hr;
  1160. }
  1161. HKEY CAssocW2k::_DDEKey(BOOL fForceLM, LPCTSTR pszVerb)
  1162. {
  1163. HKEY hkRet = NULL;
  1164. KEYCACHETYPE type;
  1165. if (!fForceLM)
  1166. {
  1167. type = KEYCACHE_HKCU;
  1168. }
  1169. else
  1170. {
  1171. type = KEYCACHE_HKLM;
  1172. }
  1173. if (_CanUseCache(_kcDDE, pszVerb, type))
  1174. {
  1175. hkRet = _kcDDE.hkCache;
  1176. }
  1177. else
  1178. {
  1179. if (SUCCEEDED(_AssocOpenRegKey(_ShellVerbKey(fForceLM, pszVerb), TEXT("ddeexec"), &hkRet)))
  1180. {
  1181. _CacheKey(_kcDDE, hkRet, pszVerb, type);
  1182. }
  1183. }
  1184. return hkRet;
  1185. }
  1186. HRESULT CAssocW2k::_GetDDEApplication(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut)
  1187. {
  1188. HRESULT hr = E_FAIL;
  1189. HKEY hk = _DDEKey(fForceLM, pszVerb);
  1190. if (hk)
  1191. {
  1192. hr = _AssocGetRegString(hk, TEXT("Application"), NULL, strOut);
  1193. if (FAILED(hr) || IsEmptyStr(strOut))
  1194. {
  1195. hr = E_FAIL;
  1196. // this means we should figure it out
  1197. if (SUCCEEDED(_GetExeString(flags, fForceLM, pszVerb, strOut)))
  1198. {
  1199. PathRemoveExtension(strOut.GetInplaceStr());
  1200. PathStripPath(strOut.GetInplaceStr());
  1201. if (!IsEmptyStr(strOut))
  1202. {
  1203. // we have a useful app name
  1204. hr = S_OK;
  1205. if (!(flags & ASSOCF_NOFIXUPS))
  1206. {
  1207. // lets put it back!
  1208. SHSetValue(_DDEKey(fForceLM, pszVerb), TEXT("Application"), NULL, REG_SZ, strOut.GetStr(), CbFromCch(strOut.GetLen() +1));
  1209. }
  1210. }
  1211. }
  1212. }
  1213. }
  1214. return hr;
  1215. }
  1216. HRESULT CAssocW2k::_GetDDETopic(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, SHSTR& strOut)
  1217. {
  1218. HRESULT hr = E_FAIL;
  1219. HKEY hk = _DDEKey(fForceLM, pszVerb);
  1220. if (hk)
  1221. {
  1222. hr = _AssocGetRegString(hk, TEXT("Topic"), NULL, strOut);
  1223. if (FAILED(hr) || IsEmptyStr(strOut))
  1224. hr = strOut.SetStr(TEXT("System"));
  1225. }
  1226. return hr;
  1227. }
  1228. HRESULT CAssocW2k::_GetContentType(SHSTR& strOut)
  1229. {
  1230. HRESULT hr = E_FAIL;
  1231. if (ISEXTENSION(_szInit))
  1232. {
  1233. HKEY hk = _ExtensionKey(TRUE);
  1234. if (hk)
  1235. {
  1236. hr = _AssocGetRegString(hk, NULL, TEXT("Content Type"), strOut);
  1237. }
  1238. }
  1239. return hr;
  1240. }
  1241. HRESULT CAssocW2k::GetString(ASSOCF flags, ASSOCSTR str, LPCTSTR pszExtra, LPTSTR pszOut, DWORD *pcchOut)
  1242. {
  1243. RIP(_fInited);
  1244. if (!_fInited)
  1245. return E_UNEXPECTED;
  1246. HRESULT hr = E_INVALIDARG;
  1247. SHSTR strOut;
  1248. if (str && str < ASSOCSTR_MAX && pcchOut && (pszOut || !IS_INTRESOURCE(pcchOut)))
  1249. {
  1250. BOOL fForceLM = (_fAppOnly) || (flags & ASSOCF_NOUSERSETTINGS);
  1251. if (!_fBaseClassOnly || ASSOCSTR_FRIENDLYDOCNAME == str)
  1252. {
  1253. switch(str)
  1254. {
  1255. case ASSOCSTR_COMMAND:
  1256. hr = _GetCommandString(flags, fForceLM, pszExtra, strOut);
  1257. break;
  1258. case ASSOCSTR_EXECUTABLE:
  1259. hr = _GetExeString(flags, fForceLM, pszExtra, strOut);
  1260. break;
  1261. case ASSOCSTR_FRIENDLYAPPNAME:
  1262. hr = _GetFriendlyAppString(flags, fForceLM, pszExtra, strOut);
  1263. break;
  1264. case ASSOCSTR_SHELLNEWVALUE:
  1265. if (!_fAppOnly)
  1266. hr = _GetShellNewValueString(fForceLM, (pszOut == NULL), pszExtra, strOut);
  1267. break;
  1268. case ASSOCSTR_NOOPEN:
  1269. if (!_fAppOnly)
  1270. hr = _AssocGetRegString(_ClassKey(fForceLM), NULL, TEXT("NoOpen"), strOut);
  1271. break;
  1272. case ASSOCSTR_FRIENDLYDOCNAME:
  1273. if (!_fAppOnly)
  1274. hr = _GetFriendlyDocName(strOut);
  1275. break;
  1276. case ASSOCSTR_DDECOMMAND:
  1277. hr = _AssocGetRegString(_DDEKey(fForceLM, pszExtra), NULL, NULL, strOut);
  1278. break;
  1279. case ASSOCSTR_DDEIFEXEC:
  1280. hr = _AssocGetRegString(_DDEKey(fForceLM, pszExtra), TEXT("IfExec"), NULL, strOut);
  1281. break;
  1282. case ASSOCSTR_DDEAPPLICATION:
  1283. hr = _GetDDEApplication(flags, fForceLM, pszExtra, strOut);
  1284. break;
  1285. case ASSOCSTR_DDETOPIC:
  1286. hr = _GetDDETopic(flags, fForceLM, pszExtra, strOut);
  1287. break;
  1288. case ASSOCSTR_INFOTIP:
  1289. hr = _GetInfoTipString(fForceLM, strOut);
  1290. break;
  1291. case ASSOCSTR_QUICKTIP:
  1292. hr = _GetQuickTipString(fForceLM, strOut);
  1293. break;
  1294. case ASSOCSTR_TILEINFO:
  1295. hr = _GetTileInfoString(fForceLM, strOut);
  1296. break;
  1297. case ASSOCSTR_CONTENTTYPE:
  1298. hr = _GetContentType(strOut);
  1299. break;
  1300. case ASSOCSTR_DEFAULTICON:
  1301. hr = _AssocGetRegString(_ClassKey(fForceLM), TEXT("DefaultIcon"), NULL, strOut);
  1302. break;
  1303. case ASSOCSTR_SHELLEXTENSION:
  1304. hr = _GetShellExtension(flags, fForceLM, pszExtra, strOut);
  1305. if (FAILED(hr) && !fForceLM)
  1306. hr = _GetShellExtension(flags, TRUE, pszExtra, strOut);
  1307. break;
  1308. default:
  1309. //
  1310. // Turn off this assert message until we have a clean way to support new ASSOCSTR types
  1311. // in both shell32 and shlwapi
  1312. //
  1313. #if 0
  1314. AssertMsg(FALSE, TEXT("CAssocW2k::GetString() mismatched headers - ZekeL"));
  1315. #endif
  1316. hr = E_INVALIDARG;
  1317. break;
  1318. }
  1319. }
  1320. if (SUCCEEDED(hr))
  1321. hr = _CopyOut(flags & ASSOCF_NOTRUNCATE, strOut, pszOut, pcchOut);
  1322. else if (!(flags & ASSOCF_IGNOREBASECLASS) && _UseBaseClass())
  1323. {
  1324. HRESULT hrT = _pqaBaseClass->GetString(flags, str, pszExtra, pszOut, pcchOut);
  1325. if (SUCCEEDED(hrT))
  1326. hr = hrT;
  1327. }
  1328. }
  1329. return hr;
  1330. }
  1331. HRESULT CAssocW2k::_GetMSIDescriptor(ASSOCF flags, BOOL fForceLM, LPCTSTR pszVerb, LPBYTE pbOut, LPDWORD pcbOut)
  1332. {
  1333. // what do we do with A/W thunks of REG_MULTI_SZ
  1334. // the darwin ID is always a value name that is the same as the name of the parent key,
  1335. // so instead of reading the default value we read the value with the name of the
  1336. // parent key.
  1337. //
  1338. // shell
  1339. // |
  1340. // -- Open
  1341. // |
  1342. // -- Command
  1343. // (Default) = "%SystemRoot%\system32\normal_app.exe" <-- this is the normal app value
  1344. // Command = "[DarwinID] /c" <-- this is the darwin ID value
  1345. //
  1346. // HACK! Access 95 (shipping product) creates a "Command" value under
  1347. // the Command key but it is >>not<< a Darwin ID. I don't know what
  1348. // they were smoking. So we also check the key type and it must be
  1349. // REG_MULTI_SZ or we will ignore it.
  1350. //
  1351. //
  1352. DWORD dwType;
  1353. HRESULT hr = _AssocGetRegData(_ShellVerbKey(fForceLM, pszVerb), TEXT("command"), TEXT("command"), &dwType, pbOut, pcbOut);
  1354. if (SUCCEEDED(hr) && dwType != REG_MULTI_SZ)
  1355. hr = E_UNEXPECTED;
  1356. return hr;
  1357. }
  1358. HRESULT CAssocW2k::GetData(ASSOCF flags, ASSOCDATA data, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut)
  1359. {
  1360. RIP(_fInited);
  1361. if (!_fInited)
  1362. return E_UNEXPECTED;
  1363. HRESULT hr = E_INVALIDARG;
  1364. if (data && data < ASSOCSTR_MAX)
  1365. {
  1366. BOOL fForceLM = (_fAppOnly) || (flags & ASSOCF_NOUSERSETTINGS);
  1367. DWORD cbReal;
  1368. if (pcbOut && IS_INTRESOURCE(pcbOut))
  1369. {
  1370. cbReal = PtrToUlong(pcbOut);
  1371. pcbOut = &cbReal;
  1372. }
  1373. if (!_fBaseClassOnly)
  1374. {
  1375. switch(data)
  1376. {
  1377. case ASSOCDATA_MSIDESCRIPTOR:
  1378. hr = _GetMSIDescriptor(flags, fForceLM, pszExtra, (LPBYTE)pvOut, pcbOut);
  1379. break;
  1380. case ASSOCDATA_NOACTIVATEHANDLER:
  1381. hr = _AssocGetRegData(_DDEKey(fForceLM, pszExtra), NULL, TEXT("NoActivateHandler"), NULL, (LPBYTE) pvOut, pcbOut);
  1382. break;
  1383. case ASSOCDATA_QUERYCLASSSTORE:
  1384. hr = _AssocGetRegData(_ClassKey(fForceLM), NULL, TEXT("QueryClassStore"), NULL, (LPBYTE) pvOut, pcbOut);
  1385. break;
  1386. case ASSOCDATA_HASPERUSERASSOC:
  1387. {
  1388. HKEY hk = _UserProgidKey();
  1389. if (hk && _ShellVerbKey(hk, KEYCACHE_HKCU, pszExtra))
  1390. hr = S_OK;
  1391. else
  1392. hr = S_FALSE;
  1393. REGFREE(hk);
  1394. }
  1395. break;
  1396. case ASSOCDATA_EDITFLAGS:
  1397. hr = _AssocGetRegData(_ClassKey(fForceLM), NULL, TEXT("EditFlags"), NULL, (LPBYTE) pvOut, pcbOut);
  1398. break;
  1399. default:
  1400. AssertMsg(FALSE, TEXT("CAssocW2k::GetString() mismatched headers - ZekeL"));
  1401. hr = E_INVALIDARG;
  1402. break;
  1403. }
  1404. }
  1405. if (FAILED(hr) && !(flags & ASSOCF_IGNOREBASECLASS) && _UseBaseClass())
  1406. {
  1407. HRESULT hrT = _pqaBaseClass->GetData(flags, data, pszExtra, pvOut, pcbOut);
  1408. if (SUCCEEDED(hrT))
  1409. hr = hrT;
  1410. }
  1411. }
  1412. return hr;
  1413. }
  1414. HRESULT CAssocW2k::GetEnum(ASSOCF flags, ASSOCENUM assocenum, LPCTSTR pszExtra, REFIID riid, LPVOID *ppvOut)
  1415. {
  1416. return E_NOTIMPL;
  1417. }
  1418. HRESULT CAssocW2k::_GetShellExecKey(ASSOCF flags, BOOL fForceLM, LPCWSTR pszVerb, HKEY *phkey)
  1419. {
  1420. HKEY hkProgid = NULL;
  1421. if (pszVerb && !*pszVerb)
  1422. pszVerb = NULL;
  1423. if (!fForceLM)
  1424. {
  1425. hkProgid = _ClassKey(FALSE);
  1426. if (hkProgid && (!(flags & ASSOCF_VERIFY) || _ShellVerbKey(hkProgid, KEYCACHE_HKCU, pszVerb)))
  1427. *phkey = SHRegDuplicateHKey(hkProgid);
  1428. }
  1429. if (!*phkey)
  1430. {
  1431. KEYCACHETYPE type = (_fAppOnly) ? KEYCACHE_APP : KEYCACHE_HKLM;
  1432. hkProgid = _ClassKey(TRUE);
  1433. if (hkProgid && (!(flags & ASSOCF_VERIFY) || _ShellVerbKey(hkProgid, type, pszVerb)))
  1434. *phkey = SHRegDuplicateHKey(hkProgid);
  1435. }
  1436. return *phkey ? S_OK : HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  1437. }
  1438. HRESULT CAssocW2k::_CloneKey(HKEY hk, HKEY *phkey)
  1439. {
  1440. if (hk)
  1441. *phkey = SHRegDuplicateHKey(hk);
  1442. return *phkey ? S_OK : HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  1443. }
  1444. HRESULT CAssocW2k::GetKey(ASSOCF flags, ASSOCKEY key, LPCTSTR pszExtra, HKEY *phkey)
  1445. {
  1446. RIP(_fInited);
  1447. if (!_fInited)
  1448. return E_UNEXPECTED;
  1449. HRESULT hr = E_INVALIDARG;
  1450. if (key && key < ASSOCKEY_MAX && phkey)
  1451. {
  1452. BOOL fForceLM = (_fAppOnly) || (flags & ASSOCF_NOUSERSETTINGS);
  1453. *phkey = NULL;
  1454. if (!_fBaseClassOnly)
  1455. {
  1456. switch (key)
  1457. {
  1458. case ASSOCKEY_SHELLEXECCLASS:
  1459. hr = _GetShellExecKey(flags, fForceLM, pszExtra, phkey);
  1460. break;
  1461. case ASSOCKEY_APP:
  1462. hr = _fAppOnly ? _CloneKey(_AppKey(NULL), phkey) : E_INVALIDARG;
  1463. break;
  1464. case ASSOCKEY_CLASS:
  1465. hr = _CloneKey(_ClassKey(fForceLM), phkey);
  1466. break;
  1467. case ASSOCKEY_BASECLASS:
  1468. // fall through and it is handled by the BaseClass handling
  1469. break;
  1470. default:
  1471. AssertMsg(FALSE, TEXT("CAssocW2k::GetKey() mismatched headers - ZekeL"));
  1472. hr = E_INVALIDARG;
  1473. break;
  1474. }
  1475. }
  1476. if (FAILED(hr) && !(flags & ASSOCF_IGNOREBASECLASS) && _UseBaseClass())
  1477. {
  1478. // it is possible to indicate the depth of the
  1479. // base class by pszExtra being an INT
  1480. if (key == ASSOCKEY_BASECLASS)
  1481. {
  1482. int depth = IS_INTRESOURCE(pszExtra) ? LOWORD(pszExtra) : 0;
  1483. if (depth)
  1484. {
  1485. // go deeper than this
  1486. depth--;
  1487. hr = _pqaBaseClass->GetKey(flags, key, MAKEINTRESOURCE(depth), phkey);
  1488. }
  1489. else
  1490. {
  1491. // just return this baseclass
  1492. hr = _pqaBaseClass->GetKey(flags, ASSOCKEY_CLASS, pszExtra, phkey);
  1493. }
  1494. }
  1495. else
  1496. {
  1497. // forward to the base class
  1498. hr = _pqaBaseClass->GetKey(flags, key, pszExtra, phkey);
  1499. }
  1500. }
  1501. }
  1502. return hr;
  1503. }
  1504. HRESULT AssocCreateW2k(REFIID riid, LPVOID *ppvOut)
  1505. {
  1506. HRESULT hr = E_OUTOFMEMORY;
  1507. CAssocW2k *passoc = new CAssocW2k();
  1508. if (passoc)
  1509. {
  1510. hr = passoc->QueryInterface(riid, ppvOut);
  1511. passoc->Release();
  1512. }
  1513. return hr;
  1514. }