|
|
//+-----------------------------------------------------------------------
//
// Matrix Array
//
//------------------------------------------------------------------------
#include "priv.h"
// Do not build this file if on Win9X or NT4
#ifndef DOWNLEVEL_PLATFORM
#include <shstr.h>
#include "mtxarray.h"
#include "adcctl.h" // for EnumArea string constants
#include "util.h" // for ReleaseShellCategory
#include "dump.h"
#include "appwizid.h"
#ifdef WINNT
#include <tsappcmp.h> // 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:
<fieldname1 "value1"><fieldname2 value2>...
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("<TR><TD class=PropLabel>")
#define c_szPropRowMid TEXT("</TD><TD class=PropValue>")
#define c_szPropRowEnd TEXT("</TD></TR>")
// 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("<TABLE id=idTblExtendedProps class=Focus>");
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("<SPAN id=idAFrequency class='FakeAnchor' tabIndex=0 onKeyDown='_OnKeyDownFakeAnchor()' onClick='_OpenDefinition();'> <U>")); else if (s_rgProp[i].dwMask & PM_SIZE) shstr.Append(TEXT("<SPAN id=idASize class='FakeAnchor' tabIndex=0 onKeyDown='_OnKeyDownFakeAnchor()' onClick='_OpenDefinition();'> <U>")); shstr.Append(s_rgProp[i].psz);
if (s_rgProp[i].dwMask & PM_TIMESUSED) shstr.Append(TEXT("</U></SPAN>")); else if (s_rgProp[i].dwMask & PM_SIZE) shstr.Append(TEXT("</U></SPAN>")); shstr.Append(c_szPropRowEnd); } }
shstr.Append(TEXT("</TABLE>"));
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("<TABLE id=idTblExtendedProps class=Focus><TR><TD class=AddPropLabel>%s</TD><TD class=AddPropValue><A id=idASchedule class=Focus href=''>%s</A></TD></TR></TABLE>")
/*-------------------------------------------------------------------------
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
|