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.

1436 lines
39 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Scheduler Job Object Handler
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: job.cxx
  9. //
  10. // Contents: ITask interface methods
  11. //
  12. // Classes: CJob
  13. //
  14. // Interfaces: ITask
  15. //
  16. // History: 23-May-95 EricB created
  17. //
  18. //-----------------------------------------------------------------------------
  19. #include "..\pch\headers.hxx"
  20. #pragma hdrstop
  21. #include "job.hxx"
  22. #include <StrSafe.h>
  23. WCHAR wszEmpty[] = L"";
  24. //+----------------------------------------------------------------------------
  25. //
  26. // Member: CJob::ITask::Run
  27. //
  28. // Synopsis: Run the job now. Sets a service bit on the job file. The
  29. // running service will notice this bit being set and will run
  30. // this job.
  31. //
  32. // Returns: HRESULTS
  33. //
  34. //-----------------------------------------------------------------------------
  35. STDMETHODIMP
  36. CJob::Run(void)
  37. {
  38. TRACE(CJob, Run)
  39. if (!IsFlagSet(JOB_I_FLAG_HAS_APPNAME))
  40. {
  41. return SCHED_E_TASK_NOT_READY;
  42. }
  43. //
  44. // Set the magic bit.
  45. //
  46. SetFlag(JOB_I_FLAG_RUN_NOW);
  47. //
  48. // Save the service flags to disk so that the change can be noted by the
  49. // service. Preserve the net schedule flag, thus allowing a user to force
  50. // a run of an AT job without clearing the AT bit.
  51. //
  52. return SaveWithRetry(NULL, FALSE, SAVEP_PRESERVE_NET_SCHEDULE);
  53. }
  54. //+----------------------------------------------------------------------------
  55. //
  56. // Member: CJob::ITask::Terminate
  57. //
  58. // Synopsis: Abort this job, if it is running. Do so by setting an abort
  59. // flag on the job object. The scheduler service local to the
  60. // job object will detect this change and abort the job.
  61. //
  62. // Arguments: None.
  63. //
  64. // Returns: HRESULTS
  65. //
  66. // Notes: None.
  67. //
  68. //-----------------------------------------------------------------------------
  69. STDMETHODIMP
  70. CJob::Terminate(void)
  71. {
  72. TRACE(CJob, Terminate)
  73. if (m_cRunningInstances > 0)
  74. {
  75. //
  76. // Set the abort status bit and rewrite the state. This will instruct
  77. // the service to process the job, detect the abort bit flag, and
  78. // abort the job. As with the Run method, this doesn't zap the AT
  79. // flag.
  80. //
  81. SetFlag(JOB_I_FLAG_ABORT_NOW);
  82. return SaveWithRetry(NULL, FALSE, SAVEP_PRESERVE_NET_SCHEDULE);
  83. }
  84. else
  85. {
  86. return(SCHED_E_TASK_NOT_RUNNING);
  87. }
  88. }
  89. //+----------------------------------------------------------------------------
  90. //
  91. // Member: CJob::ITask::CreateTrigger
  92. //
  93. // Synopsis: Create a new trigger, add it to the job object, and return a
  94. // pointer to it.
  95. //
  96. // Arguments: [piNewTrigger] - the index of the new trigger, optional
  97. // [ppTrigger] - a pointer to the new trigger
  98. //
  99. // Returns: HRESULTS
  100. //
  101. // Notes: The trigger is AddRef'd in CreateTriggerP
  102. //-----------------------------------------------------------------------------
  103. STDMETHODIMP
  104. CJob::CreateTrigger(WORD * piNewTrigger, ITaskTrigger ** ppTrigger)
  105. {
  106. TRACE3(CJob, CreateTrigger)
  107. *ppTrigger = NULL; // Init in case of error.
  108. SYSTEMTIME st;
  109. GetLocalTime(&st);
  110. TASK_TRIGGER jt = {
  111. sizeof(TASK_TRIGGER), // Trigger size.
  112. m_Triggers.GetCount(), // Reserved (trigger index).
  113. st.wYear, // Beginning year.
  114. st.wMonth, // Beginning month.
  115. st.wDay, // Beginning day.
  116. 0, // Ending year.
  117. 0, // Ending month.
  118. 0, // Ending day.
  119. st.wHour, // Starting hour.
  120. st.wMinute, // Starting minute.
  121. 0, // Minutes duration.
  122. 0, // Minutes interval.
  123. JOB_TRIGGER_I_FLAG_NOT_SET, // Flags.
  124. TASK_TIME_TRIGGER_DAILY, // Trigger type.
  125. 1, // Trigger union.
  126. 0, // Reserved2. Unused.
  127. 0 // Random minutes interval.
  128. };
  129. CTrigger * pt = new CTrigger(jt.Reserved1, this);
  130. if (pt == NULL)
  131. {
  132. CHECK_HRESULT(E_OUTOFMEMORY);
  133. return(E_OUTOFMEMORY);
  134. }
  135. HRESULT hr = m_Triggers.Add(jt);
  136. if (FAILED(hr))
  137. {
  138. delete pt;
  139. CHECK_HRESULT(hr);
  140. return(hr);
  141. }
  142. this->SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  143. this->SetTriggersDirty();
  144. *piNewTrigger = jt.Reserved1;
  145. *ppTrigger = pt;
  146. return(hr);
  147. }
  148. //+----------------------------------------------------------------------------
  149. //
  150. // Member: CJob::ITask::DeleteTrigger
  151. //
  152. // Synopsis: Remove a run trigger.
  153. //
  154. // Arguments: [iTrigger] - the index of the trigger to be removed
  155. //
  156. // Returns: HRESULTS
  157. //
  158. //-----------------------------------------------------------------------------
  159. STDMETHODIMP
  160. CJob::DeleteTrigger(WORD iTrigger)
  161. {
  162. TRACE3(CJob, DeleteTrigger)
  163. TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
  164. if (pjt != NULL)
  165. {
  166. m_Triggers.Remove(iTrigger);
  167. // Fixup remaining indices to account for deletion.
  168. //
  169. for (WORD i = iTrigger; i < m_Triggers.GetCount(); i++)
  170. {
  171. m_Triggers[i].Reserved1--;
  172. }
  173. this->SetTriggersDirty();
  174. if (!m_Triggers.GetCount())
  175. {
  176. this->ClearFlag(JOB_I_FLAG_HAS_TRIGGERS);
  177. }
  178. return(S_OK);
  179. }
  180. else
  181. {
  182. return(SCHED_E_TRIGGER_NOT_FOUND);
  183. }
  184. }
  185. //+----------------------------------------------------------------------------
  186. //
  187. // Member: CJob::_GetTrigger, private
  188. //
  189. // Synopsis: Return the TASK_TRIGGER associated with the index. The
  190. // TASK_TRIGGER reserved field specifies the trigger index.
  191. //
  192. // Arguments: [iTrigger] -- Trigger index.
  193. //
  194. // Returns: TASK_TRIGGER * -- Trigger found.
  195. // NULL -- Trigger not found.
  196. //
  197. //-----------------------------------------------------------------------------
  198. TASK_TRIGGER *
  199. CJob::_GetTrigger(WORD iTrigger)
  200. {
  201. for (WORD i = 0; i < m_Triggers.GetCount(); i++)
  202. {
  203. if (m_Triggers[i].Reserved1 == iTrigger)
  204. {
  205. return(&m_Triggers[i]);
  206. }
  207. }
  208. return(NULL);
  209. }
  210. //+----------------------------------------------------------------------------
  211. //
  212. // Member: CJob::ITask::GetTriggerCount
  213. //
  214. // Synopsis: Return the count of run triggers.
  215. //
  216. // Arguments: [pwCount] - the address where the count is to be returned
  217. //
  218. // Returns: HRESULTS
  219. //
  220. //-----------------------------------------------------------------------------
  221. STDMETHODIMP
  222. CJob::GetTriggerCount(WORD * pwCount)
  223. {
  224. TRACE3(CJob, GetTriggerCount)
  225. *pwCount = m_Triggers.GetCount();
  226. return(S_OK);
  227. }
  228. //+----------------------------------------------------------------------------
  229. //
  230. // Member: CJob::ITask::GetTrigger
  231. //
  232. // Synopsis: Return an ITaskTrigger pointer to the indicated trigger.
  233. //
  234. // Arguments: [iTrigger] - the index of the trigger to fetch
  235. // [ppTrigger] - the returned trigger pointer
  236. //
  237. // Returns: HRESULTS
  238. //
  239. // Notes: The trigger is AddRef'd in GetTriggerObj(m_Triggers.GetTrigger)
  240. //-----------------------------------------------------------------------------
  241. STDMETHODIMP
  242. CJob::GetTrigger(WORD iTrigger, ITaskTrigger ** ppTrigger)
  243. {
  244. TRACE3(CJob, GetTrigger)
  245. *ppTrigger = NULL; // Init in case of error.
  246. TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
  247. if (pjt != NULL)
  248. {
  249. CTrigger * pt = new CTrigger(iTrigger, this);
  250. if (pt == NULL)
  251. {
  252. CHECK_HRESULT(E_OUTOFMEMORY);
  253. return(E_OUTOFMEMORY);
  254. }
  255. *ppTrigger = pt;
  256. return(S_OK);
  257. }
  258. return(SCHED_E_TRIGGER_NOT_FOUND);
  259. }
  260. //+----------------------------------------------------------------------------
  261. //
  262. // Member: CJob::ITask::GetTriggerString
  263. //
  264. // Synopsis: Return the indicated run trigger as a string.
  265. //
  266. // Arguments: [iTrigger] - the index of the trigger to convert
  267. // [ppwszTrigger] - the returned string buffer
  268. //
  269. // Returns: HRESULTS
  270. //
  271. // Notes: The string is callee allocated and caller freed with
  272. // CoTaskMemFree.
  273. //-----------------------------------------------------------------------------
  274. STDMETHODIMP
  275. CJob::GetTriggerString(WORD iTrigger, LPWSTR * ppwszTrigger)
  276. {
  277. TRACE3(CJob, GetTriggerString)
  278. *ppwszTrigger = NULL; // Init in case of error.
  279. TASK_TRIGGER * pjt = this->_GetTrigger(iTrigger);
  280. if (pjt != NULL)
  281. {
  282. return(::StringFromTrigger(pjt, ppwszTrigger, NULL));
  283. }
  284. return(SCHED_E_TRIGGER_NOT_FOUND);
  285. }
  286. //+----------------------------------------------------------------------------
  287. //
  288. // Member: CJob::ITask::SetApplicationName
  289. //
  290. // Synopsis: Set the Application Name String property
  291. //
  292. // Arguments: [pwszApplicationName] - the name of the app to execute.
  293. //
  294. // Returns: HRESULTS
  295. //
  296. // Notes: The string is caller allocated and freed.
  297. //
  298. //-----------------------------------------------------------------------------
  299. STDMETHODIMP
  300. CJob::SetApplicationName(LPCWSTR pwszApplicationName)
  301. {
  302. TRACE3(CJob, SetApplicationName)
  303. if (!pwszApplicationName)
  304. return E_INVALIDARG;
  305. if (wcslen(pwszApplicationName) > MAX_PATH)
  306. return E_INVALIDARG;
  307. //
  308. // We don't use a try/catch
  309. // because the COM interface runs in the caller's context and it is their
  310. // responsibility to ensure good params. This latter statement is true for
  311. // all scheduler COM interface methods.
  312. //
  313. if (*pwszApplicationName == L'\0')
  314. {
  315. //
  316. // The caller wants the command set to an empty string.
  317. //
  318. ClearFlag(JOB_I_FLAG_HAS_APPNAME);
  319. //
  320. // We are using a null pointer for an empty string as an optimization.
  321. //
  322. if (m_pwszApplicationName == NULL)
  323. {
  324. //
  325. // Nothing to do.
  326. //
  327. return S_OK;
  328. }
  329. else
  330. {
  331. //
  332. // Setting this flag will instruct the persist code to
  333. // regenerate a GUID for this job. This is done for security
  334. // reasons.
  335. //
  336. // NB : This must be done for Win95 as well as NT.
  337. //
  338. SetFlag(JOB_I_FLAG_APPNAME_CHANGE);
  339. DELETE_CJOB_FIELD(m_pwszApplicationName);
  340. //
  341. // We want this change to trigger a wait list rebuild.
  342. //
  343. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  344. }
  345. }
  346. else
  347. {
  348. //
  349. // Update the flags and status.
  350. //
  351. SetFlag(JOB_I_FLAG_HAS_APPNAME);
  352. if (IsStatus(SCHED_S_TASK_NOT_SCHEDULED))
  353. {
  354. //
  355. // Note that if the status went from SCHED_S_TASK_NOT_SCHEDULED to
  356. // SCHED_S_TASK_HAS_NOT_RUN or SCHED_S_TASK_READY, then we
  357. // want CheckDir to issue a wait list rebuild because the job has
  358. // gone from a non-runable to a runable state. Thus, in the if
  359. // clause below we set JOB_I_FLAG_RUN_PROP_CHANGE.
  360. //
  361. if (IsFlagSet(JOB_I_FLAG_HAS_TRIGGERS) &&
  362. !IsFlagSet(JOB_I_FLAG_NO_VALID_TRIGGERS))
  363. {
  364. if (m_stMostRecentRunTime.wYear == 0)
  365. {
  366. //
  367. // Job has never run if last-run-time property is null
  368. // (all elements will be zero if null, testing year is
  369. // sufficient).
  370. //
  371. SetStatus(SCHED_S_TASK_HAS_NOT_RUN);
  372. }
  373. else
  374. {
  375. //
  376. // Job has run in the past, so it is now waiting to run
  377. // again.
  378. //
  379. SetStatus(SCHED_S_TASK_READY);
  380. }
  381. //
  382. // We want this change to trigger a wait list rebuild.
  383. //
  384. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  385. }
  386. }
  387. TCHAR tszAppName[MAX_PATH+1]; // define buffer size as 1 more than max in
  388. SecureZeroMemory(tszAppName, sizeof tszAppName); // order to leave space for NULL terminator
  389. if (IsLocalFilename(m_ptszFileName))
  390. {
  391. lstrcpyn(tszAppName, pwszApplicationName, MAX_PATH); // lstrcpyn needs to know maximum # of chars,
  392. // this needs to be 1 less than buffer size
  393. ProcessApplicationName(tszAppName, MAX_PATH+1,
  394. m_pwszWorkingDirectory ?
  395. m_pwszWorkingDirectory :
  396. wszEmpty);
  397. }
  398. else
  399. {
  400. lstrcpyn(tszAppName, pwszApplicationName, MAX_PATH); // # of chars, not buffer size
  401. StripLeadTrailSpace(tszAppName);
  402. DeleteQuotes(tszAppName);
  403. }
  404. //
  405. // Allocate a new string, adding one for the null terminator
  406. //
  407. ULONG cchAppName = lstrlen(tszAppName) + 1;
  408. LPWSTR pwszTmp = new WCHAR[cchAppName];
  409. if (pwszTmp == NULL)
  410. {
  411. ERR_OUT("CJob::SetApplicationName", E_OUTOFMEMORY);
  412. return E_OUTOFMEMORY;
  413. }
  414. StringCchCopy(pwszTmp, cchAppName, tszAppName);
  415. //
  416. // Setting this flag will instruct the persist code to
  417. // regenerate a GUID for this job. This is done for security
  418. // reasons.
  419. //
  420. // NB : This must be done for Win95 as well as NT.
  421. //
  422. // Ensure first, that the application has indeed changed.
  423. //
  424. if (m_pwszApplicationName != NULL && lstrcmpiW(
  425. m_pwszApplicationName,
  426. pwszTmp) != 0)
  427. {
  428. SetFlag(JOB_I_FLAG_APPNAME_CHANGE);
  429. }
  430. DELETE_CJOB_FIELD(m_pwszApplicationName);
  431. m_pwszApplicationName = pwszTmp;
  432. }
  433. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  434. return S_OK;
  435. }
  436. //+----------------------------------------------------------------------------
  437. //
  438. // Member: CJob::ITask::GetApplicationName
  439. //
  440. // Synopsis: Get the ApplicationName String property
  441. //
  442. // Arguments: [ppwszApplicationName] - the returned string buffer
  443. //
  444. // Returns: HRESULTS
  445. //
  446. // Notes: The command string is passed to CreateProcess to be executed
  447. // at task run time.
  448. // The string is callee allocated and caller freed with
  449. // CoTaskMemFree.
  450. //
  451. //-----------------------------------------------------------------------------
  452. STDMETHODIMP
  453. CJob::GetApplicationName(LPWSTR * ppwszApplicationName)
  454. {
  455. TRACE3(CJob, GetApplicationName)
  456. WCHAR * pwszCmd;
  457. if (m_pwszApplicationName == NULL)
  458. {
  459. //
  460. // Return an empty string rather than a null pointer
  461. //
  462. pwszCmd = wszEmpty;
  463. }
  464. else
  465. {
  466. pwszCmd = m_pwszApplicationName;
  467. }
  468. // add one for the null.
  469. *ppwszApplicationName = (LPWSTR)CoTaskMemAlloc((wcslen(pwszCmd) + 1) *
  470. sizeof(WCHAR));
  471. if (*ppwszApplicationName == NULL)
  472. {
  473. return E_OUTOFMEMORY;
  474. }
  475. StringCchCopy(*ppwszApplicationName, wcslen(pwszCmd) + 1, pwszCmd);
  476. return S_OK;
  477. }
  478. //+----------------------------------------------------------------------------
  479. //
  480. // Member: CJob::ITask::SetParameters
  481. //
  482. // Synopsis: Set the Parameters String property
  483. //
  484. // Arguments: [pwszParameters] - the application parameters string
  485. //
  486. // Returns: HRESULTS
  487. //
  488. // Notes: The Parameters string is appended to the Application Name and
  489. // passed to CreateProcess as the command line to be executed at
  490. // task run time.
  491. // The string is caller allocated and freed.
  492. //-----------------------------------------------------------------------------
  493. STDMETHODIMP
  494. CJob::SetParameters(LPCWSTR pwszParameters)
  495. {
  496. TRACE3(CJob, SetParameters)
  497. if (*pwszParameters == L'\0')
  498. {
  499. //
  500. // The caller wants the Parameters set to an empty string.
  501. //
  502. // We are using a null pointer for an empty string as an optimization.
  503. //
  504. DELETE_CJOB_FIELD(m_pwszParameters);
  505. }
  506. else
  507. {
  508. //
  509. // Allocate a new string, adding one for the null terminator
  510. //
  511. LPWSTR pwszTmp = new WCHAR[wcslen(pwszParameters) + 1];
  512. if (pwszTmp == NULL)
  513. {
  514. ERR_OUT("CJob::SetParameters", E_OUTOFMEMORY);
  515. return E_OUTOFMEMORY;
  516. }
  517. StringCchCopy(pwszTmp, wcslen(pwszParameters) + 1, pwszParameters);
  518. DELETE_CJOB_FIELD(m_pwszParameters);
  519. m_pwszParameters = pwszTmp;
  520. }
  521. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  522. return S_OK;
  523. }
  524. //+----------------------------------------------------------------------------
  525. //
  526. // Member: CJob::ITask::GetParameters
  527. //
  528. // Synopsis: Get the Parameters String property
  529. //
  530. // Arguments: [ppwszParameters] - the returned string buffer
  531. //
  532. // Returns: HRESULTS
  533. //
  534. // Notes: The Parameters string is appended to the Application Name and
  535. // passed to CreateProcess as the command line to be executed at
  536. // task run time.
  537. // The string is callee allocated and caller freed with
  538. // CoTaskMemFree.
  539. //-----------------------------------------------------------------------------
  540. STDMETHODIMP
  541. CJob::GetParameters(LPWSTR * ppwszParameters)
  542. {
  543. TRACE3(CJob, GetParameters)
  544. WCHAR * pwszTmp;
  545. if (m_pwszParameters == NULL)
  546. {
  547. //
  548. // Return an empty string rather than a null pointer
  549. //
  550. pwszTmp = wszEmpty;
  551. }
  552. else
  553. {
  554. pwszTmp = m_pwszParameters;
  555. }
  556. // add one for the null.
  557. *ppwszParameters = (LPWSTR)CoTaskMemAlloc((wcslen(pwszTmp) + 1) *
  558. sizeof(WCHAR));
  559. if (*ppwszParameters == NULL)
  560. {
  561. return E_OUTOFMEMORY;
  562. }
  563. StringCchCopy(*ppwszParameters,wcslen(pwszTmp) + 1, pwszTmp);
  564. return S_OK;
  565. }
  566. //+----------------------------------------------------------------------------
  567. //
  568. // Member: CJob::ITask::SetWorkingDirectory
  569. //
  570. // Synopsis: Set the Working Directory (current directory) property
  571. //
  572. // Arguments: [pwszWorkingDir] - the name to use
  573. //
  574. // Returns: HRESULTS
  575. //
  576. // Notes: The string is caller allocated and freed
  577. //-----------------------------------------------------------------------------
  578. STDMETHODIMP
  579. CJob::SetWorkingDirectory(LPCWSTR pwszWorkingDirectory)
  580. {
  581. TRACE3(CJob, SetWorkingDirectory)
  582. if (*pwszWorkingDirectory == L'\0')
  583. {
  584. //
  585. // The caller wants the WorkingDirectory set to an empty string.
  586. //
  587. DELETE_CJOB_FIELD(m_pwszWorkingDirectory);
  588. }
  589. else
  590. {
  591. //
  592. // Allocate a new string, adding one for the null terminator
  593. //
  594. LPWSTR pwszTmp = new WCHAR[wcslen(pwszWorkingDirectory) + 1];
  595. if (pwszTmp == NULL)
  596. {
  597. ERR_OUT("CJob::SetWorkingDirectory", E_OUTOFMEMORY);
  598. return E_OUTOFMEMORY;
  599. }
  600. StringCchCopy(pwszTmp, wcslen(pwszWorkingDirectory) + 1, pwszWorkingDirectory);
  601. DELETE_CJOB_FIELD(m_pwszWorkingDirectory);
  602. m_pwszWorkingDirectory = pwszTmp;
  603. //
  604. // Remove double quotes from working directory path; they're not supported
  605. // by SetCurrentDirectory.
  606. //
  607. DeleteQuotes(m_pwszWorkingDirectory);
  608. }
  609. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  610. return S_OK;
  611. }
  612. //+----------------------------------------------------------------------------
  613. //
  614. // Member: CJob::ITask::GetWorkingDir
  615. //
  616. // Synopsis: Get the Working Directory (current directory) property
  617. //
  618. // Arguments: [ppwszWorkingDirectory] - the returned string buffer
  619. //
  620. // Returns: HRESULTS
  621. //
  622. // Notes: The string is callee allocated and caller freed with
  623. // CoTaskMemFree.
  624. //-----------------------------------------------------------------------------
  625. STDMETHODIMP
  626. CJob::GetWorkingDirectory(LPWSTR * ppwszWorkingDirectory)
  627. {
  628. TRACE3(CJob, GetWorkingDirectory)
  629. WCHAR * pwszWorkingDir;
  630. if (m_pwszWorkingDirectory == NULL)
  631. {
  632. //
  633. // Return an empty string rather than a null pointer
  634. //
  635. pwszWorkingDir = wszEmpty;
  636. }
  637. else
  638. {
  639. pwszWorkingDir = m_pwszWorkingDirectory;
  640. }
  641. // add one for the null.
  642. *ppwszWorkingDirectory = (LPWSTR)CoTaskMemAlloc(
  643. (wcslen(pwszWorkingDir) + 1) * sizeof(WCHAR));
  644. if (*ppwszWorkingDirectory == NULL)
  645. {
  646. return E_OUTOFMEMORY;
  647. }
  648. StringCchCopy(*ppwszWorkingDirectory,wcslen(pwszWorkingDir) + 1, pwszWorkingDir);
  649. return S_OK;
  650. }
  651. //+----------------------------------------------------------------------------
  652. //
  653. // Member: CJob::ITask::SetComment
  654. //
  655. // Synopsis: Set the comment field.
  656. //
  657. // Arguments: [pwszComment] - the comment string value, caller alloc'd and
  658. // freed
  659. //
  660. // Returns: HRESULTS
  661. //
  662. //-----------------------------------------------------------------------------
  663. STDMETHODIMP
  664. CJob::SetComment(LPCWSTR pwszComment)
  665. {
  666. TRACE3(CJob, SetComment)
  667. if (*pwszComment == L'\0')
  668. {
  669. //
  670. // The caller wants the Comment set to an empty string.
  671. //
  672. DELETE_CJOB_FIELD(m_pwszComment);
  673. }
  674. else
  675. {
  676. //
  677. // Allocate a new string, adding one for the null terminator
  678. //
  679. LPWSTR pwszTmp = new WCHAR[wcslen(pwszComment) + 1];
  680. if (pwszTmp == NULL)
  681. {
  682. ERR_OUT("CJob::SetComment", E_OUTOFMEMORY);
  683. return E_OUTOFMEMORY;
  684. }
  685. StringCchCopy(pwszTmp, wcslen(pwszComment) + 1, pwszComment);
  686. DELETE_CJOB_FIELD(m_pwszComment);
  687. m_pwszComment = pwszTmp;
  688. }
  689. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  690. return S_OK;
  691. }
  692. //+----------------------------------------------------------------------------
  693. //
  694. // Member: CJob::ITask::GetComment
  695. //
  696. // Synopsis: Get the comment field property.
  697. //
  698. // Arguments: [ppwszComment] - the returned string buffer
  699. //
  700. // Returns: HRESULTS
  701. //
  702. // Notes: The string is callee allocated and caller freed with
  703. // CoTaskMemFree.
  704. //-----------------------------------------------------------------------------
  705. STDMETHODIMP
  706. CJob::GetComment(LPWSTR * ppwszComment)
  707. {
  708. TRACE3(CJob, GetComment)
  709. WCHAR * pwszCmt;
  710. if (m_pwszComment == NULL)
  711. {
  712. //
  713. // Return an empty string rather than a null pointer
  714. //
  715. pwszCmt = wszEmpty;
  716. }
  717. else
  718. {
  719. pwszCmt = m_pwszComment;
  720. }
  721. // add one for the null.
  722. *ppwszComment = (LPWSTR)CoTaskMemAlloc((wcslen(pwszCmt) + 1) *
  723. sizeof(WCHAR));
  724. if (*ppwszComment == NULL)
  725. {
  726. return E_OUTOFMEMORY;
  727. }
  728. StringCchCopy(*ppwszComment, wcslen(pwszCmt) + 1, pwszCmt);
  729. return S_OK;
  730. }
  731. //+----------------------------------------------------------------------------
  732. //
  733. // Member: CJob::_SetSignature
  734. //
  735. // Synopsis: Set the job's signature.
  736. //
  737. // Arguments: [pbSignature] - assumed to be SIGNATURE_SIZE bytes long.
  738. //
  739. // Returns: HRESULTS
  740. //
  741. //-----------------------------------------------------------------------------
  742. HRESULT
  743. CJob::_SetSignature(const BYTE * pbSignature)
  744. {
  745. TRACE3(CJob, SetSignature)
  746. LPBYTE pb = new BYTE[SIGNATURE_SIZE];
  747. if (pb == NULL)
  748. {
  749. ERR_OUT("CJob::SetSignature", E_OUTOFMEMORY);
  750. return E_OUTOFMEMORY;
  751. }
  752. CopyMemory(pb, pbSignature, SIGNATURE_SIZE);
  753. DELETE_CJOB_FIELD(m_pbSignature);
  754. m_pbSignature = pb;
  755. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  756. return S_OK;
  757. }
  758. //+----------------------------------------------------------------------------
  759. //
  760. // Member: CJob::ITask::SetPriority
  761. //
  762. // Synopsis: Set the priority property
  763. //
  764. // Arguments: [dwPriority] - the priority value
  765. //
  766. // Returns: HRESULTS
  767. //
  768. // Notes: Controls the priority at which the job will run. Applies to NT
  769. // only, a no-op on Win95. This must be one of the four values
  770. // from CreateProcess's priority class.
  771. //-----------------------------------------------------------------------------
  772. STDMETHODIMP
  773. CJob::SetPriority(DWORD dwPriority)
  774. {
  775. TRACE3(CJob, SetPriority)
  776. //
  777. // Check for valid priority values
  778. //
  779. switch (dwPriority)
  780. {
  781. case IDLE_PRIORITY_CLASS:
  782. case NORMAL_PRIORITY_CLASS:
  783. case HIGH_PRIORITY_CLASS:
  784. case REALTIME_PRIORITY_CLASS:
  785. break;
  786. default:
  787. return E_INVALIDARG;
  788. }
  789. m_dwPriority = dwPriority;
  790. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  791. return S_OK;
  792. }
  793. //+----------------------------------------------------------------------------
  794. //
  795. // Member: CJob::ITask::GetPriority
  796. //
  797. // Synopsis: Get the priority property
  798. //
  799. // Arguments: [pdwPriority] - priority return address
  800. //
  801. // Returns: HRESULTS
  802. //
  803. //-----------------------------------------------------------------------------
  804. STDMETHODIMP
  805. CJob::GetPriority(DWORD * pdwPriority)
  806. {
  807. TRACE3(CJob, GetPriority)
  808. *pdwPriority = m_dwPriority;
  809. return S_OK;
  810. }
  811. //+----------------------------------------------------------------------------
  812. //
  813. // Member: CJob::ITask::SetMaxRunTime
  814. //
  815. // Synopsis: Set the MaximumRunTime property
  816. //
  817. // Arguments: [dwMaxRunTime] - new MaxRunTime value in milliseconds
  818. //
  819. // Returns: HRESULTS
  820. //
  821. //-----------------------------------------------------------------------------
  822. STDMETHODIMP
  823. CJob::SetMaxRunTime(DWORD dwMaxRunTime)
  824. {
  825. TRACE3(CJob, SetMaxRunTime)
  826. m_dwMaxRunTime = dwMaxRunTime;
  827. // Cause a wait list rebuild
  828. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY | JOB_I_FLAG_RUN_PROP_CHANGE);
  829. return S_OK;
  830. }
  831. //+----------------------------------------------------------------------------
  832. //
  833. // Member: CJob::ITask::GetMaxRunTime
  834. //
  835. // Synopsis: Get the MaximumRunTime property
  836. //
  837. // Arguments: [pdwMaxRunTime] - MaxRunTime return address (milliseconds)
  838. //
  839. // Returns: HRESULTS
  840. //
  841. //-----------------------------------------------------------------------------
  842. STDMETHODIMP
  843. CJob::GetMaxRunTime(DWORD * pdwMaxRunTime)
  844. {
  845. TRACE3(CJob, GetMaxRunTime)
  846. *pdwMaxRunTime = m_dwMaxRunTime;
  847. return S_OK;
  848. }
  849. //+----------------------------------------------------------------------------
  850. //
  851. // Member: CJob::ITask::SetFlags
  852. //
  853. // Synopsis: Set the bit flags for the various boolean properties
  854. //
  855. // Arguments: [fLogConfig] - Boolean: should changes be logged true/false.
  856. //
  857. // Returns: HRESULTS
  858. //
  859. //-----------------------------------------------------------------------------
  860. STDMETHODIMP
  861. CJob::SetFlags(DWORD rgFlags)
  862. {
  863. TRACE3(CJob, SetFlags)
  864. if ((rgFlags ^ m_rgFlags) &
  865. (TASK_FLAG_DISABLED |
  866. TASK_FLAG_START_ONLY_IF_IDLE |
  867. TASK_FLAG_KILL_ON_IDLE_END |
  868. TASK_FLAG_DONT_START_IF_ON_BATTERIES |
  869. TASK_FLAG_KILL_IF_GOING_ON_BATTERIES |
  870. TASK_FLAG_RUN_ONLY_IF_DOCKED |
  871. TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET |
  872. TASK_FLAG_RESTART_ON_IDLE_RESUME |
  873. TASK_FLAG_SYSTEM_REQUIRED))
  874. {
  875. //
  876. // If any flag that could affect the CRun objects in the wait
  877. // list has changed, signal a wait list rebuild.
  878. // (Omitted flags: TASK_FLAG_HIDDEN, TASK_FLAG_INTERACTIVE,
  879. // TASK_FLAG_DELETE_WHEN_DONE)
  880. // CODEWORK Possible optimization: Omit some more flags and
  881. // defer reading their settings into the CRun object until the
  882. // time of running the job
  883. //
  884. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  885. }
  886. //
  887. // Only set the lower word of the internal flag property. The upper word
  888. // is reserved for internal use.
  889. //
  890. rgFlags &= ~JOB_INTERNAL_FLAG_MASK;
  891. m_rgFlags &= JOB_INTERNAL_FLAG_MASK;
  892. SetFlag(rgFlags);
  893. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  894. return S_OK;
  895. }
  896. //+----------------------------------------------------------------------------
  897. //
  898. // Member: CJob::ITask::GetFlags
  899. //
  900. // Synopsis: Get the bit flags for the various boolean properties
  901. //
  902. // Arguments: [prgFlags] - returned value placed here
  903. //
  904. // Returns: HRESULTS
  905. //
  906. //-----------------------------------------------------------------------------
  907. STDMETHODIMP
  908. CJob::GetFlags(DWORD * prgFlags)
  909. {
  910. TRACE3(CJob, GetFlags)
  911. //
  912. // Only return the lower word of the internal flag property. The upper
  913. // word is reserved for internal use.
  914. // Also return whether this is an At job.
  915. //
  916. *prgFlags = m_rgFlags & (~JOB_INTERNAL_FLAG_MASK | JOB_I_FLAG_NET_SCHEDULE);
  917. return S_OK;
  918. }
  919. //+----------------------------------------------------------------------------
  920. //
  921. // Member: CJob::ITask::SetTaskFlags
  922. //
  923. // Synopsis: Sets the job's task flags.
  924. //
  925. // Arguments: [dwFlags] - flags to be set.
  926. //
  927. // Returns: S_OK
  928. //
  929. //-----------------------------------------------------------------------------
  930. STDMETHODIMP
  931. CJob::SetTaskFlags(DWORD dwFlags)
  932. {
  933. TRACE3(CJob, SetTaskFlags)
  934. //
  935. // Only set the lower word of the internal flag property. The upper word
  936. // is reserved for internal use.
  937. // BUGBUG return an error on invalid flag bits
  938. //
  939. m_rgTaskFlags = (m_rgTaskFlags & JOB_INTERNAL_FLAG_MASK) |
  940. (dwFlags & ~JOB_INTERNAL_FLAG_MASK);
  941. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  942. return S_OK;
  943. }
  944. //+----------------------------------------------------------------------------
  945. //
  946. // Member: CJob::ITask::GetTaskFlags
  947. //
  948. // Synopsis: Returns the job's task flags.
  949. //
  950. // Arguments: [pdwFlags] - return value pointer.
  951. //
  952. // Returns: HRESULTS
  953. //
  954. //-----------------------------------------------------------------------------
  955. STDMETHODIMP
  956. CJob::GetTaskFlags(DWORD * pdwFlags)
  957. {
  958. TRACE3(CJob, GetTaskFlags)
  959. //
  960. // Only return the lower word of the internal flag property. The upper
  961. // word is reserved for internal use.
  962. //
  963. *pdwFlags = m_rgTaskFlags & ~JOB_INTERNAL_FLAG_MASK;
  964. return S_OK;
  965. }
  966. //+----------------------------------------------------------------------------
  967. //
  968. // Member: CJob::ITask::SetWorkItemData
  969. //
  970. // Synopsis: Sets the task data. Provides optional, per-task, binary
  971. // storage for the caller.
  972. //
  973. // Arguments: [cbData] -- number of bytes in buffer.
  974. // [rgbData] -- buffer of data to copy.
  975. //
  976. // Returns: S_OK
  977. // E_INVALIDARG
  978. // E_OUTOFMEMORY
  979. //
  980. // Notes: The buffer is caller allocated and freed.
  981. //
  982. //-----------------------------------------------------------------------------
  983. STDMETHODIMP
  984. CJob::SetWorkItemData(WORD cbData, BYTE rgbData[])
  985. {
  986. TRACE3(CJob, SetWorkItemData)
  987. if ((cbData != 0 && rgbData == NULL) ||
  988. cbData == 0 && rgbData != NULL)
  989. {
  990. return E_INVALIDARG;
  991. }
  992. BYTE * pbData;
  993. if (cbData)
  994. {
  995. pbData = new BYTE[cbData];
  996. if (pbData == NULL)
  997. {
  998. return E_OUTOFMEMORY;
  999. }
  1000. CopyMemory(pbData, rgbData, cbData);
  1001. }
  1002. else
  1003. {
  1004. pbData = NULL;
  1005. }
  1006. DELETE_CJOB_FIELD(m_pbTaskData);
  1007. m_pbTaskData = pbData;
  1008. m_cbTaskData = cbData;
  1009. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  1010. return S_OK;
  1011. }
  1012. //+----------------------------------------------------------------------------
  1013. //
  1014. // Member: CJob::ITask::GetWorkItemData
  1015. //
  1016. // Synopsis: Gets the task data.
  1017. //
  1018. // Arguments: [pcbData] -- returns the number of bytes in buffer.
  1019. // [prgbData] -- returns the buffer of data.
  1020. //
  1021. // Returns: S_OK for success.
  1022. // E_INVALIDARG
  1023. // E_OUTOFMEMORY.
  1024. //
  1025. // Notes: The buffer is callee allocated and caller freed with
  1026. // CoTaskMemFree. If there is no user data, then *pcBytes is set
  1027. // to zero and *ppBytes is set to NULL.
  1028. //
  1029. //-----------------------------------------------------------------------------
  1030. STDMETHODIMP
  1031. CJob::GetWorkItemData(PWORD pcbData, PBYTE * prgbData)
  1032. {
  1033. TRACE3(CJob, GetWorkItemData)
  1034. if (m_pbTaskData != NULL)
  1035. {
  1036. *prgbData = (PBYTE)CoTaskMemAlloc(m_cbTaskData);
  1037. if (*prgbData == NULL)
  1038. {
  1039. return E_OUTOFMEMORY;
  1040. }
  1041. CopyMemory(*prgbData, m_pbTaskData, m_cbTaskData);
  1042. *pcbData = m_cbTaskData;
  1043. }
  1044. else
  1045. {
  1046. *pcbData = 0;
  1047. *prgbData = NULL;
  1048. }
  1049. return S_OK;
  1050. }
  1051. //+----------------------------------------------------------------------------
  1052. //
  1053. // Member: CJob::ITask::GetMostRecentRunTime
  1054. //
  1055. // Synopsis: Returns the time that the job last ran.
  1056. //
  1057. // Arguments: [pstLastRun] - value returned here.
  1058. //
  1059. // Returns: HRESULTS
  1060. //
  1061. //-----------------------------------------------------------------------------
  1062. STDMETHODIMP
  1063. CJob::GetMostRecentRunTime(SYSTEMTIME * pstLastRun)
  1064. {
  1065. TRACE3(CJob, GetLastRunTime)
  1066. *pstLastRun = m_stMostRecentRunTime;
  1067. if (m_stMostRecentRunTime.wYear == 0)
  1068. {
  1069. //
  1070. // Job has never run if last-run-time property is null
  1071. // (all elements will be zero if null, testing year is
  1072. // sufficient).
  1073. //
  1074. return SCHED_S_TASK_HAS_NOT_RUN;
  1075. }
  1076. return S_OK;
  1077. }
  1078. //+----------------------------------------------------------------------------
  1079. //
  1080. // Member: CJob::ITask::GetNextRunTime
  1081. //
  1082. // Synopsis: Returns the next time that the job is scheduled to run.
  1083. //
  1084. // Arguments: [pstNextRun] - pointer to return value through
  1085. //
  1086. // Returns: S_OK: the next run time was returned.
  1087. // S_FALSE: there are no more scheduled runs.
  1088. // SCHED_S_EVENT_TRIGGER: there are only event triggers.
  1089. // SCHED_S_TASK_NO_VALID_TRIGGERS: either there are no triggers or
  1090. // the triggers are disabled or not set.
  1091. // HRESULT - failure code.
  1092. //
  1093. //-----------------------------------------------------------------------------
  1094. STDMETHODIMP
  1095. CJob::GetNextRunTime(SYSTEMTIME * pstNextRun)
  1096. {
  1097. TRACE3(CJob, GetNextRunTime)
  1098. HRESULT hr;
  1099. SYSTEMTIME stNow, stEmpty = { 0, 0, 0, 0, 0, 0, 0, 0 };
  1100. GetLocalTime(&stNow);
  1101. CTimeRunList RunList;
  1102. WORD cRuns = 0;
  1103. hr = GetRunTimesP(&stNow, NULL, &cRuns, 1, &RunList, NULL);
  1104. if (S_OK != hr)
  1105. {
  1106. *pstNextRun = stEmpty;
  1107. return hr;
  1108. }
  1109. else if (cRuns == 0)
  1110. {
  1111. *pstNextRun = stEmpty;
  1112. return S_FALSE;
  1113. }
  1114. FILETIME ft;
  1115. RunList.PeekHeadTime(&ft);
  1116. if (!FileTimeToSystemTime(&ft, pstNextRun))
  1117. {
  1118. *pstNextRun = stEmpty;
  1119. return HRESULT_FROM_WIN32(GetLastError());
  1120. }
  1121. return S_OK;
  1122. }
  1123. //+----------------------------------------------------------------------------
  1124. //
  1125. // Member: CJob::ITask::GetExitCode
  1126. //
  1127. // Synopsis: Returns the exit code of the jobs last run.
  1128. //
  1129. // Arguments: [pExitCode] - return value pointer.
  1130. //
  1131. // Returns: HRESULT - error from the last attempt to start the job.
  1132. //
  1133. //-----------------------------------------------------------------------------
  1134. STDMETHODIMP
  1135. CJob::GetExitCode(DWORD * pExitCode)
  1136. {
  1137. TRACE3(CJob, GetExitCode)
  1138. *pExitCode = m_ExitCode;
  1139. return m_hrStartError;
  1140. }
  1141. //+----------------------------------------------------------------------------
  1142. //
  1143. // Member: CJob::ITask::GetStatus
  1144. //
  1145. // Synopsis: Returns the current status of this job object.
  1146. //
  1147. // Arguments: [phrStatus] - yup, the return value.
  1148. //
  1149. // Returns: HRESULTS
  1150. //
  1151. // Notes: The running status is implicit in a non-zero instance count.
  1152. // Otherwise, return the status property. Note also that the
  1153. // job's status and other properties that are obtained by the
  1154. // load from disk are a snapshot that can become stale.
  1155. //-----------------------------------------------------------------------------
  1156. STDMETHODIMP
  1157. CJob::GetStatus(HRESULT * phrStatus)
  1158. {
  1159. TRACE3(CJob, GetStatus)
  1160. if (m_cRunningInstances > 0)
  1161. {
  1162. *phrStatus = SCHED_S_TASK_RUNNING;
  1163. }
  1164. else
  1165. {
  1166. *phrStatus = m_hrStatus;
  1167. }
  1168. return S_OK;
  1169. }
  1170. //+----------------------------------------------------------------------------
  1171. //
  1172. // Member: CJob::ITask::SetCreator
  1173. //
  1174. // Synopsis: Set the creator property.
  1175. //
  1176. // Arguments: [pwszCreator] -- Creator string.
  1177. //
  1178. // Returns: S_OK
  1179. // E_INVALIDARG
  1180. // E_OUTOFMEMORY
  1181. //
  1182. // Notes: None.
  1183. //
  1184. //-----------------------------------------------------------------------------
  1185. STDMETHODIMP
  1186. CJob::SetCreator(LPCWSTR pwszCreator)
  1187. {
  1188. TRACE3(CJob, SetCreator);
  1189. LPWSTR pwsz;
  1190. //
  1191. // Handle empty string.
  1192. //
  1193. if (*pwszCreator)
  1194. {
  1195. pwsz = new WCHAR[wcslen(pwszCreator) + 1];
  1196. if (pwsz == NULL)
  1197. {
  1198. return E_OUTOFMEMORY;
  1199. }
  1200. StringCchCopy(pwsz, wcslen(pwszCreator) + 1, pwszCreator);
  1201. }
  1202. else
  1203. {
  1204. pwsz = NULL;
  1205. }
  1206. DELETE_CJOB_FIELD(m_pwszCreator);
  1207. m_pwszCreator = pwsz;
  1208. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  1209. return S_OK;
  1210. }
  1211. //+----------------------------------------------------------------------------
  1212. //
  1213. // Member: CJob::ITask::GetCreator
  1214. //
  1215. // Synopsis: Fetch the creator property.
  1216. //
  1217. // Arguments: [ppwszCreator] -- Returned string.
  1218. //
  1219. // Returns: S_OK
  1220. // E_INVALIDARG
  1221. // E_OUTOFMEMORY
  1222. //
  1223. // Notes: None.
  1224. //
  1225. //-----------------------------------------------------------------------------
  1226. STDMETHODIMP
  1227. CJob::GetCreator(LPWSTR * ppwszCreator)
  1228. {
  1229. TRACE3(CJob, GetCreator);
  1230. //
  1231. // Handle empty string.
  1232. //
  1233. LPWSTR pwsz = (m_pwszCreator == NULL ? wszEmpty : m_pwszCreator);
  1234. *ppwszCreator = (LPWSTR)CoTaskMemAlloc(
  1235. (wcslen(pwsz) + 1) * sizeof(WCHAR));
  1236. if (*ppwszCreator == NULL)
  1237. {
  1238. return E_OUTOFMEMORY;
  1239. }
  1240. StringCchCopy(*ppwszCreator, wcslen(pwsz) + 1, pwsz);
  1241. return S_OK;
  1242. }