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

1744 lines
48 KiB
C++

//+-----------------------------------------------------------------------
//
// 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();'>&nbsp;<U>"));
else if (s_rgProp[i].dwMask & PM_SIZE)
shstr.Append(TEXT("<SPAN id=idASize class='FakeAnchor' tabIndex=0 onKeyDown='_OnKeyDownFakeAnchor()' onClick='_OpenDefinition();'>&nbsp;<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