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.

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