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

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Object Handler
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: triggers.cxx
  9. //
  10. // Contents: trigger object and trigger collection object
  11. //
  12. // Classes: CTrigger and CBagOTriggers
  13. //
  14. // History: 27-June-95 EricB created
  15. // 11-Nov-96 AnirudhS GetRunTimes: Changed return codes; various
  16. // other fixes.
  17. //
  18. //-----------------------------------------------------------------------------
  19. #include "..\pch\headers.hxx"
  20. #pragma hdrstop
  21. #include "job.hxx"
  22. #include <StrSafe.h>
  23. // prototypes for local functions:
  24. HRESULT AddToList(FILETIME, FILETIME, CRun *, WORD *);
  25. void AddDaysToFileTime(LPFILETIME pft, WORD Days);
  26. //+----------------------------------------------------------------------------
  27. //
  28. // Member: CTrigger::CTrigger
  29. //
  30. // Synopsis: Ctor
  31. //
  32. //-----------------------------------------------------------------------------
  33. CTrigger::CTrigger(WORD iTrigger, CJob * pJob)
  34. : m_iTrigger(iTrigger),
  35. m_pJob(pJob),
  36. m_cReferences(1)
  37. {
  38. schAssert(m_pJob != NULL);
  39. m_pJob->AddRef();
  40. }
  41. //+----------------------------------------------------------------------------
  42. //
  43. // Member: CTrigger::~CTrigger
  44. //
  45. // Synopsis: Dtor
  46. //
  47. //-----------------------------------------------------------------------------
  48. CTrigger::~CTrigger(void)
  49. {
  50. m_pJob->Release();
  51. }
  52. //+----------------------------------------------------------------------------
  53. //
  54. // Member: CTrigger::ITaskTrigger::GetTriggerString
  55. //
  56. // Synopsis: Returns a string representation of the trigger
  57. //
  58. // Arguments: [ppwszTrigger] - the place to return a string pointer
  59. //
  60. // Returns: HRESULTS
  61. //
  62. // Notes: The string is callee allocated and caller freed with
  63. // CoTaskMemFree.
  64. //-----------------------------------------------------------------------------
  65. STDMETHODIMP
  66. CTrigger::GetTriggerString(LPWSTR * ppwszTrigger)
  67. {
  68. //TRACE(CTrigger, GetTriggerString);
  69. return(m_pJob->GetTriggerString(m_iTrigger, ppwszTrigger));
  70. }
  71. //+----------------------------------------------------------------------------
  72. //
  73. // Member: CTrigger::ITaskTrigger::SetTrigger, public
  74. //
  75. // Synopsis: Sets the trigger values.
  76. //
  77. // Arguments: [pTrigger] - the struct containing the values
  78. //
  79. // Returns: HRESULTS
  80. //
  81. // Notes:
  82. //-----------------------------------------------------------------------------
  83. STDMETHODIMP
  84. CTrigger::SetTrigger(const PTASK_TRIGGER pTrigger)
  85. {
  86. TRACE(CTrigger, SetTrigger);
  87. if( NULL == pTrigger )
  88. {
  89. return E_INVALIDARG;
  90. }
  91. //
  92. // Check struct version.
  93. //
  94. if (pTrigger->cbTriggerSize != sizeof(TASK_TRIGGER))
  95. {
  96. //
  97. // Don't attempt to modify triggers created by a later revision.
  98. //
  99. return E_INVALIDARG;
  100. }
  101. return(m_pJob->SetTrigger(m_iTrigger, pTrigger));
  102. }
  103. //+----------------------------------------------------------------------------
  104. //
  105. // Member: CTrigger::ITaskTrigger::GetTrigger
  106. //
  107. // Synopsis: Gets the trigger values.
  108. //
  109. // Arguments: [pTrigger] - pointer to caller supplied trigger structure
  110. //
  111. // Returns: HRESULTS
  112. //
  113. // Notes: pTrigger->cbTriggerSize must be set to sizeof(TASK_TRIGGER) on
  114. // function entry. This provides for trigger struct versioning.
  115. //-----------------------------------------------------------------------------
  116. STDMETHODIMP
  117. CTrigger::GetTrigger(PTASK_TRIGGER pTrigger)
  118. {
  119. //TRACE(CTrigger, GetTrigger);
  120. if( NULL == pTrigger )
  121. {
  122. return E_INVALIDARG;
  123. }
  124. return(m_pJob->GetTrigger(m_iTrigger, pTrigger));
  125. }
  126. //+----------------------------------------------------------------------------
  127. //
  128. // Functions: AddDaysToFileTime
  129. //
  130. // Synopsis: Convert the days value to filetime units and add it to
  131. // the filetime.
  132. //
  133. //-----------------------------------------------------------------------------
  134. void
  135. AddDaysToFileTime(LPFILETIME pft, WORD Days)
  136. {
  137. if (!Days)
  138. {
  139. return; // Nothing to do.
  140. }
  141. //
  142. // ft = ft + Days * FILETIMES_PER_DAY;
  143. //
  144. ULARGE_INTEGER uli, uliSum;
  145. uli.LowPart = pft->dwLowDateTime;
  146. uli.HighPart = pft->dwHighDateTime;
  147. uliSum.QuadPart = uli.QuadPart + (__int64)Days * FILETIMES_PER_DAY;
  148. pft->dwLowDateTime = uliSum.LowPart;
  149. pft->dwHighDateTime = uliSum.HighPart;
  150. }
  151. //+----------------------------------------------------------------------------
  152. //
  153. // Functions: AddMinutesToFileTime
  154. //
  155. // Synopsis: Convert the minutes value to filetime units and add it to
  156. // the filetime.
  157. //
  158. //-----------------------------------------------------------------------------
  159. void
  160. AddMinutesToFileTime(LPFILETIME pft, DWORD Minutes)
  161. {
  162. if (!Minutes)
  163. {
  164. return; // Nothing to do.
  165. }
  166. //
  167. // ft = ft + Minutes * FILETIMES_PER_MINUTE;
  168. //
  169. ULARGE_INTEGER uli, uliSum;
  170. uli.LowPart = pft->dwLowDateTime;
  171. uli.HighPart = pft->dwHighDateTime;
  172. uliSum.QuadPart = uli.QuadPart + (__int64)Minutes * FILETIMES_PER_MINUTE;
  173. pft->dwLowDateTime = uliSum.LowPart;
  174. pft->dwHighDateTime = uliSum.HighPart;
  175. }
  176. //+----------------------------------------------------------------------------
  177. //
  178. // Function: GetTriggerRunTimes
  179. //
  180. // Synopsis: Computes a set of run times for this trigger that fall between
  181. // the bracketing times -- pstBracketBegin is inclusive,
  182. // pstBracketEnd is exclusive -- and merges them with the list of
  183. // run times passed in.
  184. //
  185. // Arguments: [jt] - Inspected trigger.
  186. // [pstBracketBegin] - the start of the bracketing period
  187. // [pstBracketEnd] - the end of the bracketing period, may
  188. // be NULL
  189. // [pCount] - on both entry and exit, points to the
  190. // number of CRun elements in the list.
  191. // CODEWORK: Make this a private member of
  192. // CRunList.
  193. // [cLimit] - the maximum number of elements that the
  194. // list may grow to.
  195. // [pRunList] - the list of run time objects, can
  196. // be NULL if just checking to see if there
  197. // will be *any* runs. (Note: If it's NULL,
  198. // duplicate run times are not detected, so
  199. // pCount may be overestimated on return.)
  200. // [ptszJobName],
  201. // [dwJobFlags],
  202. // [dwMaxRunTime] - the last 3 params are used for the CRun
  203. // objects as their member data.
  204. // [wIdleWait] - the job's idle wait period
  205. // [wIdleDeadline] - time to wait for idle wait period
  206. //
  207. // Returns: S_OK: The trigger is a time-based trigger and is enabled,
  208. // and zero or more of its run times have been added to the
  209. // list (subject to cLimit and the bracketing period); or,
  210. // the trigger is an event trigger but will expire before
  211. // the bracketing period.
  212. // SCHED_S_EVENT_TRIGGER: this is an event trigger that will be
  213. // active (not expired) during the bracketing period.
  214. // SCHED_S_TASK_NO_VALID_TRIGGERS: the trigger is disabled or
  215. // not set.
  216. // Failure HRESULTs: Other failures.
  217. //
  218. // Notes: The trigger time list is callee allocated and caller freed. The
  219. // caller must use delete to free this list.
  220. //-----------------------------------------------------------------------------
  221. HRESULT
  222. GetTriggerRunTimes(
  223. TASK_TRIGGER & jt,
  224. const SYSTEMTIME * pstBracketBegin,
  225. const SYSTEMTIME * pstBracketEnd,
  226. WORD * pCount,
  227. WORD cLimit,
  228. CTimeRunList * pRunList,
  229. LPTSTR ptszJobName,
  230. DWORD dwJobFlags,
  231. DWORD dwMaxRunTime,
  232. WORD wIdleWait,
  233. WORD wIdleDeadline)
  234. {
  235. TRACE_FUNCTION3(GetRunTimes);
  236. DWORD dwRet;
  237. schAssert(cLimit > 0); // If cLimit is 0, it's not clear what to return
  238. schAssert(cLimit <= TASK_MAX_RUN_TIMES);
  239. schAssert(*pCount <= cLimit);
  240. //
  241. // Return if this trigger hasn't been set or if it is disabled.
  242. //
  243. if (jt.rgFlags & JOB_TRIGGER_I_FLAG_NOT_SET ||
  244. jt.rgFlags & TASK_TRIGGER_FLAG_DISABLED)
  245. {
  246. return SCHED_S_TASK_NO_VALID_TRIGGERS;
  247. }
  248. //
  249. // Event triggers don't have set run times.
  250. //
  251. switch (jt.TriggerType)
  252. {
  253. case TASK_EVENT_TRIGGER_ON_IDLE:
  254. case TASK_EVENT_TRIGGER_AT_SYSTEMSTART:
  255. case TASK_EVENT_TRIGGER_AT_LOGON:
  256. // Not yet implemented:
  257. // case TASK_EVENT_TRIGGER_ON_APM_RESUME:
  258. //
  259. // Check if the trigger expires before the beginning of the bracket
  260. //
  261. if (jt.rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  262. {
  263. SYSTEMTIME stEnd;
  264. stEnd.wYear = jt.wEndYear;
  265. stEnd.wMonth = jt.wEndMonth;
  266. stEnd.wDay = jt.wEndDay;
  267. // IsFirstDateEarlier ignores other fields
  268. if (IsFirstDateEarlier(&stEnd, pstBracketBegin))
  269. {
  270. return S_OK;
  271. }
  272. }
  273. return SCHED_S_EVENT_TRIGGER;
  274. }
  275. SYSTEMTIME st = { 0, 0, 0, 0, 0, 0, 0, 0 };
  276. //
  277. // Convert to FILETIMEs and check if the trigger lifetime intersects the
  278. // requested run bracket.
  279. // If there is a trigger end date, then one of three conditions holds:
  280. // a. *pstBracketBegin > jt.End{Month/Day/Year}
  281. // result, no runs
  282. // b. *pstBracketBegin < jt.End{Month/Day/Year} < *pstBracketEnd
  283. // result, return all runs between *pstBracketBegin and
  284. // jt.End{Month/Day/Year}
  285. // c. jt.End{Month/Day/Year} > *pstBracketEnd
  286. // result, return all runs between *pstBracketBegin and *pstBracketEnd
  287. // In addition, if there is a bracket end we check:
  288. // d. *pstBracketEnd <= jt.Begin{Month/Day/Year}
  289. // result, no runs
  290. //
  291. FILETIME ftTriggerBegin, ftTriggerEnd, ftBracketBegin, ftBracketEnd;
  292. if (!SystemTimeToFileTime(pstBracketBegin, &ftBracketBegin))
  293. {
  294. dwRet = GetLastError();
  295. ERR_OUT("GetRunTimes, convert pstBracketBegin", dwRet);
  296. return HRESULT_FROM_WIN32(dwRet);
  297. }
  298. st.wYear = jt.wBeginYear;
  299. st.wMonth = jt.wBeginMonth;
  300. st.wDay = jt.wBeginDay;
  301. st.wHour = jt.wStartHour;
  302. st.wMinute = jt.wStartMinute;
  303. if (!SystemTimeToFileTime(&st, &ftTriggerBegin))
  304. {
  305. dwRet = GetLastError();
  306. ERR_OUT("GetRunTimes, convert TriggerBegin", dwRet);
  307. return HRESULT_FROM_WIN32(dwRet);
  308. }
  309. st.wHour = 23; // set to the last hour of the day.
  310. st.wMinute = 59; // set to the last minute of the day.
  311. st.wSecond = 59; // set to the last second of the day.
  312. st.wMilliseconds = 0;
  313. if (jt.rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  314. {
  315. st.wYear = jt.wEndYear;
  316. st.wMonth = jt.wEndMonth;
  317. st.wDay = jt.wEndDay;
  318. if (!SystemTimeToFileTime(&st, &ftTriggerEnd))
  319. {
  320. dwRet = GetLastError();
  321. ERR_OUT("GetRunTimes, convert TriggerEnd", dwRet);
  322. return HRESULT_FROM_WIN32(dwRet);
  323. }
  324. if (CompareFileTime(&ftTriggerEnd, &ftBracketBegin) < 0)
  325. {
  326. //
  327. // Trigger end time is before the run bracket begin time (case a.).
  328. //
  329. return S_OK;
  330. }
  331. }
  332. else // no trigger end date.
  333. {
  334. //
  335. // Create an end date that is reasonably large.
  336. // BUGBUG Change this to MAX_FILETIME - but should be tested.
  337. //
  338. st.wMonth = 12;
  339. st.wDay = 31;
  340. st.wYear = 2200;
  341. if (!SystemTimeToFileTime(&st, &ftTriggerEnd))
  342. {
  343. dwRet = GetLastError();
  344. ERR_OUT("GetRunTimes, convert TriggerEnd", dwRet);
  345. return HRESULT_FROM_WIN32(dwRet);
  346. }
  347. }
  348. if (pstBracketEnd)
  349. {
  350. if (!SystemTimeToFileTime(pstBracketEnd, &ftBracketEnd))
  351. {
  352. dwRet = GetLastError();
  353. ERR_OUT("GetRunTimes, convert pstBracketEnd", dwRet);
  354. return HRESULT_FROM_WIN32(dwRet);
  355. }
  356. if (CompareFileTime(&ftTriggerBegin, &ftBracketEnd) >= 0)
  357. {
  358. //
  359. // The trigger start date is after the bracket end date, there are
  360. // no runs (case d.).
  361. //
  362. return S_OK;
  363. }
  364. if (CompareFileTime(&ftTriggerEnd, &ftBracketEnd) < 0)
  365. {
  366. //
  367. // Trigger end is before bracket end, so set bracket end to
  368. // trigger end (case b.).
  369. //
  370. ftBracketEnd = ftTriggerEnd;
  371. }
  372. }
  373. else
  374. {
  375. //
  376. // No bracket end, so use trigger end (case c.).
  377. //
  378. ftBracketEnd = ftTriggerEnd;
  379. }
  380. FILETIME ftRun, ftDurationStart, ftDurationEnd;
  381. WORD rgfRunDOW[JOB_DAYS_PER_WEEK], i;
  382. WORD rgfDaysOfMonth[JOB_DAYS_PER_MONTHMAX];
  383. WORD rgfMonths[JOB_MONTHS_PER_YEAR];
  384. WORD wDay, wBeginDOW, wCurDOW, wCurDay, wLastDOM, wCurMonth, wCurYear;
  385. WORD cRunDOW, iRunDOW, IndexStart;
  386. BOOL fWrapped;
  387. fWrapped = FALSE;
  388. //
  389. // Calculate the trigger's first run time.
  390. //
  391. switch (jt.TriggerType)
  392. {
  393. case TASK_TIME_TRIGGER_ONCE:
  394. // fall through to daily:
  395. case TASK_TIME_TRIGGER_DAILY:
  396. //
  397. // The first run time is the trigger begin time.
  398. //
  399. ftRun = ftTriggerBegin;
  400. break;
  401. case TASK_TIME_TRIGGER_WEEKLY:
  402. //
  403. // At jobs clear the DOW bits, so make sure we don't have an expired
  404. // At job.
  405. //
  406. if (jt.Type.Weekly.rgfDaysOfTheWeek == 0)
  407. {
  408. return S_OK;
  409. }
  410. //
  411. // See what day of the week the trigger begin day is. SYSTEMTIME
  412. // defines Sunday = 0, Monday = 1, etc.
  413. //
  414. FileTimeToSystemTime(&ftTriggerBegin, &st);
  415. wBeginDOW = st.wDayOfWeek;
  416. //
  417. // Convert the trigger data run day bit array into a boolean array
  418. // so that the results can be compared with the SYSTEMTIME value.
  419. // This array will also be used in the main loop.
  420. //
  421. for (i = 0; i < JOB_DAYS_PER_WEEK; i++)
  422. {
  423. rgfRunDOW[i] = (jt.Type.Weekly.rgfDaysOfTheWeek >> i) & 0x1;
  424. }
  425. //
  426. // Find the first set day-of-the-week after the trigger begin day.
  427. //
  428. for (i = 0; i < JOB_DAYS_PER_WEEK; i++)
  429. {
  430. wCurDOW = wBeginDOW + i;
  431. if (wCurDOW >= JOB_DAYS_PER_WEEK)
  432. {
  433. wCurDOW -= JOB_DAYS_PER_WEEK;
  434. }
  435. if (rgfRunDOW[wCurDOW])
  436. {
  437. ftRun = ftTriggerBegin;
  438. AddDaysToFileTime(&ftRun, i);
  439. break;
  440. }
  441. }
  442. break;
  443. case TASK_TIME_TRIGGER_MONTHLYDATE:
  444. //
  445. // At jobs clear the days bits, so make sure we don't have an expired
  446. // At job.
  447. //
  448. if (jt.Type.MonthlyDate.rgfDays == 0)
  449. {
  450. return S_OK;
  451. }
  452. //
  453. // Convert the bit fields to boolean arrays.
  454. // These arrays will also be used in the main loop.
  455. //
  456. for (i = 0; i < JOB_DAYS_PER_MONTHMAX; i++)
  457. {
  458. rgfDaysOfMonth[i] = (WORD)(jt.Type.MonthlyDate.rgfDays >> i) & 0x1;
  459. }
  460. for (i = 0; i < JOB_MONTHS_PER_YEAR; i++)
  461. {
  462. rgfMonths[i] = (jt.Type.MonthlyDate.rgfMonths >> i) & 0x1;
  463. }
  464. wCurDay = jt.wBeginDay;
  465. wCurMonth = jt.wBeginMonth;
  466. wCurYear = jt.wBeginYear;
  467. BOOL fDayOverflow, fDayFound;
  468. fDayFound = FALSE;
  469. do
  470. {
  471. MonthDays(wCurMonth, wCurYear, &wLastDOM);
  472. //
  473. // Find the first run day after the trigger start day, including
  474. // the trigger start day.
  475. //
  476. for (i = 0; i < wLastDOM; i++)
  477. {
  478. if (wCurDay > wLastDOM)
  479. {
  480. //
  481. // Adjust for wrapping.
  482. //
  483. wCurDay = 1;
  484. fWrapped = TRUE;
  485. }
  486. if (rgfDaysOfMonth[wCurDay - 1])
  487. {
  488. fDayFound = TRUE;
  489. break;
  490. }
  491. wCurDay++;
  492. }
  493. //
  494. // Find the first run month.
  495. //
  496. for (i = 0; i < JOB_MONTHS_PER_YEAR; i++)
  497. {
  498. if (wCurMonth > JOB_MONTHS_PER_YEAR)
  499. {
  500. wCurMonth = 1;
  501. wCurYear++;
  502. }
  503. //
  504. // Check for run month match. Note that rgfMonths is zero based
  505. // and wCurMonth is one based.
  506. //
  507. if (rgfMonths[wCurMonth - 1])
  508. {
  509. if (fWrapped && !i)
  510. {
  511. //
  512. // Even though we have a match for run month, the run
  513. // date for the first month has passed, so move on to
  514. // the next run month.
  515. //
  516. fWrapped = FALSE;
  517. }
  518. else
  519. {
  520. break;
  521. }
  522. }
  523. wCurMonth++;
  524. }
  525. //
  526. // Check for days overflow.
  527. //
  528. MonthDays(wCurMonth, wCurYear, &wLastDOM);
  529. if (wCurDay > wLastDOM)
  530. {
  531. //
  532. // Note that this clause would be entered infinitely if there
  533. // were no valid dates. ITask::SetTrigger validates the data to
  534. // ensure that there are valid dates.
  535. //
  536. fDayOverflow = TRUE;
  537. fDayFound = FALSE;
  538. wCurDay = 1;
  539. wCurMonth++;
  540. if (wCurMonth > JOB_MONTHS_PER_YEAR)
  541. {
  542. wCurMonth = 1;
  543. wCurYear++;
  544. }
  545. }
  546. else
  547. {
  548. fDayOverflow = FALSE;
  549. }
  550. } while (fDayOverflow & !fDayFound);
  551. break;
  552. case TASK_TIME_TRIGGER_MONTHLYDOW:
  553. //
  554. // Convert the bit fields to boolean arrays.
  555. // These arrays will also be used in the main loop.
  556. //
  557. cRunDOW = 0;
  558. for (i = 0; i < JOB_DAYS_PER_WEEK; i++)
  559. {
  560. if ((jt.Type.MonthlyDOW.rgfDaysOfTheWeek >> i) & 0x1)
  561. {
  562. cRunDOW++;
  563. rgfRunDOW[i] = TRUE;
  564. }
  565. else
  566. {
  567. rgfRunDOW[i] = FALSE;
  568. }
  569. }
  570. for (i = 0; i < JOB_MONTHS_PER_YEAR; i++)
  571. {
  572. rgfMonths[i] = (jt.Type.MonthlyDOW.rgfMonths >> i) & 0x1;
  573. }
  574. //
  575. // See if the trigger start month is in rgfMonths and if not
  576. // move to the first month in rgfMonths after jt.BeginMonth.
  577. //
  578. wCurMonth = jt.wBeginMonth;
  579. wCurYear = jt.wBeginYear;
  580. BOOL fInStartMonth;
  581. IndexStart = 0;
  582. CheckNextMonth:
  583. for (i = IndexStart; i < (JOB_MONTHS_PER_YEAR + IndexStart); i++)
  584. {
  585. //
  586. // Check for run month match. Note that rgfMonths is zero based
  587. // and wCurMonth is one based.
  588. //
  589. if (rgfMonths[wCurMonth - 1])
  590. {
  591. break;
  592. }
  593. wCurMonth++;
  594. if (wCurMonth > JOB_MONTHS_PER_YEAR)
  595. {
  596. wCurMonth -= JOB_MONTHS_PER_YEAR;
  597. wCurYear++;
  598. }
  599. }
  600. fInStartMonth = i == 0;
  601. //
  602. // See what day of the week the first day of the month is.
  603. //
  604. st.wMonth = wCurMonth;
  605. st.wDay = 1;
  606. st.wYear = wCurYear;
  607. //
  608. // Convert to FILETIME and back to SYSTEMTIME to get wDayOfWeek.
  609. //
  610. SystemTimeToFileTime(&st, &ftRun);
  611. FileTimeToSystemTime(&ftRun, &st);
  612. wBeginDOW = st.wDayOfWeek;
  613. //
  614. // Find the first run DayOftheWeek. If it is before the start
  615. // day, find the next and so on until after the start day.
  616. //
  617. iRunDOW = cRunDOW;
  618. for (i = 0; i < JOB_DAYS_PER_WEEK; i++)
  619. {
  620. wCurDOW = wBeginDOW + i;
  621. wCurDay = 1 + i;
  622. if (wCurDOW >= JOB_DAYS_PER_WEEK)
  623. {
  624. wCurDOW -= JOB_DAYS_PER_WEEK;
  625. }
  626. if (rgfRunDOW[wCurDOW])
  627. {
  628. iRunDOW--;
  629. wCurDay += (jt.Type.MonthlyDOW.wWhichWeek - 1)
  630. * JOB_DAYS_PER_WEEK;
  631. MonthDays(wCurMonth, wCurYear, &wLastDOM);
  632. if (wCurDay > wLastDOM)
  633. {
  634. //
  635. // This case can be reached if
  636. // jt.Type.MonthlyDOW.wWhichWeek == TASK_LAST_WEEK
  637. // which means to always run on the last occurrence of
  638. // this day for the month.
  639. //
  640. wCurDay -= JOB_DAYS_PER_WEEK;
  641. }
  642. if (fInStartMonth && wCurDay < jt.wBeginDay)
  643. {
  644. if (iRunDOW)
  645. {
  646. //
  647. // There are more runs this month, so check those.
  648. //
  649. continue;
  650. }
  651. else
  652. {
  653. //
  654. // Start with the next run month.
  655. //
  656. IndexStart++;
  657. goto CheckNextMonth;
  658. }
  659. }
  660. break;
  661. }
  662. }
  663. wDay = 1 + i;
  664. break;
  665. default:
  666. return E_FAIL;
  667. }
  668. if (jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDATE ||
  669. jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDOW)
  670. {
  671. st.wYear = wCurYear;
  672. st.wMonth = wCurMonth;
  673. st.wDay = wCurDay;
  674. st.wHour = jt.wStartHour;
  675. st.wMinute = jt.wStartMinute;
  676. st.wSecond = st.wMilliseconds = 0;
  677. SystemTimeToFileTime(&st, &ftRun);
  678. }
  679. //
  680. // Set the initial duration period endpoints.
  681. //
  682. // ftDurationEnd = ftDurationStart + jt.MinutesDuration
  683. // * FILETIMES_PER_MINUTE;
  684. //
  685. ftDurationStart = ftDurationEnd = ftRun;
  686. AddMinutesToFileTime(&ftDurationEnd, jt.MinutesDuration);
  687. BOOL fPassedDurationEnd = FALSE;
  688. //
  689. // Main loop. Find all of the runs after the initial run.
  690. // Stop when the run goes past the bracket end.
  691. //
  692. while (CompareFileTime(&ftRun, &ftBracketEnd) < 0)
  693. {
  694. //
  695. // If the run falls within the run bracket, add it to the list.
  696. //
  697. if (CompareFileTime(&ftRun, &ftBracketBegin) >= 0)
  698. {
  699. if (pRunList != NULL)
  700. {
  701. FILETIME ftKillTime = MAX_FILETIME;
  702. if (jt.rgFlags & TASK_TRIGGER_FLAG_KILL_AT_DURATION_END)
  703. {
  704. ftKillTime = ftDurationEnd;
  705. }
  706. FILETIME ftDeadline = ftTriggerEnd;
  707. if (dwJobFlags & TASK_FLAG_START_ONLY_IF_IDLE)
  708. {
  709. FILETIME ftIdleDeadline = ftRun;
  710. AddMinutesToFileTime(&ftIdleDeadline, wIdleDeadline);
  711. ftDeadline = minFileTime(ftTriggerEnd, ftIdleDeadline);
  712. }
  713. HRESULT hr = pRunList->AddSorted(ftRun, ftDeadline, ftKillTime,
  714. ptszJobName, dwJobFlags, dwMaxRunTime,
  715. wIdleWait, pCount, cLimit);
  716. if (FAILED(hr))
  717. {
  718. return hr;
  719. }
  720. schAssert(*pCount <= cLimit);
  721. if (hr == S_FALSE)
  722. {
  723. //
  724. // The run time is later than the last element in the list
  725. // and the list has reached its size limit. So don't
  726. // bother computing any more run times.
  727. //
  728. return S_OK;
  729. }
  730. }
  731. else
  732. {
  733. if (*pCount < cLimit)
  734. {
  735. (*pCount)++;
  736. }
  737. if (*pCount == cLimit)
  738. {
  739. //
  740. // Computing more run times will have no effect.
  741. //
  742. return S_OK;
  743. }
  744. }
  745. }
  746. //
  747. // Calculate the next run time.
  748. //
  749. //
  750. // If there is minutes repetition (MinutesInterval non-zero), then
  751. // compute all of the runs in the duration period.
  752. //
  753. if (jt.MinutesInterval)
  754. {
  755. //
  756. // Add the minutes interval.
  757. //
  758. AddMinutesToFileTime(&ftRun, jt.MinutesInterval);
  759. //
  760. // See if we are at the end of this duration period.
  761. //
  762. if (CompareFileTime(&ftDurationEnd, &ftRun) <= 0)
  763. {
  764. fPassedDurationEnd = TRUE;
  765. }
  766. }
  767. //
  768. // If there is no minutes repetition (MinutesInterval is zero) or we
  769. // have passed the end of the duration period, then calculate the next
  770. // duration start (which is also the next run).
  771. //
  772. if (!jt.MinutesInterval || fPassedDurationEnd)
  773. {
  774. switch (jt.TriggerType)
  775. {
  776. case TASK_TIME_TRIGGER_ONCE:
  777. return S_OK;
  778. case TASK_TIME_TRIGGER_DAILY:
  779. //
  780. // ftNextRun = ftCurRun + DaysInterval * FILETIMES_PER_DAY;
  781. //
  782. AddDaysToFileTime(&ftDurationStart, jt.Type.Daily.DaysInterval);
  783. break;
  784. case TASK_TIME_TRIGGER_WEEKLY:
  785. fWrapped = FALSE;
  786. //
  787. // Find the next DayOfWeek to run on.
  788. //
  789. for (i = 1; i <= JOB_DAYS_PER_WEEK; i++)
  790. {
  791. wCurDOW++;
  792. if (wCurDOW >= JOB_DAYS_PER_WEEK)
  793. {
  794. //
  795. // We have wrapped into the next week.
  796. //
  797. wCurDOW -= JOB_DAYS_PER_WEEK;
  798. fWrapped = TRUE;
  799. }
  800. if (rgfRunDOW[wCurDOW])
  801. {
  802. AddDaysToFileTime(&ftDurationStart, i);
  803. break;
  804. }
  805. }
  806. if (fWrapped)
  807. {
  808. //
  809. // Starting a new week, so add the weeks increment.
  810. //
  811. AddDaysToFileTime(&ftDurationStart,
  812. (jt.Type.Weekly.WeeksInterval - 1)
  813. * JOB_DAYS_PER_WEEK);
  814. }
  815. break;
  816. case TASK_TIME_TRIGGER_MONTHLYDATE:
  817. BOOL fDayFound;
  818. fWrapped = FALSE;
  819. fDayFound = FALSE;
  820. //
  821. // Find the next day to run.
  822. //
  823. do
  824. {
  825. MonthDays(wCurMonth, wCurYear, &wLastDOM);
  826. for (i = 1; i <= wLastDOM; i++)
  827. {
  828. wCurDay++;
  829. if (wCurDay > wLastDOM)
  830. {
  831. //
  832. // Adjust for wrapping.
  833. //
  834. wCurDay = 1;
  835. fWrapped = TRUE;
  836. wCurMonth++;
  837. if (wCurMonth > JOB_MONTHS_PER_YEAR)
  838. {
  839. wCurMonth = 1;
  840. wCurYear++;
  841. }
  842. MonthDays(wCurMonth, wCurYear, &wLastDOM);
  843. }
  844. if (rgfDaysOfMonth[wCurDay - 1])
  845. {
  846. fDayFound = TRUE;
  847. break;
  848. }
  849. }
  850. if (fWrapped || !fDayFound)
  851. {
  852. //
  853. // The prior month is done, find the next month.
  854. //
  855. for (i = 1; i <= JOB_MONTHS_PER_YEAR; i++)
  856. {
  857. if (wCurMonth > JOB_MONTHS_PER_YEAR)
  858. {
  859. wCurMonth = 1;
  860. wCurYear++;
  861. }
  862. if (rgfMonths[wCurMonth - 1])
  863. {
  864. fWrapped = FALSE;
  865. break;
  866. }
  867. wCurMonth++;
  868. }
  869. }
  870. } while (!fDayFound);
  871. break;
  872. case TASK_TIME_TRIGGER_MONTHLYDOW:
  873. if (!iRunDOW)
  874. {
  875. //
  876. // All of the runs for the current month are done, find the
  877. // next month.
  878. //
  879. for (i = 0; i < JOB_MONTHS_PER_YEAR; i++)
  880. {
  881. wCurMonth++;
  882. if (wCurMonth > JOB_MONTHS_PER_YEAR)
  883. {
  884. wCurMonth = 1;
  885. wCurYear++;
  886. }
  887. if (rgfMonths[wCurMonth - 1])
  888. {
  889. break;
  890. }
  891. }
  892. //
  893. // See what day of the week the first day of the month is.
  894. //
  895. st.wMonth = wCurMonth;
  896. st.wDay = wDay = 1;
  897. st.wYear = wCurYear;
  898. SystemTimeToFileTime(&st, &ftRun);
  899. FileTimeToSystemTime(&ftRun, &st);
  900. wCurDOW = st.wDayOfWeek;
  901. iRunDOW = cRunDOW;
  902. //
  903. // Start at the first run DOW for this next month.
  904. //
  905. IndexStart = 0;
  906. }
  907. else
  908. {
  909. //
  910. // Start at the next run DOW for the current month.
  911. //
  912. IndexStart = 1;
  913. }
  914. //
  915. // Find the next DayOfWeek to run on.
  916. //
  917. for (i = IndexStart; i <= JOB_DAYS_PER_WEEK; i++)
  918. {
  919. if (i > 0)
  920. {
  921. wCurDOW++;
  922. wDay++;
  923. }
  924. if (wCurDOW >= JOB_DAYS_PER_WEEK)
  925. {
  926. wCurDOW -= JOB_DAYS_PER_WEEK;
  927. }
  928. if (rgfRunDOW[wCurDOW])
  929. {
  930. //
  931. // Found a run DayOfWeek.
  932. //
  933. iRunDOW--;
  934. wCurDay = wDay + (jt.Type.MonthlyDOW.wWhichWeek - 1)
  935. * JOB_DAYS_PER_WEEK;
  936. WORD wLastDOM;
  937. MonthDays(wCurMonth, wCurYear, &wLastDOM);
  938. if (wCurDay > wLastDOM)
  939. {
  940. //
  941. // This case can be reached if
  942. // jt.Type.MonthlyDOW.wWhichWeek == JOB_LAST_WEEK
  943. // which means to always run on the last occurance
  944. // of this day for the month.
  945. //
  946. wCurDay -= JOB_DAYS_PER_WEEK;
  947. }
  948. break;
  949. }
  950. }
  951. break;
  952. default:
  953. return E_FAIL;
  954. }
  955. if (jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDATE ||
  956. jt.TriggerType == TASK_TIME_TRIGGER_MONTHLYDOW)
  957. {
  958. st.wYear = wCurYear;
  959. st.wMonth = wCurMonth;
  960. st.wDay = wCurDay;
  961. st.wHour = jt.wStartHour;
  962. st.wMinute = jt.wStartMinute;
  963. st.wSecond = st.wMilliseconds = 0;
  964. SystemTimeToFileTime(&st, &ftDurationStart);
  965. }
  966. //
  967. // Calc the next duration period endpoints.
  968. //
  969. ftRun = ftDurationEnd = ftDurationStart;
  970. AddMinutesToFileTime(&ftDurationEnd, jt.MinutesDuration);
  971. fPassedDurationEnd = FALSE;
  972. }
  973. } // while
  974. return S_OK;
  975. }