Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

967 lines
29 KiB

  1. #include "shellprv.h"
  2. #include <dpa.h>
  3. #include <enumt.h>
  4. typedef HRESULT (*PFNELEMCREATE)(const CLSID *pclsid, PCWSTR pszClass, IAssociationElement **ppae);
  5. typedef struct _AEINFO
  6. {
  7. ASSOCELEM_MASK mask;
  8. const CLSID *pclsid;
  9. PCWSTR pszClass; // NULL indicates to use the _pszClass
  10. PFNELEMCREATE pfnCreate;
  11. IAssociationElement *pae;
  12. } AEINFO;
  13. typedef enum
  14. {
  15. GETELEM_RETRY = -2,
  16. GETELEM_DONE = -1,
  17. GETELEM_TRYNEXT = 0,
  18. GETELEM_SUCCEEDED = 1,
  19. }GETELEMRESULT;
  20. #define TRYNEXT(gr) ((gr) >= GETELEM_TRYNEXT)
  21. HRESULT _QueryString(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz, const IID *piid)
  22. {
  23. return pae->QueryString(query, pszCue, ppsz);
  24. }
  25. HRESULT _QueryDirect(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, FLAGGED_BYTE_BLOB **ppblob, const IID *piid)
  26. {
  27. return pae->QueryDirect(query, pszCue, ppblob);
  28. }
  29. HRESULT _QueryDword(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, DWORD *pdw, const IID *piid)
  30. {
  31. return pae->QueryDword(query, pszCue, pdw);
  32. }
  33. HRESULT _QueryExists(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, void *pv, const IID *piid)
  34. {
  35. return pae->QueryExists(query, pszCue);
  36. }
  37. HRESULT _QueryObject(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, void **ppv, const IID *piid)
  38. {
  39. return pae->QueryObject(query, pszCue, *piid, ppv);
  40. }
  41. class CAssocArray : public IAssociationArray,
  42. public IAssociationArrayInitialize,
  43. public IQueryAssociations
  44. {
  45. public:
  46. CAssocArray() : _cRef(1), _hrInit(-1), _maskInclude(-1) {}
  47. ~CAssocArray() { _Reset(); }
  48. // IUnknown methods
  49. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  50. STDMETHODIMP_(ULONG) AddRef()
  51. {
  52. return ++_cRef;
  53. }
  54. STDMETHODIMP_(ULONG) Release()
  55. {
  56. if (--_cRef > 0)
  57. return _cRef;
  58. delete this;
  59. return 0;
  60. }
  61. // IAssociationArrayInitialize
  62. STDMETHODIMP InitClassElements(
  63. ASSOCELEM_MASK maskBase,
  64. PCWSTR pszClass);
  65. STDMETHODIMP InsertElements(
  66. ASSOCELEM_MASK mask,
  67. IEnumAssociationElements *peae);
  68. STDMETHODIMP FilterElements(ASSOCELEM_MASK maskInclude)
  69. { _maskInclude = maskInclude; return S_OK; }
  70. // IAssociationArray
  71. STDMETHODIMP EnumElements(
  72. ASSOCELEM_MASK mask,
  73. IEnumAssociationElements **ppeae);
  74. STDMETHODIMP QueryString(
  75. ASSOCELEM_MASK mask,
  76. ASSOCQUERY query,
  77. PCWSTR pszCue,
  78. PWSTR *ppsz)
  79. {
  80. return _QueryElementAny(_QueryString, mask, query, pszCue, ppsz, NULL);
  81. }
  82. STDMETHODIMP QueryDword(
  83. ASSOCELEM_MASK mask,
  84. ASSOCQUERY query,
  85. PCWSTR pszCue,
  86. DWORD *pdw)
  87. {
  88. return _QueryElementAny(_QueryDword, mask, query, pszCue, pdw, NULL);
  89. }
  90. STDMETHODIMP QueryDirect(
  91. ASSOCELEM_MASK mask,
  92. ASSOCQUERY query,
  93. PCWSTR pszCue,
  94. FLAGGED_BYTE_BLOB **ppblob)
  95. {
  96. return _QueryElementAny(_QueryDirect, mask, query, pszCue, ppblob, NULL);
  97. }
  98. STDMETHODIMP QueryExists(
  99. ASSOCELEM_MASK mask,
  100. ASSOCQUERY query,
  101. PCWSTR pszCue)
  102. {
  103. return _QueryElementAny(_QueryExists, mask, query, pszCue, (void*)NULL, NULL);
  104. }
  105. STDMETHODIMP QueryObject(
  106. ASSOCELEM_MASK mask,
  107. ASSOCQUERY query,
  108. PCWSTR pszCue,
  109. REFIID riid,
  110. void **ppv)
  111. {
  112. return _QueryElementAny(_QueryObject, mask, query, pszCue, ppv, &riid);
  113. }
  114. // IQueryAssociations methods
  115. STDMETHODIMP Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd);
  116. STDMETHODIMP GetString(ASSOCF flags, ASSOCSTR str, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut);
  117. STDMETHODIMP GetKey(ASSOCF flags, ASSOCKEY, LPCWSTR pszExtra, HKEY *phkeyOut);
  118. STDMETHODIMP GetData(ASSOCF flags, ASSOCDATA data, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut);
  119. STDMETHODIMP GetEnum(ASSOCF flags, ASSOCENUM assocenum, LPCWSTR pszExtra, REFIID riid, LPVOID *ppvOut)
  120. { return E_NOTIMPL; }
  121. GETELEMRESULT GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae);
  122. protected: // methods
  123. void _Reset();
  124. HRESULT _InsertSingleElement(IAssociationElement *pae);
  125. HRESULT _GetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem);
  126. void _SetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement *paeVerb, IAssociationElement *paeVerbParent);
  127. GETELEMRESULT _GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae);
  128. BOOL _FirstElement(ASSOCELEM_MASK mask, IAssociationElement **ppae);
  129. HRESULT _GetVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem);
  130. void _InitDelayedElements(int i, ASSOCELEM_MASK mask);
  131. template<class T> HRESULT _QueryElementAny(HRESULT (CALLBACK *pfnAny)(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, T pData, const IID *piid), ASSOCELEM_MASK mask, ASSOCQUERY query, PCWSTR pszCue, T pData, const IID *piid)
  132. {
  133. mask &= _maskInclude;
  134. IAssociationElement *pae;
  135. HRESULT hr = E_FAIL;
  136. if ((AQF_CUEIS_SHELLVERB & query) && _CacheVerb(query))
  137. {
  138. // delegate to the verb object if the cue is a verb
  139. // except for AQVS_APPLICATION_FRIENDLYNAME which
  140. // has some funky delegation issues.
  141. IAssociationElement *paeParent;
  142. hr = _GetVerbElement(mask, pszCue, &pae, &paeParent);
  143. if (SUCCEEDED(hr))
  144. {
  145. if (query == AQVS_APPLICATION_FRIENDLYNAME)
  146. hr = pfnAny(paeParent, query, pszCue, pData, piid);
  147. else
  148. hr = pfnAny(pae, query, NULL, pData, piid);
  149. pae->Release();
  150. paeParent->Release();
  151. }
  152. }
  153. else
  154. {
  155. for (int i = 0; FAILED(hr) && TRYNEXT(GetElement(i, mask, &pae)); i++)
  156. {
  157. if (pae)
  158. {
  159. hr = pfnAny(pae, query, pszCue, pData, piid);
  160. pae->Release();
  161. if (SUCCEEDED(hr))
  162. break;
  163. }
  164. }
  165. }
  166. return hr;
  167. }
  168. BOOL _CacheVerb(ASSOCQUERY query)
  169. {
  170. // if we are init'd with an app element and
  171. // querying for app specific values dont request the verb element
  172. return !_fUsingAppElement || (query != AQVS_APPLICATION_PATH && query != AQVS_APPLICATION_FRIENDLYNAME && query != AQVO_APPLICATION_DELEGATE);
  173. }
  174. private: // members
  175. LONG _cRef;
  176. HRESULT _hrInit;
  177. PWSTR _pszClass;
  178. ASSOCELEM_MASK _maskInclude;
  179. BOOL _fUsingAppElement;
  180. CDSA<AEINFO> _dsaElems;
  181. IEnumAssociationElements *_penumData;
  182. ASSOCELEM_MASK _maskData;
  183. IEnumAssociationElements *_penumExtra;
  184. ASSOCELEM_MASK _maskExtra;
  185. IAssociationElement *_paeVerb;
  186. PWSTR _pszVerb;
  187. ASSOCELEM_MASK _maskVerb;
  188. IAssociationElement *_paeVerbParent;
  189. };
  190. int CALLBACK _AeinfoDelete(AEINFO *paei, void *pv)
  191. {
  192. if (paei->pae)
  193. paei->pae->Release();
  194. return 1;
  195. }
  196. void CAssocArray::_Reset()
  197. {
  198. if (_hrInit != -1)
  199. {
  200. if (_dsaElems)
  201. _dsaElems.DestroyCallbackEx(_AeinfoDelete, (void*)NULL);
  202. if (_pszClass)
  203. {
  204. CoTaskMemFree(_pszClass);
  205. _pszClass = NULL;
  206. }
  207. ATOMICRELEASE(_penumData);
  208. ATOMICRELEASE(_penumExtra);
  209. ATOMICRELEASE(_paeVerb);
  210. if (_pszVerb)
  211. {
  212. LocalFree(_pszVerb);
  213. _pszVerb;
  214. }
  215. ATOMICRELEASE(_paeVerbParent);
  216. _fUsingAppElement = FALSE;
  217. _hrInit = -1;
  218. }
  219. }
  220. HRESULT CAssocArray::QueryInterface(REFIID riid, void **ppv)
  221. {
  222. static const QITAB qit[] =
  223. {
  224. QITABENT(CAssocArray, IAssociationArray),
  225. QITABENT(CAssocArray, IQueryAssociations),
  226. QITABENT(CAssocArray, IAssociationArrayInitialize),
  227. { 0 },
  228. };
  229. return QISearch(this, qit, riid, ppv);
  230. }
  231. #define AEINFOPROGID(m, s) { m, &CLSID_AssocProgidElement, s, AssocElemCreateForClass, NULL}
  232. #define MAKEAEINFO(m, c, s, p) { m, c, s, p, NULL}
  233. HRESULT AssocElemCreateForUser(const CLSID *pclsid, PCWSTR pszClass, IAssociationElement **ppae)
  234. {
  235. WCHAR sz[64];
  236. DWORD cb = sizeof(sz);
  237. HRESULT hr = SKGetValue(SHELLKEY_HKCU_FILEEXTS, pszClass, L"Progid", NULL, sz, &cb);
  238. if(SUCCEEDED(hr))
  239. {
  240. hr = AssocElemCreateForClass(&CLSID_AssocProgidElement, sz, ppae);
  241. }
  242. if (FAILED(hr))
  243. {
  244. cb = sizeof(sz);
  245. hr = SKGetValue(SHELLKEY_HKCU_FILEEXTS, pszClass, L"Application", NULL, sz, &cb);
  246. if (SUCCEEDED(hr))
  247. {
  248. hr = AssocElemCreateForClass(&CLSID_AssocApplicationElement, sz, ppae);
  249. }
  250. }
  251. return hr;
  252. }
  253. static const AEINFO s_rgaeinfoProgid[] =
  254. {
  255. AEINFOPROGID(ASSOCELEM_DEFAULT, NULL),
  256. };
  257. static const AEINFO s_rgaeinfoExtension[] =
  258. {
  259. MAKEAEINFO(ASSOCELEM_USER, NULL, NULL, AssocElemCreateForUser), // app or progid
  260. AEINFOPROGID(ASSOCELEM_DEFAULT, NULL),
  261. MAKEAEINFO(ASSOCELEM_SYSTEM_EXT, &CLSID_AssocSystemElement, NULL, AssocElemCreateForClass),
  262. MAKEAEINFO(ASSOCELEM_SYSTEM_PERCEIVED, &CLSID_AssocPerceivedElement, NULL, AssocElemCreateForClass),
  263. };
  264. static const AEINFO s_rgaeinfoClsid[] =
  265. {
  266. // MAKEAEINFO(UserClsid), // clsid
  267. MAKEAEINFO(ASSOCELEM_DEFAULT, &CLSID_AssocClsidElement, NULL, AssocElemCreateForClass),
  268. // AEINFOPROGID(ASSOCELEM_PROGID, NULL), // progid
  269. };
  270. static const AEINFO s_aeinfoFolder = MAKEAEINFO(ASSOCELEM_BASEIS_FOLDER, &CLSID_AssocFolderElement, NULL, AssocElemCreateForClass);
  271. static const AEINFO s_aeinfoStar = MAKEAEINFO(ASSOCELEM_BASEIS_STAR, &CLSID_AssocStarElement, NULL, AssocElemCreateForClass);
  272. BOOL CAssocArray::_FirstElement(ASSOCELEM_MASK mask, IAssociationElement **ppae)
  273. {
  274. GETELEMRESULT res = GETELEM_TRYNEXT;
  275. int cTrys = 0;
  276. while (res == GETELEM_TRYNEXT)
  277. {
  278. // if it fails or succeeds we are done
  279. // but if it calls TRYNEXT we loop
  280. res = GetElement(cTrys++, mask, ppae);
  281. }
  282. return res == GETELEM_SUCCEEDED;
  283. }
  284. HRESULT CAssocArray::InitClassElements(ASSOCELEM_MASK maskBase, PCWSTR pszClass)
  285. {
  286. _Reset();
  287. // depending on what we think this is,
  288. // we do things a little differently
  289. ASSERT(*pszClass);
  290. HRESULT hr = SHStrDup(pszClass, &_pszClass);
  291. if (SUCCEEDED(hr))
  292. {
  293. // 6 is the most that we will need
  294. // in InitClassElements()
  295. hr = _dsaElems.Create(6) ? S_OK : E_OUTOFMEMORY;
  296. if (SUCCEEDED(hr))
  297. {
  298. const AEINFO *rgAeinfo;
  299. DWORD cAeinfo;
  300. if (*_pszClass == L'.')
  301. {
  302. rgAeinfo = s_rgaeinfoExtension;
  303. cAeinfo = ARRAYSIZE(s_rgaeinfoExtension);
  304. }
  305. else if (*_pszClass == L'{')
  306. {
  307. rgAeinfo = s_rgaeinfoClsid;
  308. cAeinfo = ARRAYSIZE(s_rgaeinfoClsid);
  309. }
  310. else
  311. {
  312. rgAeinfo = s_rgaeinfoProgid;
  313. cAeinfo = ARRAYSIZE(s_rgaeinfoProgid);
  314. }
  315. for (DWORD i = 0; i < cAeinfo; i++)
  316. {
  317. _dsaElems.AppendItem((AEINFO *)&rgAeinfo[i]);
  318. }
  319. if (ASSOCELEM_BASEIS_FOLDER & maskBase)
  320. _dsaElems.AppendItem((AEINFO *)&s_aeinfoFolder);
  321. if (ASSOCELEM_BASEIS_STAR & maskBase)
  322. _dsaElems.AppendItem((AEINFO *)&s_aeinfoStar);
  323. // we return S_FALSE if there is no default or user
  324. // association. we treat this as an unknown type
  325. IAssociationElement *pae;
  326. if (_FirstElement(ASSOCELEM_USER | ASSOCELEM_DEFAULT, &pae))
  327. {
  328. pae->Release();
  329. hr = S_OK;
  330. }
  331. else
  332. hr = S_FALSE;
  333. }
  334. }
  335. _hrInit = hr;
  336. return hr;
  337. }
  338. HRESULT CAssocArray::InsertElements(ASSOCELEM_MASK mask, IEnumAssociationElements *peae)
  339. {
  340. HRESULT hr = E_UNEXPECTED;
  341. if (!_penumData && (mask & ASSOCELEM_DATA))
  342. {
  343. _penumData = peae;
  344. peae->AddRef();
  345. _maskData = mask;
  346. hr = S_OK;
  347. }
  348. if (!_penumExtra && (mask & ASSOCELEM_EXTRA))
  349. {
  350. _penumExtra = peae;
  351. peae->AddRef();
  352. _maskExtra = mask;
  353. hr = S_OK;
  354. }
  355. return hr;
  356. }
  357. class CEnumAssocElems : public CEnumAssociationElements
  358. {
  359. public:
  360. CEnumAssocElems(CAssocArray *paa, ASSOCELEM_MASK mask) : _mask(mask), _paa(paa)
  361. { _paa->AddRef(); }
  362. ~CEnumAssocElems() { _paa->Release(); }
  363. protected: // methods
  364. virtual BOOL _Next(IAssociationElement **ppae);
  365. protected:
  366. ASSOCELEM_MASK _mask;
  367. CAssocArray *_paa;
  368. };
  369. HRESULT CAssocArray::EnumElements(ASSOCELEM_MASK mask, IEnumAssociationElements **ppeae)
  370. {
  371. mask &= _maskInclude;
  372. *ppeae = new CEnumAssocElems(this, mask);
  373. return *ppeae ? S_OK : E_OUTOFMEMORY;
  374. }
  375. void CAssocArray::_InitDelayedElements(int i, ASSOCELEM_MASK mask)
  376. {
  377. ULONG c;
  378. IAssociationElement *pae;
  379. if (i == 0 && _penumData && ((mask & _maskData) == _maskData))
  380. {
  381. // init the DSA with the data
  382. int iInsert = 0;
  383. while (S_OK == _penumData->Next(1, &pae, &c))
  384. {
  385. AEINFO ae = {ASSOCELEM_DATA, NULL, NULL, NULL, pae};
  386. if (DSA_ERR != _dsaElems.InsertItem(iInsert++, &ae))
  387. {
  388. pae->AddRef();
  389. }
  390. pae->Release();
  391. }
  392. ATOMICRELEASE(_penumData);
  393. }
  394. if (_penumExtra && (mask & ASSOCELEM_EXTRA) && i == _dsaElems.GetItemCount())
  395. {
  396. // init the DSA with the data
  397. while (S_OK == _penumExtra->Next(1, &pae, &c))
  398. {
  399. AEINFO ae = {ASSOCELEM_EXTRA, NULL, NULL, NULL, pae};
  400. if (DSA_ERR != _dsaElems.AppendItem(&ae))
  401. {
  402. pae->AddRef();
  403. }
  404. pae->Release();
  405. }
  406. ATOMICRELEASE(_penumExtra);
  407. }
  408. }
  409. GETELEMRESULT CAssocArray::_GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae)
  410. {
  411. GETELEMRESULT res = GETELEM_DONE;
  412. AEINFO *paei = _dsaElems.GetItemPtr(i);
  413. if (paei)
  414. {
  415. // if this is one that we want to use
  416. // please do
  417. if (paei->mask & mask)
  418. {
  419. if (!paei->pae)
  420. {
  421. // try to create only once
  422. PCWSTR pszClass = paei->pszClass ? paei->pszClass : _pszClass;
  423. // make sure we dont query again
  424. // if we dont need to
  425. HRESULT hr = paei->pfnCreate(paei->pclsid, pszClass, &paei->pae);
  426. if (FAILED(hr))
  427. {
  428. _dsaElems.DeleteItem(i);
  429. // retry the current index
  430. res = GETELEM_RETRY;
  431. }
  432. else if (hr == S_FALSE)
  433. {
  434. // this is returned when the element
  435. // is valid but points to an alternate location
  436. // specifically the HKCR\Progid falls back to HKCR\.ext
  437. // which is kind of weird. maybe we should move this
  438. // to ASSOCELEM_EXTRA???
  439. }
  440. }
  441. if (paei->pae)
  442. {
  443. *ppae = paei->pae;
  444. paei->pae->AddRef();
  445. res = GETELEM_SUCCEEDED;
  446. }
  447. }
  448. else
  449. {
  450. res = GETELEM_TRYNEXT;
  451. }
  452. }
  453. return res;
  454. }
  455. GETELEMRESULT CAssocArray::GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae)
  456. {
  457. GETELEMRESULT res = GETELEM_RETRY;
  458. *ppae = 0;
  459. if (_dsaElems)
  460. {
  461. _InitDelayedElements(i, mask);
  462. while (GETELEM_RETRY == res && i < _dsaElems.GetItemCount())
  463. {
  464. res = _GetElement(i, mask, ppae);
  465. }
  466. }
  467. return res;
  468. }
  469. BOOL CEnumAssocElems::_Next(IAssociationElement **ppae)
  470. {
  471. GETELEMRESULT res = GETELEM_TRYNEXT;
  472. UINT cTrys = 0;
  473. while (res == GETELEM_TRYNEXT)
  474. {
  475. // if it fails or succeeds we are done
  476. // but if it calls TRYNEXT we loop
  477. res = _paa->GetElement(_cNext + cTrys++, _mask, ppae);
  478. }
  479. // fix up _cNext when we skip
  480. _cNext += cTrys - 1;
  481. return res == GETELEM_SUCCEEDED;
  482. }
  483. typedef struct
  484. {
  485. ASSOCQUERY query;
  486. PCWSTR pszCue;
  487. } AQXLATE;
  488. #define MAKEAQX(a, q, s) { q, s }
  489. static const AQXLATE s_rgaqxStrings[] =
  490. {
  491. MAKEAQX(ASSOCSTR_COMMAND, AQVS_COMMAND, NULL),
  492. MAKEAQX(ASSOCSTR_EXECUTABLE, AQVS_APPLICATION_PATH, NULL),
  493. MAKEAQX(ASSOCSTR_FRIENDLYDOCNAME, AQS_FRIENDLYTYPENAME, NULL), // friendly name of the document type
  494. MAKEAQX(ASSOCSTR_FRIENDLYAPPNAME, AQVS_APPLICATION_FRIENDLYNAME, NULL),
  495. MAKEAQX(ASSOCSTR_NOOPEN, AQNS_NAMED_MUI_STRING, L"NoOpen"),
  496. MAKEAQX(ASSOCSTR_SHELLNEWVALUE, (ASSOCQUERY)0, NULL),
  497. MAKEAQX(ASSOCSTR_DDECOMMAND, AQVS_DDECOMMAND, NULL),
  498. MAKEAQX(ASSOCSTR_DDEIFEXEC, AQVS_DDEIFEXEC, NULL),
  499. MAKEAQX(ASSOCSTR_DDEAPPLICATION, AQVS_DDEAPPLICATION, NULL),
  500. MAKEAQX(ASSOCSTR_DDETOPIC, AQVS_DDETOPIC, NULL),
  501. MAKEAQX(ASSOCSTR_INFOTIP, AQNS_NAMED_MUI_STRING, L"InfoTip"),
  502. MAKEAQX(ASSOCSTR_QUICKTIP, AQNS_NAMED_MUI_STRING, L"QuickTip"),
  503. MAKEAQX(ASSOCSTR_TILEINFO, AQNS_NAMED_MUI_STRING, L"TileInfo"),
  504. MAKEAQX(ASSOCSTR_CONTENTTYPE, AQS_CONTENTTYPE, NULL),
  505. MAKEAQX(ASSOCSTR_DEFAULTICON, AQS_DEFAULTICON, NULL),
  506. MAKEAQX(ASSOCSTR_SHELLEXTENSION, AQNS_SHELLEX_HANDLER, NULL),
  507. };
  508. HRESULT _CopyOut(BOOL fNoTruncate, PCWSTR pszIn, PWSTR psz, DWORD *pcch)
  509. {
  510. // if caller doesnt want any return size,
  511. // the incoming pointer is actually the size of the buffer
  512. ASSERT(pcch);
  513. ASSERT(psz || !IS_INTRESOURCE(pcch));
  514. HRESULT hr;
  515. DWORD cch = IS_INTRESOURCE(pcch) ? PtrToUlong(pcch) : *pcch;
  516. DWORD cchStr = lstrlenW(pszIn);
  517. if (psz)
  518. {
  519. if (!fNoTruncate || cch > cchStr)
  520. {
  521. StrCpyNW(psz, pszIn, cch);
  522. hr = S_OK;
  523. }
  524. else
  525. hr = E_POINTER;
  526. }
  527. else
  528. hr = S_FALSE;
  529. // return the number of chars written/required
  530. if (!IS_INTRESOURCE(pcch))
  531. *pcch = (hr == S_OK) ? lstrlen(psz) + 1 : cchStr + 1;
  532. return hr;
  533. }
  534. ASSOCELEM_MASK _MaskFromFlags(ASSOCF flags)
  535. {
  536. ASSOCELEM_MASK mask = ASSOCELEM_MASK_QUERYNORMAL;
  537. if (flags & ASSOCF_IGNOREBASECLASS)
  538. mask &= (ASSOCELEM_USER | ASSOCELEM_DEFAULT);
  539. if (flags & ASSOCF_NOUSERSETTINGS)
  540. mask &= ~ASSOCELEM_USER;
  541. return mask;
  542. }
  543. HRESULT CAssocArray::GetString(ASSOCF flags, ASSOCSTR str, LPCTSTR pszCue, LPTSTR pszOut, DWORD *pcchOut)
  544. {
  545. HRESULT hr = E_UNEXPECTED;
  546. if (str && str < ASSOCSTR_MAX && pcchOut && (pszOut || !IS_INTRESOURCE(pcchOut)))
  547. {
  548. // subtract the first one to make a zero based offset
  549. int index = str - ASSOCSTR_COMMAND;
  550. if (!pszCue)
  551. pszCue = s_rgaqxStrings[index].pszCue;
  552. if (s_rgaqxStrings[index].query)
  553. {
  554. PWSTR psz;
  555. hr = QueryString(_MaskFromFlags(flags), s_rgaqxStrings[index].query, pszCue, &psz);
  556. if (SUCCEEDED(hr))
  557. {
  558. hr = _CopyOut(flags & ASSOCF_NOTRUNCATE, psz, pszOut, pcchOut);
  559. CoTaskMemFree(psz);
  560. }
  561. }
  562. // else call win2k code for shellnew?
  563. //
  564. }
  565. return hr;
  566. }
  567. static const AQXLATE s_rgaqxDatas[] =
  568. {
  569. MAKEAQX(ASSOCDATA_MSIDESCRIPTOR, AQVD_MSIDESCRIPTOR, NULL),
  570. MAKEAQX(ASSOCDATA_NOACTIVATEHANDLER, AQV_NOACTIVATEHANDLER, NULL),
  571. MAKEAQX(ASSOCDATA_QUERYCLASSSTORE, AQN_NAMED_VALUE, L"QueryClassStore"),
  572. MAKEAQX(ASSOCDATA_HASPERUSERASSOC, (ASSOCQUERY)0, NULL),
  573. MAKEAQX(ASSOCDATA_EDITFLAGS, AQN_NAMED_VALUE, L"EditFlags"),
  574. MAKEAQX(ASSOCDATA_VALUE, AQN_NAMED_VALUE, NULL),
  575. };
  576. HRESULT _CopyDataOut(BOOL fNoTruncate, FLAGGED_BYTE_BLOB *pblob, void *pv, DWORD *pcb)
  577. {
  578. // if caller doesnt want any return size,
  579. // the incoming pointer is actually the size of the buffer
  580. ASSERT(pcb);
  581. ASSERT(pv || !IS_INTRESOURCE(pcb));
  582. HRESULT hr;
  583. DWORD cb = IS_INTRESOURCE(pcb) ? PtrToUlong(pcb) : *pcb;
  584. if (pv)
  585. {
  586. if (!fNoTruncate || cb >= pblob->clSize)
  587. {
  588. // copy the smaller of the src or dst
  589. cb = min(cb, pblob->clSize);
  590. memcpy(pv, pblob->abData, cb);
  591. hr = S_OK;
  592. }
  593. else
  594. hr = E_POINTER;
  595. }
  596. else
  597. hr = S_FALSE;
  598. // return the number of chars written/required
  599. if (!IS_INTRESOURCE(pcb))
  600. *pcb = pblob->clSize;
  601. return hr;
  602. }
  603. HRESULT CAssocArray::GetData(ASSOCF flags, ASSOCDATA data, PCWSTR pszCue, LPVOID pvOut, DWORD *pcbOut)
  604. {
  605. HRESULT hr = E_INVALIDARG;
  606. if (data && data < ASSOCDATA_MAX)
  607. {
  608. // subtract the first one to make a zero based offset
  609. int index = data - ASSOCDATA_MSIDESCRIPTOR;
  610. if (!pszCue)
  611. pszCue = s_rgaqxDatas[index].pszCue;
  612. if (s_rgaqxDatas[index].query)
  613. {
  614. if (pcbOut)
  615. {
  616. FLAGGED_BYTE_BLOB *pblob;
  617. hr = QueryDirect(_MaskFromFlags(flags), s_rgaqxDatas[index].query, pszCue, &pblob);
  618. if (SUCCEEDED(hr))
  619. {
  620. hr = _CopyDataOut(flags & ASSOCF_NOTRUNCATE, pblob, pvOut, pcbOut);
  621. CoTaskMemFree(pblob);
  622. }
  623. }
  624. else
  625. {
  626. hr = QueryExists(_MaskFromFlags(flags), s_rgaqxDatas[index].query, pszCue);
  627. }
  628. }
  629. else
  630. {
  631. if (data == ASSOCDATA_HASPERUSERASSOC)
  632. {
  633. IAssociationElement *pae;
  634. if (_FirstElement(ASSOCELEM_USER, &pae))
  635. {
  636. pae->Release();
  637. hr = S_OK;
  638. }
  639. else
  640. hr = S_FALSE;
  641. }
  642. }
  643. }
  644. return hr;
  645. }
  646. BOOL _IsSameVerb(PCWSTR pszV1, PCWSTR pszV2)
  647. {
  648. if (!pszV1 && !pszV2)
  649. return TRUE;
  650. else if (pszV1 && pszV2 && 0 == StrCmpIW(pszV1, pszV2))
  651. return TRUE;
  652. return FALSE;
  653. }
  654. HRESULT CAssocArray::_GetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem)
  655. {
  656. if (_paeVerb
  657. && mask == _maskVerb
  658. && _IsSameVerb(pszVerb, _pszVerb))
  659. {
  660. *ppaeVerb = _paeVerb;
  661. _paeVerb->AddRef();
  662. if (ppaeElem)
  663. {
  664. *ppaeElem = _paeVerbParent;
  665. _paeVerbParent->AddRef();
  666. }
  667. return S_OK;
  668. }
  669. else
  670. {
  671. *ppaeVerb = NULL;
  672. if (ppaeElem)
  673. *ppaeElem = NULL;
  674. return E_FAIL;
  675. }
  676. }
  677. void CAssocArray::_SetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement *paeVerb, IAssociationElement *paeVerbParent)
  678. {
  679. if (_paeVerb)
  680. {
  681. ATOMICRELEASE(_paeVerb);
  682. ATOMICRELEASE(_paeVerbParent);
  683. if (_pszVerb)
  684. LocalFree(_pszVerb);
  685. }
  686. if (pszVerb)
  687. _pszVerb = StrDupW(pszVerb);
  688. else
  689. _pszVerb = NULL;
  690. if (_pszVerb || !pszVerb)
  691. {
  692. _paeVerb = paeVerb;
  693. _paeVerb->AddRef();
  694. _paeVerbParent = paeVerbParent;
  695. _paeVerbParent->AddRef();
  696. _maskVerb = mask;
  697. }
  698. }
  699. HRESULT CAssocArray::_GetVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem)
  700. {
  701. HRESULT hr = _GetCachedVerbElement(mask, pszVerb, ppaeVerb, ppaeElem);
  702. IAssociationElement *pae;
  703. for (int i = 0; FAILED(hr) && TRYNEXT(GetElement(i, mask, &pae)); i++)
  704. {
  705. if (pae)
  706. {
  707. hr = pae->QueryObject(AQVO_SHELLVERB_DELEGATE, pszVerb, IID_PPV_ARG(IAssociationElement, ppaeVerb));
  708. if (SUCCEEDED(hr))
  709. {
  710. _SetCachedVerbElement(mask, pszVerb, *ppaeVerb, pae);
  711. if (ppaeElem)
  712. {
  713. pae->AddRef();
  714. *ppaeElem = pae;
  715. }
  716. }
  717. pae->Release();
  718. }
  719. }
  720. return hr;
  721. }
  722. HRESULT CAssocArray::GetKey(ASSOCF flags, ASSOCKEY key, LPCTSTR pszCue, HKEY *phkey)
  723. {
  724. HRESULT hr = E_INVALIDARG;
  725. *phkey = NULL;
  726. if (key && key < ASSOCKEY_MAX)
  727. {
  728. IAssociationElement *pae;
  729. switch (key)
  730. {
  731. case ASSOCKEY_SHELLEXECCLASS:
  732. {
  733. IAssociationElement *paeVerb;
  734. hr = _GetVerbElement(_MaskFromFlags(flags) & _maskInclude, pszCue, &paeVerb, &pae);
  735. if (SUCCEEDED(hr))
  736. {
  737. // we dont use the verb element
  738. paeVerb->Release();
  739. }
  740. }
  741. break;
  742. case ASSOCKEY_APP:
  743. // get app element
  744. hr = QueryObject(ASSOCELEM_MASK_QUERYNORMAL, AQVO_APPLICATION_DELEGATE, pszCue, IID_PPV_ARG(IAssociationElement, &pae));
  745. break;
  746. case ASSOCKEY_CLASS:
  747. {
  748. hr = _FirstElement(_MaskFromFlags(flags) & _maskInclude, &pae) == GETELEM_SUCCEEDED ? S_OK : E_FAIL;
  749. }
  750. break;
  751. case ASSOCKEY_BASECLASS:
  752. hr = _FirstElement(ASSOCELEM_BASE & _maskInclude, &pae) == GETELEM_SUCCEEDED ? S_OK : E_FAIL;
  753. break;
  754. }
  755. if (SUCCEEDED(hr))
  756. {
  757. hr = AssocKeyFromElement(pae, phkey);
  758. pae->Release();
  759. }
  760. }
  761. return hr;
  762. }
  763. HRESULT CAssocArray::_InsertSingleElement(IAssociationElement *pae)
  764. {
  765. AEINFO ae = {ASSOCELEM_DEFAULT, NULL, NULL, NULL, pae};
  766. ASSERT(!_dsaElems);
  767. if (_dsaElems.Create(1) && (DSA_ERR != _dsaElems.AppendItem(&ae)))
  768. {
  769. pae->AddRef();
  770. return S_OK;
  771. }
  772. return E_OUTOFMEMORY;
  773. }
  774. HRESULT CAssocArray::Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd)
  775. {
  776. IAssociationElement *pae;
  777. HRESULT hr = (pszAssoc || hkProgid) ? S_OK : E_INVALIDARG;
  778. if (SUCCEEDED(hr))
  779. {
  780. _Reset();
  781. _fUsingAppElement = flags & ASSOCF_INIT_BYEXENAME;
  782. if (hkProgid)
  783. {
  784. const CLSID *pclsid;
  785. if (_fUsingAppElement)
  786. pclsid = &CLSID_AssocApplicationElement;
  787. else if (flags & ASSOCF_INIT_NOREMAPCLSID)
  788. pclsid = &CLSID_AssocClsidElement;
  789. else
  790. pclsid = &CLSID_AssocProgidElement;
  791. hr = AssocElemCreateForKey(pclsid, hkProgid, &pae);
  792. if (SUCCEEDED(hr))
  793. {
  794. hr = _InsertSingleElement(pae);
  795. pae->Release();
  796. }
  797. }
  798. else if (_fUsingAppElement)
  799. {
  800. ASSERT(pszAssoc);
  801. hr = AssocElemCreateForClass(&CLSID_AssocApplicationElement, pszAssoc, &pae);
  802. if (SUCCEEDED(hr))
  803. {
  804. hr = _InsertSingleElement(pae);
  805. pae->Release();
  806. }
  807. }
  808. else
  809. {
  810. ASSERT(pszAssoc);
  811. ASSOCELEM_MASK maskBase = 0;
  812. if (flags & ASSOCF_INIT_DEFAULTTOFOLDER)
  813. maskBase |= ASSOCELEM_BASEIS_FOLDER;
  814. if (flags & ASSOCF_INIT_DEFAULTTOSTAR)
  815. maskBase |= ASSOCELEM_BASEIS_STAR;
  816. if (StrChr(pszAssoc, TEXT('\\')))
  817. pszAssoc = PathFindExtension(pszAssoc);
  818. if (*pszAssoc)
  819. hr = InitClassElements(maskBase, pszAssoc);
  820. else
  821. hr = E_INVALIDARG;
  822. }
  823. }
  824. _hrInit = hr;
  825. return hr;
  826. }
  827. STDAPI CQueryAssociations_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  828. {
  829. HRESULT hr = E_INVALIDARG;
  830. if (ppv)
  831. {
  832. *ppv = NULL;
  833. if (punkOuter)
  834. return CLASS_E_NOAGGREGATION;
  835. CAssocArray *passoc = new CAssocArray();
  836. if (passoc)
  837. {
  838. hr = passoc->QueryInterface(riid, ppv);
  839. passoc->Release();
  840. }
  841. else
  842. hr = E_OUTOFMEMORY;
  843. }
  844. return hr;
  845. }