mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1743 lines
48 KiB
1743 lines
48 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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
|