Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1057 lines
34 KiB

//+----------------------------------------------------------------------------
//
// 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;
}