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.

1210 lines
35 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Schedule Application Job Object Handler
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: util.cxx
  9. //
  10. // Contents: job & trigger objects IUnknown methods, class factory, DLL fcns,
  11. // plus misc utility fcns
  12. //
  13. // Classes: CJob (continued), CJobCF, CTrigger
  14. //
  15. // Interfaces: IUnknown, IClassFactory
  16. //
  17. // History: 24-May-95 EricB created
  18. //
  19. //-----------------------------------------------------------------------------
  20. #include "..\pch\headers.hxx"
  21. #pragma hdrstop
  22. #include "job.hxx"
  23. #if !defined(_CHICAGO_) // this is not needed for Chicago version
  24. //+----------------------------------------------------------------------------
  25. //
  26. // Member: CJob::GetAtInfo
  27. //
  28. // Synopsis: for a downlevel job, return its data in an AT_INFO struct.
  29. //
  30. // Arguments: [pAt] - pointer to the AT_INFO struct
  31. // [pwszCommand] - buffer for the command string
  32. // [pcchCommand] - on input, size of supplied buffer, on output,
  33. // size needed/used.
  34. //
  35. // Returns: HRESULTS - ERROR_INSUFFICIENT_BUFFER if too small
  36. // - SCHED_E_NOT_AN_AT_JOB if not an AT job
  37. //
  38. // Notes: This method is not exposed to external clients, thus it is not
  39. // part of a public interface.
  40. //-----------------------------------------------------------------------------
  41. HRESULT
  42. CJob::GetAtInfo(PAT_INFO pAt, LPWSTR pwszCommand, DWORD * pcchCommand)
  43. {
  44. TRACE(CJob, GetAtInfo);
  45. HRESULT hr = S_OK;
  46. if (!(m_rgFlags & JOB_I_FLAG_NET_SCHEDULE))
  47. {
  48. schDebugOut((DEB_ERROR,
  49. "CJob::GetAtInfo: Task object is not an AT job!\n"));
  50. return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  51. }
  52. //
  53. // The ApplicationName and Parameters properties need to be concatonated
  54. // and returned as pwszCommand. If there is any white space in the app
  55. // name string, then it must be enclosed in quotes.
  56. //
  57. LPWSTR pwszAppName, pwszParams;
  58. hr = GetApplicationName(&pwszAppName);
  59. if (FAILED(hr))
  60. {
  61. ERR_OUT("GetAtInfo: GetApplicationName", hr);
  62. return hr;
  63. }
  64. hr = GetParameters(&pwszParams);
  65. if (FAILED(hr))
  66. {
  67. ERR_OUT("GetAtInfo: GetParameters", hr);
  68. CoTaskMemFree(pwszAppName);
  69. return hr;
  70. }
  71. //
  72. // Check for whitespace in the app name.
  73. //
  74. BOOL fAppNameHasSpaces = HasSpaces(pwszAppName);
  75. //
  76. // If there is app name whitespace, add two for the quotes to be added.
  77. //
  78. DWORD cchApp = wcslen(pwszAppName) + (fAppNameHasSpaces ? 2 : 0);
  79. DWORD cchParam = wcslen(pwszParams);
  80. //
  81. // Add one for the terminating null.
  82. //
  83. DWORD cch = cchApp + cchParam + 1;
  84. if (cch > *pcchCommand)
  85. {
  86. *pcchCommand = cch;
  87. CoTaskMemFree(pwszAppName);
  88. CoTaskMemFree(pwszParams);
  89. return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  90. }
  91. *pcchCommand = cch;
  92. if (fAppNameHasSpaces)
  93. {
  94. wcscpy(pwszCommand, L"\"");
  95. wcscat(pwszCommand, pwszAppName);
  96. wcscat(pwszCommand, L"\"");
  97. }
  98. else
  99. {
  100. wcscpy(pwszCommand, pwszAppName);
  101. }
  102. if (cchParam > 0)
  103. {
  104. wcscat(pwszCommand, L" ");
  105. wcscat(pwszCommand, pwszParams);
  106. }
  107. CoTaskMemFree(pwszAppName);
  108. CoTaskMemFree(pwszParams);
  109. //
  110. // An AT job can have one or two triggers of type TASK_TIME_TRIGGER_WEEKLY
  111. // and/or TASK_TIME_TRIGGER_MONTHLYDATE. It may also have, instead, a single trigger
  112. // of type TASK_TIME_TRIGGER_ONCE, which indicates it runs either today or
  113. // tomorrow, only.
  114. //
  115. WORD cTriggers = m_Triggers.GetCount();
  116. if (cTriggers == 0 || cTriggers > 2)
  117. {
  118. ERR_OUT("GetAtInfo: Incorrect Trigger Count", E_FAIL);
  119. return E_FAIL;
  120. }
  121. PTASK_TRIGGER pjt;
  122. pjt = _GetTrigger(0);
  123. if (pjt == NULL)
  124. {
  125. schAssert(!"GetCount > 0 but no trigger 0");
  126. return E_FAIL;
  127. }
  128. pAt->JobTime = (pjt->wStartHour * JOB_MINS_PER_HOUR +
  129. pjt->wStartMinute) * JOB_MILLISECONDS_PER_MINUTE;
  130. pAt->DaysOfMonth = pAt->DaysOfWeek = 0;
  131. switch (pjt->TriggerType)
  132. {
  133. case TASK_TIME_TRIGGER_WEEKLY:
  134. //
  135. // Convert Scheduler DOW to AT_INFO DOW:
  136. // Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
  137. // AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
  138. //
  139. pAt->DaysOfWeek = pjt->Type.Weekly.rgfDaysOfTheWeek >> 1;
  140. if (pjt->Type.Weekly.rgfDaysOfTheWeek & 0x0001)
  141. {
  142. pAt->DaysOfWeek |= 0x0040;
  143. }
  144. break;
  145. case TASK_TIME_TRIGGER_MONTHLYDATE:
  146. pAt->DaysOfMonth = pjt->Type.MonthlyDate.rgfDays;
  147. break;
  148. case TASK_TIME_TRIGGER_ONCE:
  149. // Day of Month & Week are NULL if the job runs only once.
  150. break;
  151. default:
  152. schAssert(FALSE && "GetAtInfo: wrong trigger type");
  153. ERR_OUT("GetAtInfo: wrong trigger type", hr);
  154. return E_FAIL;
  155. }
  156. if (cTriggers == 2)
  157. {
  158. pjt = _GetTrigger(1);
  159. switch (pjt->TriggerType)
  160. {
  161. case TASK_TIME_TRIGGER_WEEKLY:
  162. //
  163. // Convert Scheduler DOW to AT_INFO DOW:
  164. // Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
  165. // AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
  166. //
  167. pAt->DaysOfWeek = pjt->Type.Weekly.rgfDaysOfTheWeek >> 1;
  168. if (pjt->Type.Weekly.rgfDaysOfTheWeek & 0x0001)
  169. {
  170. pAt->DaysOfWeek |= 0x0040;
  171. }
  172. break;
  173. case TASK_TIME_TRIGGER_MONTHLYDATE:
  174. pAt->DaysOfMonth = pjt->Type.MonthlyDate.rgfDays;
  175. break;
  176. case TASK_TIME_TRIGGER_ONCE:
  177. schAssert(FALSE && "GetAtInfo: Once triggers not allowed in multiple triggers!");
  178. ERR_OUT("GetAtInfo: Once triggers not allowed in multiple triggers!", 0);
  179. return E_FAIL;
  180. break;
  181. default:
  182. schAssert(FALSE && "GetAtInfo: wrong trigger type");
  183. ERR_OUT("GetAtInfo: wrong trigger type", 0);
  184. return E_FAIL;
  185. }
  186. }
  187. //
  188. // Set the AT_INFO.Flags.
  189. //
  190. pAt->Flags = 0;
  191. if (!(m_rgFlags & TASK_FLAG_INTERACTIVE))
  192. {
  193. pAt->Flags |= JOB_NONINTERACTIVE;
  194. }
  195. if (!(pjt->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) &&
  196. (pjt->TriggerType != TASK_TIME_TRIGGER_ONCE))
  197. {
  198. pAt->Flags |= JOB_RUN_PERIODICALLY;
  199. }
  200. //
  201. // Check whether job runs today, or is even running in a time window
  202. // that is valid for a NetScheduleJob.
  203. //
  204. SYSTEMTIME stNow, stMidnight, stTomorrow;
  205. GetLocalTime(&stNow);
  206. WORD cRuns = 0;
  207. // Check to see if there is a run today and set the flag JOB_RUNS_TODAY
  208. stMidnight = stNow;
  209. stMidnight.wHour = stMidnight.wMinute = stMidnight.wSecond
  210. = stMidnight.wMilliseconds = 0;
  211. IncrementDay(&stMidnight);
  212. // Zero out cRuns - we used it, and it is not initialized in GetRunTimesP
  213. cRuns = 0;
  214. hr = GetRunTimesP(&stNow, &stMidnight, &cRuns, 1, NULL, NULL);
  215. if (FAILED(hr))
  216. {
  217. ERR_OUT("GetAtInfo: GetRunTimes", hr);
  218. return hr;
  219. }
  220. if (cRuns > 0)
  221. {
  222. pAt->Flags |= JOB_RUNS_TODAY;
  223. }
  224. //
  225. // Check exit status of last run and set TASK_EXEC_ERROR as needed.
  226. //
  227. if (IsFlagSet(JOB_I_FLAG_LAST_LAUNCH_FAILED) ||
  228. IsFlagSet(JOB_I_FLAG_ERROR_IN_LAST_RUN))
  229. {
  230. pAt->Flags |= JOB_EXEC_ERROR;
  231. }
  232. //
  233. // Safety check - if the trigger is a ONCE trigger, the job
  234. // must within the next 24 hours. If it is farther out
  235. // than 24 hours, it is an error condition - bail.
  236. //
  237. if (pjt->TriggerType == TASK_TIME_TRIGGER_ONCE &&
  238. !(pAt->Flags & JOB_EXEC_ERROR))
  239. {
  240. stTomorrow = stNow;
  241. IncrementDay(&stTomorrow);
  242. hr = GetRunTimesP(&stNow, &stTomorrow, &cRuns, 1, NULL, NULL);
  243. if (FAILED(hr))
  244. {
  245. ERR_OUT("GetAtInfo: GetRunTimes for Once Trigger", hr);
  246. return hr;
  247. }
  248. if (cRuns == 0)
  249. {
  250. ERR_OUT("GetAtInfo: Once trigger outside permitted time interval", 0);
  251. return E_FAIL;
  252. }
  253. }
  254. //
  255. // Omit jobs whose end date has passed (usually jobs that haven't yet
  256. // been deleted because they're still running)
  257. //
  258. if (pjt->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  259. {
  260. SYSTEMTIME stEnd;
  261. stEnd.wYear = pjt->wEndYear;
  262. stEnd.wMonth = pjt->wEndMonth;
  263. stEnd.wDayOfWeek = 0;
  264. stEnd.wDay = pjt->wEndDay;
  265. stEnd.wHour = pjt->wStartHour;
  266. stEnd.wMinute = pjt->wStartMinute;
  267. stEnd.wSecond = 0;
  268. stEnd.wMilliseconds = 0;
  269. if (IsFirstTimeEarlier(&stEnd, &stNow) &&
  270. !(pAt->Flags & JOB_EXEC_ERROR))
  271. {
  272. return E_FAIL;
  273. }
  274. }
  275. return S_OK;
  276. }
  277. #endif // !defined(_CHICAGO_)
  278. //+----------------------------------------------------------------------------
  279. //
  280. // Member: CJob::UpdateJobState
  281. //
  282. // Synopsis: Update the job flags and status depending on whether there are
  283. // valid triggers with more run times.
  284. //
  285. // Arguments: [fRunning] - optional param, defaults to false.
  286. // CSchedWorker::RunJobs sets this to true while
  287. // setting the new job status to
  288. // SCHED_S_TASK_RUNNING. So, don't change the job's
  289. // status if fRunning.
  290. //
  291. // Returns: HRESULTS
  292. //
  293. // Notes: The triggers have to be loaded.
  294. //-----------------------------------------------------------------------------
  295. HRESULT
  296. CJob::UpdateJobState(BOOL fRunning)
  297. {
  298. //TRACE(CJob, UpdateJobState);
  299. if (!IsFlagSet(JOB_I_FLAG_HAS_APPNAME))
  300. {
  301. //
  302. // The job can't run without an appname.
  303. //
  304. SetStatus(SCHED_S_TASK_NOT_SCHEDULED);
  305. return S_OK;
  306. }
  307. SYSTEMTIME stNow;
  308. GetLocalTime(&stNow);
  309. if (fRunning)
  310. {
  311. //
  312. // Increment the minute value so that the current run won't be returned.
  313. //
  314. stNow.wMinute++;
  315. if (stNow.wMinute >= JOB_MINS_PER_HOUR)
  316. {
  317. stNow.wHour++;
  318. stNow.wMinute = 0;
  319. if (stNow.wHour >= JOB_HOURS_PER_DAY)
  320. {
  321. stNow.wHour = 0;
  322. IncrementDay(&stNow);
  323. }
  324. }
  325. }
  326. WORD cRuns = 0;
  327. HRESULT hr;
  328. hr = GetRunTimesP(&stNow, NULL, &cRuns, 1, NULL, NULL);
  329. if (FAILED(hr))
  330. {
  331. ERR_OUT("CJob::UpdateJobState", hr);
  332. return hr;
  333. }
  334. //
  335. // Update the job flags and status properties.
  336. //
  337. if (hr == SCHED_S_TASK_NO_VALID_TRIGGERS)
  338. {
  339. SetFlag(JOB_I_FLAG_NO_VALID_TRIGGERS);
  340. SetStatus(SCHED_S_TASK_NOT_SCHEDULED);
  341. }
  342. else
  343. {
  344. ClearFlag(JOB_I_FLAG_NO_VALID_TRIGGERS);
  345. if (cRuns == 0 && hr != SCHED_S_EVENT_TRIGGER)
  346. {
  347. SetFlag(JOB_I_FLAG_NO_MORE_RUNS);
  348. }
  349. else
  350. {
  351. ClearFlag(JOB_I_FLAG_NO_MORE_RUNS);
  352. //
  353. // If the job isn't currently running and had not been ready to run but
  354. // now have both valid triggers and valid properties, set the status.
  355. // TODO: test TASK_FLAG_HAS_OBJPATH and TASK_FLAG_HAS_ACCOUNT when those
  356. // properties are functional.
  357. //
  358. if (!fRunning && IsFlagSet(JOB_I_FLAG_HAS_APPNAME))
  359. {
  360. if (m_stMostRecentRunTime.wYear == 0)
  361. {
  362. //
  363. // Job has never run if last-run-time property is null (all
  364. // elements will be zero if null, testing year is sufficient).
  365. //
  366. SetStatus(SCHED_S_TASK_HAS_NOT_RUN);
  367. }
  368. else
  369. {
  370. //
  371. // Job has run in the past, so it is now waiting to run again.
  372. //
  373. SetStatus(SCHED_S_TASK_READY);
  374. }
  375. }
  376. }
  377. }
  378. //
  379. // The disabled flag takes precedence over other states.
  380. //
  381. if (IsFlagSet(TASK_FLAG_DISABLED))
  382. {
  383. SetStatus(SCHED_S_TASK_DISABLED);
  384. }
  385. return S_OK;
  386. }
  387. //+----------------------------------------------------------------------------
  388. //
  389. // Member: CJob::PostRunUpdate
  390. //
  391. // Synopsis: update the status of a job object after a run exits
  392. //
  393. // Arguments: [ExitCode] - the run's exit code
  394. // [fFinishedOK] - only set the job object's exit code if TRUE.
  395. //
  396. // Returns: HRESULTS
  397. //
  398. // Notes: Set the ExitCode and Status values for the job and log the run
  399. // if the LogRunHistory property is set.
  400. // This method is not exposed to external clients, thus it is not
  401. // part of a public interface.
  402. // If any of the variable length properties or the triggers are
  403. // needed, then the caller will need to do a full activation of
  404. // the job.
  405. //
  406. //-----------------------------------------------------------------------------
  407. HRESULT
  408. CJob::PostRunUpdate(long ExitCode, BOOL fFinishedOK)
  409. {
  410. schDebugOut((DEB_ITRACE, "PostRunUpdate: decrementing running instance "
  411. "count (%d before decrement)\n", m_cRunningInstances));
  412. if (m_cRunningInstances > 0)
  413. {
  414. m_cRunningInstances--;
  415. }
  416. //
  417. // Don't update the status unless the running instance count is back to
  418. // zero.
  419. //
  420. if (m_cRunningInstances == 0)
  421. {
  422. if (IsFlagSet(JOB_I_FLAG_NO_VALID_TRIGGERS))
  423. {
  424. SetStatus(SCHED_S_TASK_NO_VALID_TRIGGERS);
  425. }
  426. else
  427. {
  428. SetStatus(SCHED_S_TASK_READY);
  429. }
  430. }
  431. if (fFinishedOK)
  432. {
  433. m_ExitCode = ExitCode;
  434. }
  435. ClearFlag(JOB_I_FLAG_ABORT_NOW);
  436. return S_OK;
  437. }
  438. //+----------------------------------------------------------------------------
  439. //
  440. // Member: CJob::IfEventJobAddToList
  441. //
  442. // Synopsis: Check if the job has any triggers of the specified event type.
  443. // If so, allocate and initialize a CRun object and add it to the
  444. // list. Add it to pIdleWaitList if it needs to wait for an
  445. // idle period, otherwise add it to pRunList.
  446. //
  447. // Arguments: [EventType] - the event trigger type
  448. // [ptszJobName] - the short job name to pass to CRun.
  449. // [pRunList] - a pointer to a run list object.
  450. // [pIdleWaitList] - a pointer to a run list object sorted by
  451. // idle wait times.
  452. //
  453. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  454. // fatal error code.
  455. //
  456. // Assumes: Triggers have already been loaded.
  457. // If EventType is not TASK_EVENT_TRIGGER_ON_IDLE, this method is
  458. // only called when the event has occurred. (If EventType is
  459. // TASK_EVENT_TRIGGER_ON_IDLE, this method is called when
  460. // building the wait list from the tasks folder's contents.)
  461. //
  462. //-----------------------------------------------------------------------------
  463. HRESULT
  464. CJob::IfEventJobAddToList(TASK_TRIGGER_TYPE EventType, LPCTSTR ptszJobName,
  465. CRunList * pRunList, CIdleRunList * pIdleWaitList)
  466. {
  467. schAssert(EventType == TASK_EVENT_TRIGGER_ON_IDLE ||
  468. EventType == TASK_EVENT_TRIGGER_AT_SYSTEMSTART ||
  469. EventType == TASK_EVENT_TRIGGER_AT_LOGON);
  470. if (! IsFlagSet(JOB_I_FLAG_HAS_TRIGGERS))
  471. {
  472. // (An optimization; the function would still work without it)
  473. return S_FALSE;
  474. }
  475. if (EventType == TASK_EVENT_TRIGGER_ON_IDLE && m_wIdleWait == 0)
  476. {
  477. //
  478. // We ignore all idle triggers if the idle wait time is 0.
  479. //
  480. return S_FALSE;
  481. }
  482. //
  483. // Will the job need to wait for an idle period before being started?
  484. //
  485. BOOL fNeedIdleWait = ((EventType == TASK_EVENT_TRIGGER_ON_IDLE ||
  486. IsFlagSet(TASK_FLAG_START_ONLY_IF_IDLE))
  487. && m_wIdleWait > 0);
  488. HRESULT hr;
  489. FILETIME ftNow = GetLocalTimeAsFileTime();
  490. // BUGBUG ftNow should be passed in, and the same ftNow should be used
  491. // when checking against the deadlines. Otherwise a run can be missed
  492. // if we're slow here and the deadline elapses.
  493. //
  494. // See if the job has any triggers of the specified type.
  495. // Find the latest deadline of these triggers.
  496. //
  497. FILETIME ftLatestDeadline = { 0, 0 };
  498. for (WORD i = 0; i < m_Triggers.GetCount(); i++)
  499. {
  500. if (m_Triggers[i].TriggerType != EventType)
  501. {
  502. continue;
  503. }
  504. //
  505. // If the trigger is an idle trigger then the trigger's deadline
  506. // is simply midnight on the trigger's end date.
  507. //
  508. // If the job does not have TASK_FLAG_START_ONLY_IF_IDLE then
  509. // the trigger's deadline is simply midnight on the trigger's
  510. // end date.
  511. //
  512. // If the job does have TASK_FLAG_START_ONLY_IF_IDLE then the
  513. // trigger's deadline is
  514. // min( trigger's end date,
  515. // job's start time + trigger's MinutesDuration)
  516. //
  517. FILETIME ftDeadline // This trigger's deadline
  518. = MAX_FILETIME; // End date if no end date set
  519. if (m_Triggers[i].rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  520. {
  521. SYSTEMTIME stEnd = // This trigger's end date
  522. {
  523. m_Triggers[i].wEndYear,
  524. m_Triggers[i].wEndMonth,
  525. 0, // wDayOfWeek
  526. m_Triggers[i].wEndDay,
  527. 23, // wHour
  528. 59, // wMinute
  529. 0, // wSecond
  530. 0 // wMilliseconds
  531. };
  532. if (!SystemTimeToFileTime(&stEnd, &ftDeadline))
  533. {
  534. // Presume the trigger had an invalid end date.
  535. // Ignore the trigger. BUGBUG return an error ?
  536. continue;
  537. }
  538. }
  539. if (EventType != TASK_EVENT_TRIGGER_ON_IDLE && fNeedIdleWait)
  540. {
  541. //
  542. // Calculate (job's start time + trigger's MinutesDuration)
  543. // This method is only called when the event that fires the
  544. // trigger has occurred, so the job's start time is now.
  545. //
  546. FILETIME ftDurationEnd = ftNow;
  547. AddMinutesToFileTime(&ftDurationEnd, m_Triggers[i].MinutesDuration);
  548. ftDeadline = minFileTime(ftDeadline, ftDurationEnd);
  549. }
  550. ftLatestDeadline = maxFileTime(ftLatestDeadline, ftDeadline);
  551. }
  552. if (CompareFileTime(&ftLatestDeadline, &ftNow) < 0)
  553. {
  554. //
  555. // All the triggers of this type have expired.
  556. //
  557. return S_FALSE;
  558. }
  559. //
  560. // Add the job to the appropriate run list.
  561. //
  562. CRun * pNewRun;
  563. if (fNeedIdleWait)
  564. {
  565. DBG_OUT("Adding idle job to list.");
  566. pNewRun = new CRun(m_dwMaxRunTime, GetUserFlags(), m_wIdleWait,
  567. ftLatestDeadline,
  568. (EventType == TASK_EVENT_TRIGGER_ON_IDLE));
  569. }
  570. else
  571. {
  572. pNewRun = new CRun(m_dwMaxRunTime, GetUserFlags(), MAX_FILETIME,
  573. FALSE);
  574. }
  575. if (pNewRun == NULL)
  576. {
  577. ERR_OUT("CJob::IfEventJobAddToList new CRun", E_OUTOFMEMORY);
  578. return E_OUTOFMEMORY;
  579. }
  580. // Complete job info object initialization.
  581. //
  582. hr = pNewRun->Initialize(ptszJobName);
  583. if (FAILED(hr))
  584. {
  585. ERR_OUT("CJob::IfEventJobAddToList, CRun->Initialize", hr);
  586. delete pNewRun;
  587. return hr;
  588. }
  589. if (fNeedIdleWait)
  590. {
  591. pIdleWaitList->AddSortedByIdleWait(pNewRun);
  592. }
  593. else
  594. {
  595. pRunList->Add(pNewRun);
  596. }
  597. return S_OK;
  598. }
  599. //+----------------------------------------------------------------------------
  600. //
  601. // Member: CJob::IfStartupJobAddToList
  602. //
  603. // Synopsis: Check if the job has any startup triggers. If so, allocate and
  604. // initialize a CRun object and add it to the list.
  605. //
  606. // Arguments: [ptszJobName] - the short job name to pass to CRun.
  607. // [pRunList] - a pointer to a run list object.
  608. //
  609. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  610. // fatal error code.
  611. //
  612. // Assumes: Triggers have already been loaded.
  613. //
  614. //-----------------------------------------------------------------------------
  615. HRESULT
  616. CJob::IfStartupJobAddToList(LPTSTR ptszJobName, CRunList * pRunList,
  617. CIdleRunList * pIdleWaitList)
  618. {
  619. return IfEventJobAddToList(TASK_EVENT_TRIGGER_AT_SYSTEMSTART, ptszJobName,
  620. pRunList, pIdleWaitList);
  621. }
  622. //+----------------------------------------------------------------------------
  623. //
  624. // Member: CJob::IfLogonJobAddToList
  625. //
  626. // Synopsis: Check if the job has any logon triggers. If so, allocate and
  627. // initialize a CRun object and add it to the list.
  628. //
  629. // Arguments: [ptszJobName] - the short job name to pass to CRun.
  630. // [pRunList] - a pointer to a run list object.
  631. //
  632. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  633. // fatal error code.
  634. //
  635. // Assumes: Triggers have already been loaded.
  636. //
  637. //-----------------------------------------------------------------------------
  638. HRESULT
  639. CJob::IfLogonJobAddToList(LPTSTR ptszJobName, CRunList * pRunList,
  640. CIdleRunList * pIdleWaitList)
  641. {
  642. return IfEventJobAddToList(TASK_EVENT_TRIGGER_AT_LOGON, ptszJobName,
  643. pRunList, pIdleWaitList);
  644. }
  645. //+----------------------------------------------------------------------------
  646. //
  647. // Member: CJob::IfIdleJobAddToList
  648. //
  649. // Synopsis: Check if the job has any idle triggers. If so, allocate and
  650. // initialize a CRun object and add it to the list.
  651. //
  652. // Arguments: [ptszJobName] - the short job name to pass to CRun.
  653. // [pIdleWaitList] - a pointer to a run list object.
  654. //
  655. // Returns: S_OK if the event trigger is found, S_FALSE if not, or a
  656. // fatal error code.
  657. //
  658. // Assumes: Triggers have already been loaded.
  659. //
  660. //-----------------------------------------------------------------------------
  661. HRESULT
  662. CJob::IfIdleJobAddToList(LPTSTR ptszJobName, CIdleRunList * pIdleWaitList)
  663. {
  664. return IfEventJobAddToList(TASK_EVENT_TRIGGER_ON_IDLE, ptszJobName,
  665. NULL, pIdleWaitList);
  666. }
  667. //+----------------------------------------------------------------------------
  668. //
  669. // Member: CJob::Delete
  670. //
  671. // Synopsis: Remove the file object for this job.
  672. //
  673. //-----------------------------------------------------------------------------
  674. HRESULT
  675. CJob::Delete(void)
  676. {
  677. #if defined(UNICODE)
  678. schDebugOut((DEB_ITRACE, "CJob:Delete on %S\n", m_ptszFileName));
  679. #else
  680. schDebugOut((DEB_ITRACE, "CJob:Delete on %s\n", m_ptszFileName));
  681. #endif
  682. if (m_ptszFileName == NULL)
  683. {
  684. return E_INVALIDARG;
  685. }
  686. if (!DeleteFile(m_ptszFileName))
  687. {
  688. schDebugOut((DEB_ITRACE, "DeleteFile failed with error %d\n",
  689. GetLastError()));
  690. return HRESULT_FROM_WIN32(GetLastError());
  691. }
  692. return S_OK;
  693. }
  694. //+----------------------------------------------------------------------------
  695. //
  696. // CJob::IUnknown methods
  697. //
  698. //-----------------------------------------------------------------------------
  699. //+----------------------------------------------------------------------------
  700. //
  701. // Member: CJob::IUnknown::QueryInterface
  702. //
  703. // Synopsis: Returns requested interface pointer
  704. //
  705. //-----------------------------------------------------------------------------
  706. STDMETHODIMP
  707. CJob::QueryInterface(REFIID riid, void ** ppvObject)
  708. {
  709. //schDebugOut((DEB_ITRACE, "CJob::QueryInterface\n"));
  710. if (IID_IUnknown == riid)
  711. {
  712. *ppvObject = (IUnknown *)(ITask *)this;
  713. }
  714. else if (IID_ITask == riid)
  715. {
  716. *ppvObject = (IUnknown *)(ITask *)this;
  717. }
  718. else if (IID_IScheduledWorkItem == riid)
  719. {
  720. *ppvObject = (IUnknown *)(IScheduledWorkItem *)this;
  721. }
  722. else if (IID_IPersist == riid)
  723. {
  724. *ppvObject = (IUnknown *)(IPersist *)this;
  725. }
  726. else if (IID_IPersistFile == riid)
  727. {
  728. *ppvObject = (IUnknown *)(IPersistFile *)this;
  729. }
  730. else if (IID_IProvideTaskPage == riid)
  731. {
  732. *ppvObject = (IUnknown *)(IProvideTaskPage *)this;
  733. }
  734. else
  735. {
  736. #if DBG == 1
  737. //WCHAR * pwsz;
  738. //StringFromIID(riid, &pwsz);
  739. //schDebugOut((DEB_NOPREFIX, "%S, refused\n", pwsz));
  740. //CoTaskMemFree(pwsz);
  741. #endif
  742. *ppvObject = NULL;
  743. return E_NOINTERFACE;
  744. }
  745. AddRef();
  746. return S_OK;
  747. }
  748. //+----------------------------------------------------------------------------
  749. //
  750. // Member: CJob::IUnknown::AddRef
  751. //
  752. // Synopsis: increments reference count
  753. //
  754. // Returns: the reference count
  755. //
  756. //-----------------------------------------------------------------------------
  757. STDMETHODIMP_(ULONG)
  758. CJob::AddRef(void)
  759. {
  760. //schDebugOut((DEB_ITRACE, "CJob::AddRef refcount going in %d\n", m_cReferences));
  761. return InterlockedIncrement((long *)&m_cReferences);
  762. }
  763. //+----------------------------------------------------------------------------
  764. //
  765. // Member: CJob::IUnknown::Release
  766. //
  767. // Synopsis: Decrements the object's reference count and frees it when
  768. // no longer referenced.
  769. //
  770. // Returns: the reference count
  771. //
  772. // Notes: BUGBUG: do we need to check the refcount on the triggers
  773. // before freeing the job object?
  774. //-----------------------------------------------------------------------------
  775. STDMETHODIMP_(ULONG)
  776. CJob::Release(void)
  777. {
  778. //schDebugOut((DEB_ITRACE, "CJob::Release ref count going in %d\n", m_cReferences));
  779. unsigned long uTmp;
  780. if ((uTmp = InterlockedDecrement((long *)&m_cReferences)) == 0)
  781. {
  782. delete this;
  783. }
  784. return uTmp;
  785. }
  786. //+----------------------------------------------------------------------------
  787. //
  788. // CJobCF - class factory for the Job object
  789. //
  790. //-----------------------------------------------------------------------------
  791. //+----------------------------------------------------------------------------
  792. //
  793. // Member: CJobCF::Create
  794. //
  795. // Synopsis: creates a new class factory object
  796. //
  797. //-----------------------------------------------------------------------------
  798. IClassFactory *
  799. CJobCF::Create(void)
  800. {
  801. //schDebugOut((DEB_ITRACE, "CJobCF::Create\n"));
  802. return new CJobCF;
  803. }
  804. //+----------------------------------------------------------------------------
  805. //
  806. // Member: CJobCF::CJobCF
  807. //
  808. // Synopsis: ctor
  809. //
  810. //-----------------------------------------------------------------------------
  811. CJobCF::CJobCF(void)
  812. {
  813. m_uRefs = 1;
  814. }
  815. //+----------------------------------------------------------------------------
  816. //
  817. // Member: CJobCF::~CJobCF
  818. //
  819. // Synopsis: dtor
  820. //
  821. //-----------------------------------------------------------------------------
  822. CJobCF::~CJobCF(void)
  823. {
  824. //schDebugOut((DEB_ITRACE, "~CJobCF: DLL ref count going in %d\n",
  825. // g_cDllRefs));
  826. }
  827. //+----------------------------------------------------------------------------
  828. //
  829. // Member: CJobCF::IUnknown::QueryInterface
  830. //
  831. // Synopsis: Returns requested interface pointer
  832. //
  833. //-----------------------------------------------------------------------------
  834. STDMETHODIMP
  835. CJobCF::QueryInterface(REFIID riid, void ** ppvObject)
  836. {
  837. //schDebugOut((DEB_ITRACE, "CJobCF::QueryInterface"));
  838. if (IID_IUnknown == riid)
  839. {
  840. *ppvObject = (IUnknown *)this;
  841. }
  842. else if (IsEqualIID(IID_IClassFactory, riid))
  843. {
  844. *ppvObject = (IClassFactory *)this;
  845. }
  846. else
  847. {
  848. #if DBG == 1
  849. //WCHAR * pwsz;
  850. //StringFromIID(riid, &pwsz);
  851. //schDebugOut((DEB_NOPREFIX, "%S, refused\n", pwsz));
  852. //CoTaskMemFree(pwsz);
  853. #endif
  854. *ppvObject = NULL;
  855. return E_NOINTERFACE;
  856. }
  857. AddRef();
  858. return S_OK;
  859. }
  860. //+----------------------------------------------------------------------------
  861. //
  862. // Member: CJobCF::IUnknown::AddRef
  863. //
  864. // Synopsis: increments reference count
  865. //
  866. // Returns: the reference count
  867. //
  868. //-----------------------------------------------------------------------------
  869. STDMETHODIMP_(ULONG)
  870. CJobCF::AddRef(void)
  871. {
  872. //schDebugOut((DEB_ITRACE, "CJobCF::AddRef\n"));
  873. return InterlockedIncrement((long *)&m_uRefs);
  874. }
  875. //+----------------------------------------------------------------------------
  876. //
  877. // Member: CJobCF::IUnknown::Release
  878. //
  879. // Synopsis: Decrements the object's reference count and frees it when
  880. // no longer referenced.
  881. //
  882. // Returns: the reference count
  883. //
  884. //-----------------------------------------------------------------------------
  885. STDMETHODIMP_(ULONG)
  886. CJobCF::Release(void)
  887. {
  888. //schDebugOut((DEB_ITRACE, "CJobCF::Release ref count going in %d\n",
  889. // m_uRefs));
  890. unsigned long uTmp;
  891. if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0)
  892. {
  893. delete this;
  894. }
  895. return uTmp;
  896. }
  897. //+----------------------------------------------------------------------------
  898. //
  899. // Member: CJobCF::IClassFactory::CreateInstance
  900. //
  901. // Synopsis: create an incore instance of the job class object
  902. //
  903. // Arguments: [pUnkOuter] - aggregator
  904. // [riid] - requested interface
  905. // [ppvObject] - receptor for itf ptr
  906. //
  907. // Returns: HRESULTS
  908. //
  909. //-----------------------------------------------------------------------------
  910. STDMETHODIMP
  911. CJobCF::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
  912. {
  913. //schDebugOut((DEB_ITRACE, "CJobCF::CreateInstance\n"));
  914. SCODE sc = S_OK;
  915. ITask * pJob = CJob::Create();
  916. if (pJob == NULL)
  917. {
  918. *ppvObject = NULL;
  919. return E_OUTOFMEMORY;
  920. }
  921. sc = pJob->QueryInterface(riid, ppvObject);
  922. if (FAILED(sc))
  923. {
  924. *ppvObject = NULL;
  925. return sc;
  926. }
  927. // we got a refcount of one when launched, and the above QI increments it
  928. // to 2, so call release to take it back to 1
  929. pJob->Release();
  930. return sc;
  931. }
  932. //+----------------------------------------------------------------------------
  933. //
  934. // Member: CJobCF::IClassFactory::LockServer
  935. //
  936. // Synopsis: Called with fLock set to TRUE to indicate that the server
  937. // should continue to run even if none of its objects are active
  938. //
  939. // Arguments: [fLock] - increment/decrement the instance count
  940. //
  941. // Returns: HRESULTS
  942. //
  943. // Notes: This function is a no-op since the server is in-proc.
  944. //
  945. //-----------------------------------------------------------------------------
  946. STDMETHODIMP
  947. CJobCF::LockServer(BOOL fLock)
  948. {
  949. return S_OK;
  950. }
  951. //+----------------------------------------------------------------------------
  952. //
  953. // CTrigger::IUnknown methods
  954. //
  955. // Notes: A trigger is not a first class COM object. They do not exist as
  956. // separate entities outside of Job objects; you cannot do a
  957. // CoCreateInstance on one and they do not go away when their ref
  958. // count goes to zero. When a job object is instanciated, its triggers
  959. // are also instanciated and they are freed from memory when the job
  960. // object is freed from memory. Trigger ref counting is used only to
  961. // prevent a client from deleting a trigger while it is holding a
  962. // pointer to that trigger.
  963. //
  964. // Note also that the containing job object is AddRef'd and Release'd
  965. // along with each trigger so that the job object will not be deleted
  966. // while clients are holding trigger pointers.
  967. //
  968. //-----------------------------------------------------------------------------
  969. //+----------------------------------------------------------------------------
  970. //
  971. // Member: CTrigger::IUnknown::QueryInterface
  972. //
  973. // Synopsis: Returns requested interface pointer
  974. //
  975. //-----------------------------------------------------------------------------
  976. STDMETHODIMP
  977. CTrigger::QueryInterface(REFIID riid, void ** ppvObject)
  978. {
  979. if (IID_IUnknown == riid)
  980. {
  981. *ppvObject = (IUnknown *)(ITaskTrigger *)this;
  982. }
  983. else
  984. if (IID_ITaskTrigger == riid)
  985. {
  986. *ppvObject = (IUnknown *)(ITaskTrigger *)this;
  987. }
  988. else
  989. {
  990. *ppvObject = NULL;
  991. return E_NOINTERFACE;
  992. }
  993. AddRef();
  994. return S_OK;
  995. }
  996. //+----------------------------------------------------------------------------
  997. //
  998. // Member: CTrigger::IUnknown::AddRef
  999. //
  1000. // Synopsis: increments reference count
  1001. //
  1002. // Returns: the reference count
  1003. //
  1004. //-----------------------------------------------------------------------------
  1005. STDMETHODIMP_(ULONG)
  1006. CTrigger::AddRef(void)
  1007. {
  1008. return InterlockedIncrement((long *)&m_cReferences);
  1009. }
  1010. //+----------------------------------------------------------------------------
  1011. //
  1012. // Member: CTrigger::IUnknown::Release
  1013. //
  1014. // Synopsis: Decrements the object's reference count.
  1015. //
  1016. // Returns: the reference count
  1017. //
  1018. //-----------------------------------------------------------------------------
  1019. STDMETHODIMP_(ULONG)
  1020. CTrigger::Release(void)
  1021. {
  1022. unsigned long uTmp;
  1023. if ((uTmp = InterlockedDecrement((long *)&m_cReferences)) == 0)
  1024. {
  1025. delete this;
  1026. }
  1027. return uTmp;
  1028. }
  1029. //+--------------------------------------------------------------------------
  1030. //
  1031. // Function: IsValidMonthlyDateTrigger
  1032. //
  1033. // Synopsis: Return TRUE if [pTrigger] has a valid combination of month
  1034. // and day bits set.
  1035. //
  1036. // History: 10-07-1997 DavidMun Created
  1037. //
  1038. //---------------------------------------------------------------------------
  1039. BOOL
  1040. IsValidMonthlyDateTrigger(
  1041. PTASK_TRIGGER pTrigger)
  1042. {
  1043. if (pTrigger->Type.MonthlyDate.rgfDays > JOB_RGFDAYS_MAX ||
  1044. pTrigger->Type.MonthlyDate.rgfMonths == 0 ||
  1045. pTrigger->Type.MonthlyDate.rgfMonths > JOB_RGFMONTHS_MAX)
  1046. {
  1047. return FALSE;
  1048. }
  1049. if (pTrigger->Type.MonthlyDate.rgfDays == 0)
  1050. {
  1051. //
  1052. // rgfDays must be non-zero.
  1053. //
  1054. return FALSE;
  1055. }
  1056. //
  1057. // More detailed testing to see if non-existent dates have been
  1058. // specified, for example: Feb. 30th, without specifying valid dates.
  1059. // That is, it is OK to specify invalid dates as long as there are
  1060. // valid dates. E.g., someone may want to specify: run on the 25th
  1061. // through 31st of every month. While that is acceptable, saying run
  1062. // only on Feb 31st is invalid.
  1063. //
  1064. if (pTrigger->Type.MonthlyDate.rgfDays & 0x40000000 &&
  1065. pTrigger->Type.MonthlyDate.rgfMonths & (TASK_FEBRUARY | TASK_APRIL |
  1066. TASK_JUNE | TASK_SEPTEMBER |
  1067. TASK_NOVEMBER) &&
  1068. !(pTrigger->Type.MonthlyDate.rgfMonths & ~(TASK_FEBRUARY |
  1069. TASK_APRIL |
  1070. TASK_JUNE |
  1071. TASK_SEPTEMBER |
  1072. TASK_NOVEMBER)))
  1073. {
  1074. //
  1075. // None of these months have a 31st day.
  1076. //
  1077. return FALSE;
  1078. }
  1079. if (pTrigger->Type.MonthlyDate.rgfDays & 0x20000000 &&
  1080. pTrigger->Type.MonthlyDate.rgfMonths & TASK_FEBRUARY &&
  1081. !(pTrigger->Type.MonthlyDate.rgfMonths & ~TASK_FEBRUARY))
  1082. {
  1083. //
  1084. // February does not have a 30th day. Allow for the specification
  1085. // of the 29th to run on leap year.
  1086. //
  1087. return FALSE;
  1088. }
  1089. return TRUE;
  1090. }