#include "shellprv.h" #include #include typedef HRESULT (*PFNELEMCREATE)(const CLSID *pclsid, PCWSTR pszClass, IAssociationElement **ppae); typedef struct _AEINFO { ASSOCELEM_MASK mask; const CLSID *pclsid; PCWSTR pszClass; // NULL indicates to use the _pszClass PFNELEMCREATE pfnCreate; IAssociationElement *pae; } AEINFO; typedef enum { GETELEM_RETRY = -2, GETELEM_DONE = -1, GETELEM_TRYNEXT = 0, GETELEM_SUCCEEDED = 1, }GETELEMRESULT; #define TRYNEXT(gr) ((gr) >= GETELEM_TRYNEXT) HRESULT _QueryString(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz, const IID *piid) { return pae->QueryString(query, pszCue, ppsz); } HRESULT _QueryDirect(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, FLAGGED_BYTE_BLOB **ppblob, const IID *piid) { return pae->QueryDirect(query, pszCue, ppblob); } HRESULT _QueryDword(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, DWORD *pdw, const IID *piid) { return pae->QueryDword(query, pszCue, pdw); } HRESULT _QueryExists(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, void *pv, const IID *piid) { return pae->QueryExists(query, pszCue); } HRESULT _QueryObject(IAssociationElement *pae, ASSOCQUERY query, PCWSTR pszCue, void **ppv, const IID *piid) { return pae->QueryObject(query, pszCue, *piid, ppv); } class CAssocArray : public IAssociationArray, public IAssociationArrayInitialize, public IQueryAssociations { public: CAssocArray() : _cRef(1), _hrInit(-1), _maskInclude(-1) {} ~CAssocArray() { _Reset(); } // IUnknown methods STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() { return ++_cRef; } STDMETHODIMP_(ULONG) Release() { if (--_cRef > 0) return _cRef; delete this; return 0; } // IAssociationArrayInitialize STDMETHODIMP InitClassElements( ASSOCELEM_MASK maskBase, PCWSTR pszClass); STDMETHODIMP InsertElements( ASSOCELEM_MASK mask, IEnumAssociationElements *peae); STDMETHODIMP FilterElements(ASSOCELEM_MASK maskInclude) { _maskInclude = maskInclude; return S_OK; } // IAssociationArray STDMETHODIMP EnumElements( ASSOCELEM_MASK mask, IEnumAssociationElements **ppeae); STDMETHODIMP QueryString( ASSOCELEM_MASK mask, ASSOCQUERY query, PCWSTR pszCue, PWSTR *ppsz) { return _QueryElementAny(_QueryString, mask, query, pszCue, ppsz, NULL); } STDMETHODIMP QueryDword( ASSOCELEM_MASK mask, ASSOCQUERY query, PCWSTR pszCue, DWORD *pdw) { return _QueryElementAny(_QueryDword, mask, query, pszCue, pdw, NULL); } STDMETHODIMP QueryDirect( ASSOCELEM_MASK mask, ASSOCQUERY query, PCWSTR pszCue, FLAGGED_BYTE_BLOB **ppblob) { return _QueryElementAny(_QueryDirect, mask, query, pszCue, ppblob, NULL); } STDMETHODIMP QueryExists( ASSOCELEM_MASK mask, ASSOCQUERY query, PCWSTR pszCue) { return _QueryElementAny(_QueryExists, mask, query, pszCue, (void*)NULL, NULL); } STDMETHODIMP QueryObject( ASSOCELEM_MASK mask, ASSOCQUERY query, PCWSTR pszCue, REFIID riid, void **ppv) { return _QueryElementAny(_QueryObject, mask, query, pszCue, ppv, &riid); } // IQueryAssociations methods STDMETHODIMP Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd); STDMETHODIMP GetString(ASSOCF flags, ASSOCSTR str, LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut); STDMETHODIMP GetKey(ASSOCF flags, ASSOCKEY, LPCWSTR pszExtra, HKEY *phkeyOut); STDMETHODIMP GetData(ASSOCF flags, ASSOCDATA data, LPCWSTR pszExtra, LPVOID pvOut, DWORD *pcbOut); STDMETHODIMP GetEnum(ASSOCF flags, ASSOCENUM assocenum, LPCWSTR pszExtra, REFIID riid, LPVOID *ppvOut) { return E_NOTIMPL; } GETELEMRESULT GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae); protected: // methods void _Reset(); HRESULT _InsertSingleElement(IAssociationElement *pae); HRESULT _GetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem); void _SetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement *paeVerb, IAssociationElement *paeVerbParent); GETELEMRESULT _GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae); BOOL _FirstElement(ASSOCELEM_MASK mask, IAssociationElement **ppae); HRESULT _GetVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem); void _InitDelayedElements(int i, ASSOCELEM_MASK mask); template 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) { mask &= _maskInclude; IAssociationElement *pae; HRESULT hr = E_FAIL; if ((AQF_CUEIS_SHELLVERB & query) && _CacheVerb(query)) { // delegate to the verb object if the cue is a verb // except for AQVS_APPLICATION_FRIENDLYNAME which // has some funky delegation issues. IAssociationElement *paeParent; hr = _GetVerbElement(mask, pszCue, &pae, &paeParent); if (SUCCEEDED(hr)) { if (query == AQVS_APPLICATION_FRIENDLYNAME) hr = pfnAny(paeParent, query, pszCue, pData, piid); else hr = pfnAny(pae, query, NULL, pData, piid); pae->Release(); paeParent->Release(); } } else { for (int i = 0; FAILED(hr) && TRYNEXT(GetElement(i, mask, &pae)); i++) { if (pae) { hr = pfnAny(pae, query, pszCue, pData, piid); pae->Release(); if (SUCCEEDED(hr)) break; } } } return hr; } BOOL _CacheVerb(ASSOCQUERY query) { // if we are init'd with an app element and // querying for app specific values dont request the verb element return !_fUsingAppElement || (query != AQVS_APPLICATION_PATH && query != AQVS_APPLICATION_FRIENDLYNAME && query != AQVO_APPLICATION_DELEGATE); } private: // members LONG _cRef; HRESULT _hrInit; PWSTR _pszClass; ASSOCELEM_MASK _maskInclude; BOOL _fUsingAppElement; CDSA _dsaElems; IEnumAssociationElements *_penumData; ASSOCELEM_MASK _maskData; IEnumAssociationElements *_penumExtra; ASSOCELEM_MASK _maskExtra; IAssociationElement *_paeVerb; PWSTR _pszVerb; ASSOCELEM_MASK _maskVerb; IAssociationElement *_paeVerbParent; }; int CALLBACK _AeinfoDelete(AEINFO *paei, void *pv) { if (paei->pae) paei->pae->Release(); return 1; } void CAssocArray::_Reset() { if (_hrInit != -1) { if (_dsaElems) _dsaElems.DestroyCallbackEx(_AeinfoDelete, (void*)NULL); if (_pszClass) { CoTaskMemFree(_pszClass); _pszClass = NULL; } ATOMICRELEASE(_penumData); ATOMICRELEASE(_penumExtra); ATOMICRELEASE(_paeVerb); if (_pszVerb) { LocalFree(_pszVerb); _pszVerb; } ATOMICRELEASE(_paeVerbParent); _fUsingAppElement = FALSE; _hrInit = -1; } } HRESULT CAssocArray::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CAssocArray, IAssociationArray), QITABENT(CAssocArray, IQueryAssociations), QITABENT(CAssocArray, IAssociationArrayInitialize), { 0 }, }; return QISearch(this, qit, riid, ppv); } #define AEINFOPROGID(m, s) { m, &CLSID_AssocProgidElement, s, AssocElemCreateForClass, NULL} #define MAKEAEINFO(m, c, s, p) { m, c, s, p, NULL} HRESULT AssocElemCreateForUser(const CLSID *pclsid, PCWSTR pszClass, IAssociationElement **ppae) { WCHAR sz[64]; DWORD cb = sizeof(sz); HRESULT hr = SKGetValue(SHELLKEY_HKCU_FILEEXTS, pszClass, L"Progid", NULL, sz, &cb); if(SUCCEEDED(hr)) { hr = AssocElemCreateForClass(&CLSID_AssocProgidElement, sz, ppae); } if (FAILED(hr)) { cb = sizeof(sz); hr = SKGetValue(SHELLKEY_HKCU_FILEEXTS, pszClass, L"Application", NULL, sz, &cb); if (SUCCEEDED(hr)) { hr = AssocElemCreateForClass(&CLSID_AssocApplicationElement, sz, ppae); } } return hr; } static const AEINFO s_rgaeinfoProgid[] = { AEINFOPROGID(ASSOCELEM_DEFAULT, NULL), }; static const AEINFO s_rgaeinfoExtension[] = { MAKEAEINFO(ASSOCELEM_USER, NULL, NULL, AssocElemCreateForUser), // app or progid AEINFOPROGID(ASSOCELEM_DEFAULT, NULL), MAKEAEINFO(ASSOCELEM_SYSTEM_EXT, &CLSID_AssocSystemElement, NULL, AssocElemCreateForClass), MAKEAEINFO(ASSOCELEM_SYSTEM_PERCEIVED, &CLSID_AssocPerceivedElement, NULL, AssocElemCreateForClass), }; static const AEINFO s_rgaeinfoClsid[] = { // MAKEAEINFO(UserClsid), // clsid MAKEAEINFO(ASSOCELEM_DEFAULT, &CLSID_AssocClsidElement, NULL, AssocElemCreateForClass), // AEINFOPROGID(ASSOCELEM_PROGID, NULL), // progid }; static const AEINFO s_aeinfoFolder = MAKEAEINFO(ASSOCELEM_BASEIS_FOLDER, &CLSID_AssocFolderElement, NULL, AssocElemCreateForClass); static const AEINFO s_aeinfoStar = MAKEAEINFO(ASSOCELEM_BASEIS_STAR, &CLSID_AssocStarElement, NULL, AssocElemCreateForClass); BOOL CAssocArray::_FirstElement(ASSOCELEM_MASK mask, IAssociationElement **ppae) { GETELEMRESULT res = GETELEM_TRYNEXT; int cTrys = 0; while (res == GETELEM_TRYNEXT) { // if it fails or succeeds we are done // but if it calls TRYNEXT we loop res = GetElement(cTrys++, mask, ppae); } return res == GETELEM_SUCCEEDED; } HRESULT CAssocArray::InitClassElements(ASSOCELEM_MASK maskBase, PCWSTR pszClass) { _Reset(); // depending on what we think this is, // we do things a little differently ASSERT(*pszClass); HRESULT hr = SHStrDup(pszClass, &_pszClass); if (SUCCEEDED(hr)) { // 6 is the most that we will need // in InitClassElements() hr = _dsaElems.Create(6) ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) { const AEINFO *rgAeinfo; DWORD cAeinfo; if (*_pszClass == L'.') { rgAeinfo = s_rgaeinfoExtension; cAeinfo = ARRAYSIZE(s_rgaeinfoExtension); } else if (*_pszClass == L'{') { rgAeinfo = s_rgaeinfoClsid; cAeinfo = ARRAYSIZE(s_rgaeinfoClsid); } else { rgAeinfo = s_rgaeinfoProgid; cAeinfo = ARRAYSIZE(s_rgaeinfoProgid); } for (DWORD i = 0; i < cAeinfo; i++) { _dsaElems.AppendItem((AEINFO *)&rgAeinfo[i]); } if (ASSOCELEM_BASEIS_FOLDER & maskBase) _dsaElems.AppendItem((AEINFO *)&s_aeinfoFolder); if (ASSOCELEM_BASEIS_STAR & maskBase) _dsaElems.AppendItem((AEINFO *)&s_aeinfoStar); // we return S_FALSE if there is no default or user // association. we treat this as an unknown type IAssociationElement *pae; if (_FirstElement(ASSOCELEM_USER | ASSOCELEM_DEFAULT, &pae)) { pae->Release(); hr = S_OK; } else hr = S_FALSE; } } _hrInit = hr; return hr; } HRESULT CAssocArray::InsertElements(ASSOCELEM_MASK mask, IEnumAssociationElements *peae) { HRESULT hr = E_UNEXPECTED; if (!_penumData && (mask & ASSOCELEM_DATA)) { _penumData = peae; peae->AddRef(); _maskData = mask; hr = S_OK; } if (!_penumExtra && (mask & ASSOCELEM_EXTRA)) { _penumExtra = peae; peae->AddRef(); _maskExtra = mask; hr = S_OK; } return hr; } class CEnumAssocElems : public CEnumAssociationElements { public: CEnumAssocElems(CAssocArray *paa, ASSOCELEM_MASK mask) : _mask(mask), _paa(paa) { _paa->AddRef(); } ~CEnumAssocElems() { _paa->Release(); } protected: // methods virtual BOOL _Next(IAssociationElement **ppae); protected: ASSOCELEM_MASK _mask; CAssocArray *_paa; }; HRESULT CAssocArray::EnumElements(ASSOCELEM_MASK mask, IEnumAssociationElements **ppeae) { mask &= _maskInclude; *ppeae = new CEnumAssocElems(this, mask); return *ppeae ? S_OK : E_OUTOFMEMORY; } void CAssocArray::_InitDelayedElements(int i, ASSOCELEM_MASK mask) { ULONG c; IAssociationElement *pae; if (i == 0 && _penumData && ((mask & _maskData) == _maskData)) { // init the DSA with the data int iInsert = 0; while (S_OK == _penumData->Next(1, &pae, &c)) { AEINFO ae = {ASSOCELEM_DATA, NULL, NULL, NULL, pae}; if (DSA_ERR != _dsaElems.InsertItem(iInsert++, &ae)) { pae->AddRef(); } pae->Release(); } ATOMICRELEASE(_penumData); } if (_penumExtra && (mask & ASSOCELEM_EXTRA) && i == _dsaElems.GetItemCount()) { // init the DSA with the data while (S_OK == _penumExtra->Next(1, &pae, &c)) { AEINFO ae = {ASSOCELEM_EXTRA, NULL, NULL, NULL, pae}; if (DSA_ERR != _dsaElems.AppendItem(&ae)) { pae->AddRef(); } pae->Release(); } ATOMICRELEASE(_penumExtra); } } GETELEMRESULT CAssocArray::_GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae) { GETELEMRESULT res = GETELEM_DONE; AEINFO *paei = _dsaElems.GetItemPtr(i); if (paei) { // if this is one that we want to use // please do if (paei->mask & mask) { if (!paei->pae) { // try to create only once PCWSTR pszClass = paei->pszClass ? paei->pszClass : _pszClass; // make sure we dont query again // if we dont need to HRESULT hr = paei->pfnCreate(paei->pclsid, pszClass, &paei->pae); if (FAILED(hr)) { _dsaElems.DeleteItem(i); // retry the current index res = GETELEM_RETRY; } else if (hr == S_FALSE) { // this is returned when the element // is valid but points to an alternate location // specifically the HKCR\Progid falls back to HKCR\.ext // which is kind of weird. maybe we should move this // to ASSOCELEM_EXTRA??? } } if (paei->pae) { *ppae = paei->pae; paei->pae->AddRef(); res = GETELEM_SUCCEEDED; } } else { res = GETELEM_TRYNEXT; } } return res; } GETELEMRESULT CAssocArray::GetElement(int i, ASSOCELEM_MASK mask, IAssociationElement **ppae) { GETELEMRESULT res = GETELEM_RETRY; *ppae = 0; if (_dsaElems) { _InitDelayedElements(i, mask); while (GETELEM_RETRY == res && i < _dsaElems.GetItemCount()) { res = _GetElement(i, mask, ppae); } } return res; } BOOL CEnumAssocElems::_Next(IAssociationElement **ppae) { GETELEMRESULT res = GETELEM_TRYNEXT; UINT cTrys = 0; while (res == GETELEM_TRYNEXT) { // if it fails or succeeds we are done // but if it calls TRYNEXT we loop res = _paa->GetElement(_cNext + cTrys++, _mask, ppae); } // fix up _cNext when we skip _cNext += cTrys - 1; return res == GETELEM_SUCCEEDED; } typedef struct { ASSOCQUERY query; PCWSTR pszCue; } AQXLATE; #define MAKEAQX(a, q, s) { q, s } static const AQXLATE s_rgaqxStrings[] = { MAKEAQX(ASSOCSTR_COMMAND, AQVS_COMMAND, NULL), MAKEAQX(ASSOCSTR_EXECUTABLE, AQVS_APPLICATION_PATH, NULL), MAKEAQX(ASSOCSTR_FRIENDLYDOCNAME, AQS_FRIENDLYTYPENAME, NULL), // friendly name of the document type MAKEAQX(ASSOCSTR_FRIENDLYAPPNAME, AQVS_APPLICATION_FRIENDLYNAME, NULL), MAKEAQX(ASSOCSTR_NOOPEN, AQNS_NAMED_MUI_STRING, L"NoOpen"), MAKEAQX(ASSOCSTR_SHELLNEWVALUE, (ASSOCQUERY)0, NULL), MAKEAQX(ASSOCSTR_DDECOMMAND, AQVS_DDECOMMAND, NULL), MAKEAQX(ASSOCSTR_DDEIFEXEC, AQVS_DDEIFEXEC, NULL), MAKEAQX(ASSOCSTR_DDEAPPLICATION, AQVS_DDEAPPLICATION, NULL), MAKEAQX(ASSOCSTR_DDETOPIC, AQVS_DDETOPIC, NULL), MAKEAQX(ASSOCSTR_INFOTIP, AQNS_NAMED_MUI_STRING, L"InfoTip"), MAKEAQX(ASSOCSTR_QUICKTIP, AQNS_NAMED_MUI_STRING, L"QuickTip"), MAKEAQX(ASSOCSTR_TILEINFO, AQNS_NAMED_MUI_STRING, L"TileInfo"), MAKEAQX(ASSOCSTR_CONTENTTYPE, AQS_CONTENTTYPE, NULL), MAKEAQX(ASSOCSTR_DEFAULTICON, AQS_DEFAULTICON, NULL), MAKEAQX(ASSOCSTR_SHELLEXTENSION, AQNS_SHELLEX_HANDLER, NULL), }; HRESULT _CopyOut(BOOL fNoTruncate, PCWSTR pszIn, PWSTR psz, DWORD *pcch) { // if caller doesnt want any return size, // the incoming pointer is actually the size of the buffer ASSERT(pcch); ASSERT(psz || !IS_INTRESOURCE(pcch)); HRESULT hr; DWORD cch = IS_INTRESOURCE(pcch) ? PtrToUlong(pcch) : *pcch; DWORD cchStr = lstrlenW(pszIn); if (psz) { if (!fNoTruncate || cch > cchStr) { StrCpyNW(psz, pszIn, cch); hr = S_OK; } else hr = E_POINTER; } else hr = S_FALSE; // return the number of chars written/required if (!IS_INTRESOURCE(pcch)) *pcch = (hr == S_OK) ? lstrlen(psz) + 1 : cchStr + 1; return hr; } ASSOCELEM_MASK _MaskFromFlags(ASSOCF flags) { ASSOCELEM_MASK mask = ASSOCELEM_MASK_QUERYNORMAL; if (flags & ASSOCF_IGNOREBASECLASS) mask &= (ASSOCELEM_USER | ASSOCELEM_DEFAULT); if (flags & ASSOCF_NOUSERSETTINGS) mask &= ~ASSOCELEM_USER; return mask; } HRESULT CAssocArray::GetString(ASSOCF flags, ASSOCSTR str, LPCTSTR pszCue, LPTSTR pszOut, DWORD *pcchOut) { HRESULT hr = E_UNEXPECTED; if (str && str < ASSOCSTR_MAX && pcchOut && (pszOut || !IS_INTRESOURCE(pcchOut))) { // subtract the first one to make a zero based offset int index = str - ASSOCSTR_COMMAND; if (!pszCue) pszCue = s_rgaqxStrings[index].pszCue; if (s_rgaqxStrings[index].query) { PWSTR psz; hr = QueryString(_MaskFromFlags(flags), s_rgaqxStrings[index].query, pszCue, &psz); if (SUCCEEDED(hr)) { hr = _CopyOut(flags & ASSOCF_NOTRUNCATE, psz, pszOut, pcchOut); CoTaskMemFree(psz); } } // else call win2k code for shellnew? // } return hr; } static const AQXLATE s_rgaqxDatas[] = { MAKEAQX(ASSOCDATA_MSIDESCRIPTOR, AQVD_MSIDESCRIPTOR, NULL), MAKEAQX(ASSOCDATA_NOACTIVATEHANDLER, AQV_NOACTIVATEHANDLER, NULL), MAKEAQX(ASSOCDATA_QUERYCLASSSTORE, AQN_NAMED_VALUE, L"QueryClassStore"), MAKEAQX(ASSOCDATA_HASPERUSERASSOC, (ASSOCQUERY)0, NULL), MAKEAQX(ASSOCDATA_EDITFLAGS, AQN_NAMED_VALUE, L"EditFlags"), MAKEAQX(ASSOCDATA_VALUE, AQN_NAMED_VALUE, NULL), }; HRESULT _CopyDataOut(BOOL fNoTruncate, FLAGGED_BYTE_BLOB *pblob, void *pv, DWORD *pcb) { // if caller doesnt want any return size, // the incoming pointer is actually the size of the buffer ASSERT(pcb); ASSERT(pv || !IS_INTRESOURCE(pcb)); HRESULT hr; DWORD cb = IS_INTRESOURCE(pcb) ? PtrToUlong(pcb) : *pcb; if (pv) { if (!fNoTruncate || cb >= pblob->clSize) { // copy the smaller of the src or dst cb = min(cb, pblob->clSize); memcpy(pv, pblob->abData, cb); hr = S_OK; } else hr = E_POINTER; } else hr = S_FALSE; // return the number of chars written/required if (!IS_INTRESOURCE(pcb)) *pcb = pblob->clSize; return hr; } HRESULT CAssocArray::GetData(ASSOCF flags, ASSOCDATA data, PCWSTR pszCue, LPVOID pvOut, DWORD *pcbOut) { HRESULT hr = E_INVALIDARG; if (data && data < ASSOCDATA_MAX) { // subtract the first one to make a zero based offset int index = data - ASSOCDATA_MSIDESCRIPTOR; if (!pszCue) pszCue = s_rgaqxDatas[index].pszCue; if (s_rgaqxDatas[index].query) { if (pcbOut) { FLAGGED_BYTE_BLOB *pblob; hr = QueryDirect(_MaskFromFlags(flags), s_rgaqxDatas[index].query, pszCue, &pblob); if (SUCCEEDED(hr)) { hr = _CopyDataOut(flags & ASSOCF_NOTRUNCATE, pblob, pvOut, pcbOut); CoTaskMemFree(pblob); } } else { hr = QueryExists(_MaskFromFlags(flags), s_rgaqxDatas[index].query, pszCue); } } else { if (data == ASSOCDATA_HASPERUSERASSOC) { IAssociationElement *pae; if (_FirstElement(ASSOCELEM_USER, &pae)) { pae->Release(); hr = S_OK; } else hr = S_FALSE; } } } return hr; } BOOL _IsSameVerb(PCWSTR pszV1, PCWSTR pszV2) { if (!pszV1 && !pszV2) return TRUE; else if (pszV1 && pszV2 && 0 == StrCmpIW(pszV1, pszV2)) return TRUE; return FALSE; } HRESULT CAssocArray::_GetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem) { if (_paeVerb && mask == _maskVerb && _IsSameVerb(pszVerb, _pszVerb)) { *ppaeVerb = _paeVerb; _paeVerb->AddRef(); if (ppaeElem) { *ppaeElem = _paeVerbParent; _paeVerbParent->AddRef(); } return S_OK; } else { *ppaeVerb = NULL; if (ppaeElem) *ppaeElem = NULL; return E_FAIL; } } void CAssocArray::_SetCachedVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement *paeVerb, IAssociationElement *paeVerbParent) { if (_paeVerb) { ATOMICRELEASE(_paeVerb); ATOMICRELEASE(_paeVerbParent); if (_pszVerb) LocalFree(_pszVerb); } if (pszVerb) _pszVerb = StrDupW(pszVerb); else _pszVerb = NULL; if (_pszVerb || !pszVerb) { _paeVerb = paeVerb; _paeVerb->AddRef(); _paeVerbParent = paeVerbParent; _paeVerbParent->AddRef(); _maskVerb = mask; } } HRESULT CAssocArray::_GetVerbElement(ASSOCELEM_MASK mask, PCWSTR pszVerb, IAssociationElement **ppaeVerb, IAssociationElement **ppaeElem) { HRESULT hr = _GetCachedVerbElement(mask, pszVerb, ppaeVerb, ppaeElem); IAssociationElement *pae; for (int i = 0; FAILED(hr) && TRYNEXT(GetElement(i, mask, &pae)); i++) { if (pae) { hr = pae->QueryObject(AQVO_SHELLVERB_DELEGATE, pszVerb, IID_PPV_ARG(IAssociationElement, ppaeVerb)); if (SUCCEEDED(hr)) { _SetCachedVerbElement(mask, pszVerb, *ppaeVerb, pae); if (ppaeElem) { pae->AddRef(); *ppaeElem = pae; } } pae->Release(); } } return hr; } HRESULT CAssocArray::GetKey(ASSOCF flags, ASSOCKEY key, LPCTSTR pszCue, HKEY *phkey) { HRESULT hr = E_INVALIDARG; *phkey = NULL; if (key && key < ASSOCKEY_MAX) { IAssociationElement *pae; switch (key) { case ASSOCKEY_SHELLEXECCLASS: { IAssociationElement *paeVerb; hr = _GetVerbElement(_MaskFromFlags(flags) & _maskInclude, pszCue, &paeVerb, &pae); if (SUCCEEDED(hr)) { // we dont use the verb element paeVerb->Release(); } } break; case ASSOCKEY_APP: // get app element hr = QueryObject(ASSOCELEM_MASK_QUERYNORMAL, AQVO_APPLICATION_DELEGATE, pszCue, IID_PPV_ARG(IAssociationElement, &pae)); break; case ASSOCKEY_CLASS: { hr = _FirstElement(_MaskFromFlags(flags) & _maskInclude, &pae) == GETELEM_SUCCEEDED ? S_OK : E_FAIL; } break; case ASSOCKEY_BASECLASS: hr = _FirstElement(ASSOCELEM_BASE & _maskInclude, &pae) == GETELEM_SUCCEEDED ? S_OK : E_FAIL; break; } if (SUCCEEDED(hr)) { hr = AssocKeyFromElement(pae, phkey); pae->Release(); } } return hr; } HRESULT CAssocArray::_InsertSingleElement(IAssociationElement *pae) { AEINFO ae = {ASSOCELEM_DEFAULT, NULL, NULL, NULL, pae}; ASSERT(!_dsaElems); if (_dsaElems.Create(1) && (DSA_ERR != _dsaElems.AppendItem(&ae))) { pae->AddRef(); return S_OK; } return E_OUTOFMEMORY; } HRESULT CAssocArray::Init(ASSOCF flags, LPCTSTR pszAssoc, HKEY hkProgid, HWND hwnd) { IAssociationElement *pae; HRESULT hr = (pszAssoc || hkProgid) ? S_OK : E_INVALIDARG; if (SUCCEEDED(hr)) { _Reset(); _fUsingAppElement = flags & ASSOCF_INIT_BYEXENAME; if (hkProgid) { const CLSID *pclsid; if (_fUsingAppElement) pclsid = &CLSID_AssocApplicationElement; else if (flags & ASSOCF_INIT_NOREMAPCLSID) pclsid = &CLSID_AssocClsidElement; else pclsid = &CLSID_AssocProgidElement; hr = AssocElemCreateForKey(pclsid, hkProgid, &pae); if (SUCCEEDED(hr)) { hr = _InsertSingleElement(pae); pae->Release(); } } else if (_fUsingAppElement) { ASSERT(pszAssoc); hr = AssocElemCreateForClass(&CLSID_AssocApplicationElement, pszAssoc, &pae); if (SUCCEEDED(hr)) { hr = _InsertSingleElement(pae); pae->Release(); } } else { ASSERT(pszAssoc); ASSOCELEM_MASK maskBase = 0; if (flags & ASSOCF_INIT_DEFAULTTOFOLDER) maskBase |= ASSOCELEM_BASEIS_FOLDER; if (flags & ASSOCF_INIT_DEFAULTTOSTAR) maskBase |= ASSOCELEM_BASEIS_STAR; if (StrChr(pszAssoc, TEXT('\\'))) pszAssoc = PathFindExtension(pszAssoc); if (*pszAssoc) hr = InitClassElements(maskBase, pszAssoc); else hr = E_INVALIDARG; } } _hrInit = hr; return hr; } STDAPI CQueryAssociations_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr = E_INVALIDARG; if (ppv) { *ppv = NULL; if (punkOuter) return CLASS_E_NOAGGREGATION; CAssocArray *passoc = new CAssocArray(); if (passoc) { hr = passoc->QueryInterface(riid, ppv); passoc->Release(); } else hr = E_OUTOFMEMORY; } return hr; }