|
|
//+----------------------------------------------------------------------------
//
// Job Object Handler
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: triggers.cxx
//
// Contents: trigger object and trigger collection object
//
// Classes: CTrigger and CBagOTriggers
//
// History: 27-June-95 EricB created
// 11-Nov-96 AnirudhS GetRunTimes: Changed return codes; various
// other fixes.
//
//-----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "job.hxx"
#include <StrSafe.h>
// prototypes for local functions:
HRESULT AddToList(FILETIME, FILETIME, CRun *, WORD *); void AddDaysToFileTime(LPFILETIME pft, WORD Days);
//+----------------------------------------------------------------------------
//
// Member: CTrigger::CTrigger
//
// Synopsis: Ctor
//
//-----------------------------------------------------------------------------
CTrigger::CTrigger(WORD iTrigger, CJob * pJob) : m_iTrigger(iTrigger), m_pJob(pJob), m_cReferences(1) { schAssert(m_pJob != NULL); m_pJob->AddRef(); }
//+----------------------------------------------------------------------------
//
// Member: CTrigger::~CTrigger
//
// Synopsis: Dtor
//
//-----------------------------------------------------------------------------
CTrigger::~CTrigger(void) { m_pJob->Release(); }
//+----------------------------------------------------------------------------
//
// Member: CTrigger::ITaskTrigger::GetTriggerString
//
// Synopsis: Returns a string representation of the trigger
//
// Arguments: [ppwszTrigger] - the place to return a string pointer
//
// Returns: HRESULTS
//
// Notes: The string is callee allocated and caller freed with
// CoTaskMemFree.
//-----------------------------------------------------------------------------
STDMETHODIMP CTrigger::GetTriggerString(LPWSTR * ppwszTrigger) { //TRACE(CTrigger, GetTriggerString);
return(m_pJob->GetTriggerString(m_iTrigger, ppwszTrigger)); }
//+----------------------------------------------------------------------------
//
// Member: CTrigger::ITaskTrigger::SetTrigger, public
//
// Synopsis: Sets the trigger values.
//
// Arguments: [pTrigger] - the struct containing the values
//
// Returns: HRESULTS
//
// Notes:
//-----------------------------------------------------------------------------
STDMETHODIMP CTrigger::SetTrigger(const PTASK_TRIGGER pTrigger) { TRACE(CTrigger, SetTrigger);
if( NULL == pTrigger ) { return E_INVALIDARG; } //
// Check struct version.
//
if (pTrigger->cbTriggerSize != sizeof(TASK_TRIGGER)) { //
// Don't attempt to modify triggers created by a later revision.
//
return E_INVALIDARG; }
return(m_pJob->SetTrigger(m_iTrigger, pTrigger)); }
//+----------------------------------------------------------------------------
//
// Member: CTrigger::ITaskTrigger::GetTrigger
//
// Synopsis: Gets the trigger values.
//
// Arguments: [pTrigger] - pointer to caller supplied trigger structure
//
// Returns: HRESULTS
//
// Notes: pTrigger->cbTriggerSize must be set to sizeof(TASK_TRIGGER) on
// function entry. This provides for trigger struct versioning.
//-----------------------------------------------------------------------------
STDMETHODIMP CTrigger::GetTrigger(PTASK_TRIGGER pTrigger) { //TRACE(CTrigger, GetTrigger);
if( NULL == pTrigger ) { return E_INVALIDARG; }
return(m_pJob->GetTrigger(m_iTrigger, pTrigger)); }
//+----------------------------------------------------------------------------
//
// Functions: AddDaysToFileTime
//
// Synopsis: Convert the days value to filetime units and add it to
// the filetime.
//
//-----------------------------------------------------------------------------
void AddDaysToFileTime(LPFILETIME pft, WORD Days) { if (!Days) { return; // Nothing to do.
} //
// ft = ft + Days * FILETIMES_PER_DAY;
//
ULARGE_INTEGER uli, uliSum; uli.LowPart = pft->dwLowDateTime; uli.HighPart = pft->dwHighDateTime; uliSum.QuadPart = uli.QuadPart + (__int64)Days * FILETIMES_PER_DAY; pft->dwLowDateTime = uliSum.LowPart; pft->dwHighDateTime = uliSum.HighPart; }
//+----------------------------------------------------------------------------
//
// Functions: AddMinutesToFileTime
//
// Synopsis: Convert the minutes value to filetime units and add it to
// the filetime.
//
//-----------------------------------------------------------------------------
void AddMinutesToFileTime(LPFILETIME pft, DWORD Minutes) { if (!Minutes) { return; // Nothing to do.
} //
// ft = ft + Minutes * FILETIMES_PER_MINUTE;
//
ULARGE_INTEGER uli, uliSum; uli.LowPart = pft->dwLowDateTime; uli.HighPart = pft->dwHighDateTime; uliSum.QuadPart = uli.QuadPart + (__int64)Minutes * FILETIMES_PER_MINUTE; pft->dwLowDateTime = uliSum.LowPart; pft->dwHighDateTime = uliSum.HighPart; }
//+----------------------------------------------------------------------------
//
// Function: GetTriggerRunTimes
//
// Synopsis: Computes a set of run times for this trigger that fall between
// the bracketing times -- pstBracketBegin is inclusive,
// pstBracketEnd is exclusive -- and merges them with the list of
// run times passed in.
//
// Arguments: [jt] - Inspected trigger.
// [pstBracketBegin] - the start of the bracketing period
// [pstBracketEnd] - the end of the bracketing period, may
// be NULL
// [pCount] - on both entry and exit, points to the
// number of CRun elements in the list.
// CODEWORK: Make this a private member of
// CRunList.
// [cLimit] - the maximum number of elements that the
// list may grow to.
// [pRunList] - the list of run time objects, can
// be NULL if just checking to see if there
// will be *any* runs. (Note: If it's NULL,
// duplicate run times are not detected, so
// pCount may be overestimated on return.)
// [ptszJobName],
// [dwJobFlags],
// [dwMaxRunTime] - the last 3 params are used for the CRun
// objects as their member data.
// [wIdleWait] - the job's idle wait period
// [wIdleDeadline] - time to wait for idle wait period
//
// Returns: S_OK: The trigger is a time-based trigger and is enabled,
// and zero or more of its run times have been added to the
// list (subject to cLimit and the bracketing period); or,
// the trigger is an event trigger but will expire before
// the bracketing period.
// SCHED_S_EVENT_TRIGGER: this is an event trigger that will be
// active (not expired) during the bracketing period.
// SCHED_S_TASK_NO_VALID_TRIGGERS: the trigger is disabled or
// not set.
// Failure HRESULTs: Other failures.
//
// Notes: The trigger time list is callee allocated and caller freed. The
// caller must use delete to free this list.
//-----------------------------------------------------------------------------
HRESULT GetTriggerRunTimes( TASK_TRIGGER & jt, const SYSTEMTIME * pstBracketBegin, const SYSTEMTIME * pstBracketEnd, WORD * pCount, WORD cLimit, CTimeRunList * pRunList, LPTSTR ptszJobName, DWORD dwJobFlags, DWORD dwMaxRunTime, WORD wIdleWait, WORD wIdleDeadline) { TRACE_FUNCTION3(GetRunTimes); DWORD dwRet;
schAssert(cLimit > 0); // If cLimit is 0, it's not clear what to return
schAssert(cLimit <= TASK_MAX_RUN_TIMES); schAssert(*pCount <= cLimit);
//
// Return if this trigger hasn't been set or if it is disabled.
//
if (jt.rgFlags & JOB_TRIGGER_I_FLAG_NOT_SET || jt.rgFlags & TASK_TRIGGER_FLAG_DISABLED) { return SCHED_S_TASK_NO_VALID_TRIGGERS; }
//
// Event triggers don't have set run times.
//
switch (jt.TriggerType) { case TASK_EVENT_TRIGGER_ON_IDLE: case TASK_EVENT_TRIGGER_AT_SYSTEMSTART: case TASK_EVENT_TRIGGER_AT_LOGON: // Not yet implemented:
// case TASK_EVENT_TRIGGER_ON_APM_RESUME:
//
// Check if the trigger expires before the beginning of the bracket
//
if (jt.rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) { SYSTEMTIME stEnd; stEnd.wYear = jt.wEndYear; stEnd.wMonth = jt.wEndMonth; stEnd.wDay = jt.wEndDay; // IsFirstDateEarlier ignores other fields
if (IsFirstDateEarlier(&stEnd, pstBracketBegin)) { return S_OK; } }
return SCHED_S_EVENT_TRIGGER; }
SYSTEMTIME st = { 0, 0, 0, 0, 0, 0, 0, 0 }; //
// Convert to FILETIMEs and check if the trigger lifetime intersects the
// requested run bracket.
// If there is a trigger end date, then one of three conditions holds:
// a. *pstBracketBegin > jt.End{Month/Day/Year}
// result, no runs
// b. *pstBracketBegin < jt.End{Month/Day/Year} < *pstBracketEnd
// result, return all runs between *pstBracketBegin and
// jt.End{Month/Day/Year}
// c. jt.End{Month/Day/Year} > *pstBracketEnd
// result, return all runs between *pstBracketBegin and *pstBracketEnd
// In addition, if there is a bracket end we check:
// d. *pstBracketEnd <= jt.Begin{Month/Day/Year}
// result, no runs
//
FILETIME ftTriggerBegin, ftTriggerEnd, ftBracketBegin, ftBracketEnd;
if (!SystemTimeToFileTime(pstBracketBegin, &ftBracketBegin)) { dwRet = GetLastError(); ERR_OUT("GetRunTimes, convert pstBracketBegin", dwRet); return HRESULT_FROM_WIN32(dwRet); }
st.wYear = jt.wBeginYear; st.wMonth = jt.wBeginMonth; st.wDay = jt.wBeginDay; st.wHour = jt.wStartHour; st.wMinute = jt.wStartMinute;
if (!SystemTimeToFileTime(&st, &ftTriggerBegin)) { dwRet = GetLastError(); ERR_OUT("GetRunTimes, convert TriggerBegin", dwRet); return HRESULT_FROM_WIN32(dwRet); }
st.wHour = 23; // set to the last hour of the day.
st.wMinute = 59; // set to the last minute of the day.
st.wSecond = 59; // set to the last second of the day.
st.wMilliseconds = 0;
if (jt.rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) { st.wYear = jt.wEndYear; st.wMonth = jt.wEndMonth; st.wDay = jt.wEndDay;
if (!SystemTimeToFileTime(&st, &ftTriggerEnd)) { dwRet = GetLastError(); ERR_OUT("GetRunTimes, convert TriggerEnd", dwRet); return HRESULT_FROM_WIN32(dwRet); }
if (CompareFileTime(&ftTriggerEnd, &ftBracketBegin) < 0) { //
// Trigger end time is before the run bracket begin time (case a.).
//
return S_OK; } } else // no trigger end date.
{ //
// Create an end date that is reasonably large.
// BUGBUG Change this to MAX_FILETIME - but should be tested.
//
st.wMonth = 12; st.wDay = 31; st.wYear = 2200;
if (!SystemTimeToFileTime(&st, &ftTriggerEnd)) { dwRet = GetLastError(); ERR_OUT("GetRunTimes, convert TriggerEnd", dwRet); return HRESULT_FROM_WIN32(dwRet); } }
if (pstBracketEnd) { if (!SystemTimeToFileTime(pstBracketEnd, &ftBracketEnd)) { dwRet = GetLastError(); ERR_OUT("GetRunTimes, convert pstBracketEnd", dwRet); return HRESULT_FROM_WIN32(dwRet); }
if (CompareFileTime(&ftTriggerBegin, &ftBracketEnd) >= 0) { //
// The trigger start date is after the bracket end date, there are
// no runs (case d.).
//
return S_OK; }
if (CompareFileTime(&ftTriggerEnd, &ftBracketEnd) < 0) { //
// Trigger end is before bracket end, so set bracket end to
// trigger end (case b.).
//
ftBracketEnd = ftTriggerEnd; } } else { //
// No bracket end, so use trigger end (case c.).
//
ftBracketEnd = ftTriggerEnd; }
FILETIME ftRun, ftDurationStart, ftDurationEnd; WORD rgfRunDOW[JOB_DAYS_PER_WEEK], i; WORD rgfDaysOfMonth[JOB_DAYS_PER_MONTHMAX]; WORD rgfMonths[JOB_MONTHS_PER_YEAR]; WORD wDay, wBeginDOW, wCurDOW, wCurDay, wLastDOM, wCurMonth, wCurYear; WORD cRunDOW, iRunDOW, IndexStart; BOOL fWrapped; fWrapped = FALSE;
//
// Calculate the trigger's first run time.
//
switch (jt.TriggerType) { case TASK_TIME_TRIGGER_ONCE: // fall through to daily:
case TASK_TIME_TRIGGER_DAILY: //
// The first run time is the trigger begin time.
//
ftRun = ftTriggerBegin; break;
case TASK_TIME_TRIGGER_WEEKLY: //
// At jobs clear the DOW bits, so make sure we don't have an expired
// At job.
//
if (jt.Type.Weekly.rgfDaysOfTheWeek == 0) { return S_OK; }
//
// See what day of the week the trigger begin day is. SYSTEMTIME
// defines Sunday = 0, Monday = 1, etc.
//
FileTimeToSystemTime(&ftTriggerBegin, &st); wBeginDOW = st.wDayOfWeek; //
// Convert the trigger data run day bit array into a boolean array
// so that the results can be compared with the SYSTEMTIME value.
// This array will also be used in the main loop.
//
for (i = 0; i < JOB_DAYS_PER_WEEK; i++) { rgfRunDOW[i] = (jt.Type.Weekly.rgfDaysOfTheWeek >> i) & 0x1; } //
// Find the first set day-of-the-week after the trigger begin day.
//
for (i = 0; i < JOB_DAYS_PER_WEEK; i++) { wCurDOW = wBeginDOW + i; if (wCurDOW >= JOB_DAYS_PER_WEEK) { wCurDOW -= JOB_DAYS_PER_WEEK; } if (rgfRunDOW[wCurDOW]) { ftRun = ftTriggerBegin; AddDaysToFileTime(&ftRun, i); break; } } break;
case TASK_TIME_TRIGGER_MONTHLYDATE: //
// At jobs clear the days bits, so make sure we don't have an expired
// At job.
//
if (jt.Type.MonthlyDate.rgfDays == 0) { return S_OK; }
//
// Convert the bit fields to boolean arrays.
// These arrays will also be used in the main loop.
//
for (i = 0; i < JOB_DAYS_PER_MONTHMAX; i++) { rgfDaysOfMonth[i] = (WORD)(jt.Type.MonthlyDate.rgfDays >> i) & 0x1; } for (i = 0; i < JOB_MONTHS_PER_YEAR; i++) { rgfMonths[i] = (jt.Type.MonthlyDate.rgfMonths >> i) & 0x1; }
wCurDay = jt.wBeginDay; wCurMonth = jt.wBeginMonth; wCurYear = jt.wBeginYear; BOOL fDayOverflow, fDayFound; fDayFound = FALSE; do { MonthDays(wCurMonth, wCurYear, &wLastDOM); //
// Find the first run day after the trigger start day, including
// the trigger start day.
//
for (i = 0; i < wLastDOM; i++) { if (wCurDay > wLastDOM) { //
// Adjust for wrapping.
//
wCurDay = 1; fWrapped = TRUE; } if (rgfDaysOfMonth[wCurDay - 1]) { fDayFound = TRUE; break; } wCurDay++; } //
// Find the first run month.
//
for (i = 0; i < JOB_MONTHS_PER_YEAR; i++) { if (wCurMonth > JOB_MONTHS_PER_YEAR) { wCurMonth = 1; wCurYear++; } //
// Check for run month match. Note that rgfMonths is zero based
// and wCurMonth is one based.
//
if (rgfMonths[wCurMonth - 1]) { if (fWrapped && !i) { //
// Even though we have a match for run month, the run
// date for the first month has passed, so move on to
// the next run month.
//
fWrapped = FALSE; } else { break; } } wCurMonth++; } //
// Check for days overflow.
//
MonthDays(wCurMonth, wCurYear, &wLastDOM); if (wCurDay > wLastDOM) { //
// Note that this clause would be entered infinitely if there
// were no valid dates. ITask::SetTrigger validates the data to
// ensure that there are valid dates.
//
fDayOverflow = TRUE; fDayFound = FALSE; wCurDay = 1; wCurMonth++; if (wCurMonth > JOB_MONTHS_PER_YEAR) { wCurMonth = 1; wCurYear++; } } else { fDayOverflow = FALSE; } } while (fDayOverflow & !fDayFound);
break;
case TASK_TIME_TRIGGER_MONTHLYDOW: //
// Convert the bit fields to boolean arrays.
// These arrays will also be used in the main loop.
//
cRunDOW = 0; for (i = 0; i < JOB_DAYS_PER_WEEK; i++) { if ((jt.Type.MonthlyDOW.rgfDaysOfTheWeek >> i) & 0x1) { cRunDOW++; rgfRunDOW[i] = TRUE; } else { rgfRunDOW[i] = FALSE; } } for (i = 0; i < JOB_MONTHS_PER_YEAR; i++) { rgfMonths[i] = (jt.Type.MonthlyDOW.rgfMonths >> i) & 0x1; } //
// See if the trigger start month is in rgfMonths and if not
// move to the first month in rgfMonths after jt.BeginMonth.
//
wCurMonth = jt.wBeginMonth; wCurYear = jt.wBeginYear; BOOL fInStartMonth; IndexStart = 0; CheckNextMonth: for (i = IndexStart; i < (JOB_MONTHS_PER_YEAR + IndexStart); i++) { //
// Check for run month match. Note that rgfMonths is zero based
// and wCurMonth is one based.
//
if (rgfMonths[wCurMonth - 1]) { break; }
wCurMonth++; if (wCurMonth > JOB_MONTHS_PER_YEAR) { wCurMonth -= JOB_MONTHS_PER_YEAR; wCurYear++; } }
fInStartMonth = i == 0;
//
// See what day of the week the first day of the month is.
//
st.wMonth = wCurMonth; st.wDay = 1; st.wYear = wCurYear;
//
// Convert to FILETIME and back to SYSTEMTIME to get wDayOfWeek.
//
SystemTimeToFileTime(&st, &ftRun); FileTimeToSystemTime(&ftRun, &st); wBeginDOW = st.wDayOfWeek;
//
// Find the first run DayOftheWeek. If it is before the start
// day, find the next and so on until after the start day.
//
iRunDOW = cRunDOW;
for (i = 0; i < JOB_DAYS_PER_WEEK; i++) { wCurDOW = wBeginDOW + i; wCurDay = 1 + i;
if (wCurDOW >= JOB_DAYS_PER_WEEK) { wCurDOW -= JOB_DAYS_PER_WEEK; }
if (rgfRunDOW[wCurDOW]) { iRunDOW--; wCurDay += (jt.Type.MonthlyDOW.wWhichWeek - 1) * JOB_DAYS_PER_WEEK;
MonthDays(wCurMonth, wCurYear, &wLastDOM);
if (wCurDay > wLastDOM) { //
// This case can be reached if
// jt.Type.MonthlyDOW.wWhichWeek == TASK_LAST_WEEK
// which means to always run on the last occurrence of
// this day for the month.
//
wCurDay -= JOB_DAYS_PER_WEEK; }
if (fInStartMonth && wCurDay < jt.wBeginDay) { if (iRunDOW) { //
// There are more runs this month, so check those.
//
continue; } else { //
// Start with the next run month.
//
IndexStart++; goto CheckNextMonth; } } break; } } wDay = 1 + i; break;
default: return E_FAIL; }
if (jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDATE || jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDOW) { st.wYear = wCurYear; st.wMonth = wCurMonth; st.wDay = wCurDay; st.wHour = jt.wStartHour; st.wMinute = jt.wStartMinute; st.wSecond = st.wMilliseconds = 0; SystemTimeToFileTime(&st, &ftRun); }
//
// Set the initial duration period endpoints.
//
// ftDurationEnd = ftDurationStart + jt.MinutesDuration
// * FILETIMES_PER_MINUTE;
//
ftDurationStart = ftDurationEnd = ftRun; AddMinutesToFileTime(&ftDurationEnd, jt.MinutesDuration); BOOL fPassedDurationEnd = FALSE;
//
// Main loop. Find all of the runs after the initial run.
// Stop when the run goes past the bracket end.
//
while (CompareFileTime(&ftRun, &ftBracketEnd) < 0) { //
// If the run falls within the run bracket, add it to the list.
//
if (CompareFileTime(&ftRun, &ftBracketBegin) >= 0) { if (pRunList != NULL) { FILETIME ftKillTime = MAX_FILETIME; if (jt.rgFlags & TASK_TRIGGER_FLAG_KILL_AT_DURATION_END) { ftKillTime = ftDurationEnd; }
FILETIME ftDeadline = ftTriggerEnd; if (dwJobFlags & TASK_FLAG_START_ONLY_IF_IDLE) { FILETIME ftIdleDeadline = ftRun;
AddMinutesToFileTime(&ftIdleDeadline, wIdleDeadline); ftDeadline = minFileTime(ftTriggerEnd, ftIdleDeadline); }
HRESULT hr = pRunList->AddSorted(ftRun, ftDeadline, ftKillTime, ptszJobName, dwJobFlags, dwMaxRunTime, wIdleWait, pCount, cLimit); if (FAILED(hr)) { return hr; } schAssert(*pCount <= cLimit);
if (hr == S_FALSE) { //
// The run time is later than the last element in the list
// and the list has reached its size limit. So don't
// bother computing any more run times.
//
return S_OK; } } else { if (*pCount < cLimit) { (*pCount)++; }
if (*pCount == cLimit) { //
// Computing more run times will have no effect.
//
return S_OK; } } }
//
// Calculate the next run time.
//
//
// If there is minutes repetition (MinutesInterval non-zero), then
// compute all of the runs in the duration period.
//
if (jt.MinutesInterval) { //
// Add the minutes interval.
//
AddMinutesToFileTime(&ftRun, jt.MinutesInterval);
//
// See if we are at the end of this duration period.
//
if (CompareFileTime(&ftDurationEnd, &ftRun) <= 0) { fPassedDurationEnd = TRUE; } }
//
// If there is no minutes repetition (MinutesInterval is zero) or we
// have passed the end of the duration period, then calculate the next
// duration start (which is also the next run).
//
if (!jt.MinutesInterval || fPassedDurationEnd) { switch (jt.TriggerType) { case TASK_TIME_TRIGGER_ONCE: return S_OK;
case TASK_TIME_TRIGGER_DAILY: //
// ftNextRun = ftCurRun + DaysInterval * FILETIMES_PER_DAY;
//
AddDaysToFileTime(&ftDurationStart, jt.Type.Daily.DaysInterval); break;
case TASK_TIME_TRIGGER_WEEKLY: fWrapped = FALSE; //
// Find the next DayOfWeek to run on.
//
for (i = 1; i <= JOB_DAYS_PER_WEEK; i++) { wCurDOW++; if (wCurDOW >= JOB_DAYS_PER_WEEK) { //
// We have wrapped into the next week.
//
wCurDOW -= JOB_DAYS_PER_WEEK; fWrapped = TRUE; } if (rgfRunDOW[wCurDOW]) { AddDaysToFileTime(&ftDurationStart, i); break; } }
if (fWrapped) { //
// Starting a new week, so add the weeks increment.
//
AddDaysToFileTime(&ftDurationStart, (jt.Type.Weekly.WeeksInterval - 1) * JOB_DAYS_PER_WEEK); } break;
case TASK_TIME_TRIGGER_MONTHLYDATE: BOOL fDayFound; fWrapped = FALSE; fDayFound = FALSE; //
// Find the next day to run.
//
do { MonthDays(wCurMonth, wCurYear, &wLastDOM); for (i = 1; i <= wLastDOM; i++) { wCurDay++; if (wCurDay > wLastDOM) { //
// Adjust for wrapping.
//
wCurDay = 1; fWrapped = TRUE; wCurMonth++; if (wCurMonth > JOB_MONTHS_PER_YEAR) { wCurMonth = 1; wCurYear++; } MonthDays(wCurMonth, wCurYear, &wLastDOM); } if (rgfDaysOfMonth[wCurDay - 1]) { fDayFound = TRUE; break; } } if (fWrapped || !fDayFound) { //
// The prior month is done, find the next month.
//
for (i = 1; i <= JOB_MONTHS_PER_YEAR; i++) { if (wCurMonth > JOB_MONTHS_PER_YEAR) { wCurMonth = 1; wCurYear++; } if (rgfMonths[wCurMonth - 1]) { fWrapped = FALSE; break; } wCurMonth++; } } } while (!fDayFound); break;
case TASK_TIME_TRIGGER_MONTHLYDOW: if (!iRunDOW) { //
// All of the runs for the current month are done, find the
// next month.
//
for (i = 0; i < JOB_MONTHS_PER_YEAR; i++) { wCurMonth++; if (wCurMonth > JOB_MONTHS_PER_YEAR) { wCurMonth = 1; wCurYear++; } if (rgfMonths[wCurMonth - 1]) { break; } } //
// See what day of the week the first day of the month is.
//
st.wMonth = wCurMonth; st.wDay = wDay = 1; st.wYear = wCurYear; SystemTimeToFileTime(&st, &ftRun); FileTimeToSystemTime(&ftRun, &st); wCurDOW = st.wDayOfWeek; iRunDOW = cRunDOW; //
// Start at the first run DOW for this next month.
//
IndexStart = 0; } else { //
// Start at the next run DOW for the current month.
//
IndexStart = 1; }
//
// Find the next DayOfWeek to run on.
//
for (i = IndexStart; i <= JOB_DAYS_PER_WEEK; i++) { if (i > 0) { wCurDOW++; wDay++; } if (wCurDOW >= JOB_DAYS_PER_WEEK) { wCurDOW -= JOB_DAYS_PER_WEEK; } if (rgfRunDOW[wCurDOW]) { //
// Found a run DayOfWeek.
//
iRunDOW--; wCurDay = wDay + (jt.Type.MonthlyDOW.wWhichWeek - 1) * JOB_DAYS_PER_WEEK; WORD wLastDOM; MonthDays(wCurMonth, wCurYear, &wLastDOM); if (wCurDay > wLastDOM) { //
// This case can be reached if
// jt.Type.MonthlyDOW.wWhichWeek == JOB_LAST_WEEK
// which means to always run on the last occurance
// of this day for the month.
//
wCurDay -= JOB_DAYS_PER_WEEK; } break; } } break;
default: return E_FAIL; }
if (jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDATE || jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDOW) { st.wYear = wCurYear; st.wMonth = wCurMonth; st.wDay = wCurDay; st.wHour = jt.wStartHour; st.wMinute = jt.wStartMinute; st.wSecond = st.wMilliseconds = 0; SystemTimeToFileTime(&st, &ftDurationStart); }
//
// Calc the next duration period endpoints.
//
ftRun = ftDurationEnd = ftDurationStart;
AddMinutesToFileTime(&ftDurationEnd, jt.MinutesDuration);
fPassedDurationEnd = FALSE; }
} // while
return S_OK; }
|