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

580 lines
16 KiB
C++

// Appmgr.cpp : Implementation of CShellAppManager
#include "priv.h"
#include "appmgr.h"
#include "instenum.h"
#include "util.h"
#ifndef DOWNLEVEL_PLATFORM
#include "pubenum.h"
#endif //DOWNLEVEL_PLATFORM
#include "sccls.h"
#ifndef DOWNLEVEL_PLATFORM
#pragma message("DOWNLEVEL_PLATFORM is NOT defined")
#endif //DOWNLEVEL_PLATFORM
const TCHAR c_szTSMsiHackKey[] = TEXT("Software\\Policies\\Microsoft\\Windows\\Installer\\Terminal Server");
const TCHAR c_szTSMsiHackValue[] = TEXT("EnableAdminRemote");
/////////////////////////////////////////////////////////////////////////////
// CShellAppManager
// constructor
CShellAppManager::CShellAppManager() : _cRef(1)
{
DllAddRef();
TraceAddRef(CShellAppManager, _cRef);
ASSERT(_hdpaPub == NULL);
#ifndef DOWNLEVEL_PLATFORM
InitializeCriticalSection(&_cs);
HDCA hdca = DCA_Create();
if (hdca)
{
// Enumerate all of the Application Publishers
DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, REGSTR_PATH_APPPUBLISHER);
if (DCA_GetItemCount(hdca) > 0)
{
_Lock();
// Create our internal list of IAppPublisher *
_hdpaPub = DPA_Create(4);
if(_hdpaPub)
{
int idca;
for (idca = 0; idca < DCA_GetItemCount(hdca); idca++)
{
IAppPublisher * pap;
if (FAILED(DCA_CreateInstance(hdca, idca, IID_IAppPublisher, (LPVOID *) &pap)))
continue;
ASSERT(IS_VALID_CODE_PTR(pap, IAppPublisher));
if (DPA_AppendPtr(_hdpaPub, pap) == DPA_ERR)
{
pap->Release();
break;
}
}
// if we have no pointers in this array, don't bother create one
if (DPA_GetPtrCount(_hdpaPub) == 0)
{
DPA_Destroy(_hdpaPub);
_hdpaPub = NULL;
}
}
_Unlock();
}
DCA_Destroy(hdca);
}
if (IsTerminalServicesRunning())
{
// Hack for MSI work on Terminal Server
HKEY hkeyMsiHack = NULL;
DWORD lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szTSMsiHackKey, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyMsiHack);
if (lRet == ERROR_FILE_NOT_FOUND)
{
lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szTSMsiHackKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL, &hkeyMsiHack, NULL);
}
if (lRet == ERROR_SUCCESS)
{
DWORD dwType = 0;
DWORD dwTSMsiHack = 0;
DWORD cbSize = SIZEOF(dwTSMsiHack);
if ((ERROR_SUCCESS != RegQueryValueEx(hkeyMsiHack, c_szTSMsiHackValue, 0, &dwType, (LPBYTE)&dwTSMsiHack, &cbSize))
|| (dwType != REG_DWORD) || (dwTSMsiHack != 1))
{
dwTSMsiHack = 1;
if (RegSetValueEx(hkeyMsiHack, c_szTSMsiHackValue, 0, REG_DWORD, (LPBYTE)&dwTSMsiHack, SIZEOF(dwTSMsiHack)) == ERROR_SUCCESS)
_bCreatedTSMsiHack = TRUE;
}
RegCloseKey(hkeyMsiHack);
}
}
#endif //DOWNLEVEL_PLATFORM
}
// destructor
CShellAppManager::~CShellAppManager()
{
if (_bCreatedTSMsiHack)
{
ASSERT(IsTerminalServicesRunning());
HKEY hkeyMsiHack;
DWORD lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szTSMsiHackKey, 0, KEY_SET_VALUE, &hkeyMsiHack);
if (ERROR_SUCCESS == lRet)
{
RegDeleteValue(hkeyMsiHack, c_szTSMsiHackValue);
RegCloseKey(hkeyMsiHack);
}
}
_Lock();
// The order below is important
if (_hdsaCategoryList)
_DestroyInternalCategoryList();
if (_hdpaPub)
_DestroyAppPublisherList();
_Unlock();
DeleteCriticalSection(&_cs);
DllRelease();
}
// Recursively destroys a GUIDLIST
void CShellAppManager::_DestroyGuidList(GUIDLIST * pGuidList)
{
ASSERT(IS_VALID_WRITE_PTR(pGuidList, GUIDLIST));
if (pGuidList->pNextGuid)
_DestroyGuidList(pGuidList->pNextGuid);
LocalFree(pGuidList);
}
void CShellAppManager::_DestroyCategoryItem(CATEGORYITEM * pci)
{
ASSERT(IS_VALID_WRITE_PTR(pci, CATEGORYITEM));
if (pci->pszDescription)
LocalFree(pci->pszDescription);
if (pci->pGuidList)
_DestroyGuidList(pci->pGuidList);
}
// Destroys our Category List Table
void CShellAppManager::_DestroyInternalCategoryList()
{
// The caller must enter the lock first
ASSERT(0 < _cRefLock);
ASSERT(IS_VALID_HANDLE(_hdsaCategoryList, DSA));
int idsa;
for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
{
CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
if (pci)
_DestroyCategoryItem(pci);
}
DSA_Destroy(_hdsaCategoryList);
}
// Destroys our list of IAppPublisher *
void CShellAppManager::_DestroyAppPublisherList()
{
// The caller must enter the lock first
ASSERT(0 < _cRefLock);
ASSERT(IS_VALID_HANDLE(_hdpaPub, DPA));
int idpa;
for (idpa = 0; idpa < DPA_GetPtrCount(_hdpaPub); idpa++)
{
IAppPublisher * pap = (IAppPublisher *)DPA_GetPtr(_hdpaPub, idpa);
if (EVAL(pap))
pap->Release();
}
DPA_Destroy(_hdpaPub);
_hdpaPub = NULL;
}
// IShellAppManager::QueryInterface
HRESULT CShellAppManager::QueryInterface(REFIID riid, LPVOID * ppvOut)
{
static const QITAB qit[] = {
QITABENT(CShellAppManager, IShellAppManager), // IID_IShellAppManager
{ 0 },
};
return QISearch(this, qit, riid, ppvOut);
}
// IShellAppManager::AddRef
ULONG CShellAppManager::AddRef()
{
InterlockedIncrement(&_cRef);
TraceAddRef(CShellAppManager, _cRef);
return _cRef;
}
// IShellAppManager::Release
ULONG CShellAppManager::Release()
{
InterlockedDecrement(&_cRef);
TraceRelease(CShellAppManager, _cRef);
if (_cRef > 0)
return _cRef;
delete this;
return 0;
}
void CShellAppManager::_Lock(void)
{
EnterCriticalSection(&_cs);
DEBUG_CODE( _cRefLock++; )
}
void CShellAppManager::_Unlock(void)
{
DEBUG_CODE( _cRefLock--; )
LeaveCriticalSection(&_cs);
}
STDMETHODIMP CShellAppManager::GetNumberofInstalledApps(DWORD * pdwResult)
{
return E_NOTIMPL;
}
STDMETHODIMP CShellAppManager::EnumInstalledApps(IEnumInstalledApps ** ppeia)
{
HRESULT hres = E_FAIL;
CEnumInstalledApps * peia = new CEnumInstalledApps();
if (peia)
{
*ppeia = SAFECAST(peia, IEnumInstalledApps *);
hres = S_OK;
}
else
hres = E_OUTOFMEMORY;
return hres;
}
#ifndef DOWNLEVEL_PLATFORM
HRESULT CShellAppManager::_AddCategoryToList(APPCATEGORYINFO * pai, IAppPublisher * pap)
{
// The caller must enter the lock first
ASSERT(0 < _cRefLock);
if (pai == NULL || _hdsaCategoryList == NULL)
return E_FAIL;
ASSERT(IS_VALID_CODE_PTR(pap, IAppPublisher));
// Allocate a GUIDLIST item first
GUIDLIST * pgl = (GUIDLIST *)LocalAlloc(LPTR, SIZEOF(GUIDLIST));
if (!pgl)
return E_OUTOFMEMORY;
pgl->CatGuid = pai->AppCategoryId;
pgl->papSupport = pap;
// Search in the CategoryList
int idsa;
for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
{
CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
if (pci)
{
// If we find an empty spot, fill it
if (pci->pszDescription == NULL)
{
CATEGORYITEM ci = {0};
ci.pszDescription = StrDupW(pai->pszDescription);
ci.pGuidList = pgl;
pgl->pNextGuid = NULL;
if (DSA_InsertItem(_hdsaCategoryList, idsa, &ci) == -1)
{
LocalFree(ci.pszDescription);
break;
}
}
// If we find an entry with the same description text, add our GUID to the GuidList
else if(!lstrcmpi(pai->pszDescription, pci->pszDescription))
{
pgl->pNextGuid = pci->pGuidList;
pci->pGuidList = pgl;
break;
}
}
else
ASSERT(0);
}
// We ran out of items in the list, and didn't run into an identical category string
if (idsa == DSA_GetItemCount(_hdsaCategoryList))
{
CATEGORYITEM ci = {0};
ci.pszDescription = StrDupW(pai->pszDescription);
ci.pGuidList = pgl;
pgl->pNextGuid = NULL;
if (DSA_AppendItem(_hdsaCategoryList, &ci) == -1)
LocalFree(ci.pszDescription);
}
return S_OK;
}
#endif //DOWNLEVEL_PLATFORM
#ifndef DOWNLEVEL_PLATFORM
HRESULT CShellAppManager::_BuildInternalCategoryList()
{
HRESULT hres = E_OUTOFMEMORY;
// The caller must enter the lock first
ASSERT(0 < _cRefLock);
ASSERT(IsValidHDPA(_hdpaPub));
// We have just one valid version of _hdsaCategoryList, so we should never call this function
// again once _hdsaCategoryList is created.
ASSERT(_hdsaCategoryList == NULL);
// Is the structure automatically filled with zero?
_hdsaCategoryList = DSA_Create(SIZEOF(CATEGORYITEM), CATEGORYLIST_GROW);
if (_hdsaCategoryList)
{
int idpa;
for (idpa = 0; idpa < DPA_GetPtrCount(_hdpaPub); idpa++)
{
IAppPublisher * pap = (IAppPublisher *)DPA_GetPtr(_hdpaPub, idpa);
ASSERT(pap);
if (pap)
{
UINT i;
APPCATEGORYINFOLIST AppCatList;
if (SUCCEEDED(pap->GetCategories(&AppCatList)))
{
if ((AppCatList.cCategory > 0) && AppCatList.pCategoryInfo)
{
for (i = 0; i < AppCatList.cCategory; i++)
_AddCategoryToList(&AppCatList.pCategoryInfo[i], pap);
_DestroyCategoryList(&AppCatList);
}
}
}
hres = S_OK;
}
}
return hres;
}
#endif //DOWNLEVEL_PLATFORM
#ifndef DOWNLEVEL_PLATFORM
// Compile a multi string of categories and return it to the caller
HRESULT CShellAppManager::_CompileCategoryList(PSHELLAPPCATEGORYLIST psacl)
{
HRESULT hres = E_FAIL;
ASSERT(IS_VALID_READ_PTR(psacl, SHELLAPPCATEGORYLIST));
// Don't do anything if we have an empty list
if (_hdsaCategoryList && (DSA_GetItemCount(_hdsaCategoryList) > 0))
{
psacl->pCategory = (PSHELLAPPCATEGORY) SHAlloc(DSA_GetItemCount(_hdsaCategoryList) * SIZEOF(SHELLAPPCATEGORY));
if (psacl->pCategory)
{
int idsa;
for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
{
CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
if (pci && pci->pszDescription)
{
if (SUCCEEDED(SHStrDup(pci->pszDescription, &psacl->pCategory[idsa].pszCategory)))
{
ASSERT(IS_VALID_STRING_PTR(psacl->pCategory[idsa].pszCategory, -1));
psacl->cCategories++;
}
else
break;
}
}
hres = S_OK;
}
else
hres = E_OUTOFMEMORY;
}
return hres;
}
#endif //DOWNLEVEL_PLATFORM
// IShellAppManager::GetPublishedAppCategories
STDMETHODIMP CShellAppManager::GetPublishedAppCategories(PSHELLAPPCATEGORYLIST psacl)
{
#ifndef DOWNLEVEL_PLATFORM
HRESULT hres = E_INVALIDARG;
if (psacl)
{
ASSERT(IS_VALID_READ_PTR(psacl, SHELLAPPCATEGORYLIST));
ZeroMemory(psacl, SIZEOF(SHELLAPPCATEGORYLIST));
// NOTE: keep the check inside the lock! So that only one thread
// is allowed in.
_Lock();
// Do we have any app publishers in our list at all
if (_hdpaPub)
{
if (_hdsaCategoryList == NULL)
_BuildInternalCategoryList();
hres = _CompileCategoryList(psacl);
}
_Unlock();
}
#else
HRESULT hres = E_NOTIMPL;
#endif //DOWNLEVEL_PLATFORM
return hres;
}
#ifndef DOWNLEVEL_PLATFORM
GUIDLIST * CShellAppManager::_FindGuidListForCategory(LPCWSTR pszDescription)
{
// The caller must enter the lock first
ASSERT(0 < _cRefLock);
if (_hdsaCategoryList)
{
int idsa;
for (idsa = 0; idsa < DSA_GetItemCount(_hdsaCategoryList); idsa++)
{
CATEGORYITEM * pci = (CATEGORYITEM *)DSA_GetItemPtr(_hdsaCategoryList, idsa);
if (pci && pci->pszDescription && !lstrcmpi(pszDescription, pci->pszDescription))
return pci->pGuidList;
}
}
return NULL;
}
#endif //DOWNLEVEL_PLATFORM
extern void _DestroyHdpaEnum(HDPA hdpaEnum);
// IShellAppManager::EnumPublishedApps
STDMETHODIMP CShellAppManager::EnumPublishedApps(LPCWSTR pszCategory, IEnumPublishedApps ** ppepa)
{
#ifndef DOWNLEVEL_PLATFORM
HRESULT hres = E_OUTOFMEMORY;
ASSERT(pszCategory == NULL || IS_VALID_STRING_PTRW(pszCategory, -1));
// hdpaEnum is the list of IEnumPublishedApp * we pass to the constructor of CShellEnumPublishedApps
HDPA hdpaEnum = DPA_Create(4);
if (hdpaEnum)
{
// If no category is required, we enumerate all
if (pszCategory == NULL)
{
_Lock();
if (_hdpaPub)
{
int idpa;
for (idpa = 0; idpa < DPA_GetPtrCount(_hdpaPub); idpa++)
{
IAppPublisher * pap = (IAppPublisher *)DPA_GetPtr(_hdpaPub, idpa);
IEnumPublishedApps * pepa;
if (pap && SUCCEEDED(pap->EnumApps(NULL, &pepa)))
{
ASSERT(IS_VALID_CODE_PTR(pepa, IEnumPublishedApps));
if (DPA_AppendPtr(hdpaEnum, pepa) == DPA_ERR)
{
pepa->Release();
break;
}
}
}
}
_Unlock();
}
else
{
_Lock();
// If there is no Category list, let's build one
if (_hdsaCategoryList == NULL)
_BuildInternalCategoryList();
// Otherwise we find the GuidList and enumerate all the guys in the list
GUIDLIST * pgl = _FindGuidListForCategory(pszCategory);
while (pgl && pgl->papSupport)
{
ASSERT(IS_VALID_READ_PTR(pgl, GUIDLIST) && IS_VALID_CODE_PTR(pgl->papSupport, IAppPulisher));
IEnumPublishedApps * pepa;
if (SUCCEEDED(pgl->papSupport->EnumApps(&pgl->CatGuid, &pepa)))
{
ASSERT(IS_VALID_CODE_PTR(pepa, IEnumPublishedApps));
if (DPA_AppendPtr(hdpaEnum, pepa) == DPA_ERR)
{
pepa->Release();
break;
}
}
pgl = pgl->pNextGuid;
}
_Unlock();
}
}
// Even if we have no enumerators we return success and pass back an empty enumerator
CShellEnumPublishedApps * psepa = new CShellEnumPublishedApps(hdpaEnum);
if (psepa)
{
*ppepa = SAFECAST(psepa, IEnumPublishedApps *);
hres = S_OK;
}
else
{
hres = E_OUTOFMEMORY;
if (hdpaEnum)
_DestroyHdpaEnum(hdpaEnum);
}
#else
HRESULT hres = E_NOTIMPL;
#endif //DOWNLEVEL_PLATFORM
return hres;
}
EXTERN_C STDAPI InstallAppFromFloppyOrCDROM(HWND hwnd);
STDMETHODIMP CShellAppManager::InstallFromFloppyOrCDROM(HWND hwndParent)
{
return InstallAppFromFloppyOrCDROM(hwndParent);
}
/*----------------------------------------------------------
Purpose: Create-instance function for class factory
*/
STDAPI CShellAppManager_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
// aggregation checking is handled in class factory
HRESULT hres = E_OUTOFMEMORY;
CShellAppManager* pObj = new CShellAppManager();
if (pObj)
{
*ppunk = SAFECAST(pObj, IShellAppManager *);
hres = S_OK;
}
return hres;
}