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.

1469 lines
38 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 "defines.hxx"
  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 SaveP(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 SaveP(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. //
  304. // We don't use a try/catch
  305. // because the COM interface runs in the caller's context and it is their
  306. // responsibility to ensure good params. This latter statement is true for
  307. // all scheduler COM interface methods.
  308. //
  309. if (*pwszApplicationName == L'\0')
  310. {
  311. //
  312. // The caller wants the command set to an empty string.
  313. //
  314. ClearFlag(JOB_I_FLAG_HAS_APPNAME);
  315. //
  316. // We are using a null pointer for an empty string as an optimization.
  317. //
  318. if (m_pwszApplicationName == NULL)
  319. {
  320. //
  321. // Nothing to do.
  322. //
  323. return S_OK;
  324. }
  325. else
  326. {
  327. //
  328. // Setting this flag will instruct the persist code to
  329. // regenerate a GUID for this job. This is done for security
  330. // reasons.
  331. //
  332. // NB : This must be done for Win95 as well as NT.
  333. //
  334. SetFlag(JOB_I_FLAG_APPNAME_CHANGE);
  335. DELETE_CJOB_FIELD(m_pwszApplicationName);
  336. //
  337. // We want this change to trigger a wait list rebuild.
  338. //
  339. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  340. }
  341. }
  342. else
  343. {
  344. //
  345. // Update the flags and status.
  346. //
  347. SetFlag(JOB_I_FLAG_HAS_APPNAME);
  348. if (IsStatus(SCHED_S_TASK_NOT_SCHEDULED))
  349. {
  350. //
  351. // Note that if the status went from SCHED_S_TASK_NOT_SCHEDULED to
  352. // SCHED_S_TASK_HAS_NOT_RUN or SCHED_S_TASK_READY, then we
  353. // want CheckDir to issue a wait list rebuild because the job has
  354. // gone from a non-runable to a runable state. Thus, in the if
  355. // clause below we set JOB_I_FLAG_RUN_PROP_CHANGE.
  356. //
  357. if (IsFlagSet(JOB_I_FLAG_HAS_TRIGGERS) &&
  358. !IsFlagSet(JOB_I_FLAG_NO_VALID_TRIGGERS))
  359. {
  360. if (m_stMostRecentRunTime.wYear == 0)
  361. {
  362. //
  363. // Job has never run if last-run-time property is null
  364. // (all elements will be zero if null, testing year is
  365. // sufficient).
  366. //
  367. SetStatus(SCHED_S_TASK_HAS_NOT_RUN);
  368. }
  369. else
  370. {
  371. //
  372. // Job has run in the past, so it is now waiting to run
  373. // again.
  374. //
  375. SetStatus(SCHED_S_TASK_READY);
  376. }
  377. //
  378. // We want this change to trigger a wait list rebuild.
  379. //
  380. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  381. }
  382. }
  383. TCHAR tszAppName[MAX_PATH];
  384. if (IsLocalFilename(m_ptszFileName))
  385. {
  386. #if defined(UNICODE)
  387. lstrcpyn(tszAppName, pwszApplicationName, MAX_PATH);
  388. ProcessApplicationName(tszAppName,
  389. m_pwszWorkingDirectory ?
  390. m_pwszWorkingDirectory :
  391. wszEmpty);
  392. #else
  393. UnicodeToAnsi(tszAppName, pwszApplicationName, MAX_PATH);
  394. CHAR szWorkingDir[MAX_PATH] = "";
  395. if (m_pwszWorkingDirectory)
  396. {
  397. UnicodeToAnsi(szWorkingDir, m_pwszWorkingDirectory, MAX_PATH);
  398. }
  399. ProcessApplicationName(tszAppName, szWorkingDir);
  400. #endif // defined(UNICODE)
  401. }
  402. else
  403. {
  404. #if defined(UNICODE)
  405. lstrcpyn(tszAppName, pwszApplicationName, MAX_PATH);
  406. #else
  407. UnicodeToAnsi(tszAppName, pwszApplicationName, MAX_PATH);
  408. #endif // defined(UNICODE)
  409. StripLeadTrailSpace(tszAppName);
  410. DeleteQuotes(tszAppName);
  411. }
  412. //
  413. // Allocate a new string, adding one for the null terminator
  414. //
  415. ULONG cchAppName = lstrlen(tszAppName) + 1;
  416. LPWSTR pwszTmp = new WCHAR[cchAppName];
  417. if (pwszTmp == NULL)
  418. {
  419. ERR_OUT("CJob::SetApplicationName", E_OUTOFMEMORY);
  420. return E_OUTOFMEMORY;
  421. }
  422. #if defined(UNICODE)
  423. lstrcpy(pwszTmp, tszAppName);
  424. #else
  425. HRESULT hr = AnsiToUnicode(pwszTmp, tszAppName, cchAppName);
  426. if (FAILED(hr))
  427. {
  428. delete pwszTmp;
  429. return hr;
  430. }
  431. #endif // defined(UNICODE)
  432. //
  433. // Setting this flag will instruct the persist code to
  434. // regenerate a GUID for this job. This is done for security
  435. // reasons.
  436. //
  437. // NB : This must be done for Win95 as well as NT.
  438. //
  439. // Ensure first, that the application has indeed changed.
  440. //
  441. if (m_pwszApplicationName != NULL && lstrcmpiW(
  442. m_pwszApplicationName,
  443. pwszTmp) != 0)
  444. {
  445. SetFlag(JOB_I_FLAG_APPNAME_CHANGE);
  446. }
  447. DELETE_CJOB_FIELD(m_pwszApplicationName);
  448. m_pwszApplicationName = pwszTmp;
  449. }
  450. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  451. return S_OK;
  452. }
  453. //+----------------------------------------------------------------------------
  454. //
  455. // Member: CJob::ITask::GetApplicationName
  456. //
  457. // Synopsis: Get the ApplicationName String property
  458. //
  459. // Arguments: [ppwszApplicationName] - the returned string buffer
  460. //
  461. // Returns: HRESULTS
  462. //
  463. // Notes: The command string is passed to CreateProcess to be executed
  464. // at task run time.
  465. // The string is callee allocated and caller freed with
  466. // CoTaskMemFree.
  467. //
  468. //-----------------------------------------------------------------------------
  469. STDMETHODIMP
  470. CJob::GetApplicationName(LPWSTR * ppwszApplicationName)
  471. {
  472. TRACE3(CJob, GetApplicationName)
  473. WCHAR * pwszCmd;
  474. if (m_pwszApplicationName == NULL)
  475. {
  476. //
  477. // Return an empty string rather than a null pointer
  478. //
  479. pwszCmd = wszEmpty;
  480. }
  481. else
  482. {
  483. pwszCmd = m_pwszApplicationName;
  484. }
  485. // add one for the null.
  486. *ppwszApplicationName = (LPWSTR)CoTaskMemAlloc((s_wcslen(pwszCmd) + 1) *
  487. sizeof(WCHAR));
  488. if (*ppwszApplicationName == NULL)
  489. {
  490. return E_OUTOFMEMORY;
  491. }
  492. s_wcscpy(*ppwszApplicationName, pwszCmd);
  493. return S_OK;
  494. }
  495. //+----------------------------------------------------------------------------
  496. //
  497. // Member: CJob::ITask::SetParameters
  498. //
  499. // Synopsis: Set the Parameters String property
  500. //
  501. // Arguments: [pwszParameters] - the application parameters string
  502. //
  503. // Returns: HRESULTS
  504. //
  505. // Notes: The Parameters string is appended to the Application Name and
  506. // passed to CreateProcess as the command line to be executed at
  507. // task run time.
  508. // The string is caller allocated and freed.
  509. //-----------------------------------------------------------------------------
  510. STDMETHODIMP
  511. CJob::SetParameters(LPCWSTR pwszParameters)
  512. {
  513. TRACE3(CJob, SetParameters)
  514. if (*pwszParameters == L'\0')
  515. {
  516. //
  517. // The caller wants the Parameters set to an empty string.
  518. //
  519. // We are using a null pointer for an empty string as an optimization.
  520. //
  521. DELETE_CJOB_FIELD(m_pwszParameters);
  522. }
  523. else
  524. {
  525. //
  526. // Allocate a new string, adding one for the null terminator
  527. //
  528. LPWSTR pwszTmp = new WCHAR[s_wcslen(pwszParameters) + 1];
  529. if (pwszTmp == NULL)
  530. {
  531. ERR_OUT("CJob::SetParameters", E_OUTOFMEMORY);
  532. return E_OUTOFMEMORY;
  533. }
  534. s_wcscpy(pwszTmp, pwszParameters);
  535. DELETE_CJOB_FIELD(m_pwszParameters);
  536. m_pwszParameters = pwszTmp;
  537. }
  538. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  539. return S_OK;
  540. }
  541. //+----------------------------------------------------------------------------
  542. //
  543. // Member: CJob::ITask::GetParameters
  544. //
  545. // Synopsis: Get the Parameters String property
  546. //
  547. // Arguments: [ppwszParameters] - the returned string buffer
  548. //
  549. // Returns: HRESULTS
  550. //
  551. // Notes: The Parameters string is appended to the Application Name and
  552. // passed to CreateProcess as the command line to be executed at
  553. // task run time.
  554. // The string is callee allocated and caller freed with
  555. // CoTaskMemFree.
  556. //-----------------------------------------------------------------------------
  557. STDMETHODIMP
  558. CJob::GetParameters(LPWSTR * ppwszParameters)
  559. {
  560. TRACE3(CJob, GetParameters)
  561. WCHAR * pwszTmp;
  562. if (m_pwszParameters == NULL)
  563. {
  564. //
  565. // Return an empty string rather than a null pointer
  566. //
  567. pwszTmp = wszEmpty;
  568. }
  569. else
  570. {
  571. pwszTmp = m_pwszParameters;
  572. }
  573. // add one for the null.
  574. *ppwszParameters = (LPWSTR)CoTaskMemAlloc((s_wcslen(pwszTmp) + 1) *
  575. sizeof(WCHAR));
  576. if (*ppwszParameters == NULL)
  577. {
  578. return E_OUTOFMEMORY;
  579. }
  580. s_wcscpy(*ppwszParameters, pwszTmp);
  581. return S_OK;
  582. }
  583. //+----------------------------------------------------------------------------
  584. //
  585. // Member: CJob::ITask::SetWorkingDirectory
  586. //
  587. // Synopsis: Set the Working Directory (current directory) property
  588. //
  589. // Arguments: [pwszWorkingDir] - the name to use
  590. //
  591. // Returns: HRESULTS
  592. //
  593. // Notes: The string is caller allocated and freed
  594. //-----------------------------------------------------------------------------
  595. STDMETHODIMP
  596. CJob::SetWorkingDirectory(LPCWSTR pwszWorkingDirectory)
  597. {
  598. TRACE3(CJob, SetWorkingDirectory)
  599. if (*pwszWorkingDirectory == L'\0')
  600. {
  601. //
  602. // The caller wants the WorkingDirectory set to an empty string.
  603. //
  604. DELETE_CJOB_FIELD(m_pwszWorkingDirectory);
  605. }
  606. else
  607. {
  608. //
  609. // Allocate a new string, adding one for the null terminator
  610. //
  611. LPWSTR pwszTmp = new WCHAR[s_wcslen(pwszWorkingDirectory) + 1];
  612. if (pwszTmp == NULL)
  613. {
  614. ERR_OUT("CJob::SetWorkingDirectory", E_OUTOFMEMORY);
  615. return E_OUTOFMEMORY;
  616. }
  617. s_wcscpy(pwszTmp, pwszWorkingDirectory);
  618. DELETE_CJOB_FIELD(m_pwszWorkingDirectory);
  619. m_pwszWorkingDirectory = pwszTmp;
  620. //
  621. // Remove double quotes from working directory path; they're not supported
  622. // by SetCurrentDirectory.
  623. //
  624. #if defined(UNICODE)
  625. DeleteQuotes(m_pwszWorkingDirectory);
  626. #else
  627. CHAR szWorkingDir[2 * MAX_PATH] = "";
  628. UnicodeToAnsi(szWorkingDir, m_pwszWorkingDirectory, 2 * MAX_PATH);
  629. DeleteQuotes(szWorkingDir);
  630. AnsiToUnicode(m_pwszWorkingDirectory,
  631. szWorkingDir,
  632. wcslen(m_pwszWorkingDirectory) + 1);
  633. #endif // defined(UNICODE)
  634. }
  635. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  636. return S_OK;
  637. }
  638. //+----------------------------------------------------------------------------
  639. //
  640. // Member: CJob::ITask::GetWorkingDir
  641. //
  642. // Synopsis: Get the Working Directory (current directory) property
  643. //
  644. // Arguments: [ppwszWorkingDirectory] - the returned string buffer
  645. //
  646. // Returns: HRESULTS
  647. //
  648. // Notes: The string is callee allocated and caller freed with
  649. // CoTaskMemFree.
  650. //-----------------------------------------------------------------------------
  651. STDMETHODIMP
  652. CJob::GetWorkingDirectory(LPWSTR * ppwszWorkingDirectory)
  653. {
  654. TRACE3(CJob, GetWorkingDirectory)
  655. WCHAR * pwszWorkingDir;
  656. if (m_pwszWorkingDirectory == NULL)
  657. {
  658. //
  659. // Return an empty string rather than a null pointer
  660. //
  661. pwszWorkingDir = wszEmpty;
  662. }
  663. else
  664. {
  665. pwszWorkingDir = m_pwszWorkingDirectory;
  666. }
  667. // add one for the null.
  668. *ppwszWorkingDirectory = (LPWSTR)CoTaskMemAlloc(
  669. (s_wcslen(pwszWorkingDir) + 1) * sizeof(WCHAR));
  670. if (*ppwszWorkingDirectory == NULL)
  671. {
  672. return E_OUTOFMEMORY;
  673. }
  674. s_wcscpy(*ppwszWorkingDirectory, pwszWorkingDir);
  675. return S_OK;
  676. }
  677. //+----------------------------------------------------------------------------
  678. //
  679. // Member: CJob::ITask::SetComment
  680. //
  681. // Synopsis: Set the comment field.
  682. //
  683. // Arguments: [pwszComment] - the comment string value, caller alloc'd and
  684. // freed
  685. //
  686. // Returns: HRESULTS
  687. //
  688. //-----------------------------------------------------------------------------
  689. STDMETHODIMP
  690. CJob::SetComment(LPCWSTR pwszComment)
  691. {
  692. TRACE3(CJob, SetComment)
  693. if (*pwszComment == L'\0')
  694. {
  695. //
  696. // The caller wants the Comment set to an empty string.
  697. //
  698. DELETE_CJOB_FIELD(m_pwszComment);
  699. }
  700. else
  701. {
  702. //
  703. // Allocate a new string, adding one for the null terminator
  704. //
  705. LPWSTR pwszTmp = new WCHAR[s_wcslen(pwszComment) + 1];
  706. if (pwszTmp == NULL)
  707. {
  708. ERR_OUT("CJob::SetComment", E_OUTOFMEMORY);
  709. return E_OUTOFMEMORY;
  710. }
  711. s_wcscpy(pwszTmp, pwszComment);
  712. DELETE_CJOB_FIELD(m_pwszComment);
  713. m_pwszComment = pwszTmp;
  714. }
  715. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  716. return S_OK;
  717. }
  718. //+----------------------------------------------------------------------------
  719. //
  720. // Member: CJob::ITask::GetComment
  721. //
  722. // Synopsis: Get the comment field property.
  723. //
  724. // Arguments: [ppwszComment] - the returned string buffer
  725. //
  726. // Returns: HRESULTS
  727. //
  728. // Notes: The string is callee allocated and caller freed with
  729. // CoTaskMemFree.
  730. //-----------------------------------------------------------------------------
  731. STDMETHODIMP
  732. CJob::GetComment(LPWSTR * ppwszComment)
  733. {
  734. TRACE3(CJob, GetComment)
  735. WCHAR * pwszCmt;
  736. if (m_pwszComment == NULL)
  737. {
  738. //
  739. // Return an empty string rather than a null pointer
  740. //
  741. pwszCmt = wszEmpty;
  742. }
  743. else
  744. {
  745. pwszCmt = m_pwszComment;
  746. }
  747. // add one for the null.
  748. *ppwszComment = (LPWSTR)CoTaskMemAlloc((s_wcslen(pwszCmt) + 1) *
  749. sizeof(WCHAR));
  750. if (*ppwszComment == NULL)
  751. {
  752. return E_OUTOFMEMORY;
  753. }
  754. s_wcscpy(*ppwszComment, pwszCmt);
  755. return S_OK;
  756. }
  757. #if !defined(_CHICAGO_)
  758. //+----------------------------------------------------------------------------
  759. //
  760. // Member: CJob::_SetSignature
  761. //
  762. // Synopsis: Set the job's signature.
  763. //
  764. // Arguments: [pbSignature] - assumed to be SIGNATURE_SIZE bytes long.
  765. //
  766. // Returns: HRESULTS
  767. //
  768. //-----------------------------------------------------------------------------
  769. HRESULT
  770. CJob::_SetSignature(const BYTE * pbSignature)
  771. {
  772. TRACE3(CJob, SetSignature)
  773. LPBYTE pb = new BYTE[SIGNATURE_SIZE];
  774. if (pb == NULL)
  775. {
  776. ERR_OUT("CJob::SetSignature", E_OUTOFMEMORY);
  777. return E_OUTOFMEMORY;
  778. }
  779. CopyMemory(pb, pbSignature, SIGNATURE_SIZE);
  780. DELETE_CJOB_FIELD(m_pbSignature);
  781. m_pbSignature = pb;
  782. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  783. return S_OK;
  784. }
  785. #endif // !defined(_CHICAGO_)
  786. //+----------------------------------------------------------------------------
  787. //
  788. // Member: CJob::ITask::SetPriority
  789. //
  790. // Synopsis: Set the priority property
  791. //
  792. // Arguments: [dwPriority] - the priority value
  793. //
  794. // Returns: HRESULTS
  795. //
  796. // Notes: Controls the priority at which the job will run. Applies to NT
  797. // only, a no-op on Win95. This must be one of the four values
  798. // from CreateProcess's priority class.
  799. //-----------------------------------------------------------------------------
  800. STDMETHODIMP
  801. CJob::SetPriority(DWORD dwPriority)
  802. {
  803. TRACE3(CJob, SetPriority)
  804. //
  805. // Check for valid priority values
  806. //
  807. switch (dwPriority)
  808. {
  809. case IDLE_PRIORITY_CLASS:
  810. case NORMAL_PRIORITY_CLASS:
  811. case HIGH_PRIORITY_CLASS:
  812. case REALTIME_PRIORITY_CLASS:
  813. break;
  814. default:
  815. return E_INVALIDARG;
  816. }
  817. m_dwPriority = dwPriority;
  818. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  819. return S_OK;
  820. }
  821. //+----------------------------------------------------------------------------
  822. //
  823. // Member: CJob::ITask::GetPriority
  824. //
  825. // Synopsis: Get the priority property
  826. //
  827. // Arguments: [pdwPriority] - priority return address
  828. //
  829. // Returns: HRESULTS
  830. //
  831. //-----------------------------------------------------------------------------
  832. STDMETHODIMP
  833. CJob::GetPriority(DWORD * pdwPriority)
  834. {
  835. TRACE3(CJob, GetPriority)
  836. *pdwPriority = m_dwPriority;
  837. return S_OK;
  838. }
  839. //+----------------------------------------------------------------------------
  840. //
  841. // Member: CJob::ITask::SetMaxRunTime
  842. //
  843. // Synopsis: Set the MaximumRunTime property
  844. //
  845. // Arguments: [dwMaxRunTime] - new MaxRunTime value in milliseconds
  846. //
  847. // Returns: HRESULTS
  848. //
  849. //-----------------------------------------------------------------------------
  850. STDMETHODIMP
  851. CJob::SetMaxRunTime(DWORD dwMaxRunTime)
  852. {
  853. TRACE3(CJob, SetMaxRunTime)
  854. m_dwMaxRunTime = dwMaxRunTime;
  855. // Cause a wait list rebuild
  856. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY | JOB_I_FLAG_RUN_PROP_CHANGE);
  857. return S_OK;
  858. }
  859. //+----------------------------------------------------------------------------
  860. //
  861. // Member: CJob::ITask::GetMaxRunTime
  862. //
  863. // Synopsis: Get the MaximumRunTime property
  864. //
  865. // Arguments: [pdwMaxRunTime] - MaxRunTime return address (milliseconds)
  866. //
  867. // Returns: HRESULTS
  868. //
  869. //-----------------------------------------------------------------------------
  870. STDMETHODIMP
  871. CJob::GetMaxRunTime(DWORD * pdwMaxRunTime)
  872. {
  873. TRACE3(CJob, GetMaxRunTime)
  874. *pdwMaxRunTime = m_dwMaxRunTime;
  875. return S_OK;
  876. }
  877. //+----------------------------------------------------------------------------
  878. //
  879. // Member: CJob::ITask::SetFlags
  880. //
  881. // Synopsis: Set the bit flags for the various boolean properties
  882. //
  883. // Arguments: [fLogConfig] - Boolean: should changes be logged true/false.
  884. //
  885. // Returns: HRESULTS
  886. //
  887. //-----------------------------------------------------------------------------
  888. STDMETHODIMP
  889. CJob::SetFlags(DWORD rgFlags)
  890. {
  891. TRACE3(CJob, SetFlags)
  892. if ((rgFlags ^ m_rgFlags) &
  893. (TASK_FLAG_DISABLED |
  894. TASK_FLAG_START_ONLY_IF_IDLE |
  895. TASK_FLAG_KILL_ON_IDLE_END |
  896. TASK_FLAG_DONT_START_IF_ON_BATTERIES |
  897. TASK_FLAG_KILL_IF_GOING_ON_BATTERIES |
  898. TASK_FLAG_RUN_ONLY_IF_DOCKED |
  899. TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET |
  900. TASK_FLAG_RESTART_ON_IDLE_RESUME |
  901. TASK_FLAG_SYSTEM_REQUIRED))
  902. {
  903. //
  904. // If any flag that could affect the CRun objects in the wait
  905. // list has changed, signal a wait list rebuild.
  906. // (Omitted flags: TASK_FLAG_HIDDEN, TASK_FLAG_INTERACTIVE,
  907. // TASK_FLAG_DELETE_WHEN_DONE)
  908. // CODEWORK Possible optimization: Omit some more flags and
  909. // defer reading their settings into the CRun object until the
  910. // time of running the job
  911. //
  912. SetFlag(JOB_I_FLAG_RUN_PROP_CHANGE);
  913. }
  914. //
  915. // Only set the lower word of the internal flag property. The upper word
  916. // is reserved for internal use.
  917. //
  918. rgFlags &= ~JOB_INTERNAL_FLAG_MASK;
  919. m_rgFlags &= JOB_INTERNAL_FLAG_MASK;
  920. SetFlag(rgFlags);
  921. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  922. return S_OK;
  923. }
  924. //+----------------------------------------------------------------------------
  925. //
  926. // Member: CJob::ITask::GetFlags
  927. //
  928. // Synopsis: Get the bit flags for the various boolean properties
  929. //
  930. // Arguments: [prgFlags] - returned value placed here
  931. //
  932. // Returns: HRESULTS
  933. //
  934. //-----------------------------------------------------------------------------
  935. STDMETHODIMP
  936. CJob::GetFlags(DWORD * prgFlags)
  937. {
  938. TRACE3(CJob, GetFlags)
  939. //
  940. // Only return the lower word of the internal flag property. The upper
  941. // word is reserved for internal use.
  942. // Also return whether this is an At job.
  943. //
  944. *prgFlags = m_rgFlags & (~JOB_INTERNAL_FLAG_MASK | JOB_I_FLAG_NET_SCHEDULE);
  945. return S_OK;
  946. }
  947. //+----------------------------------------------------------------------------
  948. //
  949. // Member: CJob::ITask::SetTaskFlags
  950. //
  951. // Synopsis: Sets the job's task flags.
  952. //
  953. // Arguments: [dwFlags] - flags to be set.
  954. //
  955. // Returns: S_OK
  956. //
  957. //-----------------------------------------------------------------------------
  958. STDMETHODIMP
  959. CJob::SetTaskFlags(DWORD dwFlags)
  960. {
  961. TRACE3(CJob, SetTaskFlags)
  962. //
  963. // Only set the lower word of the internal flag property. The upper word
  964. // is reserved for internal use.
  965. // BUGBUG return an error on invalid flag bits
  966. //
  967. m_rgTaskFlags = (m_rgTaskFlags & JOB_INTERNAL_FLAG_MASK) |
  968. (dwFlags & ~JOB_INTERNAL_FLAG_MASK);
  969. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  970. return S_OK;
  971. }
  972. //+----------------------------------------------------------------------------
  973. //
  974. // Member: CJob::ITask::GetTaskFlags
  975. //
  976. // Synopsis: Returns the job's task flags.
  977. //
  978. // Arguments: [pdwFlags] - return value pointer.
  979. //
  980. // Returns: HRESULTS
  981. //
  982. //-----------------------------------------------------------------------------
  983. STDMETHODIMP
  984. CJob::GetTaskFlags(DWORD * pdwFlags)
  985. {
  986. TRACE3(CJob, GetTaskFlags)
  987. //
  988. // Only return the lower word of the internal flag property. The upper
  989. // word is reserved for internal use.
  990. //
  991. *pdwFlags = m_rgTaskFlags & ~JOB_INTERNAL_FLAG_MASK;
  992. return S_OK;
  993. }
  994. //+----------------------------------------------------------------------------
  995. //
  996. // Member: CJob::ITask::SetWorkItemData
  997. //
  998. // Synopsis: Sets the task data. Provides optional, per-task, binary
  999. // storage for the caller.
  1000. //
  1001. // Arguments: [cbData] -- number of bytes in buffer.
  1002. // [rgbData] -- buffer of data to copy.
  1003. //
  1004. // Returns: S_OK
  1005. // E_INVALIDARG
  1006. // E_OUTOFMEMORY
  1007. //
  1008. // Notes: The buffer is caller allocated and freed.
  1009. //
  1010. //-----------------------------------------------------------------------------
  1011. STDMETHODIMP
  1012. CJob::SetWorkItemData(WORD cbData, BYTE rgbData[])
  1013. {
  1014. TRACE3(CJob, SetWorkItemData)
  1015. if ((cbData != 0 && rgbData == NULL) ||
  1016. cbData == 0 && rgbData != NULL)
  1017. {
  1018. return E_INVALIDARG;
  1019. }
  1020. BYTE * pbData;
  1021. if (cbData)
  1022. {
  1023. pbData = new BYTE[cbData];
  1024. if (pbData == NULL)
  1025. {
  1026. return E_OUTOFMEMORY;
  1027. }
  1028. CopyMemory(pbData, rgbData, cbData);
  1029. }
  1030. else
  1031. {
  1032. pbData = NULL;
  1033. }
  1034. DELETE_CJOB_FIELD(m_pbTaskData);
  1035. m_pbTaskData = pbData;
  1036. m_cbTaskData = cbData;
  1037. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  1038. return S_OK;
  1039. }
  1040. //+----------------------------------------------------------------------------
  1041. //
  1042. // Member: CJob::ITask::GetWorkItemData
  1043. //
  1044. // Synopsis: Gets the task data.
  1045. //
  1046. // Arguments: [pcbData] -- returns the number of bytes in buffer.
  1047. // [prgbData] -- returns the buffer of data.
  1048. //
  1049. // Returns: S_OK for success.
  1050. // E_INVALIDARG
  1051. // E_OUTOFMEMORY.
  1052. //
  1053. // Notes: The buffer is callee allocated and caller freed with
  1054. // CoTaskMemFree. If there is no user data, then *pcBytes is set
  1055. // to zero and *ppBytes is set to NULL.
  1056. //
  1057. //-----------------------------------------------------------------------------
  1058. STDMETHODIMP
  1059. CJob::GetWorkItemData(PWORD pcbData, PBYTE * prgbData)
  1060. {
  1061. TRACE3(CJob, GetWorkItemData)
  1062. if (m_pbTaskData != NULL)
  1063. {
  1064. *prgbData = (PBYTE)CoTaskMemAlloc(m_cbTaskData);
  1065. if (*prgbData == NULL)
  1066. {
  1067. return E_OUTOFMEMORY;
  1068. }
  1069. CopyMemory(*prgbData, m_pbTaskData, m_cbTaskData);
  1070. *pcbData = m_cbTaskData;
  1071. }
  1072. else
  1073. {
  1074. *pcbData = 0;
  1075. *prgbData = NULL;
  1076. }
  1077. return S_OK;
  1078. }
  1079. //+----------------------------------------------------------------------------
  1080. //
  1081. // Member: CJob::ITask::GetMostRecentRunTime
  1082. //
  1083. // Synopsis: Returns the time that the job last ran.
  1084. //
  1085. // Arguments: [pstLastRun] - value returned here.
  1086. //
  1087. // Returns: HRESULTS
  1088. //
  1089. //-----------------------------------------------------------------------------
  1090. STDMETHODIMP
  1091. CJob::GetMostRecentRunTime(SYSTEMTIME * pstLastRun)
  1092. {
  1093. TRACE3(CJob, GetLastRunTime)
  1094. *pstLastRun = m_stMostRecentRunTime;
  1095. if (m_stMostRecentRunTime.wYear == 0)
  1096. {
  1097. //
  1098. // Job has never run if last-run-time property is null
  1099. // (all elements will be zero if null, testing year is
  1100. // sufficient).
  1101. //
  1102. return SCHED_S_TASK_HAS_NOT_RUN;
  1103. }
  1104. return S_OK;
  1105. }
  1106. //+----------------------------------------------------------------------------
  1107. //
  1108. // Member: CJob::ITask::GetNextRunTime
  1109. //
  1110. // Synopsis: Returns the next time that the job is scheduled to run.
  1111. //
  1112. // Arguments: [pstNextRun] - pointer to return value through
  1113. //
  1114. // Returns: S_OK: the next run time was returned.
  1115. // S_FALSE: there are no more scheduled runs.
  1116. // SCHED_S_EVENT_TRIGGER: there are only event triggers.
  1117. // SCHED_S_TASK_NO_VALID_TRIGGERS: either there are no triggers or
  1118. // the triggers are disabled or not set.
  1119. // HRESULT - failure code.
  1120. //
  1121. //-----------------------------------------------------------------------------
  1122. STDMETHODIMP
  1123. CJob::GetNextRunTime(SYSTEMTIME * pstNextRun)
  1124. {
  1125. TRACE3(CJob, GetNextRunTime)
  1126. HRESULT hr;
  1127. SYSTEMTIME stNow, stEmpty = { 0, 0, 0, 0, 0, 0, 0, 0 };
  1128. GetLocalTime(&stNow);
  1129. CTimeRunList RunList;
  1130. WORD cRuns = 0;
  1131. hr = GetRunTimesP(&stNow, NULL, &cRuns, 1, &RunList, NULL);
  1132. if (S_OK != hr)
  1133. {
  1134. *pstNextRun = stEmpty;
  1135. return hr;
  1136. }
  1137. else if (cRuns == 0)
  1138. {
  1139. *pstNextRun = stEmpty;
  1140. return S_FALSE;
  1141. }
  1142. FILETIME ft;
  1143. RunList.PeekHeadTime(&ft);
  1144. if (!FileTimeToSystemTime(&ft, pstNextRun))
  1145. {
  1146. *pstNextRun = stEmpty;
  1147. return HRESULT_FROM_WIN32(GetLastError());
  1148. }
  1149. return S_OK;
  1150. }
  1151. //+----------------------------------------------------------------------------
  1152. //
  1153. // Member: CJob::ITask::GetExitCode
  1154. //
  1155. // Synopsis: Returns the exit code of the jobs last run.
  1156. //
  1157. // Arguments: [pExitCode] - return value pointer.
  1158. //
  1159. // Returns: HRESULT - error from the last attempt to start the job.
  1160. //
  1161. //-----------------------------------------------------------------------------
  1162. STDMETHODIMP
  1163. CJob::GetExitCode(DWORD * pExitCode)
  1164. {
  1165. TRACE3(CJob, GetExitCode)
  1166. *pExitCode = m_ExitCode;
  1167. return m_hrStartError;
  1168. }
  1169. //+----------------------------------------------------------------------------
  1170. //
  1171. // Member: CJob::ITask::GetStatus
  1172. //
  1173. // Synopsis: Returns the current status of this job object.
  1174. //
  1175. // Arguments: [phrStatus] - yup, the return value.
  1176. //
  1177. // Returns: HRESULTS
  1178. //
  1179. // Notes: The running status is implicit in a non-zero instance count.
  1180. // Otherwise, return the status property. Note also that the
  1181. // job's status and other properties that are obtained by the
  1182. // load from disk are a snapshot that can become stale.
  1183. //-----------------------------------------------------------------------------
  1184. STDMETHODIMP
  1185. CJob::GetStatus(HRESULT * phrStatus)
  1186. {
  1187. TRACE3(CJob, GetStatus)
  1188. if (m_cRunningInstances > 0)
  1189. {
  1190. *phrStatus = SCHED_S_TASK_RUNNING;
  1191. }
  1192. else
  1193. {
  1194. *phrStatus = m_hrStatus;
  1195. }
  1196. return S_OK;
  1197. }
  1198. //+----------------------------------------------------------------------------
  1199. //
  1200. // Member: CJob::ITask::SetCreator
  1201. //
  1202. // Synopsis: Set the creator property.
  1203. //
  1204. // Arguments: [pwszCreator] -- Creator string.
  1205. //
  1206. // Returns: S_OK
  1207. // E_INVALIDARG
  1208. // E_OUTOFMEMORY
  1209. //
  1210. // Notes: None.
  1211. //
  1212. //-----------------------------------------------------------------------------
  1213. STDMETHODIMP
  1214. CJob::SetCreator(LPCWSTR pwszCreator)
  1215. {
  1216. TRACE3(CJob, SetCreator);
  1217. LPWSTR pwsz;
  1218. //
  1219. // Handle empty string.
  1220. //
  1221. if (*pwszCreator)
  1222. {
  1223. pwsz = new WCHAR[wcslen(pwszCreator) + 1];
  1224. if (pwsz == NULL)
  1225. {
  1226. return E_OUTOFMEMORY;
  1227. }
  1228. s_wcscpy(pwsz, pwszCreator);
  1229. }
  1230. else
  1231. {
  1232. pwsz = NULL;
  1233. }
  1234. DELETE_CJOB_FIELD(m_pwszCreator);
  1235. m_pwszCreator = pwsz;
  1236. SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY);
  1237. return S_OK;
  1238. }
  1239. //+----------------------------------------------------------------------------
  1240. //
  1241. // Member: CJob::ITask::GetCreator
  1242. //
  1243. // Synopsis: Fetch the creator property.
  1244. //
  1245. // Arguments: [ppwszCreator] -- Returned string.
  1246. //
  1247. // Returns: S_OK
  1248. // E_INVALIDARG
  1249. // E_OUTOFMEMORY
  1250. //
  1251. // Notes: None.
  1252. //
  1253. //-----------------------------------------------------------------------------
  1254. STDMETHODIMP
  1255. CJob::GetCreator(LPWSTR * ppwszCreator)
  1256. {
  1257. TRACE3(CJob, GetCreator);
  1258. //
  1259. // Handle empty string.
  1260. //
  1261. LPWSTR pwsz = (m_pwszCreator == NULL ? wszEmpty : m_pwszCreator);
  1262. *ppwszCreator = (LPWSTR)CoTaskMemAlloc(
  1263. (s_wcslen(pwsz) + 1) * sizeof(WCHAR));
  1264. if (*ppwszCreator == NULL)
  1265. {
  1266. return E_OUTOFMEMORY;
  1267. }
  1268. s_wcscpy(*ppwszCreator, pwsz);
  1269. return S_OK;
  1270. }