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.
804 lines
24 KiB
804 lines
24 KiB
|
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// File: timer.cxx
|
|
//
|
|
// Contents: Code for a timer.
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
//
|
|
//
|
|
// History: 18-Nov-96 BillMo Created.
|
|
//
|
|
// Notes:
|
|
//
|
|
// Codework:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
#include "trklib.hxx"
|
|
|
|
BOOL
|
|
LoadPersistentFileTime(
|
|
const TCHAR * ptszStaticRegName,
|
|
CFILETIME * pcft
|
|
);
|
|
|
|
void
|
|
UpdatePersistentFileTime(
|
|
const TCHAR * ptszStaticRegName,
|
|
const CFILETIME & cft
|
|
);
|
|
|
|
void
|
|
RemovePersistentFileTime(
|
|
const TCHAR * ptszStaticRegName
|
|
);
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::Initiliaze
|
|
//
|
|
// Synopsis: Initialize the object and create the timer but don't set it
|
|
// yet.
|
|
//
|
|
// Inputs: [pTimerCallback] (in)
|
|
// Who to call when the timer fires.
|
|
// [pWorkManager] (in)
|
|
// The WorkManager with which the timer will be registered
|
|
// [ptszName] (in, optional)
|
|
// If specified, the timer is persistent, and the name is
|
|
// used to store the timer information in the registry.
|
|
// If not specified the timer is not persistent.
|
|
// If data already exists in the registry for this name, it
|
|
// is used the next time the timer is set.
|
|
// [ulTimerContext] (in)
|
|
// Passed to pTimerCallback->Timer.
|
|
// [ulPeriodInSeconds] (in)
|
|
// The length of this timer when it's set.
|
|
// [retrytype] (in)
|
|
// From the TimerRetryType enumeration. Can
|
|
// be no_retry, retry_randomly, or retry_with_backoff.
|
|
// [ulLowerRetryTime] (in)
|
|
// Only valid when retrytype isn't no_retry.
|
|
// [ulUpperRetryTime] (in)
|
|
// Only valid when retrytype isn't no_retry.
|
|
// [ulMaxLifetime] (in)
|
|
// Only valid when retrytype isn't no_retry.
|
|
//
|
|
// Returns: Void
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::Initialize( PTimerCallback *pTimerCallback,
|
|
const TCHAR *ptszName,
|
|
ULONG ulTimerContext,
|
|
ULONG ulPeriodInSeconds,
|
|
TimerRetryType retrytype,
|
|
ULONG ulLowerRetryTime,
|
|
ULONG ulUpperRetryTime,
|
|
ULONG ulMaxLifetime )
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
CFILETIME cftLastTimeSet;
|
|
|
|
|
|
TrkAssert( ulLowerRetryTime <= ulUpperRetryTime );
|
|
TrkAssert( NO_RETRY == retrytype
|
|
||
|
|
0 != ulLowerRetryTime
|
|
&&
|
|
0 != ulUpperRetryTime );
|
|
TrkAssert( NO_RETRY != retrytype || 0 == ulMaxLifetime );
|
|
|
|
// Initialize our critical section. _fIntitializeCalled is used to
|
|
// indicate that this has been done.
|
|
|
|
_cs.Initialize();
|
|
_fInitializeCalled = TRUE;
|
|
|
|
// Keep the parameters
|
|
|
|
_pTimerCallback = pTimerCallback;
|
|
_ptszName = ptszName;
|
|
_ulTimerContext = ulTimerContext;
|
|
_ulPeriodInSeconds = ulPeriodInSeconds;
|
|
_RetryType = retrytype;
|
|
_ulLowerRetryTime = ulLowerRetryTime;
|
|
_ulUpperRetryTime = ulUpperRetryTime;
|
|
_ulMaxLifetime = ulMaxLifetime;
|
|
|
|
#if DBG
|
|
// Set the workitem signature for use in debug outputs.
|
|
_stprintf( _tszWorkItemSig, TEXT("CTimer:%p"), this );
|
|
if( NULL != ptszName )
|
|
{
|
|
_tcscat( _tszWorkItemSig, TEXT("/") );
|
|
_tcscat( _tszWorkItemSig, ptszName );
|
|
}
|
|
TrkAssert( _tcslen(_tszWorkItemSig) < ELEMENTS(_tszWorkItemSig) );
|
|
#endif
|
|
|
|
// Create the NT timer.
|
|
|
|
Status = NtCreateTimer(
|
|
&_hTimer,
|
|
TIMER_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationTimer ); // this sort of timer becomes un-signalled
|
|
// when a wait is satisfied
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
_hTimer = NULL;
|
|
TrkRaiseNtStatus(Status);
|
|
}
|
|
|
|
// If this is a persistent timer, load the persisted state from the
|
|
// registry.
|
|
|
|
LoadFromRegistry();
|
|
|
|
// Register a work item with the Win32 thread pool.
|
|
|
|
_hRegisterWaitForSingleObjectEx
|
|
= TrkRegisterWaitForSingleObjectEx( _hTimer, ThreadPoolCallbackFunction,
|
|
static_cast<PWorkItem*>(this), INFINITE,
|
|
WT_EXECUTELONGFUNCTION | WT_EXECUTEDEFAULT );
|
|
|
|
if( NULL == _hRegisterWaitForSingleObjectEx )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed RegisterWaitForSingleObjectEx in CNewTimer::Initialize (%lu) for %s)"),
|
|
GetLastError(), GetTimerName() ));
|
|
TrkRaiseLastError();
|
|
}
|
|
else
|
|
TrkLog(( TRKDBG_TIMER, TEXT("Registered timer %s with thread pool (%p/%p)"),
|
|
GetTimerName(), this, *reinterpret_cast<UINT_PTR*>(this) ));
|
|
|
|
} // CNewTimer::Initialize
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::SetTimer
|
|
//
|
|
// Synopsis: Start the timer. If _cftDue isn't already set, we'll use
|
|
// _ulPeriodInSeconds (or _ulCurrentRetryTime) to set it. If this
|
|
// is a persistent timer, the registry is updated.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::SetTimer()
|
|
{
|
|
NTSTATUS Status;
|
|
CFILETIME cftNow, cftMax(0);
|
|
|
|
TrkAssert(NULL != _hTimer);
|
|
TrkAssert(sizeof(LARGE_INTEGER) == sizeof(_cftDue));
|
|
|
|
|
|
// If we're already running and not in retry mode, then there's nothing
|
|
// to be done.
|
|
|
|
if( _fRunning && 0 == _ulCurrentRetryTime )
|
|
return;
|
|
|
|
// Has the due time already been determined?
|
|
if( 0 == _cftDue )
|
|
{
|
|
// Are we in retry mode?
|
|
if( 0 != _ulCurrentRetryTime )
|
|
{
|
|
_cftDue = cftNow;
|
|
_cftDue.IncrementSeconds( _ulCurrentRetryTime );
|
|
}
|
|
else
|
|
{
|
|
_cftDue = cftNow;
|
|
_cftDue.IncrementSeconds( _ulPeriodInSeconds );
|
|
_cftSet = cftNow;
|
|
}
|
|
}
|
|
|
|
// We already have a non-zero due time. That doesn't mean that we're running,
|
|
// though, it might be a persistent timer that's been initialized but not
|
|
// started.
|
|
|
|
else if( _fRunning )
|
|
{
|
|
TrkAssert( 0 != _ulCurrentRetryTime );
|
|
|
|
// This timer was in retry mode but is now being started again.
|
|
// Restart as if it was being started for the first time.
|
|
|
|
_ulCurrentRetryTime = 0;
|
|
_cftDue = _cftSet = cftNow;
|
|
_cftDue.IncrementSeconds( _ulPeriodInSeconds );
|
|
}
|
|
|
|
if( 0 < _ulMaxLifetime )
|
|
{
|
|
cftMax = _cftSet;
|
|
cftMax.IncrementSeconds( _ulMaxLifetime );
|
|
|
|
if( cftNow > cftMax )
|
|
{
|
|
TrkLog(( TRKDBG_TIMER, TEXT("Stopping timer %s/%p due to liftime limit"),
|
|
(NULL == _ptszName) ? TEXT("") : _ptszName,
|
|
this ));
|
|
Cancel();
|
|
return;
|
|
}
|
|
|
|
else if( _cftDue > cftMax )
|
|
{
|
|
TrkLog(( TRKDBG_TIMER, TEXT("Shortening timer %s/%p due to lifetime limit"),
|
|
(NULL == _ptszName) ? TEXT("") : _ptszName,
|
|
this ));
|
|
_cftDue = cftMax;
|
|
}
|
|
}
|
|
|
|
SaveToRegistry();
|
|
|
|
// Set the timer, but not if it's currently firing. When the timer
|
|
// fires, the DoWork method is called, but that method doesn't hold
|
|
// the lock while it calls the Timer callback. Thus, if
|
|
// _fTimerSignalInProgress is true, some other thread is currently
|
|
// in the callback. When it complets, it will set this timer.
|
|
|
|
if( !_fTimerSignalInProgress )
|
|
{
|
|
Status = NtSetTimer ( _hTimer, //IN HANDLE TimerHandle,
|
|
(LARGE_INTEGER*) &_cftDue, //IN PLARGE_INTEGER DueTime,
|
|
NULL, //IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
|
|
NULL, //IN PVOID TimerContext OPTIONAL,
|
|
FALSE, //IN BOOLEAN ResumeTimer,
|
|
0, //IN LONG Period OPTIONAL,
|
|
NULL ); //OUT PBOOLEAN PreviousState OPTIONAL
|
|
TrkAssert(NT_SUCCESS(Status));
|
|
}
|
|
|
|
_fRunning = TRUE;
|
|
|
|
} // CNewTimer::SetTimer
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::Cancel
|
|
//
|
|
// Synopsis: Cancel the timer and remove its persistent state from the
|
|
// registry (if it's a persistent timer).
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::Cancel()
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
TrkAssert( _fInitializeCalled );
|
|
|
|
Lock();
|
|
__try
|
|
{
|
|
Status = NtCancelTimer(_hTimer, NULL);
|
|
TrkAssert(NT_SUCCESS(Status));
|
|
|
|
_fRunning = FALSE;
|
|
_ulCurrentRetryTime = 0;
|
|
_cftDue = _cftSet = 0;
|
|
|
|
RemoveFromRegistry();
|
|
|
|
}
|
|
__finally
|
|
{
|
|
Unlock();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::DebugStringize
|
|
//
|
|
// Synopsis: Stringize the current state of the timer.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#if DBG
|
|
void
|
|
CNewTimer::DebugStringize( ULONG cch, TCHAR *ptsz ) const
|
|
{
|
|
ULONG cchUsed = 0;
|
|
TCHAR *ptszTimerState;
|
|
|
|
TrkAssert( _fInitializeCalled );
|
|
|
|
Lock();
|
|
__try
|
|
{
|
|
if( _fRunning )
|
|
{
|
|
if( 0 != _ulCurrentRetryTime )
|
|
ptszTimerState = TEXT("retrying");
|
|
else
|
|
ptszTimerState = TEXT("running");
|
|
}
|
|
else
|
|
ptszTimerState = TEXT("stopped");
|
|
|
|
cchUsed = _stprintf( ptsz, TEXT("Timer %s/%p is %s "),
|
|
NULL == _ptszName ? TEXT("") : _ptszName,
|
|
this, ptszTimerState );
|
|
|
|
if( _fRunning )
|
|
{
|
|
LONGLONG llDelta;
|
|
llDelta = static_cast<LONGLONG>(_cftDue - CFILETIME()) / (10*1000*1000);
|
|
|
|
if( 0 <= llDelta && 120 >= llDelta )
|
|
cchUsed += _stprintf( &ptsz[cchUsed], TEXT("(expires in %I64i seconds)"), llDelta );
|
|
else
|
|
{
|
|
cchUsed += _stprintf( &ptsz[cchUsed], TEXT("(expires on ") );
|
|
_cftDue.Stringize( cch-cchUsed, &ptsz[cchUsed] );
|
|
cchUsed += _tcslen( &ptsz[cchUsed] );
|
|
cchUsed += _stprintf( &ptsz[cchUsed], TEXT(" UTC)") );
|
|
}
|
|
}
|
|
|
|
TrkAssert( cch >= cchUsed );
|
|
}
|
|
__finally
|
|
{
|
|
Unlock();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::DoWork
|
|
//
|
|
// Synopsis: This method is called by the work manager when the NT timer
|
|
// is signaled. We call pTimerCallback->Timer so that the timer
|
|
// event can be handled. That Timer method returns a status
|
|
// that tells us what we should do next. The returned status
|
|
// is a TimerContinuation, that can be Continue (causes
|
|
// a recurring timer to be set again), Break (causes a recurring
|
|
// timer to be stopped), or Retry.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::DoWork()
|
|
{
|
|
TrkAssert( _fInitializeCalled );
|
|
|
|
PTimerCallback::TimerContinuation continuation;
|
|
|
|
Lock();
|
|
{
|
|
// We were obviously running recently, or we wouldn't have been called.
|
|
// But if we're not running now, we must have been canceled after the
|
|
// timer object was signaled (waking the WaitForMultiple) but before
|
|
// entry into this routine.
|
|
|
|
if( !_fRunning )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Timer %s/%p stopped while firing"),
|
|
NULL == _ptszName ? TEXT("") : _ptszName,
|
|
this ));
|
|
Unlock();
|
|
return;
|
|
}
|
|
|
|
// For now, we're no longer running. If someone calls Set* while we're in
|
|
// the Timer callback below, this will be set true.
|
|
_fRunning = FALSE;
|
|
_cftDue = 0;
|
|
|
|
|
|
// Show that the timer has fired and is being processed. This is used
|
|
// by SetTimer so that it knows that we're in a call to the Timer
|
|
// callback.
|
|
_fTimerSignalInProgress = TRUE;
|
|
}
|
|
TrkAssert( 1 == GetLockCount() );
|
|
Unlock();
|
|
|
|
// Call the timer handler. On return, it tells us how we should
|
|
// proceed.
|
|
|
|
continuation = _pTimerCallback->Timer( _ulTimerContext );
|
|
// continuation : {Break, Continue, Retry}
|
|
|
|
Lock();
|
|
__try // __except
|
|
{
|
|
// We're no longer in the timer callback.
|
|
_fTimerSignalInProgress = FALSE;
|
|
|
|
// If, while we were in the Timer callback, another thread came along
|
|
// and set the timer, then that takes priority over the
|
|
// continuation that was just returned. In such an case, _fRunning
|
|
// will have been set to TRUE.
|
|
|
|
if( _fRunning )
|
|
{
|
|
TrkAssert( 0 != _cftDue );
|
|
TrkAssert( NULL != _hTimer );
|
|
|
|
// Show that we're not in retry mode
|
|
_ulCurrentRetryTime = 0;
|
|
|
|
NTSTATUS Status;
|
|
|
|
// _cftDue was set in the SetTimer call already
|
|
|
|
Status = NtSetTimer ( _hTimer, //IN HANDLE TimerHandle,
|
|
(LARGE_INTEGER*) &_cftDue, //IN PLARGE_INTEGER DueTime,
|
|
NULL, //IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL,
|
|
NULL, //IN PVOID TimerContext OPTIONAL,
|
|
FALSE, //IN BOOLEAN ResumeTimer,
|
|
0, //IN LONG Period OPTIONAL,
|
|
NULL ); //OUT PBOOLEAN PreviousState OPTIONAL
|
|
TrkAssert(NT_SUCCESS(Status));
|
|
}
|
|
|
|
else if( PTimerCallback::BREAK_TIMER == continuation )
|
|
{
|
|
// Break out of this timer; stop it even if it's a recurring timer.
|
|
Cancel();
|
|
}
|
|
|
|
else if( PTimerCallback::CONTINUE_TIMER == continuation )
|
|
{
|
|
// Continue with this timer; stop it if it's a single shot, set it again
|
|
// if it's recurring.
|
|
|
|
_ulCurrentRetryTime = 0; // If we were retrying, we aren't any longer
|
|
|
|
if( _fRecurring )
|
|
SetTimer();
|
|
else
|
|
Cancel();
|
|
}
|
|
|
|
else // RETRY_TIMER
|
|
{
|
|
TrkAssert( PTimerCallback::RETRY_TIMER == continuation );
|
|
TrkAssert( _ulLowerRetryTime <= _ulUpperRetryTime );
|
|
|
|
if( 0 == _ulUpperRetryTime || NO_RETRY == _RetryType )
|
|
{
|
|
TrkAssert( !TEXT("Attempted to retry a timer with no retry times set") );
|
|
Cancel();
|
|
}
|
|
|
|
if( RETRY_WITH_BACKOFF == _RetryType )
|
|
{
|
|
if( 0 == _ulCurrentRetryTime )
|
|
_ulCurrentRetryTime = _ulLowerRetryTime;
|
|
else if( (MAXULONG/2) < _ulCurrentRetryTime )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Questionable retry time") ));
|
|
TrkAssert( FALSE );
|
|
_ulCurrentRetryTime = MAXULONG;
|
|
}
|
|
else
|
|
_ulCurrentRetryTime *= 2;
|
|
|
|
if( _ulCurrentRetryTime > _ulUpperRetryTime )
|
|
_ulCurrentRetryTime = _ulUpperRetryTime;
|
|
|
|
}
|
|
else // PTimerCallback::RETRY_RANDOMLY == _RetryType
|
|
{
|
|
CFILETIME cftNow;
|
|
|
|
_ulCurrentRetryTime = _ulLowerRetryTime
|
|
+
|
|
( QuasiRandomDword() % (_ulUpperRetryTime - _ulLowerRetryTime) );
|
|
|
|
}
|
|
|
|
TrkLog(( TRKDBG_TIMER, TEXT("Retrying timer %s/%p for %d seconds"),
|
|
(NULL == _ptszName) ? TEXT("") : _ptszName,
|
|
this,
|
|
_ulCurrentRetryTime ));
|
|
|
|
// Set the timer with the just-calculated retry period
|
|
SetTimer();
|
|
|
|
} // else if( CONTINUE_TIMER == continuation ) ... else
|
|
}
|
|
__except( BreakOnDebuggableException() )
|
|
{
|
|
// The exception may have been in the timer, but more likely
|
|
// was in the PTimerCallback::Timer routine. As a cure-all,
|
|
// just reset the timer to an arbitrary value (we don't want
|
|
// to re-use _ulPeriodInSeconds, because it may be zero).
|
|
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Unexpected timer exception") ));
|
|
TrkAssert( FALSE );
|
|
|
|
__try
|
|
{
|
|
_ulPeriodInSeconds = TRKDAY;
|
|
Cancel();
|
|
SetTimer();
|
|
}
|
|
__except( BreakOnDebuggableException() )
|
|
{
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
} // CNewTimer::DoWork
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::SaveToRegistry
|
|
//
|
|
// Synopsis: Save the timer's state to the registry, using _ptszName
|
|
// as a value name.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::SaveToRegistry()
|
|
{
|
|
LONG lErr = ERROR_SUCCESS;
|
|
|
|
HKEY hk = NULL;
|
|
|
|
// If this isn't a persistent timer, then there's nothing to do.
|
|
if( NULL == _ptszName )
|
|
return;
|
|
|
|
Lock();
|
|
__try
|
|
{
|
|
lErr = RegOpenKey( HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, &hk );
|
|
|
|
PersistentState persist;
|
|
|
|
persist.cftSet = _cftSet;
|
|
persist.cftDue = _cftDue;
|
|
persist.ulCurrentRetryTime = _ulCurrentRetryTime;
|
|
|
|
if ( lErr == ERROR_SUCCESS )
|
|
{
|
|
|
|
lErr = RegSetValueEx( hk,
|
|
_ptszName,
|
|
0,
|
|
REG_BINARY,
|
|
(CONST BYTE *)&persist,
|
|
sizeof(persist) );
|
|
RegCloseKey(hk);
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
Unlock();
|
|
}
|
|
|
|
TrkAssert( lErr == ERROR_SUCCESS
|
|
||
|
|
lErr == ERROR_NOT_ENOUGH_MEMORY
|
|
||
|
|
lErr == ERROR_NO_SYSTEM_RESOURCES );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::LoadFromRegistry
|
|
//
|
|
// Synopsis: Load this timer's previously persisted state from the
|
|
// registry.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::LoadFromRegistry()
|
|
{
|
|
|
|
LONG l;
|
|
HKEY hk = NULL;
|
|
|
|
// If this isn't a persistent timer, then there's nothing to do.
|
|
if( NULL == _ptszName )
|
|
return;
|
|
|
|
Lock();
|
|
__try
|
|
{
|
|
// Open the main link-tracking key.
|
|
|
|
l = RegCreateKey(HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, &hk);
|
|
if (l != ERROR_SUCCESS)
|
|
{
|
|
hk = NULL;
|
|
}
|
|
else
|
|
{
|
|
// The main link-tracking key exists. See if we can open this
|
|
// timer's value.
|
|
|
|
PersistentState persist;
|
|
DWORD cbData = sizeof(persist);
|
|
DWORD dwType = 0;
|
|
|
|
l = RegQueryValueEx( hk,
|
|
_ptszName,
|
|
NULL,
|
|
&dwType,
|
|
(BYTE *)&persist,
|
|
&cbData );
|
|
|
|
if (l == ERROR_SUCCESS)
|
|
{
|
|
if (dwType == REG_BINARY
|
|
&&
|
|
cbData == sizeof(persist))
|
|
{
|
|
|
|
// This timer has a persistent value in the registry. Override
|
|
// the caller-provided timeout.
|
|
|
|
_cftDue = persist.cftDue;
|
|
_cftSet = persist.cftSet;
|
|
_ulCurrentRetryTime = persist.ulCurrentRetryTime;
|
|
}
|
|
else
|
|
{
|
|
RegDeleteValue( hk, _ptszName );
|
|
l = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
} // if (l == ERROR_SUCCESS)
|
|
} // if (l != ERROR_SUCCESS) ... else
|
|
}
|
|
__finally
|
|
{
|
|
if( NULL != hk )
|
|
RegCloseKey(hk);
|
|
|
|
Unlock();
|
|
}
|
|
|
|
if (l != ERROR_SUCCESS && l != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Ignoring error %08x in timer %s LoadFromRegistry"),
|
|
l, _ptszName ));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CNewTimer::RemoveFromRegistry
|
|
//
|
|
// Synopsis: Remove this timer's persistent state from the registry.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::RemoveFromRegistry()
|
|
{
|
|
LONG lErr = ERROR_SUCCESS;
|
|
HKEY hk = NULL;
|
|
|
|
if( NULL == _ptszName )
|
|
return;
|
|
|
|
Lock();
|
|
__try
|
|
{
|
|
lErr = RegOpenKey( HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, &hk );
|
|
|
|
if ( lErr == ERROR_SUCCESS )
|
|
{
|
|
lErr = RegDeleteValue( hk, _ptszName );
|
|
RegCloseKey(hk);
|
|
|
|
if( ERROR_SUCCESS != lErr
|
|
&&
|
|
ERROR_PATH_NOT_FOUND != lErr
|
|
&&
|
|
ERROR_FILE_NOT_FOUND != lErr )
|
|
{
|
|
TrkLog((TRKDBG_ERROR, TEXT("Couldn't delete timer's static reg name (\"%s\", %08x)"),
|
|
_ptszName, lErr ));
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
Unlock();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CNewTimer::UnInitialize
|
|
//
|
|
// Unregister the timer handle from the thread pool, cancel the timer,
|
|
// and release it.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
void
|
|
CNewTimer::UnInitialize()
|
|
{
|
|
if( _fInitializeCalled )
|
|
{
|
|
TrkLog(( TRKDBG_TIMER, TEXT("Uninitializing timer %s/%p"), GetTimerName(), this ));
|
|
|
|
// Take the timer out of the thread pool, which must be
|
|
// done before closing the timer.
|
|
|
|
if( NULL != _hRegisterWaitForSingleObjectEx )
|
|
{
|
|
if( !TrkUnregisterWait( _hRegisterWaitForSingleObjectEx ))
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed UnregisterWait for CNewTimer (%s/%p, %lu)"),
|
|
NULL == _ptszName ? TEXT("") : _ptszName, this,
|
|
GetLastError() ));
|
|
}
|
|
else
|
|
{
|
|
TrkLog(( TRKDBG_TIMER, TEXT("Unregistered wait for timer (%s/%p)"),
|
|
NULL == _ptszName ? TEXT("") : _ptszName, this ));
|
|
}
|
|
_hRegisterWaitForSingleObjectEx = NULL;
|
|
}
|
|
|
|
// Close the timer handle
|
|
|
|
TrkVerify( NT_SUCCESS( NtCancelTimer(_hTimer, NULL) ));
|
|
NtClose(_hTimer);
|
|
|
|
// Delete the CNewTimer critical section.
|
|
|
|
_cs.UnInitialize();
|
|
|
|
_fInitializeCalled = FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|