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.

1434 lines
42 KiB

  1. #include "priv.h"
  2. #include "ids.h"
  3. #include "assoc.h"
  4. #include <memt.h>
  5. BOOL _GetAppPath(PCWSTR pszApp, PWSTR pszExe, DWORD cchExe)
  6. {
  7. WCHAR sz[MAX_PATH];
  8. _MakeAppPathKey(pszApp, sz, SIZECHARS(sz));
  9. DWORD cb = CbFromCchW(cchExe);
  10. return ERROR_SUCCESS == SHGetValueW(HKEY_LOCAL_MACHINE, sz, NULL, NULL, pszExe, &cb);
  11. }
  12. inline HRESULT _QuerySourceCreateFromKey(HKEY hk, PCWSTR pszSub, BOOL fCreate, IQuerySource **ppqs)
  13. {
  14. return QuerySourceCreateFromKey(hk, pszSub, fCreate, IID_PPV_ARG(IQuerySource, ppqs));
  15. }
  16. typedef struct QUERYKEYVAL
  17. {
  18. ASSOCQUERY query;
  19. PCWSTR pszKey;
  20. PCWSTR pszVal;
  21. } QUERYKEYVAL;
  22. #define MAKEQKV(q, k, v) { q, k, v}
  23. static const QUERYKEYVAL s_rgqkvVerb[] =
  24. {
  25. MAKEQKV(AQVS_COMMAND, L"command", NULL),
  26. MAKEQKV(AQVS_DDECOMMAND, L"ddeexec", NULL),
  27. MAKEQKV(AQVS_DDEIFEXEC, L"ddeexec\\ifexec", NULL),
  28. MAKEQKV(AQVS_DDEAPPLICATION, L"ddeexec\\application", NULL),
  29. MAKEQKV(AQVS_DDETOPIC, L"ddeexec\\topic", NULL),
  30. MAKEQKV(AQV_NOACTIVATEHANDLER, L"ddeexec", L"NoActivateHandler"),
  31. MAKEQKV(AQVD_MSIDESCRIPTOR, L"command", L"command"),
  32. MAKEQKV(AQVS_APPLICATION_FRIENDLYNAME, NULL, L"FriendlyAppName"),
  33. };
  34. static const QUERYKEYVAL s_rgqkvShell[] =
  35. {
  36. MAKEQKV(AQS_FRIENDLYTYPENAME, NULL, L"FriendlyTypeName"),
  37. MAKEQKV(AQS_DEFAULTICON, L"DefaultIcon", NULL),
  38. MAKEQKV(AQS_CLSID, L"Clsid", NULL),
  39. MAKEQKV(AQS_PROGID, L"Progid", NULL),
  40. MAKEQKV(AQNS_SHELLEX_HANDLER, L"ShellEx\\%s", NULL),
  41. };
  42. static const QUERYKEYVAL s_rgqkvExt[] =
  43. {
  44. MAKEQKV(AQNS_SHELLEX_HANDLER, L"ShellEx\\%s", NULL),
  45. MAKEQKV(AQS_CONTENTTYPE, NULL, L"Content Type"),
  46. };
  47. static const QUERYKEYVAL s_rgqkvApp[] =
  48. {
  49. MAKEQKV(AQVS_APPLICATION_FRIENDLYNAME, NULL, L"FriendlyAppName"),
  50. };
  51. const QUERYKEYVAL *_FindKeyVal(ASSOCQUERY query, const QUERYKEYVAL *rgQkv, UINT cQkv)
  52. {
  53. for (UINT i = 0; i < cQkv; i++)
  54. {
  55. if (rgQkv[i].query == query)
  56. {
  57. return &rgQkv[i];
  58. }
  59. }
  60. return NULL;
  61. }
  62. HRESULT _SHAllocMUI(LPWSTR *ppsz)
  63. {
  64. WCHAR sz[INFOTIPSIZE];
  65. HRESULT hr = SHLoadIndirectString(*ppsz, sz, ARRAYSIZE(sz), NULL);
  66. CoTaskMemFree(*ppsz);
  67. if (SUCCEEDED(hr))
  68. hr = SHStrDupW(sz, ppsz);
  69. else
  70. *ppsz = 0;
  71. return hr;
  72. }
  73. HRESULT CALLBACK _QuerySourceString(IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszKey, PCWSTR pszValue, PWSTR *ppsz)
  74. {
  75. HRESULT hr = pqs->QueryValueString(pszKey, pszValue, ppsz);
  76. if (SUCCEEDED(hr) && (query & AQF_MUISTRING))
  77. {
  78. // NOTE - this sucks for stack usage.
  79. // since there is currently no way to get
  80. // the size of the target.
  81. hr = _SHAllocMUI(ppsz);
  82. }
  83. return hr;
  84. }
  85. HRESULT CALLBACK _QuerySourceDirect(IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszKey, PCWSTR pszValue, FLAGGED_BYTE_BLOB **ppblob)
  86. {
  87. return pqs->QueryValueDirect(pszKey, pszValue, ppblob);
  88. }
  89. HRESULT CALLBACK _QuerySourceExists(IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszKey, PCWSTR pszValue, void *pv)
  90. {
  91. return pqs->QueryValueExists(pszKey, pszValue);
  92. }
  93. HRESULT CALLBACK _QuerySourceDword(IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszKey, PCWSTR pszValue, DWORD *pdw)
  94. {
  95. return pqs->QueryValueDword(pszKey, pszValue, pdw);
  96. }
  97. class CAssocElement : public IObjectWithQuerySource,
  98. public IAssociationElement
  99. {
  100. public:
  101. CAssocElement() : _cRef(1), _pqs(0) {}
  102. virtual ~CAssocElement() { ATOMICRELEASE(_pqs); }
  103. // IUnknown refcounting
  104. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  105. STDMETHODIMP_(ULONG) AddRef(void)
  106. {
  107. return ++_cRef;
  108. }
  109. STDMETHODIMP_(ULONG) Release(void)
  110. {
  111. if (--_cRef > 0)
  112. return _cRef;
  113. delete this;
  114. return 0;
  115. }
  116. // IObjectWithQuerySource
  117. STDMETHODIMP SetSource(IQuerySource *pqs)
  118. {
  119. if (!_pqs)
  120. {
  121. _pqs = pqs;
  122. _pqs->AddRef();
  123. return S_OK;
  124. }
  125. return E_UNEXPECTED;
  126. }
  127. STDMETHODIMP GetSource(REFIID riid, void **ppv)
  128. {
  129. if (_pqs)
  130. {
  131. return _pqs->QueryInterface(riid, ppv);
  132. }
  133. *ppv = NULL;
  134. return E_NOINTERFACE;
  135. }
  136. // IAssociationElement
  137. STDMETHODIMP QueryString(
  138. ASSOCQUERY query,
  139. PCWSTR pszCue,
  140. PWSTR *ppsz)
  141. {
  142. *ppsz = 0;
  143. return _QuerySourceAny(_QuerySourceString, _pqs, (ASSOCQUERY)(AQF_DIRECT | AQF_STRING), query, pszCue, ppsz);
  144. }
  145. STDMETHODIMP QueryDword(
  146. ASSOCQUERY query,
  147. PCWSTR pszCue,
  148. DWORD *pdw)
  149. {
  150. return _QuerySourceAny(_QuerySourceDword, _pqs, (ASSOCQUERY)(AQF_DIRECT | AQF_DWORD), query, pszCue, pdw);
  151. }
  152. STDMETHODIMP QueryExists(
  153. ASSOCQUERY query,
  154. PCWSTR pszCue)
  155. {
  156. return _QuerySourceAny(_QuerySourceExists, _pqs, (ASSOCQUERY)(AQF_DIRECT | AQF_EXISTS), query, pszCue, (void*)NULL);
  157. }
  158. STDMETHODIMP QueryDirect(
  159. ASSOCQUERY query,
  160. PCWSTR pszCue,
  161. FLAGGED_BYTE_BLOB **ppblob)
  162. {
  163. *ppblob = 0;
  164. return _QuerySourceAny(_QuerySourceDirect, _pqs, AQF_DIRECT, query, pszCue, ppblob);
  165. }
  166. STDMETHODIMP QueryObject(
  167. ASSOCQUERY query,
  168. PCWSTR pszCue,
  169. REFIID riid,
  170. void **ppv)
  171. {
  172. *ppv = 0;
  173. return E_NOTIMPL;
  174. }
  175. protected:
  176. template<class T> HRESULT _QueryKeyValAny(HRESULT (CALLBACK *pfnAny)(IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszKey, PCWSTR pszValue, T *pData), const QUERYKEYVAL *rgQkv, UINT cQkv, IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszCue, T *pData)
  177. {
  178. HRESULT hr = E_INVALIDARG;
  179. const QUERYKEYVAL *pqkv = _FindKeyVal(query, rgQkv, cQkv);
  180. if (pqkv)
  181. {
  182. WCHAR szKey[128];
  183. PCWSTR pszKey = pqkv->pszKey;
  184. if (query & AQF_CUEIS_NAME)
  185. {
  186. if (pqkv->pszKey)
  187. {
  188. wnsprintfW(szKey, ARRAYSIZE(szKey), pqkv->pszKey, pszCue);
  189. pszKey = szKey;
  190. }
  191. // wnsprintf(szVal, ARRAYSIZE(szVal), pqkv->pszVal, pszCue);
  192. }
  193. hr = pfnAny(pqs, query, pszKey, pqkv->pszVal, pData);
  194. }
  195. return hr;
  196. }
  197. template<class T> HRESULT _QuerySourceAny(HRESULT (CALLBACK *pfnAny)(IQuerySource *pqs, ASSOCQUERY query, PCWSTR pszKey, PCWSTR pszValue, T *pData), IQuerySource *pqs, ASSOCQUERY mask, ASSOCQUERY query, PCWSTR pszCue, T *pData)
  198. {
  199. HRESULT hr = E_INVALIDARG;
  200. if (pqs)
  201. {
  202. if (query == AQN_NAMED_VALUE || query == AQNS_NAMED_MUI_STRING)
  203. {
  204. hr = pfnAny(pqs, query, NULL, pszCue, pData);
  205. }
  206. else if ((query & (mask)) == (mask))
  207. {
  208. const QUERYKEYVAL *rgQkv;
  209. UINT cQkv = _GetQueryKeyVal(&rgQkv);
  210. if (cQkv)
  211. {
  212. hr = _QueryKeyValAny(pfnAny, rgQkv, cQkv, pqs, query, pszCue, pData);
  213. }
  214. }
  215. }
  216. return hr;
  217. }
  218. virtual UINT _GetQueryKeyVal(const QUERYKEYVAL **prgQkv) { *prgQkv = 0; return 0; }
  219. protected:
  220. LONG _cRef;
  221. IQuerySource *_pqs;
  222. };
  223. HRESULT CAssocElement::QueryInterface(REFIID riid, void **ppv)
  224. {
  225. static const QITAB qit[] =
  226. {
  227. QITABENT(CAssocElement, IAssociationElement),
  228. QITABENT(CAssocElement, IObjectWithQuerySource),
  229. { 0 },
  230. };
  231. return QISearch(this, qit, riid, ppv);
  232. }
  233. HRESULT _QueryString(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  234. {
  235. return pae->QueryString(query, pszCue, ppsz);
  236. }
  237. HRESULT _QueryDirect(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, FLAGGED_BYTE_BLOB **ppblob)
  238. {
  239. return pae->QueryDirect(query, pszCue, ppblob);
  240. }
  241. HRESULT _QueryDword(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, DWORD *pdw)
  242. {
  243. return pae->QueryDword(query, pszCue, pdw);
  244. }
  245. HRESULT _QueryExists(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, void *pv)
  246. {
  247. return pae->QueryExists(query, pszCue);
  248. }
  249. class CAssocShellElement : public CAssocElement, public IPersistString2
  250. {
  251. public:
  252. virtual ~CAssocShellElement() { if (_pszInit && _pszInit != _szInit) LocalFree(_pszInit);}
  253. // IUnknown refcounting
  254. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  255. STDMETHODIMP_(ULONG) AddRef(void)
  256. {
  257. return ++_cRef;
  258. }
  259. STDMETHODIMP_(ULONG) Release(void)
  260. {
  261. if (--_cRef > 0)
  262. return _cRef;
  263. delete this;
  264. return 0;
  265. }
  266. // IPersist
  267. STDMETHODIMP GetClassID(CLSID *pclsid)
  268. { *pclsid = CLSID_AssocShellElement; return S_OK;}
  269. // IPersistString2
  270. STDMETHODIMP SetString(PCWSTR psz)
  271. {
  272. if (!_pszInit)
  273. {
  274. DWORD cch = lstrlenW(psz);
  275. if (cch < ARRAYSIZE(_szInit))
  276. _pszInit = _szInit;
  277. else
  278. SHLocalAlloc(CbFromCchW(cch + 1), &_pszInit);
  279. if (_pszInit)
  280. {
  281. StrCpyW(_pszInit, psz);
  282. return _InitSource();
  283. }
  284. }
  285. return E_UNEXPECTED;
  286. }
  287. STDMETHODIMP GetString(PWSTR *ppsz)
  288. { return SHStrDupW(_pszInit, ppsz); }
  289. // IAssociationElement
  290. STDMETHODIMP QueryString(
  291. ASSOCQUERY query,
  292. PCWSTR pszCue,
  293. PWSTR *ppsz)
  294. {
  295. if (AQF_CUEIS_SHELLVERB & query)
  296. return _QueryVerbAny(_QueryString, query, pszCue, ppsz);
  297. else
  298. return CAssocElement::QueryString(query, pszCue, ppsz);
  299. }
  300. STDMETHODIMP QueryDword(
  301. ASSOCQUERY query,
  302. PCWSTR pszCue,
  303. DWORD *pdw)
  304. {
  305. if (AQF_CUEIS_SHELLVERB & query)
  306. return _QueryVerbAny(_QueryDword, query, pszCue, pdw);
  307. else
  308. return CAssocElement::QueryDword(query, pszCue, pdw);
  309. }
  310. STDMETHODIMP QueryExists(
  311. ASSOCQUERY query,
  312. PCWSTR pszCue)
  313. {
  314. if (AQF_CUEIS_SHELLVERB & query)
  315. return _QueryVerbAny(_QueryExists, query, pszCue, (void*)NULL);
  316. else
  317. return CAssocElement::QueryExists(query, pszCue);
  318. }
  319. STDMETHODIMP QueryDirect(
  320. ASSOCQUERY query,
  321. PCWSTR pszCue,
  322. FLAGGED_BYTE_BLOB **ppblob)
  323. {
  324. if (AQF_CUEIS_SHELLVERB & query)
  325. return _QueryVerbAny(_QueryDirect, query, pszCue, ppblob);
  326. else
  327. return CAssocElement::QueryDirect(query, pszCue, ppblob);
  328. }
  329. STDMETHODIMP QueryObject(
  330. ASSOCQUERY query,
  331. PCWSTR pszCue,
  332. REFIID riid,
  333. void **ppv);
  334. protected:
  335. template<class T> HRESULT _QueryVerbAny(HRESULT (CALLBACK *pfnAny)(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, T pData), ASSOCQUERY query, PCWSTR pszCue, T pData)
  336. {
  337. IAssociationElement *pae;
  338. HRESULT hr = _GetVerbDelegate(pszCue, &pae);
  339. if (SUCCEEDED(hr))
  340. {
  341. hr = pfnAny(pae, query, NULL, pData);
  342. pae->Release();
  343. }
  344. return hr;
  345. }
  346. // from CAssocElement
  347. virtual UINT _GetQueryKeyVal(const QUERYKEYVAL **prgQkv)
  348. { *prgQkv = s_rgqkvShell; return ARRAYSIZE(s_rgqkvShell); }
  349. // defaults for our subclasses
  350. virtual BOOL _UseEnumForDefaultVerb()
  351. { return FALSE;}
  352. virtual HRESULT _InitSource()
  353. { return _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, _pszInit, FALSE, &_pqs); }
  354. virtual BOOL _IsAppSource()
  355. { return FALSE; }
  356. HRESULT _GetVerbDelegate(PCWSTR pszVerb, IAssociationElement **ppae);
  357. HRESULT _DefaultVerbSource(IQuerySource **ppqsVerb);
  358. HRESULT _QueryShellExtension(PCWSTR pszShellEx, PWSTR *ppsz);
  359. protected:
  360. PWSTR _pszInit;
  361. WCHAR _szInit[64];
  362. };
  363. HRESULT CAssocShellElement::QueryInterface(REFIID riid, void **ppv)
  364. {
  365. static const QITAB qit[] =
  366. {
  367. QITABENT(CAssocShellElement, IAssociationElement),
  368. QITABENT(CAssocShellElement, IObjectWithQuerySource),
  369. QITABENT(CAssocShellElement, IPersistString2),
  370. QITABENTMULTI(CAssocShellElement, IPersist, IPersistString2),
  371. { 0 },
  372. };
  373. return QISearch(this, qit, riid, ppv);
  374. }
  375. class CAssocProgidElement : public CAssocShellElement
  376. {
  377. public:
  378. virtual ~CAssocProgidElement() { ATOMICRELEASE(_pqsExt); }
  379. // then we handle fallback for IAssociationElement
  380. STDMETHODIMP QueryString(
  381. ASSOCQUERY query,
  382. PCWSTR pszCue,
  383. PWSTR *ppsz);
  384. // IPersist
  385. STDMETHODIMP GetClassID(CLSID *pclsid)
  386. { *pclsid = CLSID_AssocProgidElement; return S_OK;}
  387. protected: // methods
  388. HRESULT _InitSource();
  389. HRESULT _DefaultVerbSource(IQuerySource **ppqsVerb);
  390. BOOL _UseEnumForDefaultVerb()
  391. { return TRUE; }
  392. protected: // members
  393. IQuerySource *_pqsExt;
  394. };
  395. HRESULT _QuerySourceCreateFromKey2(HKEY hk, PCWSTR pszSub1, PCWSTR pszSub2, IQuerySource **ppqs)
  396. {
  397. WCHAR szKey[MAX_PATH];
  398. _PathAppend(pszSub1, pszSub2, szKey, SIZECHARS(szKey));
  399. return _QuerySourceCreateFromKey(hk, szKey, FALSE, ppqs);
  400. }
  401. class CAssocClsidElement : public CAssocShellElement
  402. {
  403. public:
  404. // IPersist
  405. STDMETHODIMP GetClassID(CLSID *pclsid)
  406. { *pclsid = CLSID_AssocClsidElement; return S_OK;}
  407. protected:
  408. virtual HRESULT _InitSource()
  409. { return _QuerySourceCreateFromKey2(HKEY_CLASSES_ROOT, L"CLSID", _pszInit, &_pqs);}
  410. };
  411. class CAssocSystemExtElement : public CAssocShellElement
  412. {
  413. public:
  414. // IPersist
  415. STDMETHODIMP GetClassID(CLSID *pclsid)
  416. { *pclsid = CLSID_AssocSystemElement; return S_OK;}
  417. protected:
  418. virtual HRESULT _InitSource()
  419. { return _QuerySourceCreateFromKey2(HKEY_CLASSES_ROOT, L"SystemFileAssociations", _pszInit, &_pqs);}
  420. };
  421. class CAssocPerceivedElement : public CAssocShellElement
  422. {
  423. public:
  424. // IPersist
  425. STDMETHODIMP GetClassID(CLSID *pclsid)
  426. { *pclsid = CLSID_AssocPerceivedElement; return S_OK;}
  427. protected:
  428. virtual HRESULT _InitSource();
  429. // maybe _GetVerbDelegate() to support Accepts filters
  430. };
  431. class CAssocApplicationElement : public CAssocShellElement
  432. {
  433. public:
  434. // need to fallback to the pszInit for FriendlyAppName
  435. STDMETHODIMP QueryString(
  436. ASSOCQUERY query,
  437. PCWSTR pszCue,
  438. PWSTR *ppsz);
  439. STDMETHODIMP QueryObject(
  440. ASSOCQUERY query,
  441. PCWSTR pszCue,
  442. REFIID riid,
  443. void **ppv);
  444. // IPersist
  445. STDMETHODIMP GetClassID(CLSID *pclsid)
  446. { *pclsid = CLSID_AssocApplicationElement; return S_OK;}
  447. protected:
  448. virtual HRESULT _InitSource();
  449. virtual UINT _GetQueryKeyVal(const QUERYKEYVAL **prgQkv)
  450. { *prgQkv = s_rgqkvApp; return ARRAYSIZE(s_rgqkvApp); }
  451. virtual BOOL _IsAppSource()
  452. { return TRUE; }
  453. BOOL _UseEnumForDefaultVerb()
  454. { return TRUE; }
  455. HRESULT _GetAppDisplayName(PWSTR *ppsz);
  456. protected:
  457. BOOL _fIsPath;
  458. };
  459. HRESULT CAssocApplicationElement::_GetAppDisplayName(PWSTR *ppsz)
  460. {
  461. HRESULT hr;
  462. PWSTR pszPath;
  463. if (_fIsPath)
  464. {
  465. hr = S_OK;
  466. pszPath = _pszInit;
  467. ASSERT(pszPath);
  468. }
  469. else
  470. hr = QueryString(AQVS_APPLICATION_PATH, NULL, &pszPath);
  471. if (SUCCEEDED(hr))
  472. {
  473. WCHAR sz[MAX_PATH];
  474. DWORD cb = sizeof(sz);
  475. hr = SKGetValueW(SHELLKEY_HKCULM_MUICACHE, NULL, pszPath, NULL, sz, &cb);
  476. if (FAILED(hr))
  477. {
  478. UINT cch = ARRAYSIZE(sz);
  479. if (SHGetFileDescriptionW(pszPath, NULL, NULL, sz, &cch))
  480. {
  481. hr = S_OK;
  482. SKSetValueW(SHELLKEY_HKCULM_MUICACHE, NULL, pszPath, REG_SZ, sz, CbFromCchW(lstrlenW(sz) + 1));
  483. }
  484. }
  485. if (SUCCEEDED(hr))
  486. hr = SHStrDupW(sz, ppsz);
  487. if (pszPath != _pszInit)
  488. CoTaskMemFree(pszPath);
  489. }
  490. return hr;
  491. }
  492. HRESULT CAssocApplicationElement::_InitSource()
  493. {
  494. WCHAR sz[MAX_PATH];
  495. PCWSTR pszName = PathFindFileNameW(_pszInit);
  496. _MakeApplicationsKey(pszName, sz, ARRAYSIZE(sz));
  497. HRESULT hr = _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, sz, FALSE, &_pqs);
  498. _fIsPath = pszName != _pszInit;
  499. if (FAILED(hr))
  500. {
  501. if (_fIsPath && PathFileExistsW(_pszInit))
  502. hr = S_FALSE;
  503. }
  504. return hr;
  505. }
  506. HRESULT CAssocApplicationElement::QueryObject(ASSOCQUERY query, PCWSTR pszCue, REFIID riid, void **ppv)
  507. {
  508. if (query == AQVO_APPLICATION_DELEGATE)
  509. {
  510. return QueryInterface(riid, ppv);
  511. }
  512. return CAssocShellElement::QueryObject(query, pszCue, riid, ppv);
  513. }
  514. HRESULT CAssocApplicationElement::QueryString(ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  515. {
  516. HRESULT hr = CAssocShellElement::QueryString(query, pszCue, ppsz);
  517. if (FAILED(hr))
  518. {
  519. switch (query)
  520. {
  521. case AQVS_APPLICATION_FRIENDLYNAME:
  522. hr = _GetAppDisplayName(ppsz);
  523. break;
  524. }
  525. }
  526. return hr;
  527. }
  528. class CAssocShellVerbElement : public CAssocElement
  529. {
  530. public:
  531. CAssocShellVerbElement(BOOL fIsApp) : _fIsApp(fIsApp) {}
  532. // overload QS to return default DDEExec strings
  533. STDMETHODIMP QueryString(
  534. ASSOCQUERY query,
  535. PCWSTR pszCue,
  536. PWSTR *ppsz);
  537. STDMETHODIMP QueryObject(
  538. ASSOCQUERY query,
  539. PCWSTR pszCue,
  540. REFIID riid,
  541. void **ppv);
  542. protected:
  543. virtual UINT _GetQueryKeyVal(const QUERYKEYVAL **prgQkv)
  544. { *prgQkv = s_rgqkvVerb; return ARRAYSIZE(s_rgqkvVerb); }
  545. HRESULT _GetAppDelegate(REFIID riid, void **ppv);
  546. protected:
  547. BOOL _fIsApp;
  548. };
  549. class CAssocFolderElement : public CAssocShellElement
  550. {
  551. public:
  552. // overload QS to return default MUI strings
  553. STDMETHODIMP QueryString(
  554. ASSOCQUERY query,
  555. PCWSTR pszCue,
  556. PWSTR *ppsz);
  557. // IPersist
  558. STDMETHODIMP GetClassID(CLSID *pclsid)
  559. { *pclsid = CLSID_AssocFolderElement; return S_OK;}
  560. protected:
  561. virtual HRESULT _InitSource()
  562. { return _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, L"Folder", FALSE, &_pqs); }
  563. };
  564. class CAssocStarElement : public CAssocShellElement
  565. {
  566. public:
  567. // overload QS to return default MUI strings
  568. STDMETHODIMP QueryString(
  569. ASSOCQUERY query,
  570. PCWSTR pszCue,
  571. PWSTR *ppsz);
  572. // IPersist
  573. STDMETHODIMP GetClassID(CLSID *pclsid)
  574. { *pclsid = CLSID_AssocStarElement; return S_OK;}
  575. protected:
  576. virtual HRESULT _InitSource()
  577. { return _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, L"*", FALSE, &_pqs); }
  578. };
  579. HRESULT CAssocShellElement::_DefaultVerbSource(IQuerySource **ppqsVerb)
  580. {
  581. IQuerySource *pqsShell;
  582. HRESULT hr = _pqs->OpenSource(L"shell", FALSE, &pqsShell);
  583. if (SUCCEEDED(hr))
  584. {
  585. PWSTR pszFree = NULL;
  586. PCWSTR pszVerb;
  587. // see if something is specified...
  588. if (SUCCEEDED(pqsShell->QueryValueString(NULL, NULL, &pszFree)))
  589. {
  590. pszVerb = pszFree;
  591. }
  592. else
  593. {
  594. // default to "open"
  595. pszVerb = L"open";
  596. }
  597. hr = pqsShell->OpenSource(pszVerb, FALSE, ppqsVerb);
  598. if (FAILED(hr))
  599. {
  600. if (pszFree)
  601. {
  602. // try to find one of the ordered verbs
  603. int c = StrCSpnW(pszFree, L" ,");
  604. if (c != lstrlenW(pszFree))
  605. {
  606. pszFree[c] = 0;
  607. hr = pqsShell->OpenSource(pszFree, FALSE, ppqsVerb);
  608. }
  609. }
  610. else if (_UseEnumForDefaultVerb())
  611. {
  612. // APPCOMPAT - regitems need to have the open verb - ZekeL - 30-JAN-2001
  613. // so that the IQA and ICM will behave the same,
  614. // and regitem folders will always default to
  615. // folder\shell\open unless they implement open
  616. // or specify default verbs.
  617. //
  618. // everything else, just use the first key we find....
  619. IEnumString *penum;
  620. if (SUCCEEDED(pqsShell->EnumSources(&penum)))
  621. {
  622. ULONG c;
  623. CSmartCoTaskMem<OLECHAR> spszEnum;
  624. if (S_OK == penum->Next(1, &spszEnum, &c))
  625. {
  626. hr = pqsShell->OpenSource(spszEnum, FALSE, ppqsVerb);
  627. }
  628. penum->Release();
  629. }
  630. }
  631. }
  632. if (pszFree)
  633. CoTaskMemFree(pszFree);
  634. pqsShell->Release();
  635. }
  636. return hr;
  637. }
  638. HRESULT QSOpen2(IQuerySource *pqs, PCWSTR pszSub1, PCWSTR pszSub2, BOOL fCreate, IQuerySource **ppqs)
  639. {
  640. WCHAR szKey[MAX_PATH];
  641. _PathAppend(pszSub1, pszSub2, szKey, SIZECHARS(szKey));
  642. return pqs->OpenSource(szKey, fCreate, ppqs);
  643. }
  644. HRESULT CAssocShellElement::_GetVerbDelegate(PCWSTR pszVerb, IAssociationElement **ppae)
  645. {
  646. HRESULT hr = _pqs ? S_OK : E_FAIL;
  647. if (SUCCEEDED(hr))
  648. {
  649. // we will recalc each time.
  650. // the array will cache appropriately
  651. IQuerySource *pqs;
  652. if (pszVerb)
  653. {
  654. hr = QSOpen2(_pqs, L"shell", pszVerb, FALSE, &pqs);
  655. }
  656. else
  657. {
  658. hr = _DefaultVerbSource(&pqs);
  659. }
  660. if (SUCCEEDED(hr))
  661. {
  662. CAssocShellVerbElement *pave = new CAssocShellVerbElement(_IsAppSource());
  663. if (pave)
  664. {
  665. hr = pave->SetSource(pqs);
  666. // this cant fail...
  667. ASSERT(SUCCEEDED(hr));
  668. *ppae = pave;
  669. }
  670. else
  671. hr = E_OUTOFMEMORY;
  672. pqs->Release();
  673. }
  674. }
  675. return hr;
  676. }
  677. HRESULT CAssocShellElement::QueryObject(ASSOCQUERY query, PCWSTR pszCue, REFIID riid, void **ppv)
  678. {
  679. HRESULT hr = E_INVALIDARG;
  680. if (AQF_CUEIS_SHELLVERB & query)
  681. {
  682. IAssociationElement *pae;
  683. hr = _GetVerbDelegate(pszCue, &pae);
  684. if (SUCCEEDED(hr))
  685. {
  686. if (AQVO_SHELLVERB_DELEGATE == query)
  687. hr = pae->QueryInterface(riid, ppv);
  688. else
  689. hr = pae->QueryObject(query, NULL, riid, ppv);
  690. pae->Release();
  691. }
  692. }
  693. return hr;
  694. }
  695. HKEY _OpenProgidKey(PCWSTR pszProgid)
  696. {
  697. HKEY hkOut;
  698. if (SUCCEEDED(_AssocOpenRegKey(HKEY_CLASSES_ROOT, pszProgid, &hkOut)))
  699. {
  700. // Check for a newer version of the ProgID
  701. WCHAR sz[64];
  702. DWORD cb = sizeof(sz);
  703. //
  704. // APPCOMPAT LEGACY - Quattro Pro 2000 and Excel 2000 dont get along - ZekeL - 7-MAR-2000
  705. // mill bug #129525. the problem is if Quattro is installed
  706. // first, then excel picks up quattro's CurVer key for some
  707. // reason. then we end up using Quattro.Worksheet as the current
  708. // version of the Excel.Sheet. this is bug in both of their code.
  709. // since quattro cant even open the file when we give it to them,
  710. // they never should take the assoc in the first place, and when excel
  711. // takes over it shouldnt have preserved the CurVer key from the
  712. // previous association. we could add some code to insure that the
  713. // CurVer key follows the OLE progid naming conventions and that it must
  714. // be derived from the same app name as the progid in order to take
  715. // precedence but for now we will block CurVer from working whenever
  716. // the progid is excel.sheet.8 (excel 2000)
  717. //
  718. if (StrCmpIW(L"Excel.Sheet.8", pszProgid)
  719. && ERROR_SUCCESS == SHGetValueW(hkOut, L"CurVer", NULL, NULL, sz, &cb)
  720. && (cb > sizeof(WCHAR)))
  721. {
  722. // cache this bubby
  723. HKEY hkTemp = hkOut;
  724. if (SUCCEEDED(_AssocOpenRegKey(HKEY_CLASSES_ROOT, sz, &hkOut)))
  725. {
  726. //
  727. // APPCOMPAT LEGACY - order of preference - ZekeL - 22-JUL-99
  728. // this is to support associations that installed empty curver
  729. // keys, like microsoft project.
  730. //
  731. // 1. curver with shell subkey
  732. // 2. progid with shell subkey
  733. // 3. curver without shell subkey
  734. // 4. progid without shell subkey
  735. //
  736. HKEY hkShell;
  737. if (SUCCEEDED(_AssocOpenRegKey(hkOut, L"shell", &hkShell)))
  738. {
  739. RegCloseKey(hkShell);
  740. RegCloseKey(hkTemp); // close old ProgID key
  741. }
  742. else if (SUCCEEDED(_AssocOpenRegKey(hkTemp, L"shell", &hkShell)))
  743. {
  744. RegCloseKey(hkShell);
  745. RegCloseKey(hkOut);
  746. hkOut = hkTemp;
  747. }
  748. else
  749. RegCloseKey(hkTemp);
  750. }
  751. else // reset!
  752. hkOut = hkTemp;
  753. }
  754. }
  755. return hkOut;
  756. }
  757. HRESULT CAssocProgidElement::_InitSource()
  758. {
  759. HRESULT hr = S_OK;
  760. // we need to init from an extension or Progid.
  761. // we also support redirection
  762. LPWSTR pszProgid;
  763. if (_pszInit[0] == L'.')
  764. {
  765. hr = _QuerySourceCreateFromKey(HKEY_CLASSES_ROOT, _pszInit, FALSE, &_pqsExt);
  766. if (SUCCEEDED(hr))
  767. hr = _pqsExt->QueryValueString(NULL, NULL, &pszProgid);
  768. }
  769. else
  770. pszProgid = _pszInit;
  771. if (SUCCEEDED(hr))
  772. {
  773. HKEY hk = _OpenProgidKey(pszProgid);
  774. if (hk)
  775. {
  776. hr = _QuerySourceCreateFromKey(hk, NULL, FALSE, &_pqs);
  777. RegCloseKey(hk);
  778. }
  779. else
  780. hr = E_UNEXPECTED;
  781. if (pszProgid != _pszInit)
  782. CoTaskMemFree(pszProgid);
  783. }
  784. // for legacy compat reasons, we support
  785. // falling back to "HKEY_CLASSES_ROOT\.ext"
  786. if (FAILED(hr) && _pqsExt)
  787. {
  788. _pqs = _pqsExt;
  789. _pqsExt = NULL;
  790. hr = S_FALSE;
  791. }
  792. return hr;
  793. }
  794. HRESULT CAssocProgidElement::QueryString(ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  795. {
  796. HRESULT hr = CAssocShellElement::QueryString(query, pszCue, ppsz);
  797. if (FAILED(hr))
  798. {
  799. if ((AQF_QUERY_INITCLASS & query) && _pqsExt)
  800. hr = _QueryKeyValAny(_QuerySourceString, s_rgqkvExt, ARRAYSIZE(s_rgqkvExt), _pqsExt, query, pszCue, ppsz);
  801. else if (_pqs)
  802. {
  803. switch (query)
  804. {
  805. case AQS_FRIENDLYTYPENAME:
  806. // we like to query the default value
  807. hr = _pqs->QueryValueString(NULL, NULL, ppsz);
  808. break;
  809. }
  810. }
  811. }
  812. return hr;
  813. }
  814. STDAPI _SHAllocLoadString(HINSTANCE hinst, int ids, PWSTR *ppsz)
  815. {
  816. WCHAR sz[MAX_PATH];
  817. LoadStringW(hinst, ids, sz, ARRAYSIZE(sz));
  818. return SHStrDupW(sz, ppsz);
  819. }
  820. HRESULT CAssocFolderElement::QueryString(ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  821. {
  822. if (query == AQS_FRIENDLYTYPENAME)
  823. return _SHAllocLoadString(HINST_THISDLL, IDS_FOLDERTYPENAME, ppsz);
  824. else
  825. return CAssocShellElement::QueryString(query, pszCue, ppsz);
  826. }
  827. HRESULT _GetFileTypeName(PWSTR pszExt, PWSTR *ppsz)
  828. {
  829. if (pszExt && pszExt[0] == L'.' && pszExt[1])
  830. {
  831. WCHAR sz[MAX_PATH];
  832. WCHAR szTemplate[128]; // "%s File"
  833. CharUpperW(pszExt);
  834. LoadStringW(HINST_THISDLL, IDS_EXTTYPETEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
  835. wnsprintfW(sz, ARRAYSIZE(sz), szTemplate, pszExt + 1);
  836. return SHStrDupW(sz, ppsz);
  837. }
  838. else
  839. {
  840. // load the file description "File"
  841. return _SHAllocLoadString(HINST_THISDLL, IDS_FILETYPENAME, ppsz);
  842. }
  843. }
  844. HRESULT CAssocStarElement::QueryString(ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  845. {
  846. if (query == AQS_FRIENDLYTYPENAME)
  847. return _GetFileTypeName(_pszInit, ppsz);
  848. else
  849. return CAssocShellElement::QueryString(query, pszCue, ppsz);
  850. }
  851. HRESULT _ExeFromCmd(PWSTR pszCommand, PWSTR *ppsz)
  852. {
  853. // we just need to find where the params begin, and the exe ends...
  854. HRESULT hr = S_OK;
  855. PWSTR pch = PathGetArgsW(pszCommand);
  856. WCHAR szExe[MAX_PATH];
  857. if (*pch)
  858. *(--pch) = 0;
  859. else
  860. pch = NULL;
  861. StrCpyNW(szExe, pszCommand, ARRAYSIZE(szExe));
  862. StrTrimW(szExe, L" \t");
  863. PathUnquoteSpacesW(szExe);
  864. //
  865. // WARNING: Expensive disk hits all over!
  866. //
  867. // We check for %1 since it is what appears under (for example) HKEY_CLASSES_ROOT\exefile\shell\open\command
  868. // This will save us a chain of 35 calls to _PathIsFile("%1") when launching or getting a
  869. // context menu on a shortcut to an .exe or .bat file.
  870. if (0 == StrCmpW(szExe, L"%1"))
  871. hr = S_FALSE;
  872. else if (!_PathIsFile(szExe))
  873. {
  874. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  875. if (PathIsFileSpecW(szExe))
  876. {
  877. if (_GetAppPath(szExe, szExe, ARRAYSIZE(szExe)))
  878. {
  879. if (_PathIsFile(szExe))
  880. hr = S_OK;
  881. }
  882. else if (PathFindOnPathExW(szExe, NULL, PFOPEX_DEFAULT | PFOPEX_OPTIONAL))
  883. {
  884. // the find does a disk check for us...
  885. hr = S_OK;
  886. }
  887. }
  888. else
  889. {
  890. //
  891. // sometimes the path is not properly quoted.
  892. // these keys will still work because of the
  893. // way CreateProcess works, but we need to do
  894. // some fiddling to figure that out.
  895. //
  896. // if we found args, put them back...
  897. // and try some different args
  898. while (pch)
  899. {
  900. *pch++ = L' ';
  901. if (pch = StrChrW(pch, L' '))
  902. *pch = 0;
  903. StrCpyNW(szExe, pszCommand, ARRAYSIZE(szExe));
  904. StrTrimW(szExe, L" \t");
  905. if (_PathIsFile(szExe))
  906. {
  907. hr = S_OK;
  908. // this means that we found something
  909. // but the command line was kinda screwed
  910. break;
  911. }
  912. }// while (pch)
  913. }
  914. }
  915. if (S_OK == hr && pch)
  916. {
  917. // currently right before the args, on a NULL terminator
  918. ASSERT(!*pch);
  919. pch++;
  920. if (0 == StrCmpNIW(PathFindFileNameW(szExe), L"rundll", ARRAYSIZE(L"rundll") -1))
  921. {
  922. PWSTR pchComma = StrChrW(pch, L',');
  923. // make the comma the beginning of the args
  924. if (pchComma)
  925. *pchComma = 0;
  926. if (!*(PathFindExtensionW(pch))
  927. && lstrlenW(++pchComma) > SIZECHARS(L".dll"))
  928. {
  929. StrCatW(pch, L".dll");
  930. }
  931. // can we instead just do PFOPX()
  932. // cuz i think that rundll just checks for
  933. // the comma
  934. StrCpyNW(szExe, pch, ARRAYSIZE(szExe));
  935. StrTrimW(szExe, L" \t");
  936. if (_PathIsFile(szExe)
  937. || PathFindOnPathExW(szExe, NULL, 0))
  938. {
  939. hr = S_OK;
  940. }
  941. }
  942. }
  943. if (SUCCEEDED(hr))
  944. hr = SHStrDupW(szExe, ppsz);
  945. return hr;
  946. }
  947. HRESULT CAssocShellVerbElement::QueryString(ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  948. {
  949. HRESULT hr = CAssocElement::QueryString(query, pszCue, ppsz);
  950. if (FAILED(hr))
  951. {
  952. // we havent scored yet
  953. switch (query)
  954. {
  955. case AQVS_DDEAPPLICATION:
  956. // we make one up
  957. hr = QueryString(AQVS_APPLICATION_PATH, NULL, ppsz);
  958. if (SUCCEEDED(hr))
  959. {
  960. PathRemoveExtensionW(*ppsz);
  961. PathStripPathW(*ppsz);
  962. ASSERT(**ppsz);
  963. }
  964. break;
  965. case AQVS_DDETOPIC:
  966. hr = SHStrDupW(L"System", ppsz);
  967. break;
  968. case AQVS_APPLICATION_FRIENDLYNAME:
  969. // need to delegate to the application element
  970. if (!_fIsApp)
  971. {
  972. IAssociationElement *pae;
  973. hr = _GetAppDelegate(IID_PPV_ARG(IAssociationElement, &pae));
  974. if (SUCCEEDED(hr))
  975. {
  976. hr = pae->QueryString(AQVS_APPLICATION_FRIENDLYNAME, NULL, ppsz);
  977. pae->Release();
  978. }
  979. }
  980. break;
  981. case AQVS_APPLICATION_PATH:
  982. {
  983. CSmartCoTaskMem<OLECHAR> spszCmd;
  984. hr = CAssocElement::QueryString(AQVS_COMMAND, NULL, &spszCmd);
  985. if (SUCCEEDED(hr))
  986. {
  987. hr = _ExeFromCmd(spszCmd, ppsz);
  988. }
  989. }
  990. }
  991. }
  992. return hr;
  993. }
  994. HRESULT CAssocShellVerbElement::QueryObject(ASSOCQUERY query, PCWSTR pszCue, REFIID riid, void **ppv)
  995. {
  996. HRESULT hr = E_INVALIDARG;
  997. if (query == AQVO_APPLICATION_DELEGATE)
  998. {
  999. hr = _GetAppDelegate(riid, ppv);
  1000. }
  1001. return hr;
  1002. }
  1003. HRESULT CAssocShellVerbElement::_GetAppDelegate(REFIID riid, void **ppv)
  1004. {
  1005. CSmartCoTaskMem<OLECHAR> spszApp;
  1006. HRESULT hr = QueryString(AQVS_APPLICATION_PATH, NULL, &spszApp);
  1007. if (SUCCEEDED(hr))
  1008. {
  1009. IPersistString2 *pips;
  1010. hr = AssocCreateElement(CLSID_AssocApplicationElement, IID_PPV_ARG(IPersistString2, &pips));
  1011. if (SUCCEEDED(hr))
  1012. {
  1013. hr = pips->SetString(spszApp);
  1014. if (SUCCEEDED(hr))
  1015. hr = pips->QueryInterface(riid, ppv);
  1016. pips->Release();
  1017. }
  1018. }
  1019. return hr;
  1020. }
  1021. HRESULT CAssocPerceivedElement::_InitSource()
  1022. {
  1023. // maybe support Content Type?
  1024. WCHAR sz[64];
  1025. DWORD cb = sizeof(sz);
  1026. if (ERROR_SUCCESS == SHGetValueW(HKEY_CLASSES_ROOT, _pszInit, L"PerceivedType", NULL, sz, &cb))
  1027. {
  1028. return _QuerySourceCreateFromKey2(HKEY_CLASSES_ROOT, L"SystemFileAssociations", sz, &_pqs);
  1029. }
  1030. return E_FAIL;
  1031. }
  1032. class CAssocClientElement : public CAssocShellElement
  1033. {
  1034. public:
  1035. // overload QS to return default MUI strings
  1036. STDMETHODIMP QueryString(
  1037. ASSOCQUERY query,
  1038. PCWSTR pszCue,
  1039. PWSTR *ppsz);
  1040. // IPersist
  1041. STDMETHODIMP GetClassID(CLSID *pclsid)
  1042. { *pclsid = CLSID_AssocClientElement; return S_OK;}
  1043. protected:
  1044. virtual HRESULT _InitSource();
  1045. private:
  1046. HRESULT _InitSourceFromKey(HKEY hkRoot, LPCWSTR pszKey);
  1047. HRESULT _FixNetscapeRegistration();
  1048. BOOL _CreateRepairedNetscapeRegistration(HKEY hkNSCopy);
  1049. };
  1050. HRESULT CAssocClientElement::QueryString(ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz)
  1051. {
  1052. HRESULT hr;
  1053. switch (query)
  1054. {
  1055. case AQS_FRIENDLYTYPENAME:
  1056. // First try LocalizedString; if that fails, then use the default value
  1057. // for backwards compatibility.
  1058. hr = CAssocShellElement::QueryString(AQNS_NAMED_MUI_STRING, L"LocalizedString", ppsz);
  1059. if (FAILED(hr))
  1060. {
  1061. hr = CAssocShellElement::QueryString(AQN_NAMED_VALUE, NULL, ppsz);
  1062. }
  1063. break;
  1064. case AQS_DEFAULTICON:
  1065. // First try DefaultIcon; if that fails then use the first icon of the EXE
  1066. // associated with the "open" verb.
  1067. hr = CAssocShellElement::QueryString(AQS_DEFAULTICON, pszCue, ppsz);
  1068. if (FAILED(hr))
  1069. {
  1070. hr = CAssocShellElement::QueryString(AQVS_APPLICATION_PATH, L"open", ppsz);
  1071. }
  1072. break;
  1073. default:
  1074. hr = CAssocShellElement::QueryString(query, pszCue, ppsz);
  1075. break;
  1076. }
  1077. return hr;
  1078. }
  1079. HRESULT CAssocClientElement::_InitSourceFromKey(HKEY hkRoot, LPCWSTR pszKey)
  1080. {
  1081. DWORD dwType, cbSize;
  1082. WCHAR szClient[80];
  1083. cbSize = sizeof(szClient);
  1084. LONG lRc = SHGetValueW(hkRoot, pszKey, NULL, &dwType, szClient, &cbSize);
  1085. if (lRc == ERROR_SUCCESS && dwType == REG_SZ && szClient[0])
  1086. {
  1087. // Client info is kept in HKLM
  1088. HRESULT hr = _QuerySourceCreateFromKey2(HKEY_LOCAL_MACHINE, pszKey, szClient, &_pqs);
  1089. //
  1090. // If this is the Mail client and the client is Netscape Messenger,
  1091. // then we need to do extra work to detect the broken Netscape
  1092. // Navigator 4.75 mail client and fix its registration because
  1093. // Netscape registered incorrectly. They always registered
  1094. // incorrectly, but since the only access point before Windows XP
  1095. // was an obscure menu option under IE/Tools/Mail and News, they
  1096. // never noticed that it was wrong.
  1097. //
  1098. if (SUCCEEDED(hr) &&
  1099. StrCmpICW(_pszInit, L"mail") == 0 &&
  1100. StrCmpICW(szClient, L"Netscape Messenger") == 0 &&
  1101. FAILED(QueryExists(AQVS_COMMAND, L"open")))
  1102. {
  1103. hr = _FixNetscapeRegistration();
  1104. }
  1105. return hr;
  1106. }
  1107. else
  1108. {
  1109. return E_FAIL; // no registered client
  1110. }
  1111. }
  1112. // Create a volatile copy of the Netscape registration and repair it.
  1113. // We don't touch the original registration because...
  1114. //
  1115. // 1. Its existence may break the Netscape uninstaller, and
  1116. // 2. We may be running as non-administrator so don't have write access
  1117. // anyway.
  1118. HRESULT CAssocClientElement::_FixNetscapeRegistration()
  1119. {
  1120. HKEY hkMail;
  1121. HRESULT hr = E_FAIL;
  1122. if (ERROR_SUCCESS == RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Clients\\Mail",
  1123. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
  1124. &hkMail, NULL))
  1125. {
  1126. HKEY hkNSCopy;
  1127. DWORD dwDisposition;
  1128. if (ERROR_SUCCESS == RegCreateKeyExW(hkMail, L"Netscape Messenger",
  1129. 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL,
  1130. &hkNSCopy, &dwDisposition))
  1131. {
  1132. if (dwDisposition == REG_OPENED_EXISTING_KEY ||
  1133. _CreateRepairedNetscapeRegistration(hkNSCopy))
  1134. {
  1135. // Now swap in the good registration for the bad one
  1136. _pqs->Release();
  1137. hr = _QuerySourceCreateFromKey(hkNSCopy, NULL, FALSE, &_pqs);
  1138. }
  1139. RegCloseKey(hkNSCopy);
  1140. }
  1141. if (FAILED(hr))
  1142. {
  1143. SHDeleteKeyW(hkMail, L"Netscape Messenger");
  1144. }
  1145. RegCloseKey(hkMail);
  1146. }
  1147. return hr;
  1148. }
  1149. LONG _RegQueryString(HKEY hk, PCWSTR pszSub, LPWSTR pszBuf, LONG cbBuf)
  1150. {
  1151. return RegQueryValueW(hk, pszSub, pszBuf, &cbBuf);
  1152. }
  1153. LONG _RegSetVolatileString(HKEY hk, PCWSTR pszSub, LPCWSTR pszBuf)
  1154. {
  1155. HKEY hkSub;
  1156. LONG lRc;
  1157. if (!pszSub || pszSub[0] == L'\0')
  1158. {
  1159. lRc = RegOpenKeyEx(hk, NULL, 0, KEY_WRITE, &hkSub);
  1160. }
  1161. else
  1162. {
  1163. lRc = RegCreateKeyExW(hk, pszSub,
  1164. 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL,
  1165. &hkSub, NULL);
  1166. }
  1167. if (lRc == ERROR_SUCCESS)
  1168. {
  1169. lRc = RegSetValueW(hkSub, NULL, REG_SZ, pszBuf, (lstrlenW(pszBuf) + 1) * sizeof(pszBuf[0]));
  1170. RegCloseKey(hkSub);
  1171. }
  1172. return lRc;
  1173. }
  1174. BOOL CAssocClientElement::_CreateRepairedNetscapeRegistration(HKEY hkNSCopy)
  1175. {
  1176. BOOL fSuccess = FALSE;
  1177. HKEY hkSrc;
  1178. // Sadly, we cannot use SHCopyKey because SHCopyKey does not work
  1179. // on volatile keys. So we just copy the keys we care about.
  1180. WCHAR szBuf[MAX_PATH];
  1181. if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  1182. L"Software\\Clients\\Mail\\Netscape Messenger",
  1183. 0, KEY_READ, &hkSrc))
  1184. {
  1185. // Copy default icon but don't panic if it's not there.
  1186. if (ERROR_SUCCESS == _RegQueryString(hkSrc, L"Protocols\\mailto\\DefaultIcon", szBuf, ARRAYSIZE(szBuf)))
  1187. {
  1188. // Great, Netscape also registers the wrong icon so we have to fix that too.
  1189. PathParseIconLocationW(szBuf);
  1190. StrCatBuffW(szBuf, L",-1349", ARRAYSIZE(szBuf));
  1191. _RegSetVolatileString(hkNSCopy, L"DefaultIcon", szBuf);
  1192. }
  1193. // Copy friendly name
  1194. if (ERROR_SUCCESS == _RegQueryString(hkSrc, NULL, szBuf, ARRAYSIZE(szBuf)) &&
  1195. ERROR_SUCCESS == _RegSetVolatileString(hkNSCopy, NULL, szBuf))
  1196. {
  1197. PWSTR pszExe;
  1198. // Copy command line, but with a new command line parameter
  1199. if (ERROR_SUCCESS == _RegQueryString(hkSrc, L"Protocols\\mailto\\shell\\open\\command", szBuf, ARRAYSIZE(szBuf)) &&
  1200. SUCCEEDED(_ExeFromCmd(szBuf, &pszExe)))
  1201. {
  1202. lstrcpynW(szBuf, pszExe, ARRAYSIZE(szBuf));
  1203. SHFree(pszExe);
  1204. PathQuoteSpacesW(szBuf);
  1205. StrCatBuffW(szBuf, L" -mail", ARRAYSIZE(szBuf));
  1206. if (ERROR_SUCCESS == _RegSetVolatileString(hkNSCopy, L"shell\\open\\command", szBuf))
  1207. {
  1208. fSuccess = TRUE;
  1209. }
  1210. }
  1211. }
  1212. RegCloseKey(hkSrc);
  1213. }
  1214. return fSuccess;
  1215. }
  1216. HRESULT CAssocClientElement::_InitSource()
  1217. {
  1218. // First try HKCU; if that doesn't work (no value set in HKCU or
  1219. // the value in HKCU is bogus), then try again with HKLM.
  1220. WCHAR szKey[MAX_PATH];
  1221. wnsprintfW(szKey, ARRAYSIZE(szKey), L"Software\\Clients\\%s", _pszInit);
  1222. HRESULT hr = _InitSourceFromKey(HKEY_CURRENT_USER, szKey);
  1223. if (FAILED(hr))
  1224. {
  1225. hr = _InitSourceFromKey(HKEY_LOCAL_MACHINE, szKey);
  1226. }
  1227. return hr;
  1228. }
  1229. HRESULT AssocCreateElement(REFCLSID clsid, REFIID riid, void **ppv)
  1230. {
  1231. IAssociationElement *pae = NULL;
  1232. if (clsid == CLSID_AssocShellElement)
  1233. pae = new CAssocShellElement();
  1234. else if (clsid == CLSID_AssocProgidElement)
  1235. pae = new CAssocProgidElement();
  1236. else if (clsid == CLSID_AssocClsidElement)
  1237. pae = new CAssocClsidElement();
  1238. else if (clsid == CLSID_AssocSystemElement)
  1239. pae = new CAssocSystemExtElement();
  1240. else if (clsid == CLSID_AssocPerceivedElement)
  1241. pae = new CAssocPerceivedElement();
  1242. else if (clsid == CLSID_AssocApplicationElement)
  1243. pae = new CAssocApplicationElement();
  1244. else if (clsid == CLSID_AssocFolderElement)
  1245. pae = new CAssocFolderElement();
  1246. else if (clsid == CLSID_AssocStarElement)
  1247. pae = new CAssocStarElement();
  1248. else if (clsid == CLSID_AssocClientElement)
  1249. pae = new CAssocClientElement();
  1250. HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
  1251. if (pae)
  1252. {
  1253. hr = pae->QueryInterface(riid, ppv);
  1254. pae->Release();
  1255. }
  1256. else
  1257. *ppv = 0;
  1258. return hr;
  1259. }