754 lines
18 KiB
C++
754 lines
18 KiB
C++
//
|
|
// Author: DebiM
|
|
// Date: September 1996
|
|
//
|
|
// File: csacc.cxx
|
|
//
|
|
// Class Store Manager implementation for a client desktop.
|
|
//
|
|
// This source file contains implementations for IClassAccess
|
|
// interface for CClassAccess object.
|
|
// It also contains the IEnumPackage implementation for the
|
|
// aggregate of all class containers seen by the caller.
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
#include "cstore.hxx"
|
|
|
|
/**
|
|
|
|
void
|
|
LogCsPathError(
|
|
WCHAR * pwszContainerPath,
|
|
HRESULT hr );
|
|
|
|
**/
|
|
|
|
|
|
#define MAXCLASSSTORES 10
|
|
|
|
IClassAccess *GetNextValidClassStore(PCLASSCONTAINER *pStoreList,
|
|
DWORD cStores,
|
|
DWORD *pcount);
|
|
|
|
extern HRESULT GetUserClassStores(
|
|
PCLASSCONTAINER **ppStoreList,
|
|
DWORD *pcStores);
|
|
|
|
|
|
|
|
//
|
|
// Link list pointer for Class Containers Seen
|
|
//
|
|
extern CLASSCONTAINER *gpContainerHead;
|
|
|
|
//
|
|
// Link list pointer for User Profiles Seen
|
|
//
|
|
extern USERPROFILE *gpUserHead;
|
|
|
|
//
|
|
// Global Class Factory for Class Container
|
|
//
|
|
extern CAppContainerCF *pCF;
|
|
|
|
//
|
|
// Critical Section used during operations on list of class stores
|
|
//
|
|
extern CRITICAL_SECTION ClassStoreBindList;
|
|
|
|
//
|
|
// CClassAccess implementation
|
|
//
|
|
|
|
CClassAccess::CClassAccess()
|
|
|
|
{
|
|
m_uRefs = 1;
|
|
m_cCalls = 0;
|
|
}
|
|
|
|
CClassAccess::~CClassAccess()
|
|
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
//
|
|
#ifdef DBG
|
|
|
|
void PrintClassSpec(
|
|
uCLSSPEC * pclsspec // Class Spec (GUID/Ext/MIME)
|
|
)
|
|
{
|
|
STRINGGUID szClsid;
|
|
|
|
if (pclsspec->tyspec == TYSPEC_CLSID)
|
|
{
|
|
StringFromGUID (pclsspec->tagged_union.clsid, szClsid);
|
|
CSDbgPrint((" ... GetClassSpecInfo by CLSID = %ws\n", szClsid));
|
|
}
|
|
|
|
if (pclsspec->tyspec == TYSPEC_PROGID)
|
|
{
|
|
CSDbgPrint((" ... GetClassSpecInfo by ProgID = %ws\n",
|
|
pclsspec->tagged_union.pProgId));
|
|
}
|
|
|
|
if (pclsspec->tyspec == TYSPEC_MIMETYPE)
|
|
{
|
|
CSDbgPrint((" ... GetClassSpecInfo by MimeType = %ws\n",
|
|
pclsspec->tagged_union.pMimeType));
|
|
}
|
|
|
|
if (pclsspec->tyspec == TYSPEC_FILEEXT)
|
|
{
|
|
CSDbgPrint((" ... GetClassSpecInfo by FileExt = %ws\n",
|
|
pclsspec->tagged_union.pFileExt));
|
|
}
|
|
|
|
if (pclsspec->tyspec == TYSPEC_IID)
|
|
{
|
|
StringFromGUID (pclsspec->tagged_union.iid, szClsid);
|
|
CSDbgPrint((" ... GetClassSpecInfo by IID = %ws\n", szClsid));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CClassAccess::GetAppInfo(
|
|
uCLSSPEC * pclsspec, // Class Spec (GUID/Ext/MIME)
|
|
QUERYCONTEXT * pQryContext, // Query Attributes
|
|
INSTALLINFO * pInstallInfo
|
|
)
|
|
|
|
//
|
|
// This is the most common method to access the Class Store.
|
|
// It queries the class store for implementations for a specific
|
|
// Class Id, or File Ext, or ProgID or MIME type.
|
|
//
|
|
// If a matching implementation is available for the object type,
|
|
// client architecture, locale and class context pointer to the
|
|
// binary is returned.
|
|
{
|
|
|
|
//
|
|
// Assume that this method is called in the security context
|
|
// of the user process. Hence there is no need to impersonate.
|
|
//
|
|
//
|
|
// Get the list of Class Stores for this user
|
|
//
|
|
PCLASSCONTAINER *pStoreList;
|
|
ULONG cStores=0;
|
|
HRESULT hr;
|
|
ULONG i;
|
|
ULONG chEaten;
|
|
IMoniker *pmk;
|
|
LPBC pbc;
|
|
IClassAccess *pICA = NULL;
|
|
|
|
#ifdef DBG
|
|
PrintClassSpec(pclsspec);
|
|
#endif
|
|
|
|
hr = GetUserClassStores(
|
|
&pStoreList,
|
|
&cStores);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//RpcImpersonateClient( NULL );
|
|
|
|
for (i=0; i < cStores; i++)
|
|
{
|
|
|
|
if (!(pICA = GetNextValidClassStore(pStoreList, cStores, &i)))
|
|
continue;
|
|
|
|
//
|
|
// Call method on this store
|
|
//
|
|
|
|
pICA->AddRef();
|
|
|
|
hr = pICA->GetAppInfo(
|
|
pclsspec,
|
|
pQryContext,
|
|
pInstallInfo);
|
|
|
|
// Release it after use.
|
|
|
|
pICA->Release();
|
|
|
|
//
|
|
// Special case error return E_INVALIDARG
|
|
// Do not continue to look, return this.
|
|
//
|
|
if (hr == E_INVALIDARG)
|
|
{
|
|
//RevertToSelf();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// maintain access counters
|
|
//
|
|
(pStoreList[i])->cAccess++;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//RevertToSelf();
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
(pStoreList[i])->cNotFound++;
|
|
CSDbgPrint(("CS: .. CClassAccess::GetClassSpecInfo() returned 0x%x\n", hr));
|
|
}
|
|
}
|
|
|
|
//RevertToSelf();
|
|
return CS_E_PACKAGE_NOTFOUND;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// GetNextValidClassStore
|
|
//
|
|
//
|
|
|
|
IClassAccess *GetNextValidClassStore(CLASSCONTAINER **pStoreList, DWORD cStores, DWORD *pcount)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IClassAccess *pretICA = NULL;
|
|
|
|
// BUGBUG:: Probably should move this inside the for loops so that the
|
|
// read accesses to gpClassStore do not get serialized. Debi?
|
|
|
|
EnterCriticalSection (&ClassStoreBindList);
|
|
|
|
for (pStoreList += (*pcount); (*pcount) < cStores; (*pcount)++, pStoreList++)
|
|
{
|
|
if ((*pStoreList)->gpClassStore != NULL)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
if (FALSE) // ((*pStoreList)->cBindFailures >= MAX_BIND_ATTEMPTS)
|
|
{
|
|
// Number of continuous failures have reached MAX_BIND_ATTEMPTS
|
|
// for this container.
|
|
// Will temporarily disable lookups in this container.
|
|
// Report it in EventLog once
|
|
//
|
|
|
|
if ((*pStoreList)->cBindFailures == MAX_BIND_ATTEMPTS)
|
|
{
|
|
//LogCsPathError((*pStoreList)->pszClassStorePath, hr);
|
|
(*pStoreList)->cBindFailures++;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
CSDbgPrint(("CS: .. Connecting to Store %d \n ... %ws..\n",
|
|
(*pcount),
|
|
(*pStoreList)->pszClassStorePath));
|
|
//
|
|
// Bind to this Class Store
|
|
//
|
|
|
|
if (wcsncmp ((*pStoreList)->pszClassStorePath, L"ADCS:", 5) == 0)
|
|
{
|
|
//
|
|
// If the Storename starts with ADCS
|
|
// it is NTDS based implementation. Call directly.
|
|
//
|
|
hr = pCF->CreateConnectedInstance(
|
|
((*pStoreList)->pszClassStorePath + 5),
|
|
(void **)&((*pStoreList)->gpClassStore));
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Support for Third Party Pluggable
|
|
// Class Stores is not in Beta1.
|
|
//
|
|
#if 1
|
|
hr = E_NOTIMPL;
|
|
#else
|
|
ULONG chEaten;
|
|
IMoniker *pmk;
|
|
LPBC pbc;
|
|
|
|
pbc = NULL;
|
|
hr = CreateBindCtx (0, &pbc);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pmk = NULL;
|
|
chEaten = 0;
|
|
|
|
|
|
hr = MkParseDisplayName (pbc,
|
|
(*pStoreList)->pszClassStorePath,
|
|
&chEaten,
|
|
&pmk);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pmk->BindToObject (pbc,
|
|
NULL,
|
|
IID_IClassAccess,
|
|
(void **)&((*pStoreList)->gpClassStore));
|
|
|
|
pmk->Release();
|
|
}
|
|
|
|
pbc->Release();
|
|
#endif
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
(*pStoreList)->cBindFailures = 0;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
CSDbgPrint(("CS: ... Failed to connect to this store\n"));
|
|
if ((*pStoreList)->cBindFailures == 0)
|
|
{
|
|
// First failue or First failure after successful
|
|
// binding.
|
|
// Report it in EventLog
|
|
//
|
|
|
|
//LogCsPathError((*pStoreList)->pszClassStorePath, hr);
|
|
}
|
|
|
|
((*pStoreList)->cBindFailures) ++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((*pcount) != cStores)
|
|
pretICA = (*pStoreList)->gpClassStore;
|
|
|
|
LeaveCriticalSection (&ClassStoreBindList);
|
|
|
|
return pretICA;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CClassAccess::GetUpgrades (
|
|
ULONG cClasses,
|
|
CLSID *pClassList, // CLSIDs Installed
|
|
CSPLATFORM Platform,
|
|
LCID Locale,
|
|
PACKAGEINFOLIST *pPackageInfoList)
|
|
{
|
|
//
|
|
// Assume that this method is called in the security context
|
|
// of the user process. Hence there is no need to impersonate.
|
|
//
|
|
|
|
PCLASSCONTAINER *pStoreList;
|
|
DWORD cStores=0;
|
|
HRESULT hr;
|
|
ULONG i;
|
|
IClassRefresh *pIClassRefresh = NULL;
|
|
PACKAGEINFOLIST PackageInfoList;
|
|
IClassAccess *pICA = NULL;
|
|
|
|
pPackageInfoList->cPackInfo = NULL;
|
|
pPackageInfoList->pPackageInfo = NULL;
|
|
|
|
//
|
|
// Get the list of Class Stores for this user
|
|
//
|
|
hr = GetUserClassStores(
|
|
&pStoreList,
|
|
&cStores);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
RpcImpersonateClient( NULL );
|
|
|
|
for (i=0; i < cStores; i++)
|
|
{
|
|
if (!(pICA = GetNextValidClassStore(pStoreList, cStores, &i)))
|
|
continue;
|
|
//
|
|
// Call method on this store
|
|
//
|
|
|
|
if (FAILED(pICA->QueryInterface(IID_IClassRefresh,
|
|
(void **)&pIClassRefresh)))
|
|
continue;
|
|
|
|
hr = pIClassRefresh->GetUpgrades(
|
|
cClasses,
|
|
pClassList,
|
|
Platform,
|
|
Locale,
|
|
&PackageInfoList);
|
|
|
|
pIClassRefresh->Release();
|
|
pIClassRefresh = NULL;
|
|
|
|
if (hr == E_INVALIDARG)
|
|
{
|
|
RevertToSelf();
|
|
return hr;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && (PackageInfoList.cPackInfo > 0))
|
|
{
|
|
//
|
|
// Add to the existing list of upgrades
|
|
//
|
|
UINT cCount = pPackageInfoList->cPackInfo;
|
|
|
|
if (cCount)
|
|
{
|
|
PACKAGEINFO *pInfo = pPackageInfoList->pPackageInfo;
|
|
pPackageInfoList->pPackageInfo =
|
|
(PACKAGEINFO *) CoTaskMemAlloc
|
|
((cCount + PackageInfoList.cPackInfo) * sizeof(PACKAGEINFO));
|
|
|
|
memcpy (pPackageInfoList->pPackageInfo,
|
|
pInfo,
|
|
cCount * sizeof(PACKAGEINFO));
|
|
|
|
memcpy ((pPackageInfoList->pPackageInfo)+cCount,
|
|
PackageInfoList.pPackageInfo,
|
|
PackageInfoList.cPackInfo * sizeof(PACKAGEINFO));
|
|
|
|
CoTaskMemFree (pInfo);
|
|
pPackageInfoList->cPackInfo += PackageInfoList.cPackInfo;
|
|
}
|
|
else
|
|
{
|
|
pPackageInfoList->cPackInfo = PackageInfoList.cPackInfo;
|
|
pPackageInfoList->pPackageInfo = PackageInfoList.pPackageInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
RevertToSelf();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE
|
|
CClassAccess::CommitUpgrades ()
|
|
{
|
|
//
|
|
// Assume that this method is called in the security context
|
|
// of the user process. Hence there is no need to impersonate.
|
|
//
|
|
|
|
PCLASSCONTAINER *pStoreList;
|
|
DWORD cStores=0;
|
|
HRESULT hr;
|
|
ULONG i;
|
|
IClassRefresh *pIClassRefresh;
|
|
IClassAccess *pICA = NULL;
|
|
|
|
//
|
|
// Get the list of Class Stores for this user
|
|
//
|
|
hr = GetUserClassStores(
|
|
&pStoreList,
|
|
&cStores);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
RpcImpersonateClient( NULL );
|
|
|
|
for (i=0; i < cStores; i++)
|
|
{
|
|
if (!(pICA = GetNextValidClassStore(pStoreList, cStores, &i)))
|
|
continue;
|
|
//
|
|
// Call method on this store
|
|
//
|
|
|
|
if (FAILED(pICA->QueryInterface(IID_IClassRefresh,
|
|
(void **)&pIClassRefresh)))
|
|
continue;
|
|
|
|
hr = pIClassRefresh->CommitUpgrades();
|
|
|
|
pIClassRefresh->Release();
|
|
}
|
|
|
|
RevertToSelf();
|
|
return hr;
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CClassAccess::EnumPackages(
|
|
LPOLESTR pszPackageName,
|
|
GUID *pCategory,
|
|
ULONGLONG *pLastUsn,
|
|
DWORD dwAppFlags, // AppType options
|
|
IEnumPackage **ppIEnumPackage)
|
|
{
|
|
//
|
|
// Get the list of Class Stores for this user
|
|
//
|
|
PCLASSCONTAINER *pStoreList;
|
|
DWORD cStores=0;
|
|
HRESULT hr;
|
|
ULONG i;
|
|
IEnumPackage *Enum[MAXCLASSSTORES];
|
|
ULONG cEnum = 0;
|
|
CMergedEnumPackage *EnumMerged;
|
|
IClassAccess *pICA = NULL;
|
|
|
|
//
|
|
// Get the list of Class Stores for this user
|
|
//
|
|
hr = GetUserClassStores(
|
|
&pStoreList,
|
|
&cStores);
|
|
|
|
*ppIEnumPackage = NULL;
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (cStores == 0)
|
|
{
|
|
return CS_E_NO_CLASSSTORE;
|
|
}
|
|
|
|
//RpcImpersonateClient( NULL );
|
|
|
|
for (i=0; i < cStores; i++)
|
|
{
|
|
if (!(pICA = GetNextValidClassStore(pStoreList, cStores, &i)))
|
|
continue;
|
|
//
|
|
// Call method on this store
|
|
//
|
|
|
|
hr = pICA->EnumPackages (pszPackageName,
|
|
pCategory,
|
|
pLastUsn,
|
|
dwAppFlags,
|
|
&(Enum[cEnum]));
|
|
|
|
if (hr == E_INVALIDARG)
|
|
{
|
|
//RevertToSelf();
|
|
return hr;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
cEnum++;
|
|
}
|
|
|
|
EnumMerged = new CMergedEnumPackage;
|
|
hr = EnumMerged->Initialize(Enum, cEnum);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
for (i = 0; i < cEnum; i++)
|
|
Enum[i]->Release();
|
|
delete EnumMerged;
|
|
}
|
|
else
|
|
{
|
|
hr = EnumMerged->QueryInterface(IID_IEnumPackage, (void **)ppIEnumPackage);
|
|
if (FAILED(hr))
|
|
delete EnumMerged;
|
|
}
|
|
|
|
//RevertToSelf();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
CMergedEnumPackage::CMergedEnumPackage()
|
|
{
|
|
m_pcsEnum = NULL;
|
|
m_cEnum = 0;
|
|
m_csnum = 0;
|
|
m_dwRefCount = 0;
|
|
}
|
|
|
|
CMergedEnumPackage::~CMergedEnumPackage()
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i < m_cEnum; i++)
|
|
m_pcsEnum[i]->Release();
|
|
CoTaskMemFree(m_pcsEnum);
|
|
}
|
|
|
|
HRESULT __stdcall CMergedEnumPackage::QueryInterface(REFIID riid,
|
|
void * * ppObject)
|
|
{
|
|
*ppObject = NULL; //gd
|
|
if ((riid==IID_IUnknown) || (riid==IID_IEnumPackage))
|
|
{
|
|
*ppObject=(IEnumPackage *) this;
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG __stdcall CMergedEnumPackage::AddRef()
|
|
{
|
|
InterlockedIncrement((long*) &m_dwRefCount);
|
|
return m_dwRefCount;
|
|
}
|
|
|
|
|
|
|
|
ULONG __stdcall CMergedEnumPackage::Release()
|
|
{
|
|
ULONG dwRefCount;
|
|
if ((dwRefCount = InterlockedDecrement((long*) &m_dwRefCount))==0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return dwRefCount;
|
|
}
|
|
|
|
|
|
HRESULT __stdcall CMergedEnumPackage::Next(
|
|
ULONG celt,
|
|
PACKAGEDISPINFO *rgelt,
|
|
ULONG *pceltFetched)
|
|
{
|
|
ULONG count=0, total = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
//RpcImpersonateClient( NULL );
|
|
for (; m_csnum < m_cEnum; m_csnum++)
|
|
{
|
|
count = 0;
|
|
hr = m_pcsEnum[m_csnum]->Next(celt, rgelt+total, &count);
|
|
|
|
if (hr == E_INVALIDARG)
|
|
{
|
|
//RevertToSelf();
|
|
return hr;
|
|
}
|
|
|
|
total += count;
|
|
celt -= count;
|
|
|
|
if (!celt)
|
|
break;
|
|
}
|
|
if (pceltFetched)
|
|
*pceltFetched = total;
|
|
//RevertToSelf();
|
|
if (!celt)
|
|
return S_OK;
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT __stdcall CMergedEnumPackage::Skip(
|
|
ULONG celt)
|
|
{
|
|
PACKAGEDISPINFO *pPackageInfo = NULL;
|
|
HRESULT hr = S_OK;
|
|
ULONG cgot = 0, i;
|
|
|
|
pPackageInfo = (PACKAGEDISPINFO *)CoTaskMemAlloc(sizeof(PACKAGEDISPINFO)*celt);
|
|
hr = Next(celt, pPackageInfo, &cgot);
|
|
|
|
for (i = 0; i < cgot; i++)
|
|
ReleasePackageInfo(pPackageInfo+i);
|
|
CoTaskMemFree(pPackageInfo);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall CMergedEnumPackage::Reset()
|
|
{
|
|
ULONG i;
|
|
//RpcImpersonateClient( NULL );
|
|
for (i = 0; ((i <= m_csnum) && (i < m_cEnum)); i++)
|
|
m_pcsEnum[i]->Reset(); // ignoring all error values
|
|
m_csnum = 0;
|
|
//RevertToSelf();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall CMergedEnumPackage::Clone(IEnumPackage **ppIEnumPackage)
|
|
{
|
|
ULONG i;
|
|
CMergedEnumPackage *pClone;
|
|
IEnumPackage *pcsEnumCloned[MAXCLASSSTORES];
|
|
|
|
//RpcImpersonateClient( NULL );
|
|
pClone = new CMergedEnumPackage;
|
|
for ( i = 0; i < m_cEnum; i++)
|
|
m_pcsEnum[i]->Clone(&(pcsEnumCloned[i]));
|
|
|
|
pClone->m_csnum = m_csnum;
|
|
pClone->Initialize(pcsEnumCloned, m_cEnum);
|
|
*ppIEnumPackage = (IEnumPackage *)pClone;
|
|
pClone->AddRef();
|
|
//RevertToSelf();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CMergedEnumPackage::Initialize(IEnumPackage **pcsEnum, ULONG cEnum)
|
|
{
|
|
ULONG i;
|
|
m_csnum = 0;
|
|
m_pcsEnum = (IEnumPackage **)CoTaskMemAlloc(sizeof(IEnumPackage *) * cEnum);
|
|
if (!m_pcsEnum)
|
|
return E_OUTOFMEMORY;
|
|
for (i = 0; i < cEnum; i++)
|
|
m_pcsEnum[i] = pcsEnum[i];
|
|
m_cEnum = cEnum;
|
|
return S_OK;
|
|
}
|
|
|
|
|