Source code of Windows XP (NT5)
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.

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