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.

1861 lines
57 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Scheduling Agent Service
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: misc.cxx
  9. //
  10. // Contents: Miscellaneous helper functions
  11. //
  12. // Classes: None.
  13. //
  14. // Functions: StringFromTrigger, GetDaysOfWeekString, GetExitCodeString
  15. //
  16. // History: 08-Dec-95 EricB Created.
  17. //
  18. //----------------------------------------------------------------------------
  19. #include "..\pch\headers.hxx"
  20. #pragma hdrstop
  21. #include <regstr.h> // for app path reg key constant
  22. #include "..\inc\resource.h"
  23. #include "..\inc\misc.hxx"
  24. #include "..\inc\debug.hxx"
  25. const TCHAR PATH_ENV_VAR[] = TEXT("PATH");
  26. static const CHAR gszJobScheduler[] = "SOFTWARE\\Microsoft\\SchedulingAgent";
  27. static LCID sg_lcid = GetUserDefaultLCID();
  28. HRESULT GetMonthsString(WORD rgfMonths, LPTSTR ptszBuf, UINT cchBuf);
  29. //+----------------------------------------------------------------------------
  30. //
  31. // Function: StringFromTrigger
  32. //
  33. // Synopsis: Returns the string representation of the passed in trigger
  34. // data structure.
  35. //
  36. // Arguments: [pTrigger] - the TASK_TRIGGER struct
  37. // [ppwszTrigger] - the returned string
  38. // [lpDetails] - the SHELLDETAILS struct
  39. //
  40. // Returns: HRESULTS
  41. //
  42. // Notes: The string is allocated by this function with CoTaskMemAlloc
  43. // and is caller freed with CoTaskMemFree.
  44. //
  45. // A non-event trigger string is composed of three parts: the
  46. // daily portion (tszDaily) which states when during the day the
  47. // trigger will fire, a trigger type portion (tszTrigType) which
  48. // states what days the trigger will fire, and the calendar
  49. // bracketing portion (tszStartDate and optionally tszEndDate).
  50. //-----------------------------------------------------------------------------
  51. HRESULT
  52. StringFromTrigger(const PTASK_TRIGGER pTrigger, LPWSTR * ppwszTrigger, LPSHELLDETAILS lpDetails)
  53. {
  54. if (IsBadWritePtr(ppwszTrigger, sizeof(WCHAR *)))
  55. {
  56. return E_INVALIDARG;
  57. }
  58. *ppwszTrigger = NULL;
  59. HRESULT hr;
  60. UINT uStrID = 0;
  61. DWORD dwDateFlags;
  62. TCHAR tszNumFmt[] = TEXT("%d");
  63. TCHAR tszNumber[SCH_SMBUF_LEN];
  64. TCHAR tszDOW[SCH_BIGBUF_LEN];
  65. TCHAR tszWhichWeek[SCH_MED0BUF_LEN];
  66. TCHAR tszMedBuf[SCH_MED0BUF_LEN];
  67. TCHAR tszBigBuf[SCH_XBIGBUF_LEN];
  68. TCHAR tszFormat[SCH_BUF_LEN];
  69. TCHAR tszTrigType[SCH_BIGBUF_LEN];
  70. LPTSTR rgptsz[5];
  71. //
  72. // If the trigger has not been set, return a phrase to that effect.
  73. //
  74. if (pTrigger->rgFlags & JOB_TRIGGER_I_FLAG_NOT_SET)
  75. {
  76. uStrID = IDS_TRIGGER_NOT_SET;
  77. goto DoString;
  78. }
  79. //
  80. // Compose the trigger-type string tszTrigType.
  81. //
  82. switch (pTrigger->TriggerType)
  83. {
  84. case TASK_TIME_TRIGGER_ONCE:
  85. //
  86. // Nothing to do here, handled below.
  87. //
  88. break;
  89. case TASK_EVENT_TRIGGER_ON_IDLE:
  90. //
  91. // Event triggers. Since event triggers don't have a set run time,
  92. // they load a string from the resource fork that describes the
  93. // event.
  94. //
  95. uStrID = IDS_IDLE_TRIGGER;
  96. break;
  97. case TASK_EVENT_TRIGGER_AT_SYSTEMSTART:
  98. //
  99. // Event trigger.
  100. //
  101. uStrID = IDS_STARTUP_TRIGGER;
  102. break;
  103. case TASK_EVENT_TRIGGER_AT_LOGON:
  104. //
  105. // Event trigger.
  106. //
  107. uStrID = IDS_LOGON_TRIGGER;
  108. break;
  109. // Not yet implemented:
  110. // case TASK_EVENT_TRIGGER_ON_APM_RESUME:
  111. // //
  112. // // Event trigger.
  113. // //
  114. // uStrID = IDS_RESUME_TRIGGER;
  115. // break;
  116. case TASK_TIME_TRIGGER_DAILY:
  117. //
  118. // Run every n days.
  119. //
  120. if (pTrigger->Type.Daily.DaysInterval == 1)
  121. {
  122. //
  123. // Run every day.
  124. //
  125. if (!LoadString(g_hInstance, IDS_EVERY_DAY, tszTrigType,
  126. SCH_BIGBUF_LEN))
  127. {
  128. hr = HRESULT_FROM_WIN32(GetLastError());
  129. ERR_OUT("Create trigger string: LoadString", hr);
  130. return hr;
  131. }
  132. }
  133. else
  134. {
  135. //
  136. // Run every DaysInterval days: "every %d days"
  137. //
  138. if (!LoadString(g_hInstance, IDS_DAILY_FORMAT, tszFormat,
  139. SCH_BUF_LEN))
  140. {
  141. hr = HRESULT_FROM_WIN32(GetLastError());
  142. ERR_OUT("Create trigger string: LoadString", hr);
  143. return hr;
  144. }
  145. StringCchPrintf(tszTrigType, SCH_BIGBUF_LEN, tszFormat, pTrigger->Type.Daily.DaysInterval);
  146. }
  147. break;
  148. case TASK_TIME_TRIGGER_WEEKLY:
  149. //
  150. // Run on mon, tues, etc every n weeks.
  151. //
  152. hr = GetDaysOfWeekString(pTrigger->Type.Weekly.rgfDaysOfTheWeek,
  153. tszDOW, SCH_BUF_LEN);
  154. if (FAILED(hr))
  155. {
  156. return hr;
  157. }
  158. if (pTrigger->Type.Weekly.WeeksInterval == 1)
  159. {
  160. //
  161. // Run every week: "every %s of every week"
  162. //
  163. if (!LoadString(g_hInstance, IDS_EVERY_WEEK_FORMAT, tszFormat,
  164. SCH_BUF_LEN))
  165. {
  166. hr = HRESULT_FROM_WIN32(GetLastError());
  167. ERR_OUT("Create trigger string: LoadString", hr);
  168. return hr;
  169. }
  170. StringCchPrintf(tszTrigType, SCH_BIGBUF_LEN, tszFormat, tszDOW);
  171. }
  172. else
  173. {
  174. //
  175. // Run every WeeksInterval weeks: "every %s of every %s weeks"
  176. //
  177. StringCchPrintf(tszNumber, SCH_SMBUF_LEN, tszNumFmt, pTrigger->Type.Weekly.WeeksInterval);
  178. rgptsz[0] = tszDOW;
  179. rgptsz[1] = tszNumber;
  180. if (!LoadString(g_hInstance, IDS_WEEKLY_FORMAT, tszFormat,
  181. SCH_BUF_LEN))
  182. {
  183. hr = HRESULT_FROM_WIN32(GetLastError());
  184. ERR_OUT("Create trigger string: LoadString", hr);
  185. return hr;
  186. }
  187. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  188. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat,
  189. 0, 0, tszTrigType, SCH_BIGBUF_LEN,
  190. (va_list *)rgptsz))
  191. {
  192. hr = HRESULT_FROM_WIN32(GetLastError());
  193. ERR_OUT("Create trigger string: FormatMessage", hr);
  194. return hr;
  195. }
  196. }
  197. break;
  198. case TASK_TIME_TRIGGER_MONTHLYDATE:
  199. //
  200. // On specific days of specific months.
  201. //
  202. //
  203. // Get the first run day and append etc if more than one.
  204. //
  205. WORD wFirstDay, cDays, i;
  206. cDays = 0;
  207. for (i = 0; i < JOB_DAYS_PER_MONTHMAX; i++)
  208. {
  209. if ((pTrigger->Type.MonthlyDate.rgfDays >> i) & 0x1)
  210. {
  211. cDays++;
  212. if (cDays == 1)
  213. {
  214. wFirstDay = i + 1;
  215. }
  216. }
  217. }
  218. if (pTrigger->Type.MonthlyDate.rgfMonths == JOB_RGFMONTHS_MAX)
  219. {
  220. //
  221. // Every month: "on day %d(, etc.) of every month"
  222. //
  223. if (!LoadString(g_hInstance,
  224. (cDays == 1) ? IDS_EVERY_MONTHLYDATE_FORMAT :
  225. IDS_EVERY_MONTHLYDATE_FORMAT_ETC,
  226. tszFormat, SCH_BUF_LEN))
  227. {
  228. hr = HRESULT_FROM_WIN32(GetLastError());
  229. ERR_OUT("Create trigger string: LoadString", hr);
  230. return hr;
  231. }
  232. StringCchPrintf(tszTrigType, SCH_BIGBUF_LEN, tszFormat, wFirstDay);
  233. }
  234. else
  235. {
  236. //
  237. // Specific months: "on day %s of %s"
  238. //
  239. StringCchPrintf(tszNumber, SCH_SMBUF_LEN, tszNumFmt, wFirstDay);
  240. hr = GetMonthsString(pTrigger->Type.MonthlyDate.rgfMonths,
  241. tszBigBuf, SCH_XBIGBUF_LEN);
  242. if (FAILED(hr))
  243. {
  244. return hr;
  245. }
  246. rgptsz[0] = tszNumber;
  247. rgptsz[1] = tszBigBuf;
  248. if (!LoadString(g_hInstance,
  249. (cDays == 1) ? IDS_MONTHLYDATE_FORMAT :
  250. IDS_MONTHLYDATE_FORMAT_ETC,
  251. tszFormat, SCH_BUF_LEN))
  252. {
  253. hr = HRESULT_FROM_WIN32(GetLastError());
  254. ERR_OUT("Create trigger string: LoadString", hr);
  255. return hr;
  256. }
  257. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  258. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat, 0, 0,
  259. tszTrigType, SCH_BIGBUF_LEN, (va_list *)rgptsz))
  260. {
  261. hr = HRESULT_FROM_WIN32(GetLastError());
  262. ERR_OUT("Create trigger string: FormatMessage", hr);
  263. return hr;
  264. }
  265. }
  266. break;
  267. case TASK_TIME_TRIGGER_MONTHLYDOW:
  268. //
  269. // On certain weeks of specific months.
  270. //
  271. if (!LoadString(g_hInstance,
  272. IDS_FIRST + pTrigger->Type.MonthlyDOW.wWhichWeek - 1,
  273. tszWhichWeek, SCH_MED0BUF_LEN))
  274. {
  275. hr = HRESULT_FROM_WIN32(GetLastError());
  276. ERR_OUT("Create trigger string: LoadString", hr);
  277. return hr;
  278. }
  279. hr = GetDaysOfWeekString(pTrigger->Type.MonthlyDOW.rgfDaysOfTheWeek,
  280. tszDOW, SCH_BUF_LEN);
  281. if (FAILED(hr))
  282. {
  283. return hr;
  284. }
  285. if (pTrigger->Type.MonthlyDOW.rgfMonths == JOB_RGFMONTHS_MAX)
  286. {
  287. //
  288. // Runs every month: " on the %s %s of every month"
  289. //
  290. rgptsz[0] = tszWhichWeek;
  291. rgptsz[1] = tszDOW;
  292. if (!LoadString(g_hInstance, IDS_EVERY_MONTHLYDOW_FORMAT,
  293. tszFormat, SCH_BUF_LEN))
  294. {
  295. hr = HRESULT_FROM_WIN32(GetLastError());
  296. ERR_OUT("Create trigger string: LoadString", hr);
  297. return hr;
  298. }
  299. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  300. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat, 0, 0,
  301. tszTrigType, SCH_BIGBUF_LEN, (va_list *)rgptsz))
  302. {
  303. hr = HRESULT_FROM_WIN32(GetLastError());
  304. ERR_OUT("Create trigger string: FormatMessage", hr);
  305. return hr;
  306. }
  307. }
  308. else
  309. {
  310. //
  311. // Runs on specific months:
  312. // "on the %s %s of %s"
  313. //
  314. hr = GetMonthsString(pTrigger->Type.MonthlyDOW.rgfMonths,
  315. tszBigBuf, SCH_XBIGBUF_LEN);
  316. if (FAILED(hr))
  317. {
  318. return hr;
  319. }
  320. rgptsz[0] = tszWhichWeek;
  321. rgptsz[1] = tszDOW;
  322. rgptsz[2] = tszBigBuf;
  323. if (!LoadString(g_hInstance, IDS_MONTHLYDOW_FORMAT, tszFormat,
  324. SCH_BUF_LEN))
  325. {
  326. hr = HRESULT_FROM_WIN32(GetLastError());
  327. ERR_OUT("Create trigger string: LoadString", hr);
  328. return hr;
  329. }
  330. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  331. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat, 0, 0,
  332. tszTrigType, SCH_BIGBUF_LEN, (va_list *)rgptsz))
  333. {
  334. hr = HRESULT_FROM_WIN32(GetLastError());
  335. ERR_OUT("Create trigger string: FormatMessage", hr);
  336. return hr;
  337. }
  338. }
  339. break;
  340. default:
  341. schDebugOut((DEB_ERROR, "invalid TriggerType value: 0x%x\n",
  342. pTrigger->TriggerType));
  343. return ERROR_INVALID_DATA;
  344. }
  345. DoString:
  346. //
  347. // Event trigger or Daily part.
  348. //
  349. TCHAR tszTriggerString[SCH_XBIGBUF_LEN];
  350. TCHAR tszStartDate[SCH_DATEBUF_LEN];
  351. TCHAR tszEndDate[SCH_DATEBUF_LEN];
  352. TCHAR tszDaily[SCH_BUF_LEN];
  353. switch (uStrID)
  354. {
  355. case IDS_RESUME_TRIGGER:
  356. case IDS_STARTUP_TRIGGER:
  357. case IDS_LOGON_TRIGGER:
  358. case IDS_IDLE_TRIGGER:
  359. case IDS_TRIGGER_NOT_SET:
  360. //
  361. // Event-based or invalid trigger, load the description string.
  362. //
  363. if (!LoadString(g_hInstance, uStrID, tszTriggerString,
  364. SCH_XBIGBUF_LEN))
  365. {
  366. hr = HRESULT_FROM_WIN32(GetLastError());
  367. ERR_OUT("Create trigger string, LoadString", hr);
  368. return hr;
  369. }
  370. break;
  371. case 0:
  372. //
  373. // It is a time-based trigger.
  374. //
  375. TCHAR tszNum[SCH_SMBUF_LEN];
  376. //
  377. // Get the daily run time(s): tszDaily.
  378. //
  379. TCHAR tszFromTime[SCH_TIMEBUF_LEN];
  380. TCHAR tszToTime[SCH_TIMEBUF_LEN];
  381. SYSTEMTIME st = {0};
  382. st.wHour = pTrigger->wStartHour;
  383. st.wMinute = pTrigger->wStartMinute;
  384. if (!GetTimeFormat(sg_lcid, TIME_NOSECONDS, &st, NULL,
  385. tszFromTime, SCH_TIMEBUF_LEN))
  386. {
  387. hr = HRESULT_FROM_WIN32(GetLastError());
  388. ERR_OUT("Create trigger string: GetTimeFormat", hr);
  389. return hr;
  390. }
  391. if (pTrigger->MinutesInterval == 0)
  392. {
  393. //
  394. // Runs once a day at a specific time.
  395. //
  396. if (!LoadString(g_hInstance, IDS_ONCE_DAY_FORMAT, tszFormat,
  397. SCH_BUF_LEN))
  398. {
  399. hr = HRESULT_FROM_WIN32(GetLastError());
  400. ERR_OUT("Create trigger string: LoadString", hr);
  401. return hr;
  402. }
  403. StringCchPrintf(tszDaily, SCH_BUF_LEN, tszFormat, tszFromTime);
  404. }
  405. else
  406. {
  407. //
  408. // On an interval daily from a starting time for a specified
  409. // number of minutes.
  410. //
  411. UINT uIntTimeStr, uDurTimeStr, uIntStr;
  412. if (pTrigger->MinutesInterval % JOB_MINS_PER_HOUR)
  413. {
  414. //
  415. // Runs on a minutes schedule.
  416. //
  417. StringCchPrintf(tszNum, SCH_SMBUF_LEN, tszNumFmt, pTrigger->MinutesInterval);
  418. uIntTimeStr = IDS_MINUTES_PAREN;
  419. }
  420. else
  421. {
  422. //
  423. // Runs on an hourly schedule.
  424. //
  425. StringCchPrintf(tszNum, SCH_SMBUF_LEN, tszNumFmt, pTrigger->MinutesInterval / JOB_MINS_PER_HOUR);
  426. uIntTimeStr = IDS_HOURS_PAREN;
  427. }
  428. TCHAR tszDuration[SCH_SMBUF_LEN];
  429. TCHAR tszTimePart[SCH_MED0BUF_LEN];
  430. if (!LoadString(g_hInstance, uIntTimeStr, tszTimePart,
  431. SCH_MED0BUF_LEN))
  432. {
  433. hr = HRESULT_FROM_WIN32(GetLastError());
  434. ERR_OUT("Create trigger string: LoadString", hr);
  435. return hr;
  436. }
  437. if (pTrigger->rgFlags & JOB_TRIGGER_I_FLAG_DURATION_AS_TIME)
  438. {
  439. WORD wMinutes = pTrigger->wStartHour * JOB_MINS_PER_HOUR
  440. + pTrigger->wStartMinute;
  441. wMinutes += (WORD)pTrigger->MinutesDuration;
  442. while (wMinutes > JOB_MINS_PER_DAY)
  443. {
  444. wMinutes -= JOB_MINS_PER_DAY;
  445. }
  446. st.wHour = wMinutes / JOB_MINS_PER_HOUR;
  447. st.wMinute = wMinutes % JOB_MINS_PER_HOUR;
  448. if (!GetTimeFormat(sg_lcid, TIME_NOSECONDS, &st, NULL,
  449. tszToTime, SCH_TIMEBUF_LEN))
  450. {
  451. hr = HRESULT_FROM_WIN32(GetLastError());
  452. ERR_OUT("Create trigger string: GetTimeFormat", hr);
  453. return hr;
  454. }
  455. uIntStr = IDS_MULTI_DAILY_FORMAT;
  456. rgptsz[0] = tszNum;
  457. rgptsz[1] = tszTimePart;
  458. rgptsz[2] = tszFromTime;
  459. rgptsz[3] = tszToTime;
  460. }
  461. else
  462. {
  463. if (pTrigger->MinutesDuration % JOB_MINS_PER_HOUR)
  464. {
  465. //
  466. // Express the duration in minutes.
  467. //
  468. StringCchPrintf(tszDuration, SCH_SMBUF_LEN, tszNumFmt, pTrigger->MinutesDuration);
  469. uDurTimeStr = IDS_MINUTES;
  470. }
  471. else
  472. {
  473. //
  474. // No remainder, so express the duration in hours.
  475. //
  476. StringCchPrintf(tszDuration, SCH_SMBUF_LEN, tszNumFmt, pTrigger->MinutesDuration / JOB_MINS_PER_HOUR);
  477. uDurTimeStr = IDS_HOURS_PAREN;
  478. }
  479. if (!LoadString(g_hInstance, uDurTimeStr, tszToTime,
  480. SCH_TIMEBUF_LEN))
  481. {
  482. hr = HRESULT_FROM_WIN32(GetLastError());
  483. ERR_OUT("Create trigger string: LoadString", hr);
  484. return hr;
  485. }
  486. uIntStr = IDS_MULTI_DURATION_FORMAT;
  487. rgptsz[0] = tszNum;
  488. rgptsz[1] = tszTimePart;
  489. rgptsz[2] = tszFromTime;
  490. rgptsz[3] = tszDuration;
  491. rgptsz[4] = tszToTime;
  492. }
  493. if (!LoadString(g_hInstance, uIntStr, tszFormat, SCH_BUF_LEN))
  494. {
  495. hr = HRESULT_FROM_WIN32(GetLastError());
  496. ERR_OUT("Create trigger string: LoadString", hr);
  497. return hr;
  498. }
  499. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  500. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat, 0, 0,
  501. tszDaily, SCH_BUF_LEN, (va_list *)rgptsz))
  502. {
  503. hr = HRESULT_FROM_WIN32(GetLastError());
  504. ERR_OUT("Create trigger string: FormatMessage", hr);
  505. return hr;
  506. }
  507. }
  508. //
  509. // Starting date: tszStartDate.
  510. //
  511. st.wYear = pTrigger->wBeginYear;
  512. st.wMonth = pTrigger->wBeginMonth;
  513. st.wDay = pTrigger->wBeginDay;
  514. dwDateFlags = DATE_SHORTDATE;
  515. if (lpDetails) {
  516. if (lpDetails->fmt & LVCFMT_RIGHT_TO_LEFT) {
  517. dwDateFlags |= DATE_RTLREADING;
  518. } else if (lpDetails->fmt & LVCFMT_LEFT_TO_RIGHT) {
  519. dwDateFlags |= DATE_LTRREADING;
  520. }
  521. }
  522. if (!GetDateFormat(sg_lcid, dwDateFlags, &st, NULL, tszStartDate,
  523. SCH_DATEBUF_LEN))
  524. {
  525. hr = HRESULT_FROM_WIN32(GetLastError());
  526. ERR_OUT("Create trigger string: GetDateFormat", hr);
  527. return hr;
  528. }
  529. //
  530. // Compose the complete trigger string from its parts.
  531. //
  532. if (pTrigger->TriggerType == TASK_TIME_TRIGGER_ONCE)
  533. {
  534. //
  535. // Trigger runs just on a single day: "%s on %s"
  536. //
  537. rgptsz[0] = tszDaily;
  538. rgptsz[1] = tszStartDate;
  539. if (!LoadString(g_hInstance, IDS_RUNS_ONCE_FORMAT, tszFormat,
  540. SCH_BUF_LEN))
  541. {
  542. hr = HRESULT_FROM_WIN32(GetLastError());
  543. ERR_OUT("Create trigger string: LoadString", hr);
  544. return hr;
  545. }
  546. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  547. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat, 0, 0,
  548. tszTriggerString, SCH_XBIGBUF_LEN,
  549. (va_list *)rgptsz))
  550. {
  551. hr = HRESULT_FROM_WIN32(GetLastError());
  552. ERR_OUT("Create trigger string: FormatMessage", hr);
  553. return hr;
  554. }
  555. }
  556. else
  557. {
  558. if (pTrigger->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE)
  559. {
  560. //
  561. // Job has an ending date.
  562. //
  563. st.wYear = pTrigger->wEndYear;
  564. st.wMonth = pTrigger->wEndMonth;
  565. st.wDay = pTrigger->wEndDay;
  566. dwDateFlags = DATE_SHORTDATE;
  567. if (lpDetails) {
  568. if (lpDetails->fmt & LVCFMT_RIGHT_TO_LEFT) {
  569. dwDateFlags |= DATE_RTLREADING;
  570. } else if (lpDetails->fmt & LVCFMT_LEFT_TO_RIGHT) {
  571. dwDateFlags |= DATE_LTRREADING;
  572. }
  573. }
  574. if (!GetDateFormat(sg_lcid, dwDateFlags, &st, NULL,
  575. tszEndDate, SCH_DATEBUF_LEN))
  576. {
  577. hr = HRESULT_FROM_WIN32(GetLastError());
  578. ERR_OUT("Create trigger string, GetDateFormat", hr);
  579. return hr;
  580. }
  581. //
  582. // Compose the trigger string with an end date.
  583. // "%s %s, starting %s & ending %s"
  584. //
  585. rgptsz[0] = tszDaily;
  586. rgptsz[1] = tszTrigType;
  587. rgptsz[2] = tszStartDate;
  588. rgptsz[3] = tszEndDate;
  589. if (!LoadString(g_hInstance, IDS_HAS_END_DATE_FORMAT,
  590. tszFormat, SCH_BUF_LEN))
  591. {
  592. hr = HRESULT_FROM_WIN32(GetLastError());
  593. ERR_OUT("Create trigger string: LoadString", hr);
  594. return hr;
  595. }
  596. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  597. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat,
  598. 0, 0, tszTriggerString, SCH_XBIGBUF_LEN,
  599. (va_list *)rgptsz))
  600. {
  601. hr = HRESULT_FROM_WIN32(GetLastError());
  602. ERR_OUT("Create trigger string: FormatMessage", hr);
  603. return hr;
  604. }
  605. }
  606. else
  607. {
  608. //
  609. // Trigger does not have an ending date.
  610. // "%s %s, starting %s"
  611. //
  612. rgptsz[0] = tszDaily;
  613. rgptsz[1] = tszTrigType;
  614. rgptsz[2] = tszStartDate;
  615. if (!LoadString(g_hInstance, IDS_NO_END_DATE_FORMAT,
  616. tszFormat, SCH_BUF_LEN))
  617. {
  618. hr = HRESULT_FROM_WIN32(GetLastError());
  619. ERR_OUT("Create trigger string: LoadString", hr);
  620. return hr;
  621. }
  622. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  623. FORMAT_MESSAGE_ARGUMENT_ARRAY, tszFormat,
  624. 0, 0, tszTriggerString, SCH_XBIGBUF_LEN,
  625. (va_list *)rgptsz))
  626. {
  627. hr = HRESULT_FROM_WIN32(GetLastError());
  628. ERR_OUT("Create trigger string: FormatMessage", hr);
  629. return hr;
  630. }
  631. }
  632. }
  633. break;
  634. }
  635. ULONG cch;
  636. //
  637. // Get the size to allocate for the returned string.
  638. //
  639. cch = wcslen(tszTriggerString) + 1; // Include the null in the count.
  640. //
  641. // Allocate the returned string.
  642. //
  643. *ppwszTrigger = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));
  644. if (*ppwszTrigger == NULL)
  645. {
  646. return E_OUTOFMEMORY;
  647. }
  648. StringCchCopy(*ppwszTrigger, cch, tszTriggerString);
  649. return S_OK;
  650. }
  651. //+----------------------------------------------------------------------------
  652. //
  653. // Function: GetDaysOfWeekString
  654. //
  655. // Synopsis: Builds a string containing the names of the days of the week
  656. // that correspond to bits set in the bitset array.
  657. //
  658. // Arguments: [rgfDaysOfTheWeek] - a bitset array.
  659. // [pwszBuf] - return in this string buffer.
  660. // [cchBuf] - size of string buffer.
  661. //
  662. //-----------------------------------------------------------------------------
  663. HRESULT
  664. GetDaysOfWeekString(WORD rgfDaysOfTheWeek, LPTSTR ptszBuf, UINT cchBuf)
  665. {
  666. HRESULT hr;
  667. LCID lcid = GetSystemDefaultLCID();
  668. if (rgfDaysOfTheWeek == 0)
  669. {
  670. return E_INVALIDARG;
  671. }
  672. BOOL fMoreThanOne = FALSE;
  673. int cch;
  674. TCHAR tszSep[SCH_SMBUF_LEN];
  675. if (!LoadString(g_hInstance, IDS_LIST_SEP, tszSep, SCH_SMBUF_LEN))
  676. {
  677. hr = HRESULT_FROM_WIN32(GetLastError());
  678. ERR_OUT("GetDaysOfWeekString: LoadString", hr);
  679. return hr;
  680. }
  681. *ptszBuf = TEXT('\0');
  682. if (rgfDaysOfTheWeek & TASK_MONDAY)
  683. {
  684. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME1, ptszBuf, cchBuf))
  685. {
  686. hr = HRESULT_FROM_WIN32(GetLastError());
  687. ERR_OUT("GetLocaleInfo", hr);
  688. return hr;
  689. }
  690. fMoreThanOne = TRUE;
  691. }
  692. if (rgfDaysOfTheWeek & TASK_TUESDAY)
  693. {
  694. if (fMoreThanOne)
  695. {
  696. StringCchCat(ptszBuf, cchBuf, tszSep);
  697. }
  698. cch = lstrlen(ptszBuf);
  699. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME2, ptszBuf + cch,
  700. cchBuf - cch))
  701. {
  702. hr = HRESULT_FROM_WIN32(GetLastError());
  703. ERR_OUT("GetLocaleInfo", hr);
  704. return hr;
  705. }
  706. fMoreThanOne = TRUE;
  707. }
  708. if (rgfDaysOfTheWeek & TASK_WEDNESDAY)
  709. {
  710. if (fMoreThanOne)
  711. {
  712. StringCchCat(ptszBuf, cchBuf, tszSep);
  713. }
  714. cch = lstrlen(ptszBuf);
  715. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME3, ptszBuf + cch,
  716. cchBuf - cch))
  717. {
  718. hr = HRESULT_FROM_WIN32(GetLastError());
  719. ERR_OUT("GetLocaleInfo", hr);
  720. return hr;
  721. }
  722. fMoreThanOne = TRUE;
  723. }
  724. if (rgfDaysOfTheWeek & TASK_THURSDAY)
  725. {
  726. if (fMoreThanOne)
  727. {
  728. StringCchCat(ptszBuf, cchBuf, tszSep);
  729. }
  730. cch = lstrlen(ptszBuf);
  731. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME4, ptszBuf + cch,
  732. cchBuf - cch))
  733. {
  734. hr = HRESULT_FROM_WIN32(GetLastError());
  735. ERR_OUT("GetLocaleInfo", hr);
  736. return hr;
  737. }
  738. fMoreThanOne = TRUE;
  739. }
  740. if (rgfDaysOfTheWeek & TASK_FRIDAY)
  741. {
  742. if (fMoreThanOne)
  743. {
  744. StringCchCat(ptszBuf, cchBuf, tszSep);
  745. }
  746. cch = lstrlen(ptszBuf);
  747. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME5, ptszBuf + cch,
  748. cchBuf - cch))
  749. {
  750. hr = HRESULT_FROM_WIN32(GetLastError());
  751. ERR_OUT("GetLocaleInfo", hr);
  752. return hr;
  753. }
  754. fMoreThanOne = TRUE;
  755. }
  756. if (rgfDaysOfTheWeek & TASK_SATURDAY)
  757. {
  758. if (fMoreThanOne)
  759. {
  760. StringCchCat(ptszBuf, cchBuf, tszSep);
  761. }
  762. cch = lstrlen(ptszBuf);
  763. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME6, ptszBuf + cch,
  764. cchBuf - cch))
  765. {
  766. hr = HRESULT_FROM_WIN32(GetLastError());
  767. ERR_OUT("GetLocaleInfo", hr);
  768. return hr;
  769. }
  770. fMoreThanOne = TRUE;
  771. }
  772. if (rgfDaysOfTheWeek & TASK_SUNDAY)
  773. {
  774. if (fMoreThanOne)
  775. {
  776. StringCchCat(ptszBuf, cchBuf, tszSep);
  777. }
  778. cch = lstrlen(ptszBuf);
  779. if (!GetLocaleInfo(lcid, LOCALE_SABBREVDAYNAME7, ptszBuf + cch,
  780. cchBuf - cch))
  781. {
  782. hr = HRESULT_FROM_WIN32(GetLastError());
  783. ERR_OUT("GetLocaleInfo", hr);
  784. return hr;
  785. }
  786. }
  787. return S_OK;
  788. }
  789. //+----------------------------------------------------------------------------
  790. //
  791. // Function: GetMonthsString
  792. //
  793. // Synopsis: Builds a string containing the names of the months
  794. // that correspond to bits set in the bitset array.
  795. //
  796. // Arguments: [rgfMonths] - a bitset array.
  797. // [pwszBuf] - return in this string buffer.
  798. // [cchBuf] - size of string buffer.
  799. //
  800. //-----------------------------------------------------------------------------
  801. HRESULT
  802. GetMonthsString(WORD rgfMonths, LPTSTR ptszBuf, UINT cchBuf)
  803. {
  804. if (rgfMonths == 0)
  805. {
  806. return E_INVALIDARG;
  807. }
  808. LCID lcid = GetSystemDefaultLCID();
  809. HRESULT hr;
  810. BOOL fMoreThanOne = FALSE;
  811. int cch;
  812. TCHAR tszSep[SCH_SMBUF_LEN];
  813. if (!LoadString(g_hInstance, IDS_LIST_SEP, tszSep, SCH_SMBUF_LEN))
  814. {
  815. hr = HRESULT_FROM_WIN32(GetLastError());
  816. ERR_OUT("GetMonthsString: LoadString", hr);
  817. return hr;
  818. }
  819. *ptszBuf = TEXT('\0');
  820. for (WORD i = 0; i < JOB_MONTHS_PER_YEAR; i++)
  821. {
  822. if ((rgfMonths >> i) & 0x1)
  823. {
  824. if (fMoreThanOne)
  825. {
  826. StringCchCat(ptszBuf, cchBuf, tszSep);
  827. }
  828. cch = lstrlen(ptszBuf);
  829. if (!GetLocaleInfo(lcid, LOCALE_SABBREVMONTHNAME1 + i,
  830. ptszBuf + cch, cchBuf - cch))
  831. {
  832. hr = HRESULT_FROM_WIN32(GetLastError());
  833. ERR_OUT("GetMonthsString: GetLocaleInfo", hr);
  834. return hr;
  835. }
  836. fMoreThanOne = TRUE;
  837. }
  838. }
  839. return S_OK;
  840. }
  841. //+---------------------------------------------------------------------------
  842. //
  843. // Function: SchedMapRpcError
  844. //
  845. // Purpose: Remap RPC exception codes that are unsuitable for displaying
  846. // to the user to more comprehensible errors.
  847. //
  848. // Arguments: [dwError] - the error returned by RpcExceptionCode().
  849. //
  850. // Returns: An HRESULT.
  851. //
  852. //----------------------------------------------------------------------------
  853. HRESULT
  854. SchedMapRpcError(
  855. DWORD dwError)
  856. {
  857. HRESULT hr;
  858. if (dwError == EPT_S_NOT_REGISTERED)
  859. {
  860. hr = SCHED_E_SERVICE_NOT_RUNNING;
  861. }
  862. else
  863. {
  864. hr = HRESULT_FROM_WIN32(dwError);
  865. }
  866. return hr;
  867. }
  868. //+---------------------------------------------------------------------------
  869. //
  870. // Function: ComposeErrorMsg
  871. //
  872. // Purpose: Take the two message IDs and the error code and create an
  873. // error reporting string that can be used by both service
  874. // logging and UI dialogs.
  875. //
  876. // Arguments: same as above.
  877. //
  878. // Returns: A string or NULL on failure.
  879. //
  880. // Notes: Release the string memory when done using LocalFree.
  881. //
  882. //----------------------------------------------------------------------------
  883. LPTSTR
  884. ComposeErrorMsg(
  885. UINT uErrorClassMsgID,
  886. DWORD dwErrCode,
  887. UINT uHelpHintMsgID,
  888. BOOL fIndent)
  889. {
  890. TCHAR szErrClassMsg[SCH_BIGBUF_LEN];
  891. if (!LoadString(g_hInstance,
  892. uErrorClassMsgID,
  893. szErrClassMsg,
  894. SCH_BIGBUF_LEN))
  895. {
  896. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  897. return NULL;
  898. }
  899. BOOL fDelete = FALSE;
  900. DWORD ccLength = 0;
  901. TCHAR szErrCode[SCH_MED0BUF_LEN], szGenericErr[SCH_BUF_LEN];
  902. TCHAR * psz;
  903. LPTSTR pszErrStr = NULL;
  904. DWORD dwWinErr = dwErrCode;
  905. if (dwErrCode != 0)
  906. {
  907. //
  908. // Format the error code as a hex string.
  909. //
  910. TCHAR szErrNumFormat[SCH_MED0BUF_LEN];
  911. if (!LoadString(g_hInstance,
  912. IDS_ERROR_NUMBER_FORMAT,
  913. szErrNumFormat,
  914. SCH_MED0BUF_LEN))
  915. {
  916. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  917. return NULL;
  918. }
  919. StringCchPrintf(szErrCode, SCH_MED0BUF_LEN, szErrNumFormat, dwErrCode);
  920. //
  921. // If a Win32 error code, strip the HRESULT stuff.
  922. //
  923. if (HRESULT_FACILITY(dwErrCode) == FACILITY_WINDOWS ||
  924. HRESULT_FACILITY(dwErrCode) == FACILITY_WIN32)
  925. {
  926. dwWinErr = HRESULT_CODE(dwErrCode);
  927. }
  928. //
  929. // Try to obtain the error message from the system.
  930. //
  931. if (!(ccLength = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  932. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  933. NULL,
  934. dwWinErr,
  935. LOCALE_SYSTEM_DEFAULT,
  936. (LPTSTR) &pszErrStr,
  937. 1,
  938. NULL)))
  939. {
  940. DWORD di = GetLastError( );
  941. //
  942. // Well, that didn't work, so try to get it from the service.
  943. //
  944. if (!(ccLength = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
  945. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  946. g_hInstance,
  947. dwErrCode,
  948. LOCALE_SYSTEM_DEFAULT,
  949. (LPTSTR) &pszErrStr,
  950. 1,
  951. NULL)))
  952. {
  953. //
  954. // That didn't work either, so give a generic message.
  955. //
  956. if (!LoadString(g_hInstance,
  957. IDS_GENERIC_ERROR_MSG,
  958. szGenericErr,
  959. SCH_BUF_LEN))
  960. {
  961. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  962. return NULL;
  963. }
  964. pszErrStr = szGenericErr;
  965. }
  966. }
  967. if (ccLength != 0)
  968. {
  969. fDelete = TRUE;
  970. //
  971. // Overwrite \r\n with null characters.
  972. //
  973. psz = pszErrStr + ccLength - 2;
  974. *psz++ = '\0';
  975. *psz = '\0';
  976. }
  977. }
  978. TCHAR * pwszLogStr = NULL;
  979. TCHAR * rgpszInserts[4];
  980. rgpszInserts[0] = szErrClassMsg;
  981. UINT uFormatID;
  982. TCHAR szHelpMsg[SCH_BIGBUF_LEN];
  983. if (uHelpHintMsgID == 0 && dwWinErr != 0)
  984. {
  985. //
  986. // Caller didn't specify a help hint. Try to map the error to one.
  987. //
  988. switch (dwWinErr)
  989. {
  990. case ERROR_FILE_NOT_FOUND:
  991. case ERROR_PATH_NOT_FOUND:
  992. case ERROR_BAD_PATHNAME:
  993. case ERROR_DIRECTORY:
  994. case ERROR_ACCESS_DENIED:
  995. case ERROR_NO_NET_OR_BAD_PATH:
  996. case ERROR_INVALID_DRIVE:
  997. case ERROR_INVALID_COMPUTERNAME:
  998. case ERROR_INVALID_SHARENAME:
  999. uHelpHintMsgID = IDS_HELP_HINT_BROWSE;
  1000. break;
  1001. case ERROR_TOO_MANY_OPEN_FILES:
  1002. case ERROR_NOT_ENOUGH_MEMORY:
  1003. case ERROR_OUTOFMEMORY:
  1004. case ERROR_TOO_MANY_NAMES:
  1005. case ERROR_TOO_MANY_SESS:
  1006. case ERROR_OUT_OF_STRUCTURES:
  1007. case ERROR_NO_PROC_SLOTS:
  1008. case ERROR_TOO_MANY_SEMAPHORES:
  1009. case ERROR_NO_MORE_SEARCH_HANDLES:
  1010. case ERROR_TOO_MANY_TCBS:
  1011. case ERROR_MAX_THRDS_REACHED:
  1012. case ERROR_DLL_INIT_FAILED:
  1013. case ERROR_NO_SYSTEM_RESOURCES:
  1014. case ERROR_NONPAGED_SYSTEM_RESOURCES:
  1015. case ERROR_PAGED_SYSTEM_RESOURCES:
  1016. case RPC_S_OUT_OF_RESOURCES:
  1017. uHelpHintMsgID = IDS_HELP_HINT_CLOSE_APPS;
  1018. break;
  1019. case ERROR_NOT_SUPPORTED:
  1020. case ERROR_REM_NOT_LIST:
  1021. case ERROR_DUP_NAME:
  1022. case ERROR_BAD_NETPATH:
  1023. case ERROR_NETWORK_BUSY:
  1024. case ERROR_DEV_NOT_EXIST:
  1025. case ERROR_TOO_MANY_CMDS:
  1026. case ERROR_ADAP_HDW_ERR:
  1027. case ERROR_BAD_NET_RESP:
  1028. case ERROR_UNEXP_NET_ERR:
  1029. case ERROR_BAD_REM_ADAP:
  1030. case ERROR_NETNAME_DELETED:
  1031. case ERROR_NETWORK_ACCESS_DENIED:
  1032. case ERROR_BAD_DEV_TYPE:
  1033. case ERROR_BAD_NET_NAME:
  1034. case ERROR_SHARING_PAUSED:
  1035. case ERROR_REQ_NOT_ACCEP:
  1036. case ERROR_REMOTE_SESSION_LIMIT_EXCEEDED:
  1037. case ERROR_NO_NETWORK:
  1038. case ERROR_NETWORK_UNREACHABLE:
  1039. case ERROR_HOST_UNREACHABLE:
  1040. case ERROR_PROTOCOL_UNREACHABLE:
  1041. case ERROR_PORT_UNREACHABLE:
  1042. case ERROR_CONNECTION_COUNT_LIMIT:
  1043. case ERROR_NO_LOGON_SERVERS:
  1044. uHelpHintMsgID = IDS_HELP_HINT_CALLPSS;
  1045. break;
  1046. case ERROR_BADDB:
  1047. case ERROR_BADKEY:
  1048. case ERROR_CANTOPEN:
  1049. case ERROR_CANTREAD:
  1050. case ERROR_CANTWRITE:
  1051. case ERROR_REGISTRY_CORRUPT:
  1052. case ERROR_REGISTRY_IO_FAILED:
  1053. case ERROR_KEY_DELETED:
  1054. case ERROR_DLL_NOT_FOUND:
  1055. uHelpHintMsgID = IDS_HELP_HINT_REINSTALL;
  1056. break;
  1057. case EPT_S_NOT_REGISTERED:
  1058. uHelpHintMsgID = IDS_HELP_HINT_STARTSVC;
  1059. break;
  1060. }
  1061. }
  1062. if (uHelpHintMsgID != 0)
  1063. {
  1064. //
  1065. // A help hint string has been specified.
  1066. //
  1067. if (!LoadString(g_hInstance,
  1068. uHelpHintMsgID,
  1069. szHelpMsg,
  1070. SCH_BIGBUF_LEN))
  1071. {
  1072. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  1073. goto CleanUp;
  1074. }
  1075. if (dwErrCode != 0)
  1076. {
  1077. //
  1078. // An error code has also been given.
  1079. //
  1080. rgpszInserts[1] = szErrCode;
  1081. rgpszInserts[2] = pszErrStr;
  1082. rgpszInserts[3] = szHelpMsg;
  1083. uFormatID = (fIndent ? IDS_ERROR_FORMAT_WCODE_WHELP_I :
  1084. IDS_ERROR_FORMAT_WCODE_WHELP);
  1085. }
  1086. else
  1087. {
  1088. //
  1089. // Help string but no error code.
  1090. //
  1091. rgpszInserts[1] = szHelpMsg;
  1092. uFormatID = (fIndent ? IDS_ERROR_FORMAT_WOCODE_WHELP_I :
  1093. IDS_ERROR_FORMAT_WOCODE_WHELP);
  1094. }
  1095. }
  1096. else
  1097. {
  1098. //
  1099. // No help hint.
  1100. //
  1101. if (dwErrCode != 0)
  1102. {
  1103. //
  1104. // An error code has been given.
  1105. //
  1106. rgpszInserts[1] = szErrCode;
  1107. rgpszInserts[2] = pszErrStr;
  1108. uFormatID = (fIndent ? IDS_ERROR_FORMAT_WCODE_WOHELP_I :
  1109. IDS_ERROR_FORMAT_WCODE_WOHELP);
  1110. }
  1111. else
  1112. {
  1113. //
  1114. // No help string or error code.
  1115. //
  1116. uFormatID = (fIndent ? IDS_ERROR_FORMAT_WOCODE_WOHELP_I :
  1117. IDS_ERROR_FORMAT_WOCODE_WOHELP);
  1118. }
  1119. }
  1120. TCHAR szFormat[SCH_BIGBUF_LEN];
  1121. if (!LoadString(g_hInstance,
  1122. uFormatID,
  1123. szFormat,
  1124. SCH_BIGBUF_LEN))
  1125. {
  1126. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  1127. goto CleanUp;
  1128. }
  1129. if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  1130. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  1131. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1132. szFormat,
  1133. 0,
  1134. 0,
  1135. (LPTSTR) &pwszLogStr,
  1136. 1,
  1137. (va_list *) rgpszInserts))
  1138. {
  1139. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  1140. goto CleanUp;
  1141. }
  1142. CleanUp:
  1143. if (fDelete)
  1144. {
  1145. LocalFree(pszErrStr);
  1146. }
  1147. return pwszLogStr;
  1148. }
  1149. //+---------------------------------------------------------------------------
  1150. //
  1151. // Function: GetExitCodeString
  1152. //
  1153. // Synopsis: Retrieve the string associated with the exit code from a
  1154. // message file. Algorithm:
  1155. //
  1156. // Consult the Software\Microsoft\Job Scheduler subkey.
  1157. //
  1158. // Attempt to retrieve the ExitCodeMessageFile string value from
  1159. // a subkey matching the job executable prefix (i.e., minus the
  1160. // extension).
  1161. //
  1162. // The ExitCodeMessageFile specifies a binary from which the
  1163. // message string associated with the exit code value is fetched.
  1164. //
  1165. // Arguments: [dwExitCode] -- Job exit code.
  1166. // [pwszExitCodeValue] -- Job exit code in string form.
  1167. // [pszJobExecutable] -- Binary name executed with the job.
  1168. //
  1169. // Returns: TCHAR * exit code string
  1170. // NULL on error
  1171. //
  1172. // Notes: FormatMessage allocates the return string. Use LocalFree() to
  1173. // deallocate.
  1174. //
  1175. //----------------------------------------------------------------------------
  1176. TCHAR *
  1177. GetExitCodeString(
  1178. DWORD dwExitCode,
  1179. TCHAR * ptszExitCodeValue,
  1180. TCHAR * ptszJobExecutable)
  1181. {
  1182. static TCHAR tszExitCodeMsgFile[] = TEXT("ExitCodeMessageFile");
  1183. TCHAR tszBuffer[MAX_PATH + 1];
  1184. DWORD cbBufferSize = sizeof(tszBuffer);
  1185. TCHAR * ptszExt;
  1186. TCHAR * ptszExitCode = NULL;
  1187. DWORD dwType;
  1188. // Isolate job app name prefix.
  1189. //
  1190. if ((ptszExt = _tcsrchr(ptszJobExecutable, TEXT('.'))) != NULL)
  1191. {
  1192. *ptszExt = TEXT('\0');
  1193. }
  1194. HKEY hKey;
  1195. HKEY hSubKey;
  1196. // BUGBUG : Cache job scheduler key.
  1197. if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE,
  1198. gszJobScheduler,
  1199. 0,
  1200. KEY_READ,
  1201. &hKey))
  1202. {
  1203. // Open the JobScheduler\<app> subkey and fetch the exit code
  1204. // message file name.
  1205. //
  1206. if (!RegOpenKeyEx(hKey, ptszJobExecutable, 0, KEY_READ, &hSubKey))
  1207. {
  1208. if (!RegQueryValueEx(hSubKey,
  1209. tszExitCodeMsgFile,
  1210. NULL,
  1211. &dwType,
  1212. (UCHAR *)tszBuffer,
  1213. &cbBufferSize) && dwType == REG_SZ)
  1214. {
  1215. // Load the resource as a data file, so no code executes
  1216. // in our process.
  1217. //
  1218. HINSTANCE hInst = LoadLibraryEx(tszBuffer,
  1219. NULL,
  1220. LOAD_LIBRARY_AS_DATAFILE);
  1221. if (hInst != NULL)
  1222. {
  1223. ULONG ccSize;
  1224. if (ccSize = FormatMessage(
  1225. FORMAT_MESSAGE_FROM_HMODULE |
  1226. FORMAT_MESSAGE_IGNORE_INSERTS |
  1227. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  1228. hInst,
  1229. dwExitCode,
  1230. LOCALE_SYSTEM_DEFAULT,
  1231. (TCHAR *)&ptszExitCode,
  1232. 1,
  1233. NULL))
  1234. {
  1235. // Overwrite \r\n with null characters.
  1236. //
  1237. TCHAR * ptsz = ptszExitCode + lstrlen(ptszExitCode) - 2;
  1238. *ptsz++ = TEXT('\0');
  1239. *ptsz = TEXT('\0');
  1240. }
  1241. else
  1242. {
  1243. CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
  1244. }
  1245. FreeLibrary(hInst);
  1246. }
  1247. }
  1248. RegCloseKey(hSubKey);
  1249. }
  1250. RegCloseKey(hKey);
  1251. }
  1252. if (ptszExt != NULL) // Restore '.'
  1253. {
  1254. *ptszExt = '.';
  1255. }
  1256. return(ptszExitCode);
  1257. }
  1258. //+---------------------------------------------------------------------------
  1259. //
  1260. // Function: GetParentDirectory
  1261. //
  1262. // Synopsis: Return the parent directory of the path indicated.
  1263. //
  1264. // Arguments: [ptszPath] -- Input path.
  1265. // [tszDirectory] -- Caller-allocated returned directory.
  1266. // [cchBuf] -- size of caller-allocated buffer
  1267. //
  1268. // Returns: None.
  1269. //
  1270. // Notes: None.
  1271. //
  1272. //----------------------------------------------------------------------------
  1273. void
  1274. GetParentDirectory(LPCTSTR ptszPath, TCHAR tszDirectory[], size_t cchBuf)
  1275. {
  1276. StringCchCopy(tszDirectory, cchBuf, ptszPath);
  1277. LPTSTR ptsz = _tcsrchr(tszDirectory, _T('\\'));
  1278. if (ptsz == NULL)
  1279. {
  1280. tszDirectory[0] = _T('\\');
  1281. }
  1282. else
  1283. {
  1284. *ptsz = _T('\0');
  1285. }
  1286. }
  1287. //+---------------------------------------------------------------------------
  1288. //
  1289. // Function: GetAppNameFromPath
  1290. //
  1291. // Synopsis: Copy the filename portion (without double quotes) of full
  1292. // or partial path in [tszAppPathName] into buffer pointed to
  1293. // by [tszCopyTo].
  1294. //
  1295. // Arguments: [tszAppPathName] - full or partial path
  1296. // [tszCopyTo] - destination buffer
  1297. // [cchMax] - max size, in chars, of buffer
  1298. //
  1299. // Modifies: *[tszCopyTo]
  1300. //
  1301. // History: 09-17-96 DavidMun Created
  1302. //
  1303. //----------------------------------------------------------------------------
  1304. VOID
  1305. GetAppNameFromPath(
  1306. LPCTSTR tszAppPathName,
  1307. LPTSTR tszCopyTo,
  1308. ULONG cchMax)
  1309. {
  1310. LPCTSTR ptszStart;
  1311. LPCTSTR ptszLastSlash;
  1312. if (tszAppPathName[0] == TEXT('"'))
  1313. {
  1314. ptszStart = &tszAppPathName[1];
  1315. }
  1316. else
  1317. {
  1318. ptszStart = tszAppPathName;
  1319. }
  1320. ptszLastSlash = _tcsrchr(ptszStart, TEXT('\\'));
  1321. if (ptszLastSlash)
  1322. {
  1323. lstrcpyn(tszCopyTo, ptszLastSlash + 1, cchMax);
  1324. }
  1325. else
  1326. {
  1327. lstrcpyn(tszCopyTo, ptszStart, cchMax);
  1328. }
  1329. LPTSTR ptszQuote = _tcschr(tszCopyTo, TEXT('"'));
  1330. if (ptszQuote)
  1331. {
  1332. *ptszQuote = TEXT('\0');
  1333. }
  1334. }
  1335. //+---------------------------------------------------------------------------
  1336. //
  1337. // Function: SetAppPath
  1338. //
  1339. // Synopsis: If the application in the full or partial path
  1340. // [tszAppPathName] has an app path registry entry, save the
  1341. // current value of the path variable in [pptszSavedPath] and
  1342. // prepend the app path to the current path.
  1343. //
  1344. // Arguments: [tszAppPathName] - full or partial path to application
  1345. // [pptszSavedPath] - filled with ptr to saved path string
  1346. //
  1347. // Modifies: *[pptszSavedPath], PATH environment variable
  1348. //
  1349. // Returns: nonzero - path environment var was set
  1350. // zero - path environment var not set
  1351. //
  1352. // History: 09-17-96 DavidMun Created
  1353. //
  1354. //----------------------------------------------------------------------------
  1355. BOOL SetAppPath(LPCTSTR tszAppPathName, LPTSTR *pptszSavedPath)
  1356. {
  1357. BOOL fChangedPath = FALSE;
  1358. LPTSTR tszAppName[MAX_PATH];
  1359. TCHAR tszAppPathVar[MAX_PATH_VALUE];
  1360. do
  1361. {
  1362. //
  1363. // Init out var
  1364. //
  1365. *pptszSavedPath = NULL;
  1366. //
  1367. // See if there's an app key with a PATH value. If not we're done.
  1368. //
  1369. GetAppNameFromPath(tszAppPathName, (LPTSTR)tszAppName, MAX_PATH);
  1370. GetAppPathInfo((LPCTSTR)tszAppName,
  1371. NULL,
  1372. 0,
  1373. tszAppPathVar,
  1374. MAX_PATH_VALUE);
  1375. if (!*tszAppPathVar)
  1376. {
  1377. break;
  1378. }
  1379. //
  1380. // Save the original path, if it exists.
  1381. //
  1382. ULONG cchOriginalPath = GetEnvironmentVariable(PATH_ENV_VAR, NULL,0);
  1383. if (0 != cchOriginalPath)
  1384. {
  1385. //
  1386. // It exists, try to alloc mem to save it. If we can't, we'll try
  1387. // to run the app without setting its app path, but CreateProcess
  1388. // will probably fail anyway due to lack of resources.
  1389. //
  1390. *pptszSavedPath = new TCHAR[cchOriginalPath];
  1391. if (NULL == *pptszSavedPath)
  1392. {
  1393. ERR_OUT("SetAppPath: trying to save path", E_OUTOFMEMORY);
  1394. break;
  1395. }
  1396. GetEnvironmentVariable(PATH_ENV_VAR,
  1397. *pptszSavedPath,
  1398. cchOriginalPath);
  1399. }
  1400. //
  1401. // Now build the new path by prepending the app's path to the original
  1402. // path. Note cchNewPath includes 1 for terminating nul, and
  1403. // cchOriginalPath does also, if it isn't 0. This will give us 1
  1404. // character extra if we are to be concatenating cchOriginalPath.
  1405. // We'll use that extra character for the semicolon separating new and
  1406. // original paths.
  1407. //
  1408. ULONG cchNewPath = lstrlen(tszAppPathVar) + 1;
  1409. LPTSTR ptszNewPath = new TCHAR[cchNewPath + cchOriginalPath];
  1410. if (NULL == ptszNewPath)
  1411. {
  1412. ERR_OUT("SetAppPath: trying to get AppPath", E_OUTOFMEMORY);
  1413. break;
  1414. }
  1415. StringCchCopy(ptszNewPath, cchNewPath, tszAppPathVar);
  1416. if (0 != cchOriginalPath)
  1417. {
  1418. ptszNewPath[cchNewPath - 1] = TEXT(';');
  1419. StringCchCopy(&ptszNewPath[cchNewPath], cchOriginalPath, *pptszSavedPath);
  1420. }
  1421. //
  1422. // Finally ready to set the path environment variable
  1423. //
  1424. fChangedPath = SetEnvironmentVariable(PATH_ENV_VAR, ptszNewPath);
  1425. delete [] ptszNewPath;
  1426. } while (0);
  1427. return fChangedPath;
  1428. }
  1429. //+---------------------------------------------------------------------------
  1430. //
  1431. // Function: LoadAtJob
  1432. //
  1433. // Synopsis: Load the At job indicated.
  1434. //
  1435. // Arguments: [pJob] - Job object to use.
  1436. // [pwszAtJobFilename] - Relative path to the target At job.
  1437. //
  1438. //----------------------------------------------------------------------------
  1439. HRESULT
  1440. LoadAtJob(CJob * pJob, TCHAR * ptszAtJobFilename)
  1441. {
  1442. static ULONG ccJobFolderPathSize = 0;
  1443. TCHAR tszPath[MAX_PATH + 1];
  1444. if (ccJobFolderPathSize == 0)
  1445. {
  1446. ccJobFolderPathSize = lstrlen(g_TasksFolderInfo.ptszPath);
  1447. }
  1448. //
  1449. // Compute string size to prevent possible buffer overrun.
  1450. //
  1451. // NB : size + 2 for additional for '\' + NULL character.
  1452. //
  1453. ULONG ccAtJobPathSize = lstrlen(ptszAtJobFilename) +
  1454. ccJobFolderPathSize + 2;
  1455. if (ccAtJobPathSize > MAX_PATH)
  1456. {
  1457. CHECK_HRESULT(HRESULT_FROM_WIN32(ERROR_INVALID_NAME));
  1458. return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
  1459. }
  1460. StringCchCopy(tszPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
  1461. StringCchCat(tszPath, MAX_PATH + 1, TEXT("\\"));
  1462. StringCchCat(tszPath, MAX_PATH + 1, ptszAtJobFilename);
  1463. return pJob->LoadP(tszPath, 0, FALSE, TRUE);
  1464. }
  1465. //+---------------------------------------------------------------------------
  1466. //
  1467. // Function: IsValidAtFilename
  1468. //
  1469. // Synopsis: Returns TRUE if [wszFilename] is of the form ATn.JOB, where
  1470. // n is 1 or more digits, leading zeros not allowed, n >= 1.
  1471. //
  1472. // History: 09-30-96 DavidMun Created
  1473. //
  1474. // Notes: Assumes [wszFilename] has valid extension.
  1475. //
  1476. //----------------------------------------------------------------------------
  1477. BOOL IsValidAtFilename(LPCWSTR wszFilename)
  1478. {
  1479. ULONG cchFilename;
  1480. static ULONG s_cchExtension;
  1481. cchFilename = lstrlen(wszFilename);
  1482. if (!s_cchExtension)
  1483. {
  1484. s_cchExtension = ARRAY_LEN(TSZ_DOTJOB) - 1;
  1485. }
  1486. //
  1487. // The filename must be of the form PREFIXdigitsEXTENSION, with at least
  1488. // one digit. This means that the min length is the length of PREFIX +
  1489. // EXTENSION + 1. We get the +1 from the ARRAY_LEN macro.
  1490. //
  1491. if (cchFilename < (ARRAY_LEN(TSZ_AT_JOB_PREFIX) + s_cchExtension))
  1492. {
  1493. return FALSE;
  1494. }
  1495. //
  1496. // After the prefix there must be only digits up to the extension.
  1497. // Leading zeros are not allowed.
  1498. //
  1499. ULONG i;
  1500. BOOL fZerosAreLeading = TRUE;
  1501. for (i = ARRAY_LEN(TSZ_AT_JOB_PREFIX) - 1; i < cchFilename; i++)
  1502. {
  1503. if (L'0' == wszFilename[i])
  1504. {
  1505. if (fZerosAreLeading)
  1506. {
  1507. return FALSE;
  1508. }
  1509. }
  1510. else if (wszFilename[i] < L'0' || wszFilename[i] > L'9')
  1511. {
  1512. break; // should've just hit .job
  1513. }
  1514. else
  1515. {
  1516. fZerosAreLeading = FALSE;
  1517. }
  1518. }
  1519. //
  1520. // We stopped at a non-digit. We should now be at a bogus character or
  1521. // the start of the extension. Since the findfirst/next routines wouldn't
  1522. // have returned a filename that didn't have the appropriate extension,
  1523. // it's not necessary to do a string compare, we can just require that the
  1524. // number of characters remaining is the same as the length of the
  1525. // extension.
  1526. //
  1527. if (cchFilename - i != s_cchExtension)
  1528. {
  1529. return FALSE;
  1530. }
  1531. return TRUE;
  1532. }
  1533. //+---------------------------------------------------------------------------
  1534. //
  1535. // Function: TranslateAccount
  1536. //
  1537. // Synopsis: Translates local system accounts between internal and external representations.
  1538. // Passes other accounts through unaltered.
  1539. //
  1540. // If translating a local system account into the internal representation, if the password
  1541. // passed in was empty, then it sets the pointer to NULL for use with the local system account,
  1542. // since the user has no way of setting it to NULL themselves. The password isn't touched
  1543. // otherwise.
  1544. //
  1545. // Arguments:
  1546. // IN TRANSLATION_DIRECTION tdDirection -- which way is translation being performed
  1547. // IN LPCWSTR pwszAccountIn -- account to be translated
  1548. // IN OUT LPWSTR pwszAccountOut -- translated account
  1549. // IN DWORD cchAccountOut -- size of buffer for translated account
  1550. // IN OUT LPWSTR* ppwszPassword -- pointer to pointer to password
  1551. //
  1552. // Returns: HRESULT indicating success or failure
  1553. //
  1554. //----------------------------------------------------------------------------
  1555. HRESULT
  1556. TranslateAccount(
  1557. TRANSLATION_DIRECTION tdDirection,
  1558. LPCWSTR pwszAccountIn,
  1559. LPWSTR pwszAccountOut,
  1560. DWORD cchAccountOut,
  1561. LPWSTR* ppwszPassword)
  1562. {
  1563. if (!pwszAccountIn || !pwszAccountOut)
  1564. return E_FAIL;
  1565. const WCHAR DISPLAY_LOCAL_SYSTEM[] = L"NT AUTHORITY\\SYSTEM";
  1566. const WCHAR* pwszDisplaySystem = DISPLAY_LOCAL_SYSTEM + 13; // points to "SYSTEM" substring in string above
  1567. if (tdDirection == TRANSLATE_FOR_DISPLAY)
  1568. {
  1569. if (pwszAccountIn[0] == L'\0') // internal representation of local system
  1570. {
  1571. lstrcpynW(pwszAccountOut, DISPLAY_LOCAL_SYSTEM, cchAccountOut);
  1572. }
  1573. else
  1574. {
  1575. lstrcpynW(pwszAccountOut, pwszAccountIn, cchAccountOut);
  1576. }
  1577. }
  1578. else // translating for internal use
  1579. {
  1580. if (lstrcmpiW(pwszAccountIn, DISPLAY_LOCAL_SYSTEM) == 0 || lstrcmpiW(pwszAccountIn, pwszDisplaySystem) == 0)
  1581. {
  1582. pwszAccountOut[0] = L'\0'; // internal representation of local system
  1583. if (ppwszPassword && (*ppwszPassword[0] == L'\0'))
  1584. *ppwszPassword = NULL;
  1585. }
  1586. else
  1587. {
  1588. lstrcpynW(pwszAccountOut, pwszAccountIn, cchAccountOut);
  1589. }
  1590. }
  1591. return S_OK;
  1592. }
  1593. //+---------------------------------------------------------------------------
  1594. //
  1595. // Function: OpenFileWithRetry
  1596. //
  1597. // Synopsis: Attempt to open file with access and share mode supplied.
  1598. // If open fails due to sharing violation, try again, up to two times.
  1599. //
  1600. // Arguments: [ptszFileName]
  1601. // [dwDesiredAccess]
  1602. // [dwDesiredShareMode]
  1603. // [hFile]
  1604. //
  1605. // Returns: HRESULT
  1606. //
  1607. // Notes: None.
  1608. //
  1609. //----------------------------------------------------------------------------
  1610. HRESULT
  1611. OpenFileWithRetry(LPCTSTR ptszFileName, DWORD dwDesiredAccess, DWORD dwDesiredShareMode, HANDLE* phFile)
  1612. {
  1613. DWORD Status = 0;
  1614. HANDLE hFile;
  1615. //
  1616. // Retry open if it is in use by another process, up to two more times
  1617. //
  1618. for (DWORD i = 3; i; i--)
  1619. {
  1620. hFile = CreateFile(ptszFileName,
  1621. dwDesiredAccess,
  1622. dwDesiredShareMode,
  1623. NULL,
  1624. OPEN_EXISTING,
  1625. FILE_FLAG_SEQUENTIAL_SCAN,
  1626. NULL);
  1627. if (hFile == INVALID_HANDLE_VALUE)
  1628. {
  1629. Status = GetLastError();
  1630. if (Status == ERROR_SHARING_VIOLATION)
  1631. {
  1632. //
  1633. // Wait semi-pseudo-random amount of time before trying again.
  1634. //
  1635. Sleep(250 + (rand() % 250));
  1636. continue;
  1637. }
  1638. }
  1639. else
  1640. {
  1641. Status = 0;
  1642. }
  1643. break;
  1644. }
  1645. if (Status)
  1646. {
  1647. schDebugOut((DEB_ERROR, "Open of task file '%ws' failed with error %u\n", ptszFileName, Status));
  1648. return HRESULT_FROM_WIN32(Status);
  1649. }
  1650. *phFile = hFile;
  1651. return S_OK;
  1652. }