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.

769 lines
22 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Job Scheduler Service
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: sch_itf.cxx
  9. //
  10. // Contents: job scheduler service interface impementation
  11. //
  12. // Classes: CSchedule
  13. //
  14. // Interfaces: ITaskScheduler
  15. //
  16. // History: 08-Sep-95 EricB created
  17. //
  18. //-----------------------------------------------------------------------------
  19. #include "..\pch\headers.hxx"
  20. #pragma hdrstop
  21. #include "Sched.hxx"
  22. #include "..\inc\misc.hxx"
  23. #include "..\inc\network.hxx"
  24. //+----------------------------------------------------------------------------
  25. //
  26. // Member: CSchedule::ITaskScheduler::GetTargetComputer, public
  27. //
  28. // Synopsis: Returns the name of the machine towards which the interface is
  29. // currently targetted.
  30. //
  31. // Arguments: [ppwszComputer] - the returned buffer with the machine name
  32. //
  33. // Returns: hresults
  34. //
  35. // Notes: The string is callee allocated and caller freed with
  36. // CoTaskMemFree.
  37. //-----------------------------------------------------------------------------
  38. STDMETHODIMP
  39. CSchedule::GetTargetComputer(LPWSTR * ppwszComputer)
  40. {
  41. TRACE(CSchedule, GetTargetComputer);
  42. HRESULT hr;
  43. DWORD cch = SA_MAX_COMPUTERNAME_LENGTH + 1;
  44. TCHAR tszLocalName[SA_MAX_COMPUTERNAME_LENGTH + 3] = TEXT("\\\\");
  45. TCHAR * ptszTargetMachine;
  46. WCHAR * pwszTargetMachine;
  47. if (m_ptszTargetMachine)
  48. {
  49. ptszTargetMachine = m_ptszTargetMachine;
  50. cch = lstrlen(ptszTargetMachine) + 1;
  51. }
  52. else // A NULL m_ptszTargetMachine means that we are targetted locally
  53. {
  54. if (!GetComputerName(tszLocalName + 2, &cch))
  55. {
  56. hr = HRESULT_FROM_WIN32(GetLastError());
  57. ERR_OUT("GetTargetComputer: GetComputerName", hr);
  58. return hr;
  59. }
  60. ptszTargetMachine = tszLocalName;
  61. cch += 3; // 2 for the leading slashes + 1 for the NULL
  62. }
  63. pwszTargetMachine = ptszTargetMachine;
  64. *ppwszComputer = (LPWSTR)CoTaskMemAlloc(cch * sizeof(WCHAR));
  65. if (*ppwszComputer == NULL)
  66. {
  67. return E_OUTOFMEMORY;
  68. }
  69. s_wcscpy(*ppwszComputer, pwszTargetMachine);
  70. return S_OK;
  71. }
  72. //+----------------------------------------------------------------------------
  73. //
  74. // Member: CSchedule::ITaskScheduler::SetTargetComputer, public
  75. //
  76. // Synopsis: Sets the machine towards which subsequent ITaskScheduler
  77. // calls will be directed
  78. //
  79. // Arguments: [pwszComputer] - the machine name string
  80. //
  81. // Returns: hresults
  82. //
  83. // Notes: The string is Caller allocated and freed. The machine name
  84. // must include two leading backslashes.
  85. // The caller may indicate using the local machine in one of two
  86. // ways: by setting pwszComputer to NULL or to the UNC name of the
  87. // local machine.
  88. //-----------------------------------------------------------------------------
  89. STDMETHODIMP
  90. CSchedule::SetTargetComputer(LPCWSTR pwszComputer)
  91. {
  92. TRACE(CSchedule, SetTargetComputer);
  93. HRESULT hr;
  94. DWORD cch;
  95. BOOL fLocal = FALSE;
  96. //
  97. // Parameter validation. A null param means to target the local computer.
  98. //
  99. if (!pwszComputer)
  100. {
  101. fLocal = TRUE;
  102. }
  103. LPCTSTR tszPassedInName = pwszComputer;
  104. if (!fLocal)
  105. {
  106. //
  107. // Get the local machine name to compare with that passed in.
  108. //
  109. TCHAR tszLocalName[SA_MAX_COMPUTERNAME_LENGTH + 1];
  110. cch = SA_MAX_COMPUTERNAME_LENGTH + 1;
  111. if (!GetComputerName(tszLocalName, &cch))
  112. {
  113. hr = HRESULT_FROM_WIN32(GetLastError());
  114. ERR_OUT("SetTargetComputer: GetComputerName", hr);
  115. return hr;
  116. }
  117. TCHAR tszFQDN[SA_MAX_COMPUTERNAME_LENGTH + 1];
  118. cch = SA_MAX_COMPUTERNAME_LENGTH + 1;
  119. if (!GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, tszFQDN, &cch))
  120. {
  121. hr = HRESULT_FROM_WIN32(GetLastError());
  122. ERR_OUT("SetTargetComputer: GetComputerNameEx", hr);
  123. return hr;
  124. }
  125. //
  126. // skip over first two characters ("\\") of tszPassedInName when comparing
  127. //
  128. fLocal = (lstrcmpi(tszPassedInName + 2, tszLocalName) == 0) ||
  129. (lstrcmpi(tszPassedInName + 2, tszFQDN) == 0);
  130. }
  131. //
  132. // If targeted remotely, get the folder path out of that machine's
  133. // registry.
  134. //
  135. TCHAR tszFolderPath[MAX_PATH];
  136. if (!fLocal)
  137. {
  138. //
  139. // Open the remote registry.
  140. //
  141. long lErr;
  142. HKEY hRemoteKey, hSchedKey;
  143. lErr = RegConnectRegistry(tszPassedInName, HKEY_LOCAL_MACHINE,
  144. &hRemoteKey);
  145. if (lErr != ERROR_SUCCESS)
  146. {
  147. schDebugOut((DEB_ERROR, "SetTargetComputer: RegConnectRegistry "
  148. "failed with error %ld\n",
  149. lErr));
  150. return(HRESULT_FROM_WIN32(lErr));
  151. }
  152. lErr = RegOpenKeyEx(hRemoteKey, SCH_AGENT_KEY, 0, KEY_READ,
  153. &hSchedKey);
  154. if (lErr != ERROR_SUCCESS)
  155. {
  156. RegCloseKey(hRemoteKey);
  157. if (lErr == ERROR_BADKEY || lErr == ERROR_FILE_NOT_FOUND)
  158. {
  159. return SCHED_E_SERVICE_NOT_INSTALLED;
  160. }
  161. schDebugOut((DEB_ERROR, "SetTargetComputer: RegOpenKeyEx "
  162. "of Scheduler key failed with error %ld\n",
  163. lErr));
  164. return HRESULT_FROM_WIN32(lErr);
  165. }
  166. //
  167. // Get the jobs folder location from the remote registry.
  168. //
  169. DWORD cb = MAX_PATH * sizeof(TCHAR);
  170. TCHAR tszRegFolderValue[MAX_PATH];
  171. lErr = RegQueryValueEx(hSchedKey, SCH_FOLDER_VALUE, NULL, NULL,
  172. (LPBYTE)tszRegFolderValue, &cb);
  173. if (lErr != ERROR_SUCCESS)
  174. {
  175. // use default if value absent
  176. lstrcpy(tszRegFolderValue, TEXT("%SystemRoot%\\Tasks"));
  177. }
  178. RegCloseKey(hSchedKey);
  179. //
  180. // BUGBUG: temporary code to expand %SystemRoot% or %WinDir%
  181. // The installer will have to write a full path to the registry 'cause
  182. // expanding arbitrary environment strings remotely is too much work.
  183. //
  184. cch = ARRAY_LEN("%SystemRoot%") - 1;
  185. if (_tcsncicmp(tszRegFolderValue, TEXT("%SystemRoot%"), cch) != 0)
  186. {
  187. cch = ARRAY_LEN("%WinDir%") - 1;
  188. if (_tcsncicmp(tszRegFolderValue, TEXT("%WinDir%"), cch) != 0)
  189. {
  190. cch = 0;
  191. }
  192. }
  193. if (cch != 0)
  194. {
  195. HKEY hCurVerKey;
  196. lErr = RegOpenKeyEx(hRemoteKey,
  197. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
  198. 0, KEY_ALL_ACCESS,
  199. &hCurVerKey);
  200. if (lErr != ERROR_SUCCESS)
  201. {
  202. RegCloseKey(hRemoteKey);
  203. schDebugOut((DEB_ERROR, "SetTargetComputer: RegOpenKeyEx "
  204. "of CurrentVersion key failed with error %ld\n",
  205. lErr));
  206. return HRESULT_FROM_WIN32(lErr);
  207. }
  208. TCHAR tszSystemRoot[MAX_PATH];
  209. cb = MAX_PATH * sizeof(TCHAR);
  210. lErr = RegQueryValueEx(hCurVerKey, TEXT("SystemRoot"), NULL, NULL,
  211. (LPBYTE)tszSystemRoot, &cb);
  212. if (lErr != ERROR_SUCCESS)
  213. {
  214. RegCloseKey(hCurVerKey);
  215. RegCloseKey(hRemoteKey);
  216. schDebugOut((DEB_ERROR, "SetTargetComputer: RegQueryValueEx "
  217. "of CurrentVersion key failed with error %ld\n",
  218. lErr));
  219. return HRESULT_FROM_WIN32(lErr);
  220. }
  221. RegCloseKey(hCurVerKey);
  222. lstrcpy(tszFolderPath, tszSystemRoot);
  223. lstrcat(tszFolderPath, tszRegFolderValue + cch);
  224. }
  225. else
  226. {
  227. lstrcpy(tszFolderPath, tszRegFolderValue);
  228. }
  229. //
  230. // end of temporary code to expand %SystemRoot%
  231. //
  232. RegCloseKey(hRemoteKey);
  233. //
  234. // Check the folder path for being a fully qualified path name where
  235. // the first char is the drive designator and the second char is a
  236. // colon.
  237. //
  238. if (!s_isDriveLetter(tszFolderPath[0]) || tszFolderPath[1] != TEXT(':'))
  239. {
  240. ERR_OUT("SetTargetComputer: registry path", ERROR_BAD_PATHNAME);
  241. return HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  242. }
  243. //
  244. // The UNC path to the job folder will be the result of concatonating
  245. // the machine name and the expanded folder path. The drive designator
  246. // in the folder path will be turned in an administrative share name
  247. // by replacing the colon with a dollar sign and will look like:
  248. // \\machine\c$\windir\jobs
  249. // so that the count below includes the slash trailing the machine name
  250. // plus the terminating null.
  251. //
  252. cch = lstrlen(tszPassedInName) + 1 + lstrlen(tszFolderPath) + 1;
  253. }
  254. else // Targetted locally.
  255. {
  256. //
  257. // Use the local path. Include one for the null terminator.
  258. //
  259. cch = lstrlen(g_TasksFolderInfo.ptszPath) + 1;
  260. }
  261. //
  262. // Allocate the ITaskScheduler folder path string buffer.
  263. //
  264. TCHAR * ptszPathBuf = new TCHAR[cch];
  265. if (!ptszPathBuf)
  266. {
  267. ERR_OUT("SetTargetComputer: Job folder path buffer allocation",
  268. E_OUTOFMEMORY);
  269. return E_OUTOFMEMORY;
  270. }
  271. //
  272. // Allocate the ITaskScheduler machine name string buffer.
  273. //
  274. TCHAR * ptszTargetMachine;
  275. if (!fLocal)
  276. {
  277. cch = lstrlen(tszPassedInName) + 1;
  278. ptszTargetMachine = new TCHAR[cch];
  279. if (!ptszTargetMachine)
  280. {
  281. ERR_OUT("CSchedule::SetTargetComputer", E_OUTOFMEMORY);
  282. delete ptszPathBuf;
  283. return E_OUTOFMEMORY;
  284. }
  285. }
  286. //
  287. // Now that all failable operation have completed sucessfully, we can
  288. // update the machine name and folder path members.
  289. //
  290. if (m_ptszTargetMachine)
  291. {
  292. delete m_ptszTargetMachine;
  293. }
  294. if (m_ptszFolderPath)
  295. {
  296. delete m_ptszFolderPath;
  297. }
  298. //
  299. // Save the new machine name.
  300. //
  301. if (fLocal)
  302. {
  303. //
  304. // If we are targetted locally, the machine name member is set to
  305. // NULL.
  306. //
  307. m_ptszTargetMachine = NULL;
  308. }
  309. else
  310. {
  311. m_ptszTargetMachine = ptszTargetMachine;
  312. lstrcpy(m_ptszTargetMachine, tszPassedInName);
  313. }
  314. //
  315. // Save the folder path name.
  316. //
  317. m_ptszFolderPath = ptszPathBuf;
  318. if (fLocal)
  319. {
  320. lstrcpy(m_ptszFolderPath, g_TasksFolderInfo.ptszPath);
  321. }
  322. else
  323. {
  324. //
  325. // Convert the folder location to an UNC path.
  326. //
  327. // Turn the drive designator into the admin share by replacing the
  328. // colon with the dollar sign.
  329. //
  330. tszFolderPath[1] = TEXT('$');
  331. //
  332. // Compose the UNC path.
  333. //
  334. lstrcpy(m_ptszFolderPath, tszPassedInName);
  335. lstrcat(m_ptszFolderPath, TEXT("\\"));
  336. lstrcat(m_ptszFolderPath, tszFolderPath);
  337. }
  338. schDebugOut((DEB_ITRACE,
  339. "SetTargetComputer: path to sched folder: \"" FMT_TSTR "\"\n",
  340. m_ptszFolderPath));
  341. return S_OK;
  342. }
  343. //+----------------------------------------------------------------------------
  344. //
  345. // Member: CSchedule::ITaskScheduler::Enum, public
  346. //
  347. // Synopsis: Returns a job/queue object enumerator.
  348. //
  349. // Arguments: [ppEnumJobs] - a place to return a pointer to the enumerator
  350. //
  351. // Returns: hresults
  352. //
  353. //-----------------------------------------------------------------------------
  354. STDMETHODIMP
  355. CSchedule::Enum(IEnumWorkItems ** ppEnumJobs)
  356. {
  357. TRACE(CSchedule, Enum);
  358. HRESULT hr;
  359. CEnumJobs * pEnumJobs = new CEnumJobs;
  360. if (pEnumJobs == NULL)
  361. {
  362. *ppEnumJobs = NULL;
  363. return E_OUTOFMEMORY;
  364. }
  365. hr = pEnumJobs->Init(m_ptszFolderPath);
  366. if (FAILED(hr))
  367. {
  368. delete pEnumJobs;
  369. *ppEnumJobs = NULL;
  370. }
  371. *ppEnumJobs = pEnumJobs;
  372. return hr;
  373. }
  374. //+----------------------------------------------------------------------------
  375. //
  376. // Member: CSchedule::ITaskScheduler::NewWorkItem, public
  377. //
  378. // Synopsis: Create a new job object.
  379. //
  380. // Arguments: [pwszJobName] - the name of the new job *REQUIRED*
  381. // [riid] - the interface desired
  382. // [ppunk] - a place to return a pointer to the new job object
  383. //
  384. // Returns: hresults
  385. //
  386. // Notes: ppwszJobName is caller allocated and freed. The CJob::Save
  387. // method will copy it before returning. The job name must conform
  388. // to NT file naming conventions but must not include
  389. // [back]slashes because nesting within the job object folder is
  390. // not allowed.
  391. //-----------------------------------------------------------------------------
  392. STDMETHODIMP
  393. CSchedule::NewWorkItem(LPCWSTR pwszJobName, REFCLSID rclsid,
  394. REFIID riid, IUnknown ** ppunk)
  395. {
  396. TRACE(CSchedule, NewWorkItem);
  397. *ppunk = NULL;
  398. if (!IsEqualCLSID(rclsid, CLSID_CTask))
  399. {
  400. return CLASS_E_CLASSNOTAVAILABLE;
  401. }
  402. TCHAR * ptszFullName;
  403. HANDLE hFile;
  404. HRESULT hr = CheckJobName(pwszJobName, &ptszFullName);
  405. if (FAILED(hr))
  406. {
  407. ERR_OUT("CSchedule::NewWorkItem: CheckJobName", hr);
  408. return hr;
  409. }
  410. CJob * pJob = CJob::Create();
  411. if (pJob == NULL)
  412. {
  413. delete [] ptszFullName;
  414. return E_OUTOFMEMORY;
  415. }
  416. //
  417. // Do the QI before the CreateFile so that if the caller asks for a non-
  418. // supported interface, the failure will not result in disk operations.
  419. //
  420. hr = pJob->QueryInterface(riid, (void **)ppunk);
  421. if (FAILED(hr))
  422. {
  423. ERR_OUT("CSchedule::NewWorkItem: QueryInterface(riid)", hr);
  424. goto CleanExit;
  425. }
  426. // the above QI increased the refcount to 2, so set it back to 1
  427. pJob->Release();
  428. //
  429. // Per the spec for this method, the file must not already exist.
  430. //
  431. hFile = CreateFile(ptszFullName,
  432. 0, // desired access: none
  433. FILE_SHARE_READ | FILE_SHARE_WRITE,
  434. // share mode: all
  435. NULL, // security attributes
  436. OPEN_EXISTING,
  437. 0,
  438. NULL);
  439. if (hFile == INVALID_HANDLE_VALUE)
  440. {
  441. DWORD dwErr = GetLastError();
  442. if (dwErr == ERROR_FILE_NOT_FOUND)
  443. {
  444. //
  445. // This is good. Save the new filename.
  446. //
  447. pJob->m_ptszFileName = ptszFullName;
  448. return S_OK;
  449. }
  450. else
  451. {
  452. hr = HRESULT_FROM_WIN32(dwErr);
  453. ERR_OUT("CSchedule::NewWorkItem: CreateFile", hr);
  454. }
  455. }
  456. else
  457. {
  458. //
  459. // Opened successfully - the file exists
  460. //
  461. CloseHandle(hFile);
  462. hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
  463. ERR_OUT("CSchedule::NewWorkItem", hr);
  464. }
  465. CleanExit:
  466. delete [] ptszFullName;
  467. delete pJob; // on error, completely destroy the job object
  468. *ppunk = NULL;
  469. return hr;
  470. }
  471. //+----------------------------------------------------------------------------
  472. //
  473. // Member: CSchedule::ITaskScheduler::AddWorkItem, public
  474. //
  475. // Synopsis: Saves the job to the job scheduler folder.
  476. //
  477. // Arguments: [pwszJobName] - the name of the job *REQUIRED*
  478. // [pJob] - pointer to the job object
  479. //
  480. // Returns: hresults
  481. //
  482. // Notes: Same job name conditions as above.
  483. //-----------------------------------------------------------------------------
  484. STDMETHODIMP
  485. CSchedule::AddWorkItem(LPCWSTR pwszJobName,
  486. IScheduledWorkItem * pWorkItem)
  487. {
  488. TRACE(CSchedule, AddWorkItem);
  489. TCHAR * ptszFullName;
  490. HRESULT hr = CheckJobName(pwszJobName, &ptszFullName);
  491. if (FAILED(hr))
  492. {
  493. ERR_OUT("CSchedule::AddWorkItem: CheckJobName", hr);
  494. return hr;
  495. }
  496. IPersistFile * pFile;
  497. hr = pWorkItem->QueryInterface(IID_IPersistFile, (void **)&pFile);
  498. if (FAILED(hr))
  499. {
  500. ERR_OUT("CSchedule::AddWorkItem: QI(IPersistFile)", hr);
  501. delete [] ptszFullName;
  502. return hr;
  503. }
  504. WCHAR * pwszName;
  505. #if !defined(UNICODE)
  506. int cch = lstrlen(ptszFullName) + 1;
  507. pwszName = new WCHAR[cch];
  508. if (!pwszName)
  509. {
  510. ERR_OUT("CSchedule::AddWorkItem", E_OUTOFMEMORY);
  511. pFile->Release();
  512. delete [] ptszFullName;
  513. return E_OUTOFMEMORY;
  514. }
  515. MultiByteToWideChar(CP_ACP, 0, ptszFullName, -1, pwszName, cch);
  516. #else
  517. pwszName = ptszFullName;
  518. #endif
  519. hr = pFile->Save(pwszName, TRUE);
  520. // Add this if we want nested folders
  521. //
  522. // if (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
  523. // {
  524. // //
  525. // // Create folders as needed
  526. // //
  527. // hr = CreateFolders(ptszFullName, TRUE);
  528. // if (FAILED(hr))
  529. // {
  530. // ERR_OUT("AddWorkItem: CreateFolders", hr);
  531. // pFile->Release();
  532. // delete [] ptszFullName;
  533. // #if !defined(UNICODE)
  534. // delete [] pwszName;
  535. // #endif
  536. // return hr;
  537. // }
  538. // //
  539. // // Try again
  540. // //
  541. // hr = pFile->Save(pwszName, TRUE);
  542. // }
  543. pFile->Release();
  544. delete [] ptszFullName;
  545. #if !defined(UNICODE)
  546. delete [] pwszName;
  547. #endif
  548. return hr;
  549. }
  550. //+----------------------------------------------------------------------------
  551. //
  552. // Member: CSchedule::ITaskScheduler::Delete, public
  553. //
  554. // Synopsis: Deletes the job/queue.
  555. //
  556. // Arguments: [pwszJobName] - indicates the job/queue to delete
  557. //
  558. // Returns: hresults
  559. //
  560. //-----------------------------------------------------------------------------
  561. STDMETHODIMP
  562. CSchedule::Delete(LPCWSTR pwszJobName)
  563. {
  564. TRACE(CSchedule, Delete);
  565. HRESULT hr;
  566. TCHAR * ptszFullName;
  567. hr = CheckJobName(pwszJobName, &ptszFullName);
  568. if (FAILED(hr))
  569. {
  570. ERR_OUT("CSchedule::Delete: CheckJobName", hr);
  571. return hr;
  572. }
  573. if (!DeleteFile(ptszFullName))
  574. {
  575. hr = HRESULT_FROM_WIN32(GetLastError());
  576. ERR_OUT("CSchedule::Delete: DeleteFile", hr);
  577. delete ptszFullName;
  578. return hr;
  579. }
  580. delete ptszFullName;
  581. return S_OK;
  582. }
  583. //+----------------------------------------------------------------------------
  584. //
  585. // Member: CSchedule::ITaskScheduler::Activate, public
  586. //
  587. // Synopsis: Given a valid name, returns a pointer to the activated job
  588. // object
  589. //
  590. // Arguments: [pwszName] - the name of the job to activate
  591. // [riid] - the interface to return
  592. // [ppunk] - a pointer to the job object interface
  593. //
  594. // Returns: hresults
  595. //
  596. //-----------------------------------------------------------------------------
  597. STDMETHODIMP
  598. CSchedule::Activate(LPCWSTR pwszName, REFIID riid, IUnknown ** ppunk)
  599. {
  600. TRACE(CSchedule, Activate);
  601. TCHAR * ptszFullName;
  602. HRESULT hr = CheckJobName(pwszName, &ptszFullName);
  603. if (FAILED(hr))
  604. {
  605. *ppunk = NULL;
  606. return hr;
  607. }
  608. CJob * pJob;
  609. //
  610. // CJob is a single-use, in-proc handler, so no need to get OLE in the
  611. // loop here. Use new (called by CJob::Create) instead of CoCreateInstance.
  612. //
  613. pJob = CJob::Create();
  614. if (pJob == NULL)
  615. {
  616. *ppunk = NULL;
  617. delete [] ptszFullName;
  618. return E_OUTOFMEMORY;
  619. }
  620. hr = pJob->LoadP(ptszFullName, 0, TRUE, TRUE);
  621. delete [] ptszFullName;
  622. if (FAILED(hr))
  623. {
  624. ERR_OUT("CSchedule::Activate, Load", hr);
  625. *ppunk = NULL;
  626. pJob->Release(); // on error, completely release the job object
  627. return hr;
  628. }
  629. hr = pJob->QueryInterface(riid, (void **)ppunk);
  630. if (FAILED(hr))
  631. {
  632. ERR_OUT("CSchedule::Activate: QueryInterface(riid)", hr);
  633. *ppunk = NULL;
  634. pJob->Release(); // on error, completely release the job object
  635. return hr;
  636. }
  637. //
  638. // The above QI increased the refcount to 2, so set it back to 1.
  639. //
  640. pJob->Release();
  641. return S_OK;
  642. }
  643. //+----------------------------------------------------------------------------
  644. //
  645. // Member: CSchedule::ITaskScheduler::IsOfType, public
  646. //
  647. // Synopsis: Does this object support the desired interface?
  648. //
  649. // Arguments: [pwszName] - indicates the object name
  650. // [riid] - indicates the interface of interest, typically
  651. // IID_ITask or IID_IScheduledQueue
  652. //
  653. // Returns: S_OK if it is, S_FALSE otherwise.
  654. //
  655. //-----------------------------------------------------------------------------
  656. STDMETHODIMP
  657. CSchedule::IsOfType(LPCWSTR pwszName, REFIID riid)
  658. {
  659. TRACE(CSchedule, IsOfType);
  660. // CODEWORK: A heavyweight implementation for now. It could possibly
  661. // be optimized by doing the QueryInterface before the LoadP, and
  662. // doing a lightweight LoadP.
  663. IUnknown * punk;
  664. HRESULT hr = Activate(pwszName, riid, &punk);
  665. if (SUCCEEDED(hr))
  666. {
  667. punk->Release();
  668. hr = S_OK;
  669. }
  670. else
  671. {
  672. if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_DATA) ||
  673. hr == SCHED_E_UNKNOWN_OBJECT_VERSION ||
  674. hr == E_NOINTERFACE)
  675. {
  676. //
  677. // These errors mean that the object is definitely not of a
  678. // type that we support. We translate them to S_FALSE.
  679. // Other errors could include file-not-found, access-denied,
  680. // invalid-arg, etc. We return those errors unmodified.
  681. //
  682. hr = S_FALSE;
  683. }
  684. }
  685. return hr;
  686. }