//+----------------------------------------------------------------------- // // Matrix Array // //------------------------------------------------------------------------ #include "priv.h" // Do not build this file if on Win9X or NT4 #ifndef DOWNLEVEL_PLATFORM #include #include "mtxarray.h" #include "adcctl.h" // for EnumArea string constants #include "util.h" // for ReleaseShellCategory #include "dump.h" #include "appwizid.h" #ifdef WINNT #include // for TermsrvAppInstallMode #endif //-------------------------------------------------------------------- // // // CAppData class // // //-------------------------------------------------------------------- #define MAX_DATE_SIZE 50 #define IFS_APPINFODATA 1 #define IFS_SHELLCAT 2 #define IFS_CAPABILITY 3 #define IFS_SUPPORTINFO 4 #define IFS_INDEXLABEL 5 #define IFS_INDEXVALUE 6 #define IFS_PROPERTIES 7 #define IFS_SIZE 8 #define IFS_TIMESUSED 9 #define IFS_LASTUSED 10 #define IFS_OCSETUP 11 #define IFS_SCHEDULE 12 #define IFS_ICON 13 #define IFS_ISINSTALLED 14 const APPFIELDS g_rginstfields[] = { { /* 0 */ L"displayname", SORT_NAME, IFS_APPINFODATA, VT_BSTR, FIELD_OFFSET(APPINFODATA, pszDisplayName) }, { /* 1 */ L"size", SORT_SIZE, IFS_SIZE, VT_BSTR, 0 }, { /* 2 */ L"timesused", SORT_TIMESUSED, IFS_TIMESUSED, VT_BSTR, 0 }, { /* 3 */ L"lastused", SORT_LASTUSED, IFS_LASTUSED, VT_BSTR, 0 }, { /* 4 */ L"capability", SORT_NA, IFS_CAPABILITY , VT_UI4, 0 }, { /* 5 */ L"supportinfo", SORT_NA, IFS_SUPPORTINFO, VT_BSTR, 0 }, { /* 6 */ L"indexlabel", SORT_NA, IFS_INDEXLABEL, VT_BSTR, 0 }, { /* 7 */ L"indexvalue", SORT_NA, IFS_INDEXVALUE, VT_BSTR, 0 }, { /* 8 */ L"htmlproperties", SORT_NA, IFS_PROPERTIES, VT_BSTR, 0 }, { /* 9 */ L"icon", SORT_NA, IFS_ICON, VT_BSTR, 0 }, }; const APPFIELDS g_rgpubfields[] = { { /* 0 */ L"displayname", SORT_NAME, IFS_APPINFODATA, VT_BSTR, FIELD_OFFSET(APPINFODATA, pszDisplayName) }, { /* 1 */ L"capability", SORT_NA, IFS_CAPABILITY , VT_UI4, 0 }, { /* 2 */ L"supporturl", SORT_NA, IFS_APPINFODATA, VT_BSTR, FIELD_OFFSET(APPINFODATA, pszSupportUrl) }, { /* 3 */ L"htmlproperties", SORT_NA, IFS_PROPERTIES, VT_BSTR, 0 }, { /* 4 */ L"addlaterschedule", SORT_NA, IFS_SCHEDULE, VT_BSTR, 0 }, { /* 5 */ L"isinstalled", SORT_NA, IFS_ISINSTALLED, VT_BSTR, 0 }, }; const APPFIELDS g_rgsetupfields[] = { { /* 0 */ L"displayname", SORT_NAME, IFS_OCSETUP, VT_BSTR, FIELD_OFFSET(COCSetupApp, _szDisplayName) }, }; const APPFIELDS g_rgcatfields[] = { { /* 0 */ L"DisplayName", SORT_NAME, IFS_SHELLCAT, VT_BSTR, FIELD_OFFSET(SHELLAPPCATEGORY, pszCategory) }, { /* 1 */ L"ID", SORT_NA, IFS_SHELLCAT, VT_UI4, FIELD_OFFSET(SHELLAPPCATEGORY, idCategory) }, }; // Overloaded constructor CAppData::CAppData(IInstalledApp* pia, APPINFODATA* paid, PSLOWAPPINFO psai) : _cRef(1) { _pia = pia; CopyMemory(&_ai, paid, sizeof(_ai)); //NOTE: psai can be NULL if (psai) { CopyMemory(&_aiSlow, psai, sizeof(_aiSlow)); // Let's massage some values. _MassageSlowAppInfo(); } _dwEnum = ENUM_INSTALLED; InitializeCriticalSection(&_cs); _fCsInitialized = TRUE; } // Overloaded constructor CAppData::CAppData(IPublishedApp* ppa, APPINFODATA* paid, PUBAPPINFO * ppai) : _cRef(1) { _ppa = ppa; CopyMemory(&_ai, paid, sizeof(_ai)); CopyMemory(&_pai, ppai, sizeof(_pai)); _dwEnum = ENUM_PUBLISHED; } // Overloaded constructor CAppData::CAppData(COCSetupApp * pocsa, APPINFODATA* paid) : _cRef(1) { _pocsa = pocsa; CopyMemory(&_ai, paid, sizeof(_ai)); _dwEnum = ENUM_OCSETUP; } // Overloaded constructor CAppData::CAppData(SHELLAPPCATEGORY * psac) : _cRef(1) { _psac = psac; _dwEnum = ENUM_CATEGORIES; } // destructor CAppData::~CAppData() { if (ENUM_CATEGORIES == _dwEnum) ReleaseShellCategory(_psac); else if (ENUM_OCSETUP == _dwEnum) { delete _pocsa; ClearAppInfoData(&_ai); } else { // Release _pia or _ppa. Take your pick. Either way releases // the object because both inherit from IUnknown. _pia->Release(); ClearAppInfoData(&_ai); ClearSlowAppInfo(&_aiSlow); ClearPubAppInfo(&_pai); } if (_fCsInitialized) DeleteCriticalSection(&_cs); } /*-------------------------------------------------------------------- Purpose: IUnknown::QueryInterface */ STDMETHODIMP CAppData::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENT(CAppData, IAppData), { 0 }, }; return QISearch(this, (LPCQITAB)qit, riid, ppvObj); } STDMETHODIMP_(ULONG) CAppData::AddRef() { InterlockedIncrement(&_cRef); TraceAddRef(CAppData, _cRef); return _cRef; } STDMETHODIMP_(ULONG) CAppData::Release() { ASSERT(_cRef > 0); InterlockedDecrement(&_cRef); TraceRelease(CAppData, _cRef); if (_cRef > 0) return _cRef; delete this; return 0; } /*------------------------------------------------------------------------- Purpose: Massage some values so they are sorted correctly */ void CAppData::_MassageSlowAppInfo(void) { // We don't tell the difference b/t unknown app sizes and zero // app sizes, so to make sorting easier, change the unknown app // size to zero. if (-1 == (__int64)_aiSlow.ullSize) _aiSlow.ullSize = 0; // Unmarked last-used fields will get marked as 'not used', so // they are sorted correctly. if (0 == _aiSlow.ftLastUsed.dwHighDateTime && 0 == _aiSlow.ftLastUsed.dwLowDateTime) { _aiSlow.ftLastUsed.dwHighDateTime = NOTUSED_HIGHDATETIME; _aiSlow.ftLastUsed.dwLowDateTime = NOTUSED_LOWDATETIME; } } /*------------------------------------------------------------------------- Purpose: IAppData::ReadSlowData Read the slow app data. Call GetSlowDataPtr() to get it. */ STDMETHODIMP CAppData::ReadSlowData(void) { HRESULT hres = E_FAIL; if (ENUM_INSTALLED == _dwEnum && _pia) { SLOWAPPINFO sai = {0}; hres = _pia->GetSlowAppInfo(&sai); if (S_OK == hres) { // Switch our current SLOWAPPINFO with the new one // This is necessary because our current one is from the GetCachedSlowAppInfo // It may not have the most up-to-date info. if (IsSlowAppInfoChanged(&_aiSlow, &sai)) { EnterCriticalSection(&_cs); { ClearSlowAppInfo(&_aiSlow); _aiSlow = sai; hres = S_OK; } LeaveCriticalSection(&_cs); // Let's massage some values. _MassageSlowAppInfo(); } else hres = S_FALSE; } } return hres; } /*------------------------------------------------------------------------- Purpose: IAppData::GetDataPtr Returns the pointer to the internal data structure. The caller must hold the appdata object to guarantee the pointer remains valid. */ STDMETHODIMP_(APPINFODATA *) CAppData::GetDataPtr(void) { return &_ai; } /*------------------------------------------------------------------------- Purpose: IAppData::GetSlowDataPtr Returns the pointer to the internal data structure for slow data. The caller must hold the appdata object to guarantee the pointer remains valid. */ STDMETHODIMP_(SLOWAPPINFO *) CAppData::GetSlowDataPtr(void) { return &_aiSlow; } /*------------------------------------------------------------------------- Purpose: Return the capability flags */ DWORD CAppData::_GetCapability(void) { DWORD dwCapability = 0; if (ENUM_INSTALLED == _dwEnum || ENUM_PUBLISHED == _dwEnum) { // We can use _pia or _ppa for this method because both // inherit from IShellApp. _pia->GetPossibleActions(&dwCapability); } return dwCapability; } /*------------------------------------------------------------------------- Purpose: Returns the current sort index */ DWORD CAppData::_GetSortIndex(void) { DWORD dwRet = SORT_NAME; // default if (_pmtxParent) _pmtxParent->GetSortIndex(&dwRet); return dwRet; } /*------------------------------------------------------------------------- Purpose: Return the amount of disk space the app occupies. Returns S_OK if the field is valid. */ HRESULT CAppData::_GetDiskSize(LPTSTR pszBuf, int cchBuf) { HRESULT hres = S_FALSE; ULONGLONG ullSize = GetSlowDataPtr()->ullSize; if (pszBuf && 0 < cchBuf) *pszBuf = 0; // Is this size printable? if (-1 != (__int64)ullSize && 0 != (__int64)ullSize) { // Yes if (pszBuf) ShortSizeFormat64(ullSize, pszBuf); hres = S_OK; } return hres; } #define FREQDECAY_OFTEN 10 /*------------------------------------------------------------------------- Purpose: IAppData::GetFrequencyOfUse Return friendly names that map from the frequency-of-use metric from the UEM. Returns S_OK if the field is valid. */ STDMETHODIMP CAppData::GetFrequencyOfUse(LPTSTR pszBuf, int cchBuf) { HRESULT hres; int iTemp = GetSlowDataPtr()->iTimesUsed; if (0 > iTemp) { if (pszBuf && 0 < cchBuf) *pszBuf = 0; hres = S_FALSE; } else { UINT ids = IDS_OFTEN; if (2 >= iTemp) ids = IDS_RARELY; else if (FREQDECAY_OFTEN >= iTemp) ids = IDS_SOMETIMES; if (pszBuf) LoadString(g_hinst, ids, pszBuf, cchBuf); hres = S_OK; } return hres; } /*------------------------------------------------------------------------- Purpose: Return the friendly date when the app was last used. Returns S_OK if the field is valid. */ HRESULT CAppData::_GetLastUsed(LPTSTR pszBuf, int cchBuf) { HRESULT hres; FILETIME ft = GetSlowDataPtr()->ftLastUsed; if (NOTUSED_HIGHDATETIME == ft.dwHighDateTime && NOTUSED_LOWDATETIME == ft.dwLowDateTime) { if (pszBuf && 0 < cchBuf) *pszBuf = 0; hres = S_FALSE; } else { DWORD dwFlags = FDTF_SHORTDATE; ASSERT(0 != ft.dwHighDateTime || 0 != ft.dwLowDateTime); if (pszBuf) SHFormatDateTime(&ft, &dwFlags, pszBuf, cchBuf); hres = S_OK; } return hres; } /*------------------------------------------------------------------------- Purpose: Build up a structured string that the html script can read and parse. The Boilerplate JavaScript class assumes the fields follow this format: ... It can accept quoted or non-quoted values. If the value has a '>' in it, then it should be quoted, otherwise the Boilerplate code will get confused. The order of the fieldnames is not important, and some or all may be missing. */ LPTSTR CAppData::_BuildSupportInfo(void) { const static struct { LPWSTR pszFieldname; DWORD ibOffset; // offset into structure designated by dwStruct } s_rgsupfld[] = { { L"comments", FIELD_OFFSET(APPINFODATA, pszComments) }, { L"contact", FIELD_OFFSET(APPINFODATA, pszContact) }, { L"displayname", FIELD_OFFSET(APPINFODATA, pszDisplayName) }, { L"helpurl", FIELD_OFFSET(APPINFODATA, pszHelpLink) }, { L"helpphone", FIELD_OFFSET(APPINFODATA, pszSupportTelephone) }, { L"productID", FIELD_OFFSET(APPINFODATA, pszProductID) }, { L"publisher", FIELD_OFFSET(APPINFODATA, pszPublisher) }, { L"readmeUrl", FIELD_OFFSET(APPINFODATA, pszReadmeUrl) }, { L"regcompany", FIELD_OFFSET(APPINFODATA, pszRegisteredCompany) }, { L"regowner", FIELD_OFFSET(APPINFODATA, pszRegisteredOwner) }, { L"supporturl", FIELD_OFFSET(APPINFODATA, pszSupportUrl) }, { L"updateinfoUrl", FIELD_OFFSET(APPINFODATA, pszUpdateInfoUrl) }, { L"version", FIELD_OFFSET(APPINFODATA, pszVersion) }, }; ShStr shstr; int i; // Now populate the structured string for (i = 0; i < ARRAYSIZE(s_rgsupfld); i++) { LPWSTR pszT = *(LPWSTR *)((LPBYTE)&_ai + s_rgsupfld[i].ibOffset); if (pszT && *pszT) { TCHAR sz[64]; wsprintf(sz, TEXT("<%s \""), s_rgsupfld[i].pszFieldname); shstr.Append(sz); shstr.Append(pszT); shstr.Append(TEXT("\">")); } } return shstr.CloneStr(); } #define c_szPropRowBegin TEXT("") #define c_szPropRowMid TEXT("") #define c_szPropRowEnd TEXT("") // CAppData::_BuildPropertiesHTML // Build up a string of HTML that represents the list of useful // properties for this app. We skip any blank fields. LPTSTR CAppData::_BuildPropertiesHTML(void) { LPTSTR pszRet = NULL; DWORD dwSort = _GetSortIndex(); // The HTML consists of a table, each row is a property label and value. // Since the 'indexlabel' and 'indexvalue' properties vary depending // on the sort criteria, we exclude them to avoid duplication. For // example, if the user is sorting by size, we don't show the size // in the properties HTML because the size is being shown via the // 'indexvalue' property. // Let's first make a pass of the eligible properties to see which // ones to include. #define PM_SIZE 0x0001 #define PM_TIMESUSED 0x0002 #define PM_LASTUSED 0x0004 #define PM_INSTALLEDON 0x0008 TCHAR szSize[64]; TCHAR szFreq[64]; TCHAR szLastUsed[64]; DWORD dwPropMask = 0; // Can be combination of PM_* dwPropMask |= (S_OK == _GetDiskSize(szSize, SIZECHARS(szSize))) ? PM_SIZE : 0; dwPropMask |= (S_OK == GetFrequencyOfUse(szFreq, SIZECHARS(szFreq))) ? PM_TIMESUSED : 0; dwPropMask |= (S_OK == _GetLastUsed(szLastUsed, SIZECHARS(szLastUsed))) ? PM_LASTUSED : 0; // The sorting criteria should be removed if (SORT_NAME == dwSort || SORT_SIZE == dwSort) dwPropMask &= ~PM_SIZE; else if (SORT_TIMESUSED == dwSort) dwPropMask &= ~PM_TIMESUSED; else if (SORT_LASTUSED == dwSort) dwPropMask &= ~PM_LASTUSED; // Are there any properties to show at all? if (dwPropMask) { // Yes; build the html for the table. ShStr shstr; TCHAR szLabel[64]; shstr = TEXT(""); struct { DWORD dwMask; UINT ids; LPTSTR psz; } s_rgProp[] = { { PM_SIZE, IDS_LABEL_SIZE, szSize }, { PM_TIMESUSED, IDS_LABEL_TIMESUSED, szFreq }, { PM_LASTUSED, IDS_LABEL_LASTUSED, szLastUsed }, }; int i; for (i = 0; i < ARRAYSIZE(s_rgProp); i++) { if (dwPropMask & s_rgProp[i].dwMask) { LoadString(g_hinst, s_rgProp[i].ids, szLabel, SIZECHARS(szLabel)); shstr.Append(c_szPropRowBegin); shstr.Append(szLabel); shstr.Append(c_szPropRowMid); // Size and frequency-of-use get anchor elements around them... if (s_rgProp[i].dwMask & PM_TIMESUSED) shstr.Append(TEXT(" ")); else if (s_rgProp[i].dwMask & PM_SIZE) shstr.Append(TEXT(" ")); shstr.Append(s_rgProp[i].psz); if (s_rgProp[i].dwMask & PM_TIMESUSED) shstr.Append(TEXT("")); else if (s_rgProp[i].dwMask & PM_SIZE) shstr.Append(TEXT("")); shstr.Append(c_szPropRowEnd); } } shstr.Append(TEXT("
")); pszRet = shstr.CloneStr(); // clone for the caller } return pszRet; } #define c_szIconHTMLFormat TEXT("sysimage://%s/small") /*------------------------------------------------------------------------- Purpose: Return the HTML that points to the icon of this application use instshld.ico as a default */ void CAppData::_GetIconHTML(LPTSTR pszIconHTML, UINT cch) { if (cch > (MAX_PATH + ARRAYSIZE(c_szIconHTMLFormat))) { TCHAR szImage[MAX_PATH+10]; if (_ai.pszImage && _ai.pszImage[0]) { StrCpyN(szImage, _ai.pszImage, ARRAYSIZE(szImage)); //PathParseIconLocation(szImage); } else { BOOL bUseDefault = FALSE; EnterCriticalSection(&_cs); if (_aiSlow.pszImage && _aiSlow.pszImage[0]) lstrcpy(szImage, _aiSlow.pszImage); else bUseDefault = TRUE; LeaveCriticalSection(&_cs); if (bUseDefault) goto DEFAULT; } wnsprintf(pszIconHTML, cch, c_szIconHTMLFormat, szImage); } else DEFAULT: // In the default case, return instshld.ico lstrcpy(pszIconHTML, TEXT("res://appwiz.cpl/instshld.ico")); } /*------------------------------------------------------------------------- Purpose: Sets the variant with the correct data given the field */ HRESULT CAppData::_VariantFromData(const APPFIELDS * pfield, LPVOID pvData, VARIANT * pvar) { HRESULT hres = S_OK; VariantInit(pvar); if (pvData) { if (VT_BSTR == pfield->vt) { LPWSTR psz = *(LPWSTR*)pvData; if (NULL == psz) psz = L""; ASSERT(IS_VALID_STRING_PTR(psz, -1)); pvar->bstrVal = SysAllocString(psz); if (pvar->bstrVal) pvar->vt = VT_BSTR; else hres = E_OUTOFMEMORY; } else if (VT_UI4 == pfield->vt) { pvar->lVal = *(LONG*)pvData; pvar->vt = pfield->vt; } } return hres; } /*------------------------------------------------------------------------- Purpose: Get the value of the installed-app field. */ HRESULT CAppData::_GetInstField(DB_LORDINAL iField, VARIANT * pvar) { HRESULT hres = E_FAIL; ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT)); ASSERT(ENUM_INSTALLED == _dwEnum); // Columns map to different fields. if (IsInRange(iField, 0, ARRAYSIZE(g_rginstfields))) { const APPFIELDS * pfield = &g_rginstfields[iField]; LPVOID pvData = NULL; // assume no value TCHAR szTemp[MAX_PATH*2]; LPCTSTR pszTmp; DWORD dwTemp; // Map the field ordinal to the actual value in the appropriate structure switch (pfield->dwStruct) { case IFS_APPINFODATA: pvData = (LPVOID)((LPBYTE)&_ai + pfield->ibOffset); break; case IFS_CAPABILITY: dwTemp = _GetCapability(); pvData = (LPVOID) &dwTemp; break; case IFS_SUPPORTINFO: pszTmp = CAppData::_BuildSupportInfo(); if (pszTmp) { TraceMsg(TF_VERBOSEDSO, "HTML (supportinfo): \"%s\"", pszTmp); pvData = (LPVOID) &pszTmp; } break; case IFS_SIZE: _GetDiskSize(szTemp, SIZECHARS(szTemp)); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; break; case IFS_TIMESUSED: GetFrequencyOfUse(szTemp, SIZECHARS(szTemp)); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; break; case IFS_LASTUSED: _GetLastUsed(szTemp, SIZECHARS(szTemp)); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; break; case IFS_INDEXLABEL: // Unknown values shouldn't show the labels, so assume the // value will be unknown dwTemp = 0; // The index label is according to the current sort criteria switch (_GetSortIndex()) { case SORT_NAME: // when sorting by name, we show the size case SORT_SIZE: if (S_OK == _GetDiskSize(NULL, 0)) dwTemp = IDS_LABEL_SIZE; break; case SORT_TIMESUSED: if (S_OK == GetFrequencyOfUse(NULL, 0)) dwTemp = IDS_LABEL_TIMESUSED; break; case SORT_LASTUSED: if (S_OK == _GetLastUsed(NULL, 0)) dwTemp = IDS_LABEL_LASTUSED; break; } if (0 != dwTemp) { LoadString(g_hinst, dwTemp, szTemp, SIZECHARS(szTemp)); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; TraceMsg(TF_VERBOSEDSO, "HTML (indexlabel): \"%s\"", pszTmp); } break; case IFS_INDEXVALUE: // The index value is according to the current sort criteria switch (_GetSortIndex()) { case SORT_NAME: // when sorting by name, we show the size case SORT_SIZE: _GetDiskSize(szTemp, SIZECHARS(szTemp)); break; case SORT_TIMESUSED: GetFrequencyOfUse(szTemp, SIZECHARS(szTemp)); break; case SORT_LASTUSED: _GetLastUsed(szTemp, SIZECHARS(szTemp)); break; } pszTmp = szTemp; pvData = (LPVOID) &pszTmp; TraceMsg(TF_VERBOSEDSO, "HTML (indexvalue) for %s: \"%s\"", GetDataPtr()->pszDisplayName, pszTmp); break; case IFS_PROPERTIES: pszTmp = _BuildPropertiesHTML(); if (pszTmp) { pvData = (LPVOID) &pszTmp; TraceMsg(TF_VERBOSEDSO, "HTML (properties) for %s: \"%s\"", GetDataPtr()->pszDisplayName, pszTmp); } break; case IFS_ICON: _GetIconHTML(szTemp, ARRAYSIZE(szTemp)); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; TraceMsg(TF_VERBOSEDSO, "Icon HTML for %s: \"%s\"", GetDataPtr()->pszDisplayName, pszTmp); break; default: ASSERTMSG(0, "invalid field type"); break; } hres = _VariantFromData(pfield, pvData, pvar); // Clean up switch (pfield->dwStruct) { case IFS_SUPPORTINFO: case IFS_PROPERTIES: if (pvData) { pszTmp = *(LPWSTR*)pvData; LocalFree((HLOCAL)pszTmp); } break; } } return hres; } #define c_szAddLaterHTML TEXT("
%s%s
") /*------------------------------------------------------------------------- Purpose: Get the value of the published app field. */ HRESULT CAppData::_GetPubField(DB_LORDINAL iField, VARIANT * pvar) { HRESULT hres = E_FAIL; ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT)); ASSERT(ENUM_PUBLISHED == _dwEnum); // Columns map to different fields. if (IsInRange(iField, 0, ARRAYSIZE(g_rgpubfields))) { DWORD dwTemp = 0; const APPFIELDS * pfield = &g_rgpubfields[iField]; LPVOID pvData = NULL; WCHAR szTemp[MAX_PATH*2]; LPWSTR pszTmp = NULL; // Map the field ordinal to the actual value in the appropriate structure switch (pfield->dwStruct) { case IFS_APPINFODATA: // For the display name, we want to display it in the following format if there are // duplicate entries: Ex "Word : Policy1" and "Word : Policy2" if ((iField == 0) && _bNameDupe && (_pai.dwMask & PAI_SOURCE) && _pai.pszSource && _pai.pszSource[0]) { wnsprintf(szTemp, ARRAYSIZE(szTemp), L"%ls: %ls", _ai.pszDisplayName, _pai.pszSource); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; } else pvData = (LPVOID)((LPBYTE)&_ai + pfield->ibOffset); break; case IFS_CAPABILITY: dwTemp = _GetCapability(); pvData = (LPVOID) &dwTemp; break; case IFS_SCHEDULE: if ((_GetCapability() & APPACTION_ADDLATER) && (_pai.dwMask & PAI_SCHEDULEDTIME)) { TCHAR szTime[MAX_PATH]; if (FormatSystemTimeString(&_pai.stScheduled, szTime, ARRAYSIZE(szTime))) { TCHAR szAddLater[100]; LoadString(g_hinst, IDS_ADDLATER, szAddLater, ARRAYSIZE(szAddLater)); wsprintf(szTemp, c_szAddLaterHTML, szAddLater, szTime); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; } } break; case IFS_ISINSTALLED: if (S_OK == _ppa->IsInstalled()) { LoadString(g_hinst, IDS_INSTALLED, szTemp, SIZECHARS(szTemp)); pszTmp = szTemp; pvData = (LPVOID) &pszTmp; } break; default: ASSERTMSG(0, "invalid field type"); hres = E_FAIL; break; } hres = _VariantFromData(pfield, pvData, pvar); } return hres; } /*------------------------------------------------------------------------- Purpose: Get the value of the ocsetup app field. */ HRESULT CAppData::_GetSetupField(DB_LORDINAL iField, VARIANT * pvar) { HRESULT hres = E_FAIL; ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT)); ASSERT(ENUM_OCSETUP == _dwEnum); // Columns map to different fields. if (IsInRange(iField, 0, ARRAYSIZE(g_rgsetupfields))) { const APPFIELDS * pfield = &g_rgsetupfields[iField]; LPVOID pvData = NULL; ASSERT(IFS_OCSETUP == pfield->dwStruct); pvData = (LPVOID)((LPBYTE)&_pocsa + pfield->ibOffset); hres = _VariantFromData(pfield, pvData, pvar); } return hres; } /*------------------------------------------------------------------------- Purpose: Get the value of the category field. */ HRESULT CAppData::_GetCatField(DB_LORDINAL iField, VARIANT * pvar) { HRESULT hres = E_FAIL; ASSERT(IS_VALID_WRITE_PTR(pvar, VARIANT)); ASSERT(ENUM_CATEGORIES == _dwEnum); // Columns map to different fields. if (IsInRange(iField, 0, ARRAYSIZE(g_rgcatfields))) { const APPFIELDS * pfield = &g_rgcatfields[iField]; LPVOID pvData = NULL; ASSERT(IFS_SHELLCAT == pfield->dwStruct); pvData = (LPVOID)((LPBYTE)_psac + pfield->ibOffset); hres = _VariantFromData(pfield, pvData, pvar); } return hres; } /*------------------------------------------------------------------------- Purpose: IAppData::GetVariant Return the particular field as a variant. The caller is responsible for clearing/freeing the variant. The iField is assumed to be 0-based. */ STDMETHODIMP CAppData::GetVariant(DB_LORDINAL iField, VARIANT * pvar) { HRESULT hres; switch (_dwEnum) { case ENUM_INSTALLED: hres = _GetInstField(iField, pvar); break; case ENUM_PUBLISHED: hres = _GetPubField(iField, pvar); break; case ENUM_OCSETUP: hres = _GetSetupField(iField, pvar); break; case ENUM_CATEGORIES: hres = _GetCatField(iField, pvar); break; } return hres; } /*------------------------------------------------------------------------- Purpose: IAppData::SetMtxParent */ STDMETHODIMP CAppData::SetMtxParent(IMtxArray * pmtxParent) { // We don't AddRef because this appdata object lives within the // lifetime of the matrix object. _pmtxParent = pmtxParent; return S_OK; } /*------------------------------------------------------------------------- Purpose: IAppData::DoCommand Executes the given command depending on the capabilities of the item. */ STDMETHODIMP CAppData::DoCommand(HWND hwndParent, APPCMD appcmd) { HRESULT hres = S_FALSE; DWORD dwActions = 0; switch (_dwEnum) { case ENUM_INSTALLED: // Find out of the requested command is allowed // FEATURE (scotth): need to add policies check here _pia->GetPossibleActions(&dwActions); switch (appcmd) { case APPCMD_UNINSTALL: if (g_dwPrototype & PF_FAKEUNINSTALL) { // Say the app was deleted (this is optimized out in retail) hres = S_OK; } else { if (dwActions & APPACTION_UNINSTALL) { hres = _pia->Uninstall(hwndParent); if (SUCCEEDED(hres)) { // Return S_FALSE if the app is still installed (the user // might have cancelled the uninstall) hres = (S_OK == _pia->IsInstalled()) ? S_FALSE : S_OK; } } } break; case APPCMD_MODIFY: if (dwActions & APPACTION_MODIFY) hres = _pia->Modify(hwndParent); break; case APPCMD_REPAIR: if (dwActions & APPACTION_REPAIR) hres = _pia->Repair(TRUE); break; case APPCMD_UPGRADE: if (dwActions & APPACTION_UPGRADE) hres = _pia->Upgrade(); break; default: TraceMsg(TF_ERROR, "(Ctl) invalid appcmd %s", Dbg_GetAppCmd(appcmd)); break; } break; case ENUM_PUBLISHED: // Find out of the requested command is allowed // FEATURE (scotth): need to add policies check here _ppa->GetPossibleActions(&dwActions); switch (appcmd) { case APPCMD_INSTALL: if (dwActions & APPACTION_INSTALL) { hres = S_OK; // Does the app have an expired publishing time? if (_pai.dwMask & PAI_EXPIRETIME) { // Yes, it does. Let's compare the expired time with our current time SYSTEMTIME stCur = {0}; GetLocalTime(&stCur); // Is "now" later than the expired time? if (CompareSystemTime(&stCur, &_pai.stExpire) > 0) { // Yes, warn the user and return failure ShellMessageBox(g_hinst, hwndParent, MAKEINTRESOURCE(IDS_EXPIRED), MAKEINTRESOURCE(IDS_NAME), MB_OK | MB_ICONEXCLAMATION); hres = E_FAIL; } } // if hres is not set by the above code, preceed with installation if (hres == S_OK) { HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT)); #ifdef WINNT // On NT, let Terminal Services know that we are about to install an application. // NOTE: This function should be called no matter the Terminal Services // is running or not. BOOL bPrevMode = TermsrvAppInstallMode(); SetTermsrvAppInstallMode(TRUE); #endif hres = _ppa->Install(NULL); #ifdef WINNT SetTermsrvAppInstallMode(bPrevMode); #endif SetCursor(hcur); } } break; case APPCMD_ADDLATER: if (dwActions & APPACTION_ADDLATER) { ADDLATERDATA ald = {0}; if (_pai.dwMask & PAI_SCHEDULEDTIME) { ald.stSchedule = _pai.stScheduled; ald.dwMasks |= ALD_SCHEDULE; } if (_pai.dwMask & PAI_ASSIGNEDTIME) { ald.stAssigned = _pai.stAssigned; ald.dwMasks |= ALD_ASSIGNED; } if (_pai.dwMask & PAI_EXPIRETIME) { ald.stExpire = _pai.stExpire; ald.dwMasks |= ALD_EXPIRE; } if (GetNewInstallTime(hwndParent, &ald)) { if (ald.dwMasks & ALD_SCHEDULE) hres = _ppa->Install(&ald.stSchedule); else hres = _ppa->Unschedule(); } } default: TraceMsg(TF_ERROR, "(Ctl) invalid appcmd %s", Dbg_GetAppCmd(appcmd)); break; } break; case ENUM_OCSETUP: switch (appcmd) { case APPCMD_INSTALL: // call some command that "installs" the selected item. _pocsa->Run(); hres = S_OK; break; default: TraceMsg(TF_ERROR, "(Ctl) invalid appcmd %s", Dbg_GetAppCmd(appcmd)); break; } break; case ENUM_CATEGORIES: TraceMsg(TF_WARNING, "(Ctl) tried to exec appcmd %s on a category. Not supported.", Dbg_GetAppCmd(appcmd)); break; } return hres; } //--------------------------------------------------------------------------- // Matrix Array class //--------------------------------------------------------------------------- // constructor CMtxArray2::CMtxArray2() { ASSERT(NULL == _hdpa); ASSERT(0 == _cRefLock); TraceMsg(TF_OBJLIFE, "(MtxArray) creating"); InitializeCriticalSection(&_cs); } // destructor CMtxArray2::~CMtxArray2() { DBROWCOUNT cItems; ASSERT(0 == _cRefLock); TraceMsg(TF_OBJLIFE, "(MtxArray) destroying"); GetItemCount(&cItems); DeleteItems(0, cItems); if (_hdpa) DPA_Destroy(_hdpa); DeleteCriticalSection(&_cs); } /*-------------------------------------------------------------------- Purpose: IUnknown::QueryInterface */ STDMETHODIMP CMtxArray2::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENT(CMtxArray2, IMtxArray), { 0 }, }; HRESULT hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj); if (FAILED(hres)) hres = CWorkerThread::QueryInterface(riid, ppvObj); return hres; } void CMtxArray2::_Lock(void) { EnterCriticalSection(&_cs); DEBUG_CODE( _cRefLock++; ) } void CMtxArray2::_Unlock(void) { DEBUG_CODE( _cRefLock--; ) LeaveCriticalSection(&_cs); } /*------------------------------------------------------------------------- Purpose: Create array */ HRESULT CMtxArray2::_CreateArray(void) { _Lock(); if (NULL == _hdpa) _hdpa = DPA_Create(8); _Unlock(); return _hdpa ? S_OK : E_OUTOFMEMORY; } HRESULT CMtxArray2::_DeleteItem(DBROWCOUNT idpa) { // The caller must enter the lock first ASSERT(0 < _cRefLock); ASSERT(_hdpa); IAppData * pappdata = (IAppData *)DPA_GetPtr(_hdpa, (LONG)idpa); if (pappdata) pappdata->Release(); DPA_DeletePtr(_hdpa, (LONG)idpa); return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::Initialize */ STDMETHODIMP CMtxArray2::Initialize(DWORD dwEnum) { _dwEnum = dwEnum; return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::AddItem Add an item to the array Returns the row in which the record was added (0-based). */ STDMETHODIMP CMtxArray2::AddItem(IAppData * pappdata, DBROWCOUNT * piRow) { HRESULT hres = E_FAIL; ASSERT(pappdata); ASSERT(NULL == piRow || IS_VALID_WRITE_PTR(piRow, DBROWCOUNT)); hres = _CreateArray(); if (SUCCEEDED(hres)) { _Lock(); { pappdata->AddRef(); // AddRef since we're storing it away LONG iRow = DPA_AppendPtr(_hdpa, pappdata); if (DPA_ERR != iRow) { DEBUG_CODE( TraceMsg(TF_DSO, "(CMtxArray) added record %d", iRow); ) pappdata->SetMtxParent(SAFECAST(this, IMtxArray *)); hres = S_OK; } else { pappdata->Release(); hres = E_OUTOFMEMORY; iRow = -1; } if (piRow) *piRow = iRow; } _Unlock(); } return hres; } /*------------------------------------------------------------------------- Purpose: IMtxArray::DeleteItems Delete a group of records (count of cRows) starting at iRow. Assumes iRow is 0-based. */ STDMETHODIMP CMtxArray2::DeleteItems(DBROWCOUNT idpa, DBROWCOUNT cdpa) { DBROWCOUNT i; if (_hdpa) { _Lock(); { ASSERT(IS_VALID_HANDLE(_hdpa, DPA)); ASSERT(IsInRange(idpa, 0, DPA_GetPtrCount(_hdpa))); for (i = idpa + cdpa - 1; i >= idpa; i--) { _DeleteItem(i); } } _Unlock(); } return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::GetAppData Return a pointer to the appdata element of the given row. The caller must Release the returned appdata. */ STDMETHODIMP CMtxArray2::GetAppData(DBROWCOUNT iRow, IAppData ** ppappdata) { HRESULT hres = E_FAIL; IAppData * pappdata = NULL; if (_hdpa) { _Lock(); pappdata = (IAppData *)DPA_GetPtr(_hdpa, iRow); if (pappdata) { pappdata->AddRef(); hres = S_OK; } _Unlock(); } *ppappdata = pappdata; return hres; } /*------------------------------------------------------------------------- Purpose: IMtxArray::GetItemCount Return the count of items in the array. */ STDMETHODIMP CMtxArray2::GetItemCount(DBROWCOUNT * pcItems) { if (_hdpa) *pcItems = DPA_GetPtrCount(_hdpa); else *pcItems = 0; return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::GetFieldCount Return the count of fields based upon what we are enumerating. */ STDMETHODIMP CMtxArray2::GetFieldCount(DB_LORDINAL * pcFields) { DB_LORDINAL lRet = 0; switch (_dwEnum) { case ENUM_INSTALLED: lRet = ARRAYSIZE(g_rginstfields); break; case ENUM_PUBLISHED: lRet = ARRAYSIZE(g_rgpubfields); break; case ENUM_OCSETUP: lRet = ARRAYSIZE(g_rgsetupfields); break; case ENUM_CATEGORIES: lRet = ARRAYSIZE(g_rgcatfields); break; } *pcFields = lRet; return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::GetFieldName Return the field name. */ STDMETHODIMP CMtxArray2::GetFieldName(DB_LORDINAL iField, VARIANT * pvar) { HRESULT hres = E_INVALIDARG; DB_LORDINAL cfields = 0; const APPFIELDS *rgfield; VariantInit(pvar); switch (_dwEnum) { case ENUM_INSTALLED: cfields = ARRAYSIZE(g_rginstfields); rgfield = g_rginstfields; break; case ENUM_PUBLISHED: cfields = ARRAYSIZE(g_rgpubfields); rgfield = g_rgpubfields; break; case ENUM_OCSETUP: cfields = ARRAYSIZE(g_rgsetupfields); rgfield = g_rgsetupfields; break; case ENUM_CATEGORIES: cfields = ARRAYSIZE(g_rgcatfields); rgfield = g_rgcatfields; break; } if (IsInRange(iField, 0, cfields)) { pvar->bstrVal = SysAllocString(rgfield[iField].pszLabel); if (pvar->bstrVal) { pvar->vt = VT_BSTR; hres = S_OK; } else hres = E_OUTOFMEMORY; } return hres; } /*------------------------------------------------------------------------- Purpose: IMtxArray:GetSortIndex */ STDMETHODIMP CMtxArray2::GetSortIndex(DWORD * pdwSort) { ASSERT(pdwSort); *pdwSort = _dwSort; return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::SetSortCriteria Determines which field to sort by depending on the given criteria. */ STDMETHODIMP CMtxArray2::SetSortCriteria(LPCWSTR pszSortField) { int i; int cfields; const APPFIELDS * pfield; ASSERT(IS_VALID_STRING_PTRW(pszSortField, -1)); switch (_dwEnum) { case ENUM_INSTALLED: cfields = ARRAYSIZE(g_rginstfields); pfield = g_rginstfields; break; case ENUM_PUBLISHED: cfields = ARRAYSIZE(g_rgpubfields); pfield = g_rgpubfields; break; case ENUM_OCSETUP: cfields = ARRAYSIZE(g_rgsetupfields); pfield = g_rgsetupfields; break; case ENUM_CATEGORIES: cfields = ARRAYSIZE(g_rgcatfields); pfield = g_rgcatfields; break; } // Find the given field's sort index for (i = 0; i < cfields; i++, pfield++) { if (0 == StrCmpW(pszSortField, pfield->pszLabel)) { // Can this field be sorted? if (SORT_NA != pfield->dwSort) { // Yes _dwSort = pfield->dwSort; } break; } } return S_OK; } /*------------------------------------------------------------------------- Purpose: Static callback function to handle sorting. lParam is the pointer to the IMtxArray. */ int CMtxArray2::s_SortItemsCallbackWrapper(LPVOID pv1, LPVOID pv2, LPARAM lParam) { IMtxArray * pmtxarray = (IMtxArray *)lParam; ASSERT(pmtxarray); return pmtxarray->CompareItems((IAppData *)pv1, (IAppData *)pv2); } /*------------------------------------------------------------------------- Purpose: IMtxArray::CompareItems Compares two appdata objects according to the current sort index. */ STDMETHODIMP_(int) CMtxArray2::CompareItems(IAppData * pappdata1, IAppData * pappdata2) { switch (_dwSort) { case SORT_SIZE: { ULONGLONG ull1 = pappdata1->GetSlowDataPtr()->ullSize; ULONGLONG ull2 = pappdata2->GetSlowDataPtr()->ullSize; // Big apps come before smaller apps if (ull1 > ull2) return -1; else if (ull1 < ull2) return 1; goto sort_by_name; } break; case SORT_TIMESUSED: { // Rarely used apps come before frequently used apps. Blank // (unknown) apps go last. Unknown apps are -1, so those sort // to the bottom if we simply compare unsigned values. UINT u1 = (UINT)pappdata1->GetSlowDataPtr()->iTimesUsed; UINT u2 = (UINT)pappdata2->GetSlowDataPtr()->iTimesUsed; if (u1 < u2) return -1; else if (u1 > u2) return 1; goto sort_by_name; } break; case SORT_LASTUSED: { FILETIME ft1 = pappdata1->GetSlowDataPtr()->ftLastUsed; FILETIME ft2 = pappdata2->GetSlowDataPtr()->ftLastUsed; LONG diff = CompareFileTime(&ft1, &ft2); if (0 != diff) return diff; goto sort_by_name; } break; sort_by_name: case SORT_NAME: { LPWSTR pszName1 = pappdata1->GetDataPtr()->pszDisplayName; LPWSTR pszName2 = pappdata2->GetDataPtr()->pszDisplayName; if (pszName1 && pszName2) return StrCmpW(pszName1, pszName2); else if (pszName1) return 1; else if (pszName2) return -1; } break; } return 0; } /*------------------------------------------------------------------------- Purpose: IMtxArray::SortItems Sorts the array according to the current sort index. */ STDMETHODIMP CMtxArray2::SortItems(void) { if (_hdpa) { _Lock(); { ASSERT(IS_VALID_HANDLE(_hdpa, DPA)); DPA_Sort(_hdpa, s_SortItemsCallbackWrapper, (LPARAM)this); } _Unlock(); } return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::MarkDupEntries Mark the (name) duplicate entries in the DPA array of app data */ STDMETHODIMP CMtxArray2::MarkDupEntries(void) { if (_hdpa) { _Lock(); { int idpa; for (idpa = 0; idpa < DPA_GetPtrCount(_hdpa) - 1; idpa++) { IAppData * pad = (IAppData *)DPA_GetPtr(_hdpa, idpa); IAppData * padNext = (IAppData *)DPA_GetPtr(_hdpa, idpa+1); LPWSTR pszName = pad->GetDataPtr()->pszDisplayName; LPWSTR pszNameNext = padNext->GetDataPtr()->pszDisplayName; if (pszName && pszNameNext && !StrCmpW(pszName, pszNameNext)) { pad->SetNameDupe(TRUE); padNext->SetNameDupe(TRUE); } } } _Unlock(); } return S_OK; } /*------------------------------------------------------------------------- Purpose: IMtxArray::KillWT Kills the worker thread. */ STDMETHODIMP CMtxArray2::KillWT() { return CWorkerThread::KillWT(); } /*---------------------------------------------------------- Purpose: Create-instance function for CMtxArray */ HRESULT CMtxArray_CreateInstance(REFIID riid, LPVOID * ppvObj) { HRESULT hres = E_OUTOFMEMORY; *ppvObj = NULL; CMtxArray2 * pObj = new CMtxArray2(); if (pObj) { hres = pObj->QueryInterface(riid, ppvObj); pObj->Release(); } return hres; } /*------------------------------------------------------------------------- Purpose: IMtxArray::_ThreadStartProc() */ DWORD CMtxArray2::_ThreadStartProc() { DBROWCOUNT i; TraceMsg(TF_TASKS, "[%x] Starting slow app info thread", _dwThreadId); DBROWCOUNT cItems = 0; GetItemCount(&cItems); // Loop through all enumerated items, getting the 'slow' information for (i = 0 ; i < cItems ; i++) { IAppData * pappdata; // If we've been asked to bail, do so if (IsKilled()) break; GetAppData(i, &pappdata); if (pappdata) { HRESULT hres = pappdata->ReadSlowData(); // Call to get the slow info for the item. Fire an event on success if (hres == S_OK) { PostWorkerMessage(WORKERWIN_FIRE_ROW_READY, i, 0); TraceMsg(TF_TASKS, "[%x] Slow info for %d (%ls): hit", _dwThreadId, i, pappdata->GetDataPtr()->pszDisplayName); } else { TraceMsg(TF_TASKS, "[%x] Slow info for %d (%ls): miss", _dwThreadId, i, pappdata->GetDataPtr()->pszDisplayName); } pappdata->Release(); } } PostWorkerMessage(WORKERWIN_FIRE_FINISHED, 0, 0); return CWorkerThread::_ThreadStartProc(); } #endif //DOWNLEVEL_PLATFORM