windows-nt/Source/XPSP1/NT/shell/shell32/assocarray.cpp
2020-09-26 16:20:57 +08:00

968 lines
28 KiB
C++

#include "shellprv.h"
#include <dpa.h>
#include <enumt.h>
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<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)
{
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<AEINFO> _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;
}