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.

734 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 1996.
  5. //
  6. // File: jobprop.cxx
  7. //
  8. // Contents: Implementation of job property container class.
  9. //
  10. // History: 01-04-96 DavidMun Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <headers.hxx>
  14. #pragma hdrstop
  15. #include "jt.hxx"
  16. typedef HRESULT (STDMETHODCALLTYPE ITask::* SETSTRINGPROPFN)(LPCWSTR);
  17. typedef HRESULT (STDMETHODCALLTYPE ITask::* GETSTRINGPROPFN)(LPWSTR *);
  18. VOID SetStringProp(
  19. ITask *pJob,
  20. LPCSTR szMethodName,
  21. LPCWSTR wszNewValue,
  22. SETSTRINGPROPFN pfnSetJobStrProp,
  23. HRESULT *phr);
  24. VOID GetStringProp(
  25. ITask *pJob,
  26. LPCSTR szMethodName,
  27. GETSTRINGPROPFN pfnGetJobStrProp,
  28. WCHAR **ppwszProp,
  29. HRESULT *phr);
  30. #define JP_PRIORITY 0x0001
  31. #define JP_MAXRUNTIME 0x0002
  32. #define JP_IDLEWAIT 0x0004
  33. #define JP_TASKFLAGS 0x0008
  34. const ULONG MS_PER_SECOND = 1000;
  35. const ULONG MS_PER_MINUTE = 60 * MS_PER_SECOND;
  36. const ULONG MS_PER_HOUR = 60 * MS_PER_MINUTE;
  37. const ULONG MS_PER_DAY = 24 * MS_PER_HOUR;
  38. //+---------------------------------------------------------------------------
  39. //
  40. // Member: CJobProp::CJobProp
  41. //
  42. // Synopsis: Init this.
  43. //
  44. // History: 01-08-96 DavidMun Created
  45. //
  46. // Notes: Note it is not safe to call Clear(), since it assumes any
  47. // non-NULL values in the string member vars are valid.
  48. //
  49. //----------------------------------------------------------------------------
  50. CJobProp::CJobProp()
  51. {
  52. pwszAppName = NULL;
  53. pwszParams = NULL;
  54. pwszWorkingDirectory = NULL;
  55. pwszComment = NULL;
  56. pwszCreator = NULL;
  57. dwPriority = NORMAL_PRIORITY_CLASS;
  58. dwFlags = 0;
  59. }
  60. //+---------------------------------------------------------------------------
  61. //
  62. // Member: CJobProp::~CJobProp
  63. //
  64. // Synopsis: Free resources
  65. //
  66. // History: 01-04-96 DavidMun Created
  67. //
  68. //----------------------------------------------------------------------------
  69. CJobProp::~CJobProp()
  70. {
  71. Clear();
  72. }
  73. //+---------------------------------------------------------------------------
  74. //
  75. // Member: CJobProp::Clear
  76. //
  77. // Synopsis: Release all resources held by this and set all properties
  78. // to initial values.
  79. //
  80. // History: 01-04-96 DavidMun Created
  81. //
  82. //----------------------------------------------------------------------------
  83. VOID CJobProp::Clear()
  84. {
  85. dwFlags = 0;
  86. dwPriority = NORMAL_PRIORITY_CLASS;
  87. dwMaxRunTime = 0;
  88. wIdleWait = 0;
  89. wIdleDeadline = 0;
  90. _flSet = 0;
  91. _flSetFlags = 0;
  92. CoTaskMemFree(pwszAppName);
  93. CoTaskMemFree(pwszParams);
  94. CoTaskMemFree(pwszWorkingDirectory);
  95. CoTaskMemFree(pwszComment);
  96. CoTaskMemFree(pwszCreator);
  97. pwszAppName = NULL;
  98. pwszParams = NULL;
  99. pwszWorkingDirectory = NULL;
  100. pwszComment = NULL;
  101. pwszCreator = NULL;
  102. ZeroMemory(&stMostRecentRun, sizeof stMostRecentRun);
  103. ZeroMemory(&stNextRun, sizeof stNextRun);
  104. dwExitCode = 0;
  105. hrStatus = 0;
  106. }
  107. //+---------------------------------------------------------------------------
  108. //
  109. // Member: CJobProp::Dump
  110. //
  111. // Synopsis: Write job properties to the log
  112. //
  113. // History: 01-08-96 DavidMun Created
  114. //
  115. //----------------------------------------------------------------------------
  116. VOID CJobProp::Dump()
  117. {
  118. g_Log.Write(LOG_TEXT, "");
  119. g_Log.Write(LOG_TEXT, " ApplicationName: '%S'", pwszAppName);
  120. g_Log.Write(LOG_TEXT, " Parameters: '%S'", pwszParams);
  121. g_Log.Write(LOG_TEXT, " WorkingDirectory: '%S'", pwszWorkingDirectory);
  122. g_Log.Write(LOG_TEXT, " Comment: '%S'", pwszComment);
  123. g_Log.Write(LOG_TEXT, " Creator: '%S'", pwszCreator);
  124. g_Log.Write(LOG_TEXT, " Priority: %s", GetPriorityString(dwPriority));
  125. if (dwMaxRunTime == INFINITE)
  126. {
  127. g_Log.Write(LOG_TEXT, " MaxRunTime: INFINITE");
  128. }
  129. else
  130. {
  131. ULONG ulMRT = dwMaxRunTime;
  132. ULONG ulDays = ulMRT / MS_PER_DAY;
  133. ulMRT %= MS_PER_DAY;
  134. ULONG ulHours = ulMRT / MS_PER_HOUR;
  135. ulMRT %= MS_PER_HOUR;
  136. ULONG ulMinutes = ulMRT / MS_PER_MINUTE;
  137. ulMRT %= MS_PER_MINUTE;
  138. ULONG ulSeconds = (ulMRT + MS_PER_SECOND / 2) / MS_PER_SECOND;
  139. g_Log.Write(
  140. LOG_TEXT,
  141. " MaxRunTime: %u (%ud %2u:%02u:%02u)",
  142. dwMaxRunTime,
  143. ulDays,
  144. ulHours,
  145. ulMinutes,
  146. ulSeconds);
  147. }
  148. g_Log.Write(LOG_TEXT, " IdleWait: %u", wIdleWait);
  149. g_Log.Write(LOG_TEXT, " IdleDeadline: %u", wIdleDeadline);
  150. g_Log.Write(LOG_TEXT, " MostRecentRun: %02u/%02u/%04u %2u:%02u:%02u",
  151. stMostRecentRun.wMonth,
  152. stMostRecentRun.wDay,
  153. stMostRecentRun.wYear,
  154. stMostRecentRun.wHour,
  155. stMostRecentRun.wMinute,
  156. stMostRecentRun.wSecond);
  157. g_Log.Write(LOG_TEXT, " NextRun: %02u/%02u/%04u %2u:%02u:%02u",
  158. stNextRun.wMonth,
  159. stNextRun.wDay,
  160. stNextRun.wYear,
  161. stNextRun.wHour,
  162. stNextRun.wMinute,
  163. stNextRun.wSecond);
  164. g_Log.Write(LOG_TEXT, " StartError: %s", GetStatusString(hrStartError));
  165. g_Log.Write(LOG_TEXT, " ExitCode: %#x", dwExitCode);
  166. g_Log.Write(LOG_TEXT, " Status: %s", GetStatusString(hrStatus));
  167. g_Log.Write(LOG_TEXT, " ScheduledWorkItem Flags:");
  168. DumpJobFlags(dwFlags);
  169. g_Log.Write(LOG_TEXT, " TaskFlags: %#x", dwTaskFlags);
  170. }
  171. //+---------------------------------------------------------------------------
  172. //
  173. // Member: CJobProp::Parse
  174. //
  175. // Synopsis: Set this object's members according to the values specified
  176. // on the command line.
  177. //
  178. // Arguments: [ppwsz] - command line.
  179. //
  180. // Returns: S_OK - this valid
  181. // E_* - error logged
  182. //
  183. // History: 01-03-96 DavidMun Created
  184. //
  185. //----------------------------------------------------------------------------
  186. HRESULT CJobProp::Parse(WCHAR **ppwsz)
  187. {
  188. HRESULT hr = S_OK;
  189. TOKEN tkn;
  190. TOKEN tknProp;
  191. WCHAR *pwszStringValue;
  192. Clear();
  193. tkn = PeekToken(ppwsz);
  194. while (tkn != TKN_SWITCH && tkn != TKN_EOL && tkn != TKN_INVALID)
  195. {
  196. //
  197. // Get the property name token in tknProp, then eat the equal sign
  198. // and, depending on the property, get a string, token or number
  199. // value.
  200. //
  201. tknProp = GetToken(ppwsz);
  202. hr = Expect(TKN_EQUAL, ppwsz, L"=");
  203. BREAK_ON_FAILURE(hr);
  204. if (tknProp == TKN_WORKINGDIRECTORY)
  205. {
  206. hr = GetFilename(ppwsz, L"filename");
  207. if (SUCCEEDED(hr))
  208. {
  209. hr = DupString(g_wszLastStringToken, &pwszStringValue);
  210. }
  211. }
  212. else if (tknProp == TKN_APPNAME ||
  213. tknProp == TKN_PARAMS ||
  214. tknProp == TKN_COMMENT ||
  215. tknProp == TKN_CREATOR)
  216. {
  217. hr = Expect(TKN_STRING, ppwsz, L"string property value");
  218. if (SUCCEEDED(hr))
  219. {
  220. hr = DupString(g_wszLastStringToken, &pwszStringValue);
  221. }
  222. }
  223. else if (tknProp == TKN_PRIORITY)
  224. {
  225. tkn = GetToken(ppwsz);
  226. }
  227. else
  228. {
  229. hr = Expect(TKN_NUMBER, ppwsz, L"numeric property value");
  230. }
  231. BREAK_ON_FAILURE(hr);
  232. //
  233. // Now assign the value to the appropriate member
  234. //
  235. switch (tknProp)
  236. {
  237. case TKN_APPNAME:
  238. pwszAppName = pwszStringValue;
  239. break;
  240. case TKN_PARAMS:
  241. pwszParams = pwszStringValue;
  242. break;
  243. case TKN_WORKINGDIRECTORY:
  244. pwszWorkingDirectory = pwszStringValue;
  245. break;
  246. case TKN_COMMENT:
  247. pwszComment = pwszStringValue;
  248. break;
  249. case TKN_CREATOR:
  250. pwszCreator = pwszStringValue;
  251. break;
  252. case TKN_PRIORITY:
  253. _flSet |= JP_PRIORITY;
  254. switch (tkn)
  255. {
  256. case TKN_IDLE: // used here as priority
  257. dwPriority = IDLE_PRIORITY_CLASS;
  258. break;
  259. case TKN_NORMAL:
  260. dwPriority = NORMAL_PRIORITY_CLASS;
  261. break;
  262. case TKN_HIGH:
  263. dwPriority = HIGH_PRIORITY_CLASS;
  264. break;
  265. case TKN_REALTIME:
  266. dwPriority = REALTIME_PRIORITY_CLASS;
  267. break;
  268. default:
  269. hr = E_FAIL;
  270. LogSyntaxError(tkn, L"IDLE, NORMAL, HIGH, or REALTIME");
  271. }
  272. break;
  273. case TKN_MAXRUNTIME:
  274. _flSet |= JP_MAXRUNTIME;
  275. dwMaxRunTime = g_ulLastNumberToken;
  276. break;
  277. case TKN_IDLE: // used here as idlewait/deadline property
  278. _flSet |= JP_IDLEWAIT;
  279. wIdleWait = (WORD)g_ulLastNumberToken;
  280. hr = Expect(TKN_NUMBER, ppwsz, L"idle deadline");
  281. BREAK_ON_FAILURE(hr);
  282. wIdleDeadline = (WORD)g_ulLastNumberToken;
  283. break;
  284. case TKN_TASKFLAGS:
  285. _flSet |= JP_TASKFLAGS;
  286. dwTaskFlags = g_ulLastNumberToken;
  287. break;
  288. #ifndef RES_KIT
  289. case TKN_INTERACTIVE:
  290. _flSetFlags |= TASK_FLAG_INTERACTIVE;
  291. if (g_ulLastNumberToken)
  292. {
  293. dwFlags |= TASK_FLAG_INTERACTIVE;
  294. }
  295. break;
  296. #endif
  297. case TKN_DONTRUNONBATTERIES:
  298. _flSetFlags |= TASK_FLAG_DONT_START_IF_ON_BATTERIES;
  299. if (g_ulLastNumberToken)
  300. {
  301. dwFlags |= TASK_FLAG_DONT_START_IF_ON_BATTERIES;
  302. }
  303. break;
  304. case TKN_KILLIFGOINGONBATS:
  305. _flSetFlags |= TASK_FLAG_KILL_IF_GOING_ON_BATTERIES;
  306. if (g_ulLastNumberToken)
  307. {
  308. dwFlags |= TASK_FLAG_KILL_IF_GOING_ON_BATTERIES;
  309. }
  310. break;
  311. case TKN_RUNONLYIFLOGGEDON:
  312. _flSetFlags |= TASK_FLAG_RUN_ONLY_IF_LOGGED_ON;
  313. if (g_ulLastNumberToken)
  314. {
  315. dwFlags |= TASK_FLAG_RUN_ONLY_IF_LOGGED_ON;
  316. }
  317. break;
  318. case TKN_SYSTEMREQUIRED:
  319. _flSetFlags |= TASK_FLAG_SYSTEM_REQUIRED;
  320. if (g_ulLastNumberToken)
  321. {
  322. dwFlags |= TASK_FLAG_SYSTEM_REQUIRED;
  323. }
  324. break;
  325. case TKN_DELETEWHENDONE:
  326. _flSetFlags |= TASK_FLAG_DELETE_WHEN_DONE;
  327. if (g_ulLastNumberToken)
  328. {
  329. dwFlags |= TASK_FLAG_DELETE_WHEN_DONE;
  330. }
  331. break;
  332. case TKN_SUSPEND:
  333. _flSetFlags |= TASK_FLAG_DISABLED;
  334. if (g_ulLastNumberToken)
  335. {
  336. dwFlags |= TASK_FLAG_DISABLED;
  337. }
  338. break;
  339. case TKN_ONLYIFIDLE:
  340. _flSetFlags |= TASK_FLAG_START_ONLY_IF_IDLE;
  341. if (g_ulLastNumberToken)
  342. {
  343. dwFlags |= TASK_FLAG_START_ONLY_IF_IDLE;
  344. }
  345. break;
  346. case TKN_KILLATIDLEEND:
  347. _flSetFlags |= TASK_FLAG_KILL_ON_IDLE_END;
  348. if (g_ulLastNumberToken)
  349. {
  350. dwFlags |= TASK_FLAG_KILL_ON_IDLE_END;
  351. }
  352. break;
  353. case TKN_RESTARTONIDLERESUME:
  354. _flSetFlags |= TASK_FLAG_RESTART_ON_IDLE_RESUME;
  355. if (g_ulLastNumberToken)
  356. {
  357. dwFlags |= TASK_FLAG_RESTART_ON_IDLE_RESUME;
  358. }
  359. break;
  360. case TKN_HIDDEN:
  361. _flSetFlags |= TASK_FLAG_HIDDEN;
  362. if (g_ulLastNumberToken)
  363. {
  364. dwFlags |= TASK_FLAG_HIDDEN;
  365. }
  366. break;
  367. default:
  368. hr = E_FAIL;
  369. LogSyntaxError(tknProp, L"job property name");
  370. break;
  371. }
  372. tkn = PeekToken(ppwsz);
  373. }
  374. return hr;
  375. }
  376. //+---------------------------------------------------------------------------
  377. //
  378. // Member: CJobProp::InitFromActual
  379. //
  380. // Synopsis: Read properties from [pJob] and set this object to match
  381. //
  382. // Arguments: [pJob] - job object whose properties we'll read
  383. //
  384. // Returns: S_OK - this initialized
  385. // E_* - error logged, this only partly initialized
  386. //
  387. // History: 01-08-96 DavidMun Created
  388. //
  389. //----------------------------------------------------------------------------
  390. HRESULT CJobProp::InitFromActual(ITask *pJob)
  391. {
  392. HRESULT hr = S_OK;
  393. do
  394. {
  395. Clear();
  396. hr = pJob->GetApplicationName(&pwszAppName);
  397. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetApplicationName(&pwszAppName)");
  398. CoTaskMemFree(pwszAppName);
  399. GetStringProp(pJob, "ApplicationName", &ITask::GetApplicationName, &pwszAppName, &hr);
  400. GetStringProp(pJob, "Parameters", &ITask::GetParameters, &pwszParams, &hr);
  401. GetStringProp(pJob, "WorkingDirectory", &ITask::GetWorkingDirectory, &pwszWorkingDirectory, &hr);
  402. GetStringProp(pJob, "Comment", &ITask::GetComment, &pwszComment, &hr);
  403. GetStringProp(pJob, "Creator", &ITask::GetCreator, &pwszCreator, &hr);
  404. BREAK_ON_FAILURE(hr);
  405. hr = pJob->GetPriority(&dwPriority);
  406. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetPriority");
  407. hr = pJob->GetMaxRunTime(&dwMaxRunTime);
  408. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetMaxRunTime");
  409. hr = pJob->GetIdleWait(&wIdleWait, &wIdleDeadline);
  410. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetIdleWait");
  411. hr = pJob->GetFlags(&dwFlags);
  412. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetFlags");
  413. hr = pJob->GetTaskFlags(&dwTaskFlags);
  414. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetTaskFlags");
  415. hr = pJob->GetMostRecentRunTime(&stMostRecentRun);
  416. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetMostRecentRunTime");
  417. hr = pJob->GetNextRunTime(&stNextRun);
  418. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetNextRunTime");
  419. hrStartError = pJob->GetExitCode(&dwExitCode);
  420. hr = pJob->GetStatus(&hrStatus);
  421. LOG_AND_BREAK_ON_FAIL(hr, "ITask::GetStatus");
  422. }
  423. while (0);
  424. return hr;
  425. }
  426. //+---------------------------------------------------------------------------
  427. //
  428. // Member: CJobProp::SetActual
  429. //
  430. // Synopsis: Set the properties of the job object with interface [pJob]
  431. // to the ones stored in this.
  432. //
  433. // Arguments: [pJob] - ITask interface
  434. //
  435. // Returns: S_OK - all props set
  436. // E_* - error logged
  437. //
  438. // History: 01-08-96 DavidMun Created
  439. //
  440. //----------------------------------------------------------------------------
  441. HRESULT CJobProp::SetActual(ITask *pJob)
  442. {
  443. HRESULT hr = S_OK;
  444. HRESULT hrReturn = S_OK;
  445. ULONG flCurFlags;
  446. SetStringProp(pJob, "ApplicationName", pwszAppName, &ITask::SetApplicationName, &hrReturn);
  447. SetStringProp(pJob, "Parameters", pwszParams, &ITask::SetParameters, &hrReturn);
  448. SetStringProp(pJob, "WorkingDirectory", pwszWorkingDirectory, &ITask::SetWorkingDirectory, &hrReturn);
  449. SetStringProp(pJob, "Comment", pwszComment , &ITask::SetComment , &hrReturn);
  450. SetStringProp(pJob, "Creator", pwszCreator, &ITask::SetCreator, &hrReturn);
  451. if (_flSet & JP_PRIORITY)
  452. {
  453. hr = pJob->SetPriority(dwPriority);
  454. if (FAILED(hr))
  455. {
  456. g_Log.Write(
  457. LOG_FAIL,
  458. "ITask::SetPriority(%u) hr=%#010x",
  459. dwPriority,
  460. hr);
  461. hrReturn = hr;
  462. }
  463. }
  464. if (_flSet & JP_MAXRUNTIME)
  465. {
  466. hr = pJob->SetMaxRunTime(dwMaxRunTime);
  467. if (FAILED(hr))
  468. {
  469. g_Log.Write(
  470. LOG_FAIL,
  471. "ITask::SetMaxRunTime(%u) hr=%#010x",
  472. dwMaxRunTime,
  473. hr);
  474. hrReturn = hr;
  475. }
  476. }
  477. if (_flSet & JP_IDLEWAIT)
  478. {
  479. hr = pJob->SetIdleWait(wIdleWait, wIdleDeadline);
  480. if (FAILED(hr))
  481. {
  482. g_Log.Write(
  483. LOG_FAIL,
  484. "ITask::SetIdleWait(%u,%u) hr=%#010x",
  485. wIdleWait,
  486. wIdleDeadline,
  487. hr);
  488. hrReturn = hr;
  489. }
  490. }
  491. if (_flSet & JP_TASKFLAGS)
  492. {
  493. hr = pJob->SetTaskFlags(dwTaskFlags);
  494. if (FAILED(hr))
  495. {
  496. g_Log.Write(
  497. LOG_FAIL,
  498. "ITask::SetTaskFlags(%u) hr=%#010x",
  499. dwTaskFlags,
  500. hr);
  501. hrReturn = hr;
  502. }
  503. }
  504. hr = pJob->GetFlags(&flCurFlags);
  505. if (SUCCEEDED(hr))
  506. {
  507. //
  508. // Turn off all flags user touched, then turn back on ones
  509. // he specified nonzero value for.
  510. //
  511. flCurFlags &= ~_flSetFlags;
  512. flCurFlags |= dwFlags;
  513. hr = pJob->SetFlags(flCurFlags);
  514. if (FAILED(hr))
  515. {
  516. g_Log.Write(LOG_FAIL, "ITask::SetFlags hr=%#010x", hr);
  517. hrReturn = hr;
  518. }
  519. }
  520. else
  521. {
  522. g_Log.Write(LOG_FAIL, "ITask::GetFlags hr=%#010x", hr);
  523. hrReturn = hr;
  524. }
  525. return hrReturn;
  526. }
  527. //+---------------------------------------------------------------------------
  528. //
  529. // Function: SetStringProp
  530. //
  531. // Synopsis: Set a string property on a job object
  532. //
  533. // Arguments: [pJob] - job object to modify
  534. // [szMethodName] - for logging, name of method we're calling
  535. // [wszNewValue] - new string value for property
  536. // [pfnSetJobStrProp] - ITask member function pointer
  537. // [phr] - filled with error if failure occurs
  538. //
  539. // Modifies: *[phr] on error
  540. //
  541. // History: 01-05-96 DavidMun Created
  542. //
  543. //----------------------------------------------------------------------------
  544. VOID SetStringProp(
  545. ITask *pJob,
  546. LPCSTR szMethodName,
  547. LPCWSTR wszNewValue,
  548. SETSTRINGPROPFN pfnSetJobStrProp,
  549. HRESULT *phr)
  550. {
  551. HRESULT hr = S_OK;
  552. if (wszNewValue)
  553. {
  554. hr = (pJob->*pfnSetJobStrProp)(wszNewValue);
  555. if (hr == E_NOTIMPL)
  556. {
  557. g_Log.Write(
  558. LOG_WARN,
  559. "Ignoring E_NOTIMPL from ITask::Set%s",
  560. szMethodName);
  561. }
  562. else if (FAILED(hr))
  563. {
  564. g_Log.Write(LOG_FAIL, "ITask::Set%s hr=%#010x", szMethodName, hr);
  565. *phr = hr;
  566. }
  567. }
  568. }
  569. //+---------------------------------------------------------------------------
  570. //
  571. // Function: GetStringProp
  572. //
  573. // Synopsis: Read a string property from a job object
  574. //
  575. // Arguments: [pJob] - object from which to read property
  576. // [szMethodName] - for logging, name of method to call
  577. // [pfnGetJobStrProp] - pointer to ITask method to call
  578. // [ppwszProp] - filled with new'd string
  579. // [phr] - modified on error
  580. //
  581. // Modifies: *[phr] on error
  582. //
  583. // History: 01-05-96 DavidMun Created
  584. //
  585. //----------------------------------------------------------------------------
  586. VOID GetStringProp(
  587. ITask *pJob,
  588. LPCSTR szMethodName,
  589. GETSTRINGPROPFN pfnGetJobStrProp,
  590. WCHAR **ppwszProp,
  591. HRESULT *phr)
  592. {
  593. HRESULT hr = S_OK;
  594. UINT cchProp;
  595. do
  596. {
  597. hr = (pJob->*pfnGetJobStrProp)(ppwszProp);
  598. if (hr == E_NOTIMPL)
  599. {
  600. g_Log.Write(
  601. LOG_WARN,
  602. "Ignoring E_NOTIMPL from ITask::Get%s",
  603. szMethodName);
  604. hr = S_OK;
  605. break;
  606. }
  607. if (FAILED(hr))
  608. {
  609. g_Log.Write(
  610. LOG_FAIL,
  611. "ITask::Get%s(ppwszProp) hr=%#010x",
  612. szMethodName,
  613. hr);
  614. }
  615. }
  616. while (0);
  617. if (FAILED(hr))
  618. {
  619. *phr = hr;
  620. }
  621. }