Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

886 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996-1998 Microsoft Corporation
  3. Module Name:
  4. comoem.cpp
  5. Abstract:
  6. Necessary COM class definition to Unidrv
  7. OEM rendering module plug-in.
  8. Environment:
  9. Windows NT Unidrv driver
  10. Revision History:
  11. 98/4/24 takashim:
  12. Written the original sample so that it is more C++.
  13. 98/9/3 v-yutah:
  14. Because of suggestion from ganeshp,
  15. Modified PublishDriverInterface(), and related to it,
  16. Modified EnableDriver(), DisableDriver()
  17. 03/11/02 v-satois
  18. For security issues, addition null-checking of pointer.
  19. --*/
  20. // NTRAID#NTBUG9-588572-2002/03/28-v-sueyas-: Correct the return values for each COM I/F methods
  21. #define INITGUID // for GUID one-time initialization
  22. #include "pdev.h"
  23. #include "names.h"
  24. // Globals
  25. static HMODULE g_hModule = NULL ; // DLL module handle
  26. static long g_cComponents = 0 ; // Count of active components
  27. static long g_cServerLocks = 0 ; // Count of locks
  28. //
  29. // IOemCB Definition
  30. //
  31. class IOemCB : public IPrintOemUni
  32. {
  33. public:
  34. //
  35. // IUnknown methods
  36. //
  37. STDMETHODIMP
  38. QueryInterface(
  39. const IID& iid, void** ppv)
  40. {
  41. VERBOSE((DLLTEXT("IOemCB: QueryInterface entry\n")));
  42. if (ppv == NULL) // Checking null-pointer.
  43. {
  44. return E_NOINTERFACE;
  45. }
  46. if (iid == IID_IUnknown)
  47. {
  48. *ppv = static_cast<IUnknown*>(this);
  49. VERBOSE((DLLTEXT("IOemCB:Return pointer to IUnknown.\n")));
  50. }
  51. else if (iid == IID_IPrintOemUni)
  52. {
  53. *ppv = static_cast<IPrintOemUni*>(this);
  54. VERBOSE((DLLTEXT("IOemCB:Return pointer to IPrintOemUni.\n")));
  55. }
  56. else
  57. {
  58. *ppv = NULL ;
  59. VERBOSE((DLLTEXT("IOemCB:Return NULL.\n")));
  60. return E_NOINTERFACE ;
  61. }
  62. reinterpret_cast<IUnknown*>(*ppv)->AddRef();
  63. return S_OK ;
  64. }
  65. STDMETHODIMP_(ULONG)
  66. AddRef()
  67. {
  68. VERBOSE((DLLTEXT("IOemCB::AddRef() entry.\n")));
  69. return InterlockedIncrement(&m_cRef);
  70. }
  71. STDMETHODIMP_(ULONG)
  72. Release()
  73. {
  74. VERBOSE((DLLTEXT("IOemCB::Release() entry.\n")));
  75. if (InterlockedDecrement(&m_cRef) == 0)
  76. {
  77. delete this ;
  78. return 0 ;
  79. }
  80. return m_cRef ;
  81. }
  82. //
  83. // IPrintOemCommon methods
  84. //
  85. // Function Name: GetInfo
  86. // Plug-in: Any
  87. // Driver: Any
  88. // Type: Mandatory
  89. //
  90. STDMETHODIMP
  91. GetInfo(
  92. DWORD dwMode,
  93. PVOID pBuffer,
  94. DWORD cbSize,
  95. PDWORD pcbNeeded)
  96. {
  97. VERBOSE((DLLTEXT("IOemCB::GetInfo() entry.\n")));
  98. if (OEMGetInfo(dwMode, pBuffer, cbSize, pcbNeeded))
  99. return S_OK;
  100. else
  101. return E_FAIL;
  102. }
  103. //
  104. // Function Name: DevMode
  105. // Plug-in: Rendering module
  106. // Driver: Any
  107. // Type: Optional
  108. //
  109. STDMETHODIMP
  110. DevMode(
  111. DWORD dwMode,
  112. POEMDMPARAM pOemDMParam)
  113. {
  114. VERBOSE((DLLTEXT("IOemCB::DevMode() entry.\n")));
  115. return E_NOTIMPL;
  116. }
  117. //
  118. // IPrintOemEngine methods
  119. //
  120. //
  121. // Function Name: EnableDriver
  122. // Plug-in: Rendering module
  123. // Driver: Any
  124. // Type: Optional
  125. //
  126. STDMETHODIMP
  127. EnableDriver(
  128. DWORD dwDriverVersion,
  129. DWORD cbSize,
  130. PDRVENABLEDATA pded)
  131. {
  132. VERBOSE((DLLTEXT("IOemCB::EnableDriver() entry.\n")));
  133. #if 1 // Suggested by ganeshp
  134. // Need to return S_OK so that DisableDriver() will be called, which Releases
  135. // the reference to the Printer Driver's interface.
  136. return S_OK;
  137. #else // Suggested by ganeshp
  138. return E_NOTIMPL;
  139. #endif // Suggested by ganeshp
  140. }
  141. //
  142. // Function Name: DisableDriver
  143. // Plug-in: Rendering module
  144. // Driver: Any
  145. // Type: Optional
  146. //
  147. STDMETHODIMP
  148. DisableDriver(VOID)
  149. {
  150. VERBOSE((DLLTEXT("IOemCB::DisaleDriver() entry.\n")));
  151. #if 1 // Suggested by ganeshp
  152. // Release reference to Printer Driver's interface.
  153. if (this->pOEMHelp)
  154. {
  155. this->pOEMHelp->Release();
  156. this->pOEMHelp = NULL;
  157. }
  158. return S_OK;
  159. #else // Suggested by ganeshp
  160. return E_NOTIMPL;
  161. #endif // Suggested by ganeshp
  162. }
  163. //
  164. // Function Name: EnablePDEV
  165. // Plug-in: Rendering module
  166. // Driver: Any
  167. // Type: Optional
  168. //
  169. STDMETHODIMP
  170. EnablePDEV(
  171. PDEVOBJ pdevobj,
  172. PWSTR pPrinterName,
  173. ULONG cPatterns,
  174. HSURF *phsurfPatterns,
  175. ULONG cjGdiInfo,
  176. GDIINFO *pGdiInfo,
  177. ULONG cjDevInfo,
  178. DEVINFO *pDevInfo,
  179. DRVENABLEDATA *pded,
  180. OUT PDEVOEM *pDevOem)
  181. {
  182. PDEVOEM pTemp;
  183. VERBOSE((DLLTEXT("IOemCB::EnablePDEV() entry.\n")));
  184. if (pDevOem == NULL) // Checking null-pointer.
  185. {
  186. return E_FAIL;
  187. }
  188. if ((pTemp = OEMEnablePDEV(pdevobj, pPrinterName,
  189. cPatterns, phsurfPatterns, cjGdiInfo,
  190. pGdiInfo, cjDevInfo, pDevInfo, pded))) {
  191. *pDevOem = pTemp;
  192. return S_OK;
  193. }
  194. return E_FAIL;
  195. }
  196. //
  197. // Function Name: DisablePDEV
  198. // Plug-in: Rendering module
  199. // Driver: Any
  200. // Type: Optional
  201. //
  202. STDMETHODIMP
  203. DisablePDEV(
  204. PDEVOBJ pdevobj)
  205. {
  206. VERBOSE((DLLTEXT("IOemCB::DisablePDEV() entry.\n")));
  207. OEMDisablePDEV(pdevobj);
  208. return S_OK;
  209. }
  210. //
  211. // Function Name: ResetPDEV
  212. // Plug-in: Rendering module
  213. // Driver: Any
  214. // Type: Optional
  215. //
  216. STDMETHODIMP
  217. ResetPDEV(
  218. PDEVOBJ pdevobjOld,
  219. PDEVOBJ pdevobjNew)
  220. {
  221. // VERBOSE((DLLTEXT("IOemCB::ResetPDEV() entry.\n")));
  222. if (OEMResetPDEV(pdevobjOld, pdevobjNew))
  223. return S_OK;
  224. else
  225. return E_FAIL;
  226. }
  227. //
  228. // IPrintOemUni methods
  229. //
  230. //
  231. // Function Name: PublishDriverInterface
  232. // Plug-in: Rendering module
  233. // Driver: Any
  234. // Type: Mandatory
  235. //
  236. STDMETHODIMP
  237. PublishDriverInterface(
  238. IUnknown *pIUnknown)
  239. {
  240. VERBOSE((DLLTEXT("IOemCB::PublishDriverInterface() entry.\n")));
  241. #if 1 // Suggested by ganeshp
  242. // Need to store pointer to Driver Helper functions, if we already haven't.
  243. if (this->pOEMHelp == NULL)
  244. {
  245. HRESULT hResult;
  246. // Get Interface to Helper Functions.
  247. if (pIUnknown == NULL) // Checking null-pointer.
  248. {
  249. return E_FAIL;
  250. }
  251. hResult = pIUnknown->QueryInterface(IID_IPrintOemDriverUni, (void** ) &(this->pOEMHelp));
  252. if(!SUCCEEDED(hResult))
  253. {
  254. // Make sure that interface pointer reflects interface query failure.
  255. this->pOEMHelp = NULL;
  256. return E_FAIL;
  257. }
  258. }
  259. return S_OK;
  260. #else // Suggested by ganeshp
  261. if (this->pOEMHelp == NULL)
  262. {
  263. if (pIUnknown == NULL) // Checking null-pointer.
  264. {
  265. return E_FAIL;
  266. }
  267. pIUnknown->AddRef();
  268. }
  269. this->pOEMHelp = reinterpret_cast<IPrintOemDriverUni*>(pIUnknown);
  270. return S_OK;
  271. #endif // Suggested by ganeshp
  272. }
  273. //
  274. // Function Name: GetImplementationMethod
  275. // Plug-in: Rendering module
  276. // Driver: Any
  277. // Type: Mandatory
  278. //
  279. //
  280. // Needed to be static so that it can be passed
  281. // to the bsearch() as a pointer to a functin.
  282. //
  283. static
  284. int __cdecl
  285. iCompNames(
  286. const void *p1,
  287. const void *p2) {
  288. return strcmp(
  289. *((char **)p1),
  290. *((char **)p2));
  291. }
  292. STDMETHODIMP
  293. GetImplementedMethod(
  294. PSTR pMethodName)
  295. {
  296. LONG lRet = E_NOTIMPL;
  297. PSTR pTemp;
  298. VERBOSE((DLLTEXT("IOemCB::GetImplementedMethod() entry.\n")));
  299. if (NULL != pMethodName) {
  300. pTemp = (PSTR)bsearch(
  301. &pMethodName,
  302. gMethodsSupported,
  303. (sizeof (gMethodsSupported) / sizeof (PSTR)),
  304. sizeof (PSTR),
  305. iCompNames);
  306. if (NULL != pTemp)
  307. lRet = S_OK;
  308. }
  309. VERBOSE((DLLTEXT("pMethodName = %s, lRet = %d\n"), pMethodName, lRet));
  310. return lRet;
  311. }
  312. //
  313. // Function Name: CommandCallback
  314. // Plug-in: Rendering module
  315. // Driver: Unidrv
  316. // Type: Optional
  317. //
  318. STDMETHODIMP
  319. CommandCallback(
  320. PDEVOBJ pdevobj,
  321. DWORD dwCallbackID,
  322. DWORD dwCount,
  323. PDWORD pdwParams,
  324. OUT INT *piResult)
  325. {
  326. INT retv;
  327. VERBOSE((DLLTEXT("IOemCB::CommandCallback() entry.\n")));
  328. if (piResult == NULL) // Checking null-pointer.
  329. {
  330. return E_FAIL;
  331. }
  332. // Checking the return value.
  333. retv = OEMCommandCallback(pdevobj, dwCallbackID, dwCount, pdwParams);
  334. if (retv < 0)
  335. {
  336. *piResult = 0;
  337. return E_FAIL;
  338. }
  339. else
  340. {
  341. *piResult = retv;
  342. }
  343. return S_OK;
  344. }
  345. //
  346. // Function Name: ImageProcessing
  347. // Plug-in: Rendering module
  348. // Driver: Unidrv
  349. // Type: Optional
  350. //
  351. STDMETHODIMP
  352. ImageProcessing(
  353. PDEVOBJ pdevobj,
  354. PBYTE pSrcBitmap,
  355. PBITMAPINFOHEADER pBitmapInfoHeader,
  356. PBYTE pColorTable,
  357. DWORD dwCallbackID,
  358. PIPPARAMS pIPParams,
  359. OUT PBYTE *ppbResult)
  360. {
  361. VERBOSE((DLLTEXT("IOemCB::ImageProcessing() entry.\n")));
  362. return E_NOTIMPL;
  363. }
  364. //
  365. // Function Name: FilterGraphics
  366. // Plug-in: Rendering module
  367. // Driver: Unidrv
  368. // Type: Optional
  369. //
  370. STDMETHODIMP
  371. FilterGraphics(
  372. PDEVOBJ pdevobj,
  373. PBYTE pBuf,
  374. DWORD dwLen)
  375. {
  376. VERBOSE((DLLTEXT("IOemCB::FilterGraphis() entry.\n")));
  377. return E_NOTIMPL;
  378. }
  379. //
  380. // Function Name: Compression
  381. // Plug-in: Rendering module
  382. // Driver: Unidrv
  383. // Type: Optional
  384. //
  385. STDMETHODIMP
  386. Compression(
  387. PDEVOBJ pdevobj,
  388. PBYTE pInBuf,
  389. PBYTE pOutBuf,
  390. DWORD dwInLen,
  391. DWORD dwOutLen,
  392. OUT INT *piResult)
  393. {
  394. VERBOSE((DLLTEXT("IOemCB::Compression() entry.\n")));
  395. return E_NOTIMPL;
  396. }
  397. //
  398. // Function Name: HalftonePattern
  399. // Plug-in: Rendering module
  400. // Driver: Unidrv
  401. // Type: Optional
  402. //
  403. STDMETHODIMP
  404. HalftonePattern(
  405. PDEVOBJ pdevobj,
  406. PBYTE pHTPattern,
  407. DWORD dwHTPatternX,
  408. DWORD dwHTPatternY,
  409. DWORD dwHTNumPatterns,
  410. DWORD dwCallbackID,
  411. PBYTE pResource,
  412. DWORD dwResourceSize)
  413. {
  414. VERBOSE((DLLTEXT("IOemCB::HalftonePattern() entry.\n")));
  415. return E_NOTIMPL;
  416. }
  417. //
  418. // Function Name: MemoryUsge
  419. // Plug-in: Rendering module
  420. // Driver: Unidrv
  421. // Type: Optional
  422. //
  423. STDMETHODIMP
  424. MemoryUsage(
  425. PDEVOBJ pdevobj,
  426. POEMMEMORYUSAGE pMemoryUsage)
  427. {
  428. VERBOSE((DLLTEXT("IOemCB::MemoryUsage() entry.\n")));
  429. return E_NOTIMPL;
  430. }
  431. //
  432. // Function Name: DownloadFontHeader
  433. // Plug-in: Rendering module
  434. // Driver: Unidrv
  435. // Type: Optional
  436. //
  437. STDMETHODIMP
  438. DownloadFontHeader(
  439. PDEVOBJ pdevobj,
  440. PUNIFONTOBJ pUFObj,
  441. OUT DWORD *pdwResult)
  442. {
  443. VERBOSE((DLLTEXT("IOemCB::DownloadFontHeader() entry.\n")));
  444. #ifdef DOWNLOADFONT
  445. if (pdwResult == NULL) // Checking null-pointer.
  446. {
  447. return E_FAIL;
  448. }
  449. if (0 < (*pdwResult = OEMDownloadFontHeader(pdevobj, pUFObj))) {
  450. return S_OK;
  451. }
  452. else {
  453. return E_FAIL;
  454. }
  455. #else // DOWNLOADFONT
  456. return E_NOTIMPL;
  457. #endif // DOWNLOADFONT
  458. }
  459. //
  460. // Function Name: DownloadCharGlyph
  461. // Plug-in: Rendering module
  462. // Driver: Unidrv
  463. // Type: Optional
  464. //
  465. STDMETHODIMP
  466. DownloadCharGlyph(
  467. PDEVOBJ pdevobj,
  468. PUNIFONTOBJ pUFObj,
  469. HGLYPH hGlyph,
  470. PDWORD pdwWidth,
  471. OUT DWORD *pdwResult)
  472. {
  473. VERBOSE((DLLTEXT("IOemCB::DownloadCharGlyph() entry.\n")));
  474. #ifdef DOWNLOADFONT
  475. if (pdwResult == NULL) // Checking null-pointer.
  476. {
  477. return E_FAIL;
  478. }
  479. if (0 < (*pdwResult = OEMDownloadCharGlyph(pdevobj, pUFObj,
  480. hGlyph, pdwWidth))) {
  481. return S_OK;
  482. }
  483. else {
  484. return E_FAIL;
  485. }
  486. #else // DOWNLOADFONT
  487. return E_NOTIMPL;
  488. #endif // DOWNLOADFONT
  489. }
  490. //
  491. // Function Name: TTDonwloadMethod
  492. // Plug-in: Rendering module
  493. // Driver: Unidrv
  494. // Type: Optional
  495. //
  496. STDMETHODIMP
  497. TTDownloadMethod(
  498. PDEVOBJ pdevobj,
  499. PUNIFONTOBJ pUFObj,
  500. OUT DWORD *pdwResult)
  501. {
  502. VERBOSE((DLLTEXT("IOemCB::TTDownloadMethod() entry.\n")));
  503. #ifdef DOWNLOADFONT
  504. if (pdwResult == NULL) // Checking null-pointer.
  505. {
  506. return E_FAIL;
  507. }
  508. *pdwResult = OEMTTDownloadMethod(pdevobj, pUFObj);
  509. return S_OK;
  510. #else // DOWNLOADFONT
  511. return E_NOTIMPL;
  512. #endif // DOWNLOADFONT
  513. }
  514. //
  515. // Function Name: OutputCharStr
  516. // Plug-in: Rendering module
  517. // Driver: Unidrv
  518. // Type: Optional
  519. //
  520. STDMETHODIMP
  521. OutputCharStr(
  522. PDEVOBJ pdevobj,
  523. PUNIFONTOBJ pUFObj,
  524. DWORD dwType,
  525. DWORD dwCount,
  526. PVOID pGlyph)
  527. {
  528. VERBOSE((DLLTEXT("IOemCB::OutputCharStr() entry.\n")));
  529. OEMOutputCharStr(pdevobj, pUFObj, dwType, dwCount, pGlyph);
  530. return S_OK;
  531. }
  532. //
  533. // Function Name: SendFontCmd
  534. // Plug-in: Rendering module
  535. // Driver: Unidrv
  536. // Type: Optional
  537. //
  538. STDMETHODIMP
  539. SendFontCmd(
  540. PDEVOBJ pdevobj,
  541. PUNIFONTOBJ pUFObj,
  542. PFINVOCATION pFInv)
  543. {
  544. VERBOSE((DLLTEXT("IOemCB::SendFontCmd() entry.\n")));
  545. OEMSendFontCmd(pdevobj, pUFObj, pFInv);
  546. return S_OK;
  547. }
  548. //
  549. // Function Name: DriverDMS
  550. // Plug-in: Rendering module
  551. // Driver: Unidrv
  552. // Type: Optional
  553. //
  554. STDMETHODIMP
  555. DriverDMS(
  556. PVOID pDevObj,
  557. PVOID pBuffer,
  558. DWORD cbSize,
  559. PDWORD pcbNeeded)
  560. {
  561. VERBOSE((DLLTEXT("IOemCB::DriverDMS() entry.\n")));
  562. return E_NOTIMPL;
  563. }
  564. //
  565. // Function Name: TextOutputAsBitmap
  566. // Plug-in: Rendering module
  567. // Driver: Unidrv
  568. // Type: Optional
  569. //
  570. STDMETHODIMP
  571. TextOutAsBitmap(
  572. SURFOBJ *pso,
  573. STROBJ *pstro,
  574. FONTOBJ *pfo,
  575. CLIPOBJ *pco,
  576. RECTL *prclExtra,
  577. RECTL *prclOpaque,
  578. BRUSHOBJ *pboFore,
  579. BRUSHOBJ *pboOpaque,
  580. POINTL *pptlOrg,
  581. MIX mix)
  582. {
  583. VERBOSE((DLLTEXT("IOemCB::TextOutAsBitmap() entry.\n")));
  584. return E_NOTIMPL;
  585. }
  586. //
  587. // Function Name: TTYGetInfo
  588. // Plug-in: Rendering module
  589. // Driver: Unidrv
  590. // Type: Optional
  591. //
  592. STDMETHODIMP
  593. TTYGetInfo(
  594. PDEVOBJ pdevobj,
  595. DWORD dwInfoIndex,
  596. PVOID pOutputBuf,
  597. DWORD dwSize,
  598. DWORD *pcbcNeeded)
  599. {
  600. VERBOSE((DLLTEXT("IOemCB::TTYGetInfo() entry.\n")));
  601. return E_NOTIMPL;
  602. }
  603. //
  604. // Constructors
  605. //
  606. IOemCB() { m_cRef = 1; pOEMHelp = NULL; };
  607. ~IOemCB() { };
  608. protected:
  609. IPrintOemDriverUni* pOEMHelp;
  610. LONG m_cRef;
  611. };
  612. //
  613. // Class factory definition
  614. //
  615. class IOemCF : public IClassFactory
  616. {
  617. public:
  618. //
  619. // IUnknown methods
  620. //
  621. STDMETHODIMP
  622. QueryInterface(const IID& iid, void** ppv)
  623. {
  624. if (ppv == NULL) // Checking null-pointer.
  625. {
  626. return E_NOINTERFACE;
  627. }
  628. if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
  629. {
  630. *ppv = static_cast<IOemCF*>(this);
  631. }
  632. else
  633. {
  634. *ppv = NULL ;
  635. return E_NOINTERFACE ;
  636. }
  637. reinterpret_cast<IUnknown*>(*ppv)->AddRef();
  638. return S_OK ;
  639. }
  640. STDMETHODIMP_(ULONG)
  641. AddRef()
  642. {
  643. return InterlockedIncrement(&m_cRef);
  644. }
  645. STDMETHODIMP_(ULONG)
  646. Release()
  647. {
  648. if (InterlockedDecrement(&m_cRef) == 0)
  649. {
  650. delete this ;
  651. return 0 ;
  652. }
  653. return m_cRef ;
  654. }
  655. //
  656. // IClassFactory methods
  657. //
  658. STDMETHODIMP
  659. CreateInstance(
  660. IUnknown *pUnknownOuter,
  661. const IID &iid,
  662. void **ppv)
  663. {
  664. //VERBOSE((DLLTEXT("IOemCF::CreateInstance() called\n.")));
  665. // Cannot aggregate.
  666. if (NULL != pUnknownOuter) {
  667. return CLASS_E_NOAGGREGATION;
  668. }
  669. // Create component.
  670. IOemCB* pOemCB = new IOemCB;
  671. if (NULL == pOemCB) {
  672. return E_OUTOFMEMORY;
  673. }
  674. // Get the requested interface.
  675. HRESULT hr = pOemCB->QueryInterface(iid, ppv);
  676. // Release the IUnknown pointer.
  677. // (If QueryInterface failed, component will delete itself.)
  678. pOemCB->Release();
  679. return hr ;
  680. }
  681. // LockServer
  682. STDMETHODIMP
  683. LockServer(BOOL bLock)
  684. {
  685. if (bLock)
  686. {
  687. InterlockedIncrement(&g_cServerLocks);
  688. }
  689. else
  690. {
  691. InterlockedDecrement(&g_cServerLocks);
  692. }
  693. return S_OK ;
  694. }
  695. //
  696. // Constructor
  697. //
  698. IOemCF(): m_cRef(1) { };
  699. ~IOemCF() { };
  700. protected:
  701. LONG m_cRef;
  702. };
  703. //
  704. // Export functions
  705. //
  706. //
  707. // Get class factory
  708. //
  709. STDAPI
  710. DllGetClassObject(
  711. const CLSID &clsid,
  712. const IID &iid,
  713. void **ppv)
  714. {
  715. //VERBOSE((DLLTEXT("DllGetClassObject:\tCreate class factory.")));
  716. // Can we create this component?
  717. if (clsid != CLSID_OEMRENDER)
  718. {
  719. return CLASS_E_CLASSNOTAVAILABLE ;
  720. }
  721. // Create class factory.
  722. IOemCF* pFontCF = new IOemCF ; // Reference count set to 1
  723. // in constructor
  724. if (pFontCF == NULL)
  725. {
  726. return E_OUTOFMEMORY ;
  727. }
  728. // Get requested interface.
  729. HRESULT hr = pFontCF->QueryInterface(iid, ppv);
  730. pFontCF->Release();
  731. return hr ;
  732. }
  733. //
  734. //
  735. // Can DLL unload now?
  736. //
  737. STDAPI
  738. DllCanUnloadNow()
  739. {
  740. if ((g_cComponents == 0) && (g_cServerLocks == 0))
  741. {
  742. return S_OK;
  743. }
  744. else
  745. {
  746. return S_FALSE;
  747. }
  748. }