windows-nt/Source/XPSP1/NT/shell/browseui/emclient/uacount.cpp

386 lines
9.4 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//*** CUACount -- user-assistance counter w/ decay
// NOTES
// todo: scavenging to clean out registry. but see caveats in UAC_CDEF.
#include "priv.h"
#include "uacount.h"
#include "uareg.h"
#define DM_UEMTRACE TF_UEM
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
//*** UAC_CDEFAULT -- initial _cCnt for entry (we *always* show items)
// NOTES
// eventually we might want to scavenge all entries, decaying them down
// and deleting any that decay to 0. note however that this will cause
// them to look like they have a default count of 1 (see CUAC::Init), so
// they'll suddenly appear on the menus again.
#define UAC_CDEFAULT 0 // initial _cCnt for entry
#define SID_SDEFAULT SID_SNOWREAD // initial _sidMru for new entry
//***
// NOTES
// it's getting to the point that we should disallow stack-alloc'ed
// guys and instead count on new() to 0-init us.
CUACount::CUACount()
{
// Since this is created on the stack, we don't get the benefits of the
// Heap allocator's zero initialization...
ZeroMemory(_GetRawData(), _GetRawCount());
_fInited = FALSE; // need to call Initialize
#if XXX_VERSIONED
_cbSize = -1;
#endif
#if XXX_DELETE
_fInherited = FALSE;
#endif
_fDirty = FALSE;
_fNoDecay = _fNoPurge = FALSE;
return;
}
#ifdef DEBUG
BOOL CUACount::DBIsInit()
{
#if XXX_VERSIONED
ASSERT((_cbSize == SIZEOF(SUACount)) == BOOLIFY(_fInited));
#endif
return _fInited;
}
#endif
HRESULT CUACount::Initialize(IUASession *puas)
{
_puas = puas;
if (!_fInited) {
_fInited = TRUE;
#if XXX_VERSIONED
// todo: _cbSize -1 means no entry, < SIZEOF means version upgrade
_cbSize = SIZEOF(SUACount);
#endif
// hardcode the SZ_CUACount_ctor values here
_cCnt = UAC_CDEFAULT; // all items start out visible
_sidMruDisk = SID_SNOWREAD; // ... and non-aged
}
_sidMru = _sidMruDisk;
if (ISSID_SSPECIAL(_sidMruDisk)) {
_sidMru = _ExpandSpecial(_sidMruDisk);
if (_sidMruDisk == SID_SNOWINIT) {
_sidMruDisk = _sidMru;
_fDirty = TRUE;
}
else if (_sidMruDisk == SID_SNOWREAD) {
_sidMruDisk = _sidMru;
ASSERT(!_fDirty);
}
}
return S_OK;
}
HRESULT CUACount::LoadFrom(PFNNRW3 pfnIO, PNRWINFO pRwi)
{
HRESULT hr;
hr = (*pfnIO->_pfnRead)(_GetRawData(), _GetRawCount(), pRwi);
if (SUCCEEDED(hr))
_fInited = TRUE;
return hr;
}
HRESULT CUACount::SaveTo(BOOL fForce, PFNNRW3 pfnIO, PNRWINFO pRwi)
{
HRESULT hr;
hr = S_FALSE;
if (fForce || _fDirty) {
if (!ISSID_SSPECIAL(_sidMruDisk))
_sidMruDisk = _sidMru;
#if XXX_DELETE
if (_cCnt == 0 && !_fNoPurge && pfnIO->_pfnDelete)
hr = (*pfnIO->_pfnDelete)(_GetRawData(), _GetRawCount(), pRwi);
else
#endif
hr = (*pfnIO->_pfnWrite)(_GetRawData(), _GetRawCount(), pRwi);
// ASSERT(SUCCEEDED(hr)); // this legitimately happens (low memory, access denied)
_fDirty = FALSE;
}
return hr;
}
//*** GetCount -- get count info (w/ lazy decay)
//
int CUACount::GetCount()
{
ASSERT(DBIsInit());
int cCnt = _DecayCount(FALSE);
return cCnt;
}
void CUACount::IncCount()
{
AddCount(1);
return;
}
void CUACount::AddCount(int i)
{
ASSERT(DBIsInit());
_DecayCount(TRUE);
_cCnt += i;
if (_cCnt == 0 && i > 0) {
// nt5:173048
// handle wrap
// should never happen, but what the heck
// do *not* remove this assert, if we ever let people do DecCount
// we'll need to rethink it...
ASSERT(0); // 'impossible'
_cCnt++;
}
// 981029 new incr algorithm per ie5 PM
// UAC_MINCOUNT: initial inc starts at 6
// _fNoDecay: but, UAssist2 doesn't do this
if (_cCnt < UAC_MINCOUNT && !_fNoDecay)
_cCnt = UAC_MINCOUNT;
return;
}
//***
// NOTES
// should we update the timestamp? maybe add a fMru param?
void CUACount::SetCount(int cCnt)
{
ASSERT(DBIsInit());
_cCnt = cCnt;
return;
}
void CUACount::SetFileTime(const FILETIME *pft)
{
ASSERT(DBIsInit());
_ftExecuteTime = *pft;
return;
}
#if XXX_DELETE
#define BTOM(b, m) ((b) ? (m) : 0)
DWORD CUACount::_SetFlags(DWORD dwMask, DWORD dwFlags)
{
// standard guys
if (dwMask & UAXF_NOPURGE)
_fNoPurge = BOOLIFY(dwFlags & UAXF_NOPURGE);
#if 0
if (dwMask & UAXF_BACKUP)
_fBackup = BOOLIFY(dwFlags & UAXF_BACKUP);
#endif
if (dwMask & UAXF_NODECAY)
_fNoDecay = BOOLIFY(dwFlags & UAXF_NODECAY);
// my guys
if (dwMask & UACF_INHERITED)
_fInherited = BOOLIFY(dwFlags & UACF_INHERITED);
return 0 // n.b. see continuation line(s)!!!
#if XXX_DELETE
| BTOM(_fInherited, UACF_INHERITED)
#endif
| BTOM(_fNoPurge, UAXF_NOPURGE)
| BTOM(_fNoDecay, UAXF_NODECAY)
;
}
#endif
//*** PCTOF -- p% of n (w/o floating point!)
//
#define PCTOF(n, p) (((n) * (p)) / 100)
//*** _DecayCount -- decay (and propagate) count
// ENTRY/EXIT
// fWrite TRUE if want to update object and timestamp, o.w. FALSE
// cNew (return) new count
// DESCRIPTION
// on a read, we do the decay but don't update the object. on the write
// we decay and update.
// NOTES
// todo: if/when we make cCnt a vector, we can propagate stuff here.
// this would allow us to usually inc a single small-granularity elt,
// and propagate to the large-gran elts only when we really need them.
// perf: we could make the table 'cumulative', then we wouldn't have
// to do as much computation. not worth the trouble...
int CUACount::_DecayCount(BOOL fWrite)
{
int cCnt;
cCnt = _cCnt;
if (cCnt > 0 || fWrite) {
UINT sidNow;
sidNow = _puas->GetSessionId();
if (!_fNoDecay) {
// from mso-9 spec
// last used 'timTab' sessions ago => dec by >-of abs, pct
// n.b. this table is non-cumulative
static const int timTab[] = { 3, 6, 9, 12, 17, 23, 29, 31, -1, };
static const int absTab[] = { 1, 1, 1, 2, 3, 4, 5, 0, 0, };
static const int pctTab[] = { 0, 0, 0, 25, 25, 50, 75, 100, 100, };
UINT sidMru;
int dt;
int i;
sidMru = _sidMru;
ASSERT(!ISSID_SSPECIAL(_sidMru));
ASSERT(sidMru != SID_SDEFAULT);
if (sidMru != SID_SDEFAULT) {
dt = sidNow - sidMru;
// iterate fwd not bkwd so bail early in common case
for (i = 0; i < ARRAYSIZE(timTab); i++) {
if ((UINT)dt < (UINT)timTab[i])
break;
cCnt -= MAX(absTab[i], PCTOF(cCnt, pctTab[i]));
// don't go negative!
// gotta check *each* time thru loop (o.w. PCT is bogus)
cCnt = MAX(0, cCnt);
}
}
}
if (cCnt != _cCnt)
TraceMsg(DM_UEMTRACE, "uac.dc: decay %d->%d", _cCnt, cCnt);
if (fWrite) {
_sidMru = sidNow;
_cCnt = cCnt;
}
#if XXX_DELETE
if (cCnt == 0 && !_fInherited) {
// if we decay down to 0, mark so it will be deleted
TraceMsg(DM_UEMTRACE, "uac.dc: decay %d->%d => mark dirty pRaw=0x%x", _cCnt, cCnt, _GetRawData());
_cCnt = 0;
_fDirty = TRUE;
}
#endif
}
return cCnt;
}
//***
// NOTES
// perf: currently all special guys return sidNow so no 'switch' necessary
UINT CUACount::_ExpandSpecial(UINT sidMru)
{
UINT sidNow;
if (EVAL(ISSID_SSPECIAL(sidMru))) {
ASSERT(_puas);
sidNow = _puas->GetSessionId(); // perf: multiple calls
switch (sidMru) {
case SID_SNOWALWAYS:
return sidNow;
//break;
case SID_SNOWREAD:
case SID_SNOWINIT:
return sidNow;
//break;
#ifdef DEBUG
default:
ASSERT(0);
break;
#endif
}
}
return sidMru;
}
// Return the encoded filetime. This is read from the registry or
// generated from UpdateFileTime.
FILETIME CUACount::GetFileTime()
{
return _ftExecuteTime;
}
// Updates the internal filetime information. This info
// will be later persisted to the registry.
void CUACount::UpdateFileTime()
{
SYSTEMTIME st;
// Get the current system time.
GetSystemTime(&st);
// This is done for ARP. They use filetimes, not the system time
// for the calculation of the last execute time.
SystemTimeToFileTime(&st, &_ftExecuteTime);
}
// {
//*** UATIME --
//*** FTToUATime -- convert FILETIME to UATIME
// DESCRIPTION
// UATIME granularity is (approximately) 1 minute. the math works out
// roughly as follows:
// filetime granularity is 100 nanosec
// 1 ft = 10^-7 sec
// highword is 2^32 ft = 2^32 * 10^-7 sec
// 1 sec = hiw / (2^32 * 10^-7)
// 1 min = hiw * 60 / (2^32 * 10^-7)
// = hiw * 60 / (1G * 10^-7)
// ~= hiw * 60 / ~429
// = hiw / 7.15
// ~= hiw / 8 approx
// the exact granularity is:
// ...
#define FTToUATime(pft) ((DWORD)(*(_int64 *)(pft) >> 29)) // 1 minute (approx)
//*** GetUaTime -- convert systemtime (or 'now') to UATIME
//
UATIME GetUaTime(LPSYSTEMTIME pst)
{
FILETIME ft;
UATIME uat;
if (pst == NULL)
{
GetSystemTimeAsFileTime(&ft);
}
else
{
SystemTimeToFileTime(pst, &ft);
}
uat = FTToUATime(&ft); // minutes
return uat;
}
// }