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.

816 lines
19 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. cprinter.cxx
  5. Abstract:
  6. Contains methods for PrintQueue object, GeneralInfo property set
  7. and Operation property set for the Print Queue object for the Windows NT
  8. provider
  9. Author:
  10. Ram Viswanathan (ramv) 11-09-95
  11. Revision History:
  12. --*/
  13. #include "winnt.hxx"
  14. #pragma hdrstop
  15. //
  16. // Class CWinNTPrintQueue Methods
  17. //
  18. DEFINE_IDispatch_ExtMgr_Implementation(CWinNTPrintQueue)
  19. DEFINE_IADsExtension_ExtMgr_Implementation(CWinNTPrintQueue)
  20. DEFINE_IADs_TempImplementation(CWinNTPrintQueue);
  21. DEFINE_IADs_PutGetImplementation(CWinNTPrintQueue,PrintQueueClass,gdwPrinterTableSize);
  22. DEFINE_IADsPropertyList_Implementation(CWinNTPrintQueue, PrintQueueClass,gdwPrinterTableSize)
  23. CWinNTPrintQueue::CWinNTPrintQueue()
  24. {
  25. _pszPrinterName = NULL;
  26. _pDispMgr = NULL;
  27. _pExtMgr = NULL;
  28. _pPropertyCache = NULL;
  29. ENLIST_TRACKING(CWinNTPrintQueue);
  30. return;
  31. }
  32. CWinNTPrintQueue::~CWinNTPrintQueue()
  33. {
  34. delete _pExtMgr; // created last, destroyed first
  35. delete _pDispMgr;
  36. delete _pPropertyCache;
  37. if(_pszPrinterName){
  38. FreeADsStr(_pszPrinterName);
  39. }
  40. return;
  41. }
  42. HRESULT
  43. CWinNTPrintQueue:: CreatePrintQueue(
  44. LPTSTR pszADsParent,
  45. DWORD dwParentId,
  46. LPTSTR pszDomainName,
  47. LPTSTR pszServerName,
  48. LPTSTR pszPrinterName,
  49. DWORD dwObjectState,
  50. REFIID riid,
  51. CWinNTCredentials& Credentials,
  52. LPVOID * ppvoid
  53. )
  54. {
  55. CWinNTPrintQueue *pPrintQueue = NULL;
  56. HRESULT hr;
  57. //
  58. // Create the printer object
  59. //
  60. hr = AllocatePrintQueueObject(pszServerName,
  61. pszPrinterName,
  62. &pPrintQueue
  63. );
  64. BAIL_ON_FAILURE(hr);
  65. ADsAssert(pPrintQueue->_pDispMgr);
  66. //
  67. // initialize the core object
  68. //
  69. hr = pPrintQueue->InitializeCoreObject(pszADsParent,
  70. pszPrinterName,
  71. PRINTER_CLASS_NAME,
  72. PRINTER_SCHEMA_NAME,
  73. CLSID_WinNTPrintQueue,
  74. dwObjectState);
  75. BAIL_ON_FAILURE(hr);
  76. pPrintQueue->_Credentials = Credentials;
  77. hr = pPrintQueue->_Credentials.RefServer(pszServerName);
  78. BAIL_ON_FAILURE(hr);
  79. //
  80. // Load ext mgr and extensions
  81. //
  82. hr = ADSILoadExtensionManager(
  83. PRINTER_CLASS_NAME,
  84. (IADsPrintQueue *) pPrintQueue,
  85. pPrintQueue->_pDispMgr,
  86. Credentials,
  87. &pPrintQueue->_pExtMgr
  88. );
  89. BAIL_ON_FAILURE(hr);
  90. ADsAssert(pPrintQueue->_pExtMgr);
  91. // check if the call is from UMI
  92. if(Credentials.GetFlags() & ADS_AUTH_RESERVED) {
  93. //
  94. // we do not pass riid to InitUmiObject below. This is because UMI object
  95. // does not support IDispatch. There are several places in ADSI code where
  96. // riid passed into this function is defaulted to IID_IDispatch -
  97. // IADsContainer::Create for example. To handle these cases, we always
  98. // request IID_IUnknown from the UMI object. Subsequent code within UMI
  99. // will QI for the appropriate interface.
  100. //
  101. if(3 == pPrintQueue->_dwNumComponents) {
  102. pPrintQueue->_CompClasses[0] = L"Domain";
  103. pPrintQueue->_CompClasses[1] = L"Computer";
  104. pPrintQueue->_CompClasses[2] = L"PrintQueue";
  105. }
  106. else if(2 == pPrintQueue->_dwNumComponents) {
  107. // no workstation services
  108. pPrintQueue->_CompClasses[0] = L"Computer";
  109. pPrintQueue->_CompClasses[1] = L"PrintQueue";
  110. }
  111. else
  112. BAIL_ON_FAILURE(hr = UMI_E_FAIL);
  113. hr = pPrintQueue->InitUmiObject(
  114. pPrintQueue->_Credentials,
  115. PrintQueueClass,
  116. gdwPrinterTableSize,
  117. pPrintQueue->_pPropertyCache,
  118. (IUnknown *)(INonDelegatingUnknown *) pPrintQueue,
  119. pPrintQueue->_pExtMgr,
  120. IID_IUnknown,
  121. ppvoid
  122. );
  123. BAIL_ON_FAILURE(hr);
  124. //
  125. // UMI object was created and the interface was obtained successfully.
  126. // UMI object now has a reference to the inner unknown of IADs, since
  127. // the call to Release() below is not going to be made in this case.
  128. //
  129. RRETURN(hr);
  130. }
  131. hr = pPrintQueue->QueryInterface(riid, (void **)ppvoid);
  132. BAIL_ON_FAILURE(hr);
  133. pPrintQueue->Release();
  134. RRETURN(hr);
  135. error:
  136. delete pPrintQueue;
  137. RRETURN_EXP_IF_ERR(hr);
  138. }
  139. /* IUnknown methods for printer object */
  140. //----------------------------------------------------------------------------
  141. // Function: QueryInterface
  142. //
  143. // Synopsis: If this object is aggregated within another object, then
  144. // all calls will delegate to the outer object. Otherwise, the
  145. // non-delegating QI is called
  146. //
  147. // Arguments:
  148. //
  149. // iid interface requested
  150. // ppInterface Returns pointer to interface requested. NULL if interface
  151. // is not supported.
  152. //
  153. // Returns: S_OK on success. Error code otherwise.
  154. //
  155. // Modifies: *ppInterface to return interface pointer
  156. //
  157. //----------------------------------------------------------------------------
  158. STDMETHODIMP CWinNTPrintQueue::QueryInterface(
  159. REFIID iid,
  160. LPVOID *ppInterface
  161. )
  162. {
  163. if(_pUnkOuter != NULL)
  164. RRETURN(_pUnkOuter->QueryInterface(
  165. iid,
  166. ppInterface
  167. ));
  168. RRETURN(NonDelegatingQueryInterface(
  169. iid,
  170. ppInterface
  171. ));
  172. }
  173. //----------------------------------------------------------------------------
  174. // Function: AddRef
  175. //
  176. // Synopsis: IUnknown::AddRef. If this object is aggregated within
  177. // another, all calls will delegate to the outer object.
  178. // Otherwise, the non-delegating AddRef is called
  179. //
  180. // Arguments:
  181. //
  182. // None
  183. //
  184. // Returns: New reference count
  185. //
  186. // Modifies: Nothing
  187. //
  188. //----------------------------------------------------------------------------
  189. STDMETHODIMP_(ULONG) CWinNTPrintQueue::AddRef(void)
  190. {
  191. if(_pUnkOuter != NULL)
  192. RRETURN(_pUnkOuter->AddRef());
  193. RRETURN(NonDelegatingAddRef());
  194. }
  195. //----------------------------------------------------------------------------
  196. // Function: Release
  197. //
  198. // Synopsis: IUnknown::Release. If this object is aggregated within
  199. // another, all calls will delegate to the outer object.
  200. // Otherwise, the non-delegating Release is called
  201. //
  202. // Arguments:
  203. //
  204. // None
  205. //
  206. // Returns: New reference count
  207. //
  208. // Modifies: Nothing
  209. //
  210. //----------------------------------------------------------------------------
  211. STDMETHODIMP_(ULONG) CWinNTPrintQueue::Release(void)
  212. {
  213. if(_pUnkOuter != NULL)
  214. RRETURN(_pUnkOuter->Release());
  215. RRETURN(NonDelegatingRelease());
  216. }
  217. //----------------------------------------------------------------------------
  218. STDMETHODIMP
  219. CWinNTPrintQueue::NonDelegatingQueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  220. {
  221. HRESULT hr = S_OK;
  222. if(!ppvObj)
  223. {
  224. RRETURN(E_POINTER);
  225. }
  226. if (IsEqualIID(riid, IID_IUnknown))
  227. {
  228. *ppvObj = (IADsPrintQueue *)this;
  229. }
  230. else if (IsEqualIID(riid, IID_IDispatch))
  231. {
  232. *ppvObj = (IADsPrintQueue *)this;
  233. }
  234. else if (IsEqualIID(riid, IID_ISupportErrorInfo))
  235. {
  236. *ppvObj = (ISupportErrorInfo FAR *) this;
  237. }
  238. else if (IsEqualIID(riid, IID_IADsPropertyList))
  239. {
  240. *ppvObj = (IADsPropertyList *)this;
  241. }
  242. else if (IsEqualIID(riid, IID_IADs))
  243. {
  244. *ppvObj = (IADsPrintQueue FAR *) this;
  245. }
  246. else if (IsEqualIID(riid, IID_IADsPrintQueue))
  247. {
  248. *ppvObj = (IADsPrintQueue FAR *) this;
  249. }
  250. else if (IsEqualIID(riid, IID_IADsPrintQueueOperations))
  251. {
  252. *ppvObj = (IADsPrintQueueOperations FAR *) this;
  253. }
  254. else if( (_pDispatch != NULL) &&
  255. IsEqualIID(riid, IID_IADsExtension) )
  256. {
  257. *ppvObj = (IADsExtension *) this;
  258. }
  259. else if (_pExtMgr)
  260. {
  261. RRETURN( _pExtMgr->QueryInterface(riid, ppvObj));
  262. }
  263. else
  264. {
  265. *ppvObj = NULL;
  266. RRETURN(E_NOINTERFACE);
  267. }
  268. ((LPUNKNOWN)*ppvObj)->AddRef();
  269. RRETURN(S_OK);
  270. }
  271. /* ISupportErrorInfo method */
  272. STDMETHODIMP
  273. CWinNTPrintQueue::InterfaceSupportsErrorInfo(
  274. THIS_ REFIID riid
  275. )
  276. {
  277. if (IsEqualIID(riid, IID_IADs) ||
  278. IsEqualIID(riid, IID_IADsPrintQueue) ||
  279. IsEqualIID(riid, IID_IADsPrintQueueOperations) ||
  280. IsEqualIID(riid, IID_IADsPropertyList)) {
  281. RRETURN(S_OK);
  282. } else {
  283. RRETURN(S_FALSE);
  284. }
  285. }
  286. //+---------------------------------------------------------------------------
  287. //
  288. // Function: SetInfo
  289. //
  290. // Synopsis: Binds to real printer as specified in _PrinterName and attempts
  291. // to set the real printer.
  292. //
  293. // Arguments: void
  294. //
  295. // Returns: HRESULT.
  296. //
  297. // Modifies:
  298. //
  299. // History: 11/08/95 RamV Created
  300. // part of code appropriated from NetOle project
  301. //----------------------------------------------------------------------------
  302. STDMETHODIMP
  303. CWinNTPrintQueue::SetInfo(THIS)
  304. {
  305. BOOL fStatus = FALSE;
  306. LPPRINTER_INFO_2 lpPrinterInfo2 = NULL;
  307. BOOL fPrinterAdded = FALSE;
  308. POBJECTINFO pObjectInfo = NULL;
  309. #if (!defined(BUILD_FOR_NT40))
  310. LPPRINTER_INFO_7 lpPrinterInfo7 = NULL;
  311. #endif
  312. HRESULT hr;
  313. if (GetObjectState() == ADS_OBJECT_UNBOUND) {
  314. hr = WinNTAddPrinter();
  315. BAIL_IF_ERROR(hr);
  316. SetObjectState(ADS_OBJECT_BOUND);
  317. fPrinterAdded = TRUE;
  318. }
  319. //
  320. // first do a getinfo to get properties that werent changed.
  321. //
  322. hr = GetPrinterInfo(&lpPrinterInfo2, _pszPrinterName);
  323. BAIL_IF_ERROR(hr);
  324. hr = MarshallAndSet(lpPrinterInfo2);
  325. BAIL_IF_ERROR(hr);
  326. #if (!defined(BUILD_FOR_NT40))
  327. hr = GetPrinterInfo7(&lpPrinterInfo7, _pszPrinterName);
  328. if (SUCCEEDED(hr)) {
  329. MarshallAndSet(lpPrinterInfo7);
  330. }
  331. else if(hr == HRESULT_FROM_WIN32(ERROR_INVALID_LEVEL))
  332. // Level 7 is not supported on NT4. So, ignore this error.
  333. hr = S_OK;
  334. #endif
  335. if(SUCCEEDED(hr))
  336. _pPropertyCache->ClearModifiedFlags();
  337. cleanup:
  338. //
  339. // If we added a printer and hr is set, we should delete it now
  340. // as the SetInfo failed in subsequent operations.
  341. //
  342. if (FAILED(hr) && fPrinterAdded) {
  343. //
  344. // Build ObjectInfo first
  345. //
  346. BuildObjectInfo(
  347. _ADsPath,
  348. &pObjectInfo
  349. );
  350. //
  351. // Call delete printer only if the pObjectInfo is valid
  352. // We cannot do anything in the other case.
  353. //
  354. if (pObjectInfo) {
  355. WinNTDeletePrinter(pObjectInfo);
  356. FreeObjectInfo(pObjectInfo);
  357. }
  358. }
  359. if(lpPrinterInfo2){
  360. FreeADsMem((LPBYTE)lpPrinterInfo2);
  361. }
  362. #if (!defined(BUILD_FOR_NT40))
  363. if (lpPrinterInfo7) {
  364. FreeADsMem((LPBYTE)lpPrinterInfo7);
  365. }
  366. #endif
  367. RRETURN_EXP_IF_ERR(hr);
  368. }
  369. //+---------------------------------------------------------------------------
  370. //
  371. // Function: GetInfo(function overloaded: part of CoreADsObject as well
  372. // as IADs).This function here is part of IADs
  373. //
  374. // Synopsis: Binds to real printer as specified in _PrinterName and attempts
  375. // to get information from the real printer.
  376. //
  377. // Arguments: void
  378. //
  379. // Returns: HRESULT.
  380. //
  381. // Modifies:
  382. //
  383. // History: 11/08/95 RamV Created
  384. // part of code appropriated from NetOle project
  385. //----------------------------------------------------------------------------
  386. STDMETHODIMP
  387. CWinNTPrintQueue::GetInfo(THIS)
  388. {
  389. HRESULT hr = S_OK;
  390. #if (!defined(BUILD_FOR_NT40))
  391. hr = GetInfo(7,TRUE);
  392. #endif
  393. hr = GetInfo(2,TRUE);
  394. RRETURN_EXP_IF_ERR(hr);
  395. }
  396. STDMETHODIMP
  397. CWinNTPrintQueue::ImplicitGetInfo(THIS)
  398. {
  399. HRESULT hr = S_OK;
  400. #if (!defined(BUILD_FOR_NT40))
  401. hr = GetInfo(7,FALSE);
  402. #endif
  403. hr = GetInfo(2,FALSE);
  404. RRETURN_EXP_IF_ERR(hr);
  405. }
  406. //+---------------------------------------------------------------------------
  407. //
  408. // Function: GetInfo (overloaded)
  409. //
  410. // Synopsis: Calls the IADs GetInfo, because the printer object has just
  411. // one info level on which to retrieve info from.
  412. //
  413. // Arguments: dwApiLevel and fExplicit (Both Ignored)
  414. //
  415. // Returns: HRESULT.
  416. //
  417. // Modifies:
  418. //
  419. // History: 01-05-96 RamV Created
  420. //----------------------------------------------------------------------------
  421. STDMETHODIMP
  422. CWinNTPrintQueue::GetInfo(THIS_ DWORD dwApiLevel, BOOL fExplicit)
  423. {
  424. LPPRINTER_INFO_2 lpPrinterInfo2= NULL;
  425. HRESULT hr = S_OK;
  426. #if (!defined(BUILD_FOR_NT40))
  427. LPPRINTER_INFO_7 lpPrinterInfo7 = NULL;
  428. HRESULT hr7 = S_OK;
  429. #endif
  430. hr = GetPrinterInfo(&lpPrinterInfo2, _pszPrinterName);
  431. BAIL_IF_ERROR(hr);
  432. hr = UnMarshall(lpPrinterInfo2,
  433. fExplicit);
  434. #if (!defined(BUILD_FOR_NT40))
  435. hr7 = GetPrinterInfo7(&lpPrinterInfo7, _pszPrinterName);
  436. if (SUCCEEDED(hr7)) {
  437. hr7 = UnMarshall7(lpPrinterInfo7, fExplicit);
  438. }
  439. #endif
  440. cleanup:
  441. if(lpPrinterInfo2)
  442. FreeADsMem((LPBYTE)lpPrinterInfo2);
  443. #if (!defined(BUILD_FOR_NT40))
  444. if (lpPrinterInfo7) {
  445. FreeADsMem((LPBYTE)lpPrinterInfo7);
  446. }
  447. #endif
  448. RRETURN_EXP_IF_ERR(hr);
  449. }
  450. //
  451. // helper function WinNTAddPrinter
  452. //
  453. HRESULT
  454. CWinNTPrintQueue::WinNTAddPrinter(void)
  455. {
  456. HRESULT hr = S_OK;
  457. TCHAR szUncServerName[MAX_PATH];
  458. TCHAR szServerName[MAX_PATH];
  459. PRINTER_INFO_2 PrinterInfo2;
  460. HANDLE hPrinter = NULL;
  461. LPTSTR pszPrintDevices = NULL;
  462. LPTSTR pszModel = NULL;
  463. LPTSTR pszDatatype = NULL;
  464. LPTSTR pszPrintProcessor = NULL;
  465. LPTSTR pszPrinterName = NULL;
  466. DWORD dwSyntaxId = 0;
  467. DWORD dwNumValues = 0;
  468. PNTOBJECT pNTObject = NULL;
  469. memset(&PrinterInfo2, 0, sizeof(PRINTER_INFO_2));
  470. hr = GetDelimitedStringPropertyFromCache(
  471. _pPropertyCache,
  472. TEXT("PrintDevices"),
  473. &pszPrintDevices
  474. );
  475. if(SUCCEEDED(hr)){
  476. PrinterInfo2.pPortName = pszPrintDevices;
  477. }
  478. hr = GetLPTSTRPropertyFromCache(
  479. _pPropertyCache,
  480. TEXT("Model"),
  481. &pszModel
  482. );
  483. if(SUCCEEDED(hr)){
  484. PrinterInfo2.pDriverName = pszModel;
  485. }
  486. hr = GetLPTSTRPropertyFromCache(
  487. _pPropertyCache,
  488. TEXT("PrinterName"),
  489. &pszPrinterName
  490. );
  491. if(SUCCEEDED(hr)){
  492. PrinterInfo2.pPrinterName = pszPrinterName;
  493. }
  494. else {
  495. PrinterInfo2.pPrinterName = (LPTSTR) _Name;
  496. }
  497. hr = GetLPTSTRPropertyFromCache(
  498. _pPropertyCache,
  499. TEXT("PrintProcessor"),
  500. &pszPrintProcessor
  501. );
  502. if(SUCCEEDED(hr)){
  503. PrinterInfo2.pPrintProcessor = pszPrintProcessor;
  504. }
  505. hr = GetLPTSTRPropertyFromCache(
  506. _pPropertyCache,
  507. TEXT("Datatype"),
  508. &pszDatatype
  509. );
  510. if(SUCCEEDED(hr)){
  511. PrinterInfo2.pDatatype = pszDatatype;
  512. }
  513. hr = GetServerFromPath(_ADsPath, szServerName);
  514. BAIL_IF_ERROR(hr);
  515. hr = MakeUncName(szServerName, szUncServerName);
  516. BAIL_IF_ERROR(hr);
  517. PrinterInfo2.pServerName = szServerName;
  518. PrinterInfo2.pShareName = (LPTSTR)_Name;
  519. PrinterInfo2.pComment = NULL;
  520. PrinterInfo2.pLocation = NULL;
  521. PrinterInfo2.pDevMode = NULL;
  522. PrinterInfo2.pSepFile = NULL;
  523. PrinterInfo2.pParameters = NULL;
  524. PrinterInfo2.pSecurityDescriptor = NULL;
  525. PrinterInfo2.Attributes = PRINTER_ATTRIBUTE_SHARED;
  526. PrinterInfo2.Priority = 0;
  527. PrinterInfo2.DefaultPriority = 0;
  528. PrinterInfo2.StartTime = 0;
  529. PrinterInfo2.UntilTime = 0;
  530. PrinterInfo2.Status = 0;
  531. PrinterInfo2.cJobs= 0;
  532. PrinterInfo2.AveragePPM = 0;
  533. //
  534. // set properties on printer
  535. //
  536. hPrinter = AddPrinter(szUncServerName,
  537. 2,
  538. (LPBYTE)&PrinterInfo2);
  539. if(hPrinter == NULL){
  540. hr = HRESULT_FROM_WIN32(GetLastError());
  541. goto cleanup;
  542. }
  543. cleanup:
  544. if(pszPrintDevices){
  545. FreeADsStr(pszPrintDevices);
  546. }
  547. if(pszModel){
  548. FreeADsStr(pszModel);
  549. }
  550. if(pszPrintProcessor){
  551. FreeADsStr(pszPrintProcessor);
  552. }
  553. if(pszDatatype){
  554. FreeADsStr(pszDatatype);
  555. }
  556. RRETURN(hr);
  557. }
  558. HRESULT
  559. CWinNTPrintQueue::AllocatePrintQueueObject(
  560. LPTSTR pszServerName,
  561. LPTSTR pszPrinterName,
  562. CWinNTPrintQueue ** ppPrintQueue
  563. )
  564. {
  565. CWinNTPrintQueue FAR * pPrintQueue = NULL;
  566. HRESULT hr = S_OK;
  567. TCHAR szUncServerName[MAX_PATH];
  568. TCHAR szUncPrinterName [MAX_PATH];
  569. CAggregatorDispMgr FAR * pDispMgr = NULL;
  570. CPropertyCache FAR * pPropertyCache = NULL;
  571. pPrintQueue = new CWinNTPrintQueue();
  572. if (pPrintQueue == NULL) {
  573. hr = E_OUTOFMEMORY;
  574. }
  575. BAIL_ON_FAILURE(hr);
  576. //
  577. // Build the UNC form of printer name from the supplied information
  578. //
  579. if( (wcslen(pszServerName) + 3) > MAX_PATH) {
  580. BAIL_ON_FAILURE(hr = E_INVALIDARG);
  581. }
  582. MakeUncName(pszServerName,
  583. szUncServerName);
  584. if( (wcslen(szUncServerName) + wcslen(pszPrinterName) + 2) > MAX_PATH) {
  585. BAIL_ON_FAILURE(hr = E_INVALIDARG);
  586. }
  587. wcscpy(szUncPrinterName, szUncServerName);
  588. wcscat(szUncPrinterName, TEXT("\\"));
  589. wcscat(szUncPrinterName, pszPrinterName);
  590. pPrintQueue->_pszPrinterName =
  591. AllocADsStr(szUncPrinterName);
  592. if(!(pPrintQueue->_pszPrinterName)){
  593. hr = E_OUTOFMEMORY;
  594. goto error;
  595. }
  596. pDispMgr = new CAggregatorDispMgr;
  597. if (pDispMgr == NULL) {
  598. hr = E_OUTOFMEMORY;
  599. }
  600. BAIL_ON_FAILURE(hr);
  601. hr = LoadTypeInfoEntry(pDispMgr,
  602. LIBID_ADs,
  603. IID_IADsPrintQueue,
  604. (IADsPrintQueue *)pPrintQueue,
  605. DISPID_REGULAR);
  606. hr = LoadTypeInfoEntry(pDispMgr,
  607. LIBID_ADs,
  608. IID_IADsPrintQueueOperations,
  609. (IADsPrintQueueOperations *)pPrintQueue,
  610. DISPID_REGULAR);
  611. BAIL_ON_FAILURE(hr);
  612. hr = LoadTypeInfoEntry(pDispMgr,
  613. LIBID_ADs,
  614. IID_IADsPropertyList,
  615. (IADsPropertyList *)pPrintQueue,
  616. DISPID_VALUE);
  617. BAIL_ON_FAILURE(hr);
  618. hr = CPropertyCache::createpropertycache(
  619. PrintQueueClass,
  620. gdwPrinterTableSize,
  621. (CCoreADsObject *)pPrintQueue,
  622. &pPropertyCache
  623. );
  624. BAIL_ON_FAILURE(hr);
  625. pDispMgr->RegisterPropertyCache(
  626. pPropertyCache
  627. );
  628. pPrintQueue->_pPropertyCache = pPropertyCache;
  629. pPrintQueue->_pDispMgr = pDispMgr;
  630. *ppPrintQueue = pPrintQueue;
  631. RRETURN(hr);
  632. error:
  633. delete pPropertyCache;
  634. delete pDispMgr;
  635. delete pPrintQueue;
  636. RRETURN(hr);
  637. }