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.

1415 lines
32 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1994 - 1999
  3. All rights reserved.
  4. Module Name:
  5. printui.c
  6. Abstract:
  7. Singleton class that exists when printer queues are open.
  8. Author:
  9. Albert Ting (AlbertT) 22-Jun-1995
  10. Revision History:
  11. --*/
  12. #include "precomp.hxx"
  13. #pragma hdrstop
  14. #define _GLOBALS
  15. #include "globals.hxx"
  16. #include "time.hxx"
  17. #include "shlobj.h"
  18. #include "folder.hxx"
  19. #include "spllibex.hxx"
  20. #include "spinterf.hxx"
  21. #if DBG
  22. //#define DBG_PUIINFO DBG_INFO
  23. #define DBG_PUIINFO DBG_NONE
  24. #endif
  25. //
  26. // Singleton PrintLib object.
  27. //
  28. TPrintLib * TPrintLib::_pPrintLib;
  29. extern "C" {
  30. BOOL
  31. DllMain(
  32. IN HINSTANCE hInst,
  33. IN DWORD dwReason,
  34. IN LPVOID lpRes
  35. );
  36. }
  37. VOID
  38. DllCleanUp(
  39. VOID
  40. );
  41. BOOL
  42. DllMain(
  43. IN HINSTANCE hInst,
  44. IN DWORD dwReason,
  45. IN LPVOID lpRes
  46. )
  47. /*++
  48. Routine Description:
  49. Dll entry point.
  50. Arguments:
  51. Return Value:
  52. --*/
  53. {
  54. BOOL bReturn = TRUE;
  55. switch( dwReason ){
  56. case DLL_PROCESS_ATTACH:
  57. ghInst = hInst;
  58. #if DBG
  59. // init the debug library
  60. if( FAILED(_DbgInit()) )
  61. {
  62. DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to initialize debug library %d\n", GetLastError()));
  63. bReturn = FALSE;
  64. }
  65. #endif // DBG
  66. if( bReturn && !SHFusionInitializeFromModule(ghInst) )
  67. {
  68. DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to initialize fusion %d\n", GetLastError()));
  69. bReturn = FALSE;
  70. }
  71. // init (load) winspool.drv
  72. if( bReturn && FAILED(Winspool_Init()) )
  73. {
  74. DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to load winspool.drv %d\n", GetLastError()));
  75. bReturn = FALSE;
  76. }
  77. if( bReturn && !bPrintLibInit() )
  78. {
  79. DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to init PrintLib %d\n", GetLastError()));
  80. bReturn = FALSE;
  81. }
  82. if( !bReturn )
  83. {
  84. DllCleanUp();
  85. return FALSE;
  86. }
  87. // setup CRT memory leak detection
  88. CRT_DEBUG_SET_FLAG(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
  89. CRT_DEBUG_REPORT_TO_STDOUT();
  90. InitCommonControls();
  91. //
  92. // Initialize the custom time edit control class.
  93. //
  94. INITCOMMONCONTROLSEX icc;
  95. icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
  96. icc.dwICC = ICC_DATE_CLASSES;
  97. InitCommonControlsEx(&icc);
  98. //
  99. // Initialize the per therad message box counter here
  100. //
  101. CMsgBoxCounter::Initialize();
  102. DisableThreadLibraryCalls( hInst );
  103. break;
  104. case DLL_PROCESS_DETACH:
  105. //
  106. // Uninitialize the message box counter
  107. //
  108. CMsgBoxCounter::Uninitialize();
  109. VDSConnection::bStaticInitShutdown( TRUE );
  110. //
  111. // lpRes is NULL if it's a FreeLibrary, non-NULL if it's
  112. // process termination. Don't do cleanup if it's process
  113. // termination.
  114. //
  115. if( !lpRes )
  116. {
  117. DllCleanUp();
  118. }
  119. break;
  120. default:
  121. break;
  122. }
  123. return TRUE;
  124. }
  125. VOID
  126. DllCleanUp(
  127. VOID
  128. )
  129. /*++
  130. Routine Description:
  131. Clean up memory and do the uninitialization.
  132. Arguments:
  133. Return Value:
  134. Notes:
  135. Each UnInitialize function should take care of the case that Initialize is
  136. not called. If Initialize is not called, UnInitialize will do nothing.
  137. --*/
  138. {
  139. //
  140. // Unregister the class.
  141. //
  142. UnregisterClass( gszClassName, ghInst );
  143. UnregisterClass( gszQueueCreateClassName, ghInst );
  144. delete gpCritSec;
  145. delete TFolderList::gpFolderLock;
  146. delete gpTrayLock;
  147. // unload winspool.drv
  148. VERIFY(SUCCEEDED(Winspool_Done()));
  149. SHFusionUninitialize();
  150. #if DBG
  151. // shutdown the debug library
  152. VERIFY(SUCCEEDED(_DbgDone()));
  153. #endif // DBG
  154. return;
  155. }
  156. TPrintLib::
  157. TPrintLib(
  158. VOID
  159. ) : TExec( gpCritSec ), _hEventInit( NULL )
  160. /*++
  161. Routine Description:
  162. Initializes application object. This is a singleton object.
  163. This will create the message pump and also the hwndQueueCreate
  164. window which listens for requests.
  165. Arguments:
  166. Return Value:
  167. Notes:
  168. _strComputerName is the valid variable.
  169. --*/
  170. {
  171. SPLASSERT( gpCritSec->bInside( ));
  172. if( !VALID_BASE( TExec ) )
  173. {
  174. return;
  175. }
  176. TNotify* pNotify = new TNotify;
  177. if( !VALID_PTR( pNotify ))
  178. {
  179. if( pNotify )
  180. {
  181. // at this point pNotify ref count should be one, but since
  182. // the ref count class is badly designed it is zero. we can't delete
  183. // the class directly because the destructor is private and
  184. // there is not much of a choise left rather than to artificially
  185. // inc/dec the ref count so the object can get properly destroyed.
  186. // do not keep weak ref while shutting down
  187. pNotify->vIncRef();
  188. pNotify->vDelete();
  189. pNotify->cDecRef();
  190. }
  191. return;
  192. }
  193. _pNotify.vAcquire( pNotify );
  194. }
  195. TPrintLib::
  196. ~TPrintLib(
  197. VOID
  198. )
  199. /*++
  200. Routine Description:
  201. Destroys the printui.
  202. Arguments:
  203. Return Value:
  204. --*/
  205. {
  206. SPLASSERT( Queue_bEmpty( ));
  207. if( _hEventInit ){
  208. CloseHandle( _hEventInit );
  209. }
  210. // if pNotify().pGet() is NULL that means the destructor is called
  211. // twice which means that somebody is deleting the object twice.
  212. // let's see who is the culprit here!!!
  213. RIP(pNotify().pGet());
  214. if( pNotify().pGet() )
  215. {
  216. //
  217. // We are shutting down. Tell pNotify that it can start shutting
  218. // down too.
  219. //
  220. pNotify()->vDelete();
  221. // Release the reference. should not be used beyond this point
  222. pNotify().vRelease();
  223. }
  224. //
  225. // RL_Notify will automatically shut down when the last
  226. // reference to it has been deleted.
  227. //
  228. }
  229. BOOL
  230. TPrintLib::
  231. bInitialize(
  232. VOID
  233. )
  234. /*++
  235. Routine Description:
  236. Initializes the print library
  237. Arguments:
  238. None
  239. Return Value:
  240. --*/
  241. {
  242. HRESULT hr = S_OK;
  243. SPLASSERT( gpCritSec->bInside( ));
  244. //
  245. // The computer name needs to be formatted as "\\computername."
  246. //
  247. if( !bGetMachineName( _strComputerName, FALSE ) ){
  248. return FALSE;
  249. }
  250. //
  251. // Create init event. This event is used to synchronize
  252. // the message pump initialization and this thread.
  253. //
  254. _hEventInit = CreateEvent( NULL, FALSE, FALSE, NULL );
  255. if( !_hEventInit ){
  256. return FALSE;
  257. }
  258. DWORD dwThreadId;
  259. HANDLE hThread;
  260. //
  261. // Start the message pump by spawning a UI thread.
  262. //
  263. hThread = TSafeThread::Create( NULL,
  264. 0,
  265. (LPTHREAD_START_ROUTINE)TPrintLib::xMessagePump,
  266. this,
  267. 0,
  268. &dwThreadId );
  269. if( !hThread ){
  270. return FALSE;
  271. }
  272. CloseHandle( hThread );
  273. //
  274. // Wait for thread to initialize.
  275. //
  276. hr = MsgInfinitelyWaitForSingleObject( _hEventInit );
  277. CloseHandle( _hEventInit );
  278. _hEventInit = NULL;
  279. //
  280. // _hwndQueueCreate is our valid check. If it failed, cleanup.
  281. // This is set by the worker thread (xMessagePump). We can access
  282. // it only after _hEventInit has been triggered.
  283. //
  284. if( FAILED(hr) || !_hwndQueueCreate ){
  285. PostThreadMessage( dwThreadId,
  286. WM_QUIT,
  287. 0,
  288. 0 );
  289. }
  290. return (SUCCEEDED(hr) && bValid());
  291. }
  292. VOID
  293. TPrintLib::
  294. vHandleCreateQueue(
  295. IN TInfo* pInfo ADOPT
  296. )
  297. /*++
  298. Routine Description:
  299. Handle the creation of a new Queue window.
  300. Arguments:
  301. pInfo - Which queue should be created and extra parms.
  302. Return Value:
  303. --*/
  304. {
  305. DBGMSG( DBG_PUIINFO, ( "PrintLib.vHandleQueueCreate: received pInfo %x\n", pInfo ));
  306. //
  307. // The Add Printer wizard is invoked by double clicking
  308. // a printer called "WinUtils_NewObject." I hope no one
  309. // ever tries to create a printer under this name...
  310. //
  311. if( !lstrcmp( gszNewObject, pInfo->_szPrinter )){
  312. DBGMSG( DBG_WARN,
  313. ( "PrintLib.lrQueueCreateWndProc: WinUtils_NewObject called here!\n" ));
  314. } else {
  315. HWND hwndQueue = NULL;
  316. TQueue* pQueue = new TQueue(this, pInfo->_szPrinter, pInfo->_hEventClose);
  317. if( VALID_PTR( pQueue )){
  318. //
  319. // don't keep weak refs...
  320. //
  321. pQueue->vIncRef();
  322. if( pQueue->bInitialize( pInfo->_hwndOwner,
  323. pInfo->_nCmdShow ) ){
  324. hwndQueue = pQueue->hwnd();
  325. ASSERT(hwndQueue);
  326. } else {
  327. if( pQueue->hwnd() ) {
  328. DestroyWindow( pQueue->hwnd() );
  329. }
  330. }
  331. //
  332. // don't need the reference from now on
  333. //
  334. pQueue->vDecRefDelete();
  335. } else {
  336. delete pQueue;
  337. //
  338. // !! LATER !!
  339. //
  340. // Put up error message.
  341. //
  342. }
  343. if( hwndQueue ){
  344. SetForegroundWindow( hwndQueue );
  345. ShowWindow( hwndQueue, SW_RESTORE );
  346. } else {
  347. if( pInfo->_hEventClose ) {
  348. // in case of failure, set the handle, otherwise the caller
  349. // will keep waiting on it forever.
  350. SetEvent( pInfo->_hEventClose );
  351. }
  352. }
  353. }
  354. delete pInfo;
  355. }
  356. // very private worker proc
  357. static DWORD WINAPI DefPrinterChanged_WorkerProc(LPVOID lpParameter)
  358. {
  359. // call back into the folder cache to update the default printer by calling SHChangeNotify.
  360. TFolder::vDefaultPrinterChanged();
  361. return 0;
  362. }
  363. LRESULT
  364. CALLBACK
  365. TPrintLib::
  366. lrQueueCreateWndProc(
  367. IN HWND hwnd,
  368. IN UINT uMsg,
  369. IN WPARAM wParam,
  370. IN LPARAM lParam
  371. )
  372. {
  373. switch( uMsg )
  374. {
  375. case WM_SETTINGCHANGE:
  376. // push this work in background as it may hit back in the folder cache and
  377. // this should not happen in the UI thread. ask shell to run this in bkgnd.
  378. // we don't really care if this fails, since that would mean that we are out
  379. // of resources.
  380. SHQueueUserWorkItem(reinterpret_cast<LPTHREAD_START_ROUTINE>(DefPrinterChanged_WorkerProc),
  381. NULL, 0, 0, NULL, "printui.dll", 0);
  382. break;
  383. case WM_PRINTLIB_NEW:
  384. SPLASSERT( _pPrintLib );
  385. _pPrintLib->vHandleCreateQueue( (TInfo*)lParam );
  386. break;
  387. case WM_DESTROY_REQUEST:
  388. DestroyWindow( hwnd );
  389. break;
  390. case WM_DESTROY:
  391. SINGLETHREADRESET(UIThread);
  392. PostQuitMessage(0);
  393. break;
  394. default:
  395. return DefWindowProc( hwnd, uMsg, wParam, lParam);
  396. }
  397. return 0;
  398. }
  399. /********************************************************************
  400. Public interface functions
  401. ********************************************************************/
  402. VOID
  403. vQueueCreateWOW64(
  404. IN HWND hwndOwner,
  405. IN LPCTSTR pszPrinter,
  406. IN INT nCmdShow,
  407. IN LPARAM lParam
  408. )
  409. /*++
  410. Routine Description:
  411. WOW64 version. see vQueueCreate below.
  412. Arguments:
  413. see vQueueCreate below.
  414. Return Value:
  415. --*/
  416. {
  417. //
  418. // This function potentially may load the driver UI so we call a private API
  419. // exported by winspool.drv, which will RPC the call to a special 64 bit surrogate
  420. // process where the 64 bit driver can be loaded.
  421. //
  422. CDllLoader dll(TEXT("winspool.drv"));
  423. ptr_PrintUIQueueCreate pfnPrintUIQueueCreate =
  424. (ptr_PrintUIQueueCreate )dll.GetProcAddress(ord_PrintUIQueueCreate);
  425. if( pfnPrintUIQueueCreate )
  426. {
  427. pfnPrintUIQueueCreate( hwndOwner, pszPrinter, nCmdShow, lParam );
  428. }
  429. }
  430. VOID
  431. vQueueCreateNative(
  432. IN HWND hwndOwner,
  433. IN LPCTSTR pszPrinter,
  434. IN INT nCmdShow,
  435. IN LPARAM lParam
  436. )
  437. /*++
  438. Routine Description:
  439. Native version. see vQueueCreate below.
  440. Arguments:
  441. see vQueueCreate below.
  442. Return Value:
  443. --*/
  444. {
  445. if( lstrlen( pszPrinter ) >= kPrinterBufMax ){
  446. DBGMSG( DBG_WARN, ( "vQueueCreate: printer name too long "TSTR"\n", pszPrinter ));
  447. iMessage( hwndOwner,
  448. IDS_DESC,
  449. IDS_ERR_BAD_PRINTER_NAME,
  450. MB_OK|MB_ICONSTOP,
  451. kMsgNone,
  452. NULL );
  453. return;
  454. }
  455. //
  456. // If this print queue is using the fax driver,
  457. // then launch the fax queue view.
  458. //
  459. if( bIsPrinterFaxDevice( pszPrinter ) )
  460. {
  461. vFaxQueueCreate( hwndOwner, pszPrinter, nCmdShow, lParam );
  462. return;
  463. }
  464. //
  465. // Bring up the win32 queue view.
  466. //
  467. vQueueCreateInternal( hwndOwner, pszPrinter, nCmdShow, lParam );
  468. }
  469. VOID
  470. vQueueCreate(
  471. IN HWND hwndOwner,
  472. IN LPCTSTR pszPrinter,
  473. IN INT nCmdShow,
  474. IN LPARAM lParam
  475. )
  476. /*++
  477. Routine Description:
  478. Creates a printer queue.
  479. Arguments:
  480. lParam - TRUE => fModal.
  481. Return Value:
  482. --*/
  483. {
  484. if( IsRunningWOW64() )
  485. {
  486. vQueueCreateWOW64( hwndOwner, pszPrinter, nCmdShow, lParam );
  487. }
  488. else
  489. {
  490. vQueueCreateNative( hwndOwner, pszPrinter, nCmdShow, lParam );
  491. }
  492. }
  493. VOID
  494. vQueueCreateInternal(
  495. IN HWND hwndOwner,
  496. IN LPCTSTR pszPrinter,
  497. IN INT nCmdShow,
  498. IN LPARAM lParam
  499. )
  500. {
  501. TStatusB bSuccess;
  502. bSuccess DBGNOCHK = TRUE;
  503. HANDLE hEventClose = NULL;
  504. //
  505. // Attempt to find a duplicate queue window.
  506. //
  507. HWND prevhwnd;
  508. if( TQueue::bIsDuplicateWindow( hwndOwner, pszPrinter, &prevhwnd ) ){
  509. SetForegroundWindow( prevhwnd );
  510. ShowWindow( prevhwnd, SW_RESTORE );
  511. return;
  512. }
  513. //
  514. // Initialize TInfo and acquire the gpPrintLib.
  515. //
  516. TPrintLib::TInfo* pInfo = new TPrintLib::TInfo;
  517. if( !VALID_PTR( pInfo )){
  518. goto Fail;
  519. }
  520. //
  521. // If modal, send in an event to trigger when the window is closed.
  522. //
  523. if( lParam ){
  524. // create a manual reset event
  525. hEventClose = CreateEvent( NULL, TRUE, FALSE, NULL );
  526. if( !hEventClose ){
  527. goto Fail;
  528. }
  529. }
  530. StringCchCopy( pInfo->_szPrinter, ARRAYSIZE(pInfo->_szPrinter), pszPrinter );
  531. pInfo->_nCmdShow = nCmdShow;
  532. pInfo->_hwndOwner = hwndOwner;
  533. pInfo->_hEventClose = hEventClose;
  534. //
  535. // Need to grab the critical section, since this call should
  536. // be reentrant.
  537. //
  538. {
  539. CCSLock::Locker CSL( *gpCritSec );
  540. TRefLock<TPrintLib> pPrintLib;
  541. bSuccess DBGCHK = TPrintLib::bGetSingleton(pPrintLib);
  542. //
  543. // Queue creation the first time just passes the TInfo to
  544. // a worker function that serves as the message pump. If
  545. // gpPrintLib has been instantiated, then we'll post a message
  546. // and return immediately (freeing this thread).
  547. //
  548. // If gpPrintLib has not been instantiated, then we create the
  549. // singleton here.
  550. //
  551. if( bSuccess ){
  552. //
  553. // Acquire a reference to gpPrintLib so that
  554. // the pInfo can be posted without worrying about losing
  555. // gpPrintLib. This reference will automatically be
  556. // release when pInfo is destroyed.
  557. //
  558. pInfo->PrintLib.vAcquire( pPrintLib.pGet() );
  559. //
  560. // Send a message to the UI thread to create a new window.
  561. // We want all queues to use the same UI thread.
  562. //
  563. DBGMSG( DBG_PUIINFO, ( "vQueueCreate: posted pInfo %x\n", pInfo ));
  564. bSuccess DBGCHK = PostMessage( pPrintLib->hwndQueueCreate(),
  565. WM_PRINTLIB_NEW,
  566. 0,
  567. (LPARAM)pInfo );
  568. }
  569. }
  570. Fail:
  571. if( bSuccess ){
  572. //
  573. // Success - check if needs to be modal. If so, wait until
  574. // window is closed.
  575. //
  576. if( lParam ){
  577. VERIFY( SUCCEEDED( MsgInfinitelyWaitForSingleObject( hEventClose ) ) );
  578. }
  579. } else {
  580. //
  581. // Destroy the pInfo data. This will automatically decrement
  582. // the reference count on gpPrintLib (if necessary).
  583. //
  584. delete pInfo;
  585. vShowResourceError( hwndOwner );
  586. }
  587. if( hEventClose ){
  588. CloseHandle( hEventClose );
  589. }
  590. return;
  591. }
  592. BOOL
  593. bIsPrinterFaxDevice(
  594. IN LPCTSTR pszPrinter
  595. )
  596. /*++
  597. Routine Description:
  598. Determine if this printer is a fax device.
  599. Arguments:
  600. Return Value:
  601. --*/
  602. {
  603. TStatusB bStatus;
  604. BOOL bReturn = FALSE;
  605. DWORD dwLastError = ERROR_SUCCESS;
  606. HANDLE hPrinter = NULL;
  607. bStatus DBGCHK = OpenPrinter( (LPTSTR)pszPrinter, &hPrinter, NULL );
  608. if( bStatus )
  609. {
  610. PPRINTER_INFO_2 pInfo2 = NULL;
  611. DWORD cbInfo2 = 0;
  612. //
  613. // Get the current printer info 2.
  614. //
  615. bStatus DBGCHK = VDataRefresh::bGetPrinter( hPrinter, 2, (PVOID*)&pInfo2, &cbInfo2 );
  616. if( bStatus )
  617. {
  618. if( !_tcscmp( pInfo2->pDriverName, FAX_DRIVER_NAME ) )
  619. {
  620. bReturn = TRUE;
  621. }
  622. //
  623. // Release the printer info 2 structure.
  624. //
  625. FreeMem( pInfo2 );
  626. }
  627. //
  628. // Close the printer handle.
  629. //
  630. ClosePrinter( hPrinter );
  631. }
  632. return bReturn;
  633. }
  634. VOID
  635. vFaxQueueCreate(
  636. IN HWND hwndOwner,
  637. IN LPCTSTR pszPrinter,
  638. IN INT nCmdShow,
  639. IN LPARAM lParam
  640. )
  641. /*++
  642. Routine Description:
  643. Creates a printer fax queue view.
  644. Arguments:
  645. Return Value:
  646. --*/
  647. {
  648. TStatusB bStatus;
  649. TString strParam;
  650. //
  651. // Build the command string.
  652. //
  653. bStatus DBGCHK = strParam.bCat(FAX_CLIENT_CONSOLE_IMAGE_NAME);
  654. if (bStatus)
  655. {
  656. //
  657. // Use our parents startup info.
  658. //
  659. STARTUPINFO StartupInfo;
  660. GetStartupInfo( &StartupInfo );
  661. PROCESS_INFORMATION ProcessInformation;
  662. //
  663. // Create the rundll32 process.
  664. //
  665. if (CreateProcess(NULL, (LPTSTR)(LPCTSTR)strParam, NULL, NULL, FALSE,
  666. DETACHED_PROCESS, NULL, NULL, &StartupInfo, &ProcessInformation))
  667. {
  668. CloseHandle(ProcessInformation.hThread);
  669. CloseHandle(ProcessInformation.hProcess);
  670. }
  671. //
  672. // If we cannot create the process then the fax queue exe may not be on
  673. // this machine or in the path, if this happens we will fall back
  674. // and birng up the Dumb win32 queue view.
  675. //
  676. else
  677. {
  678. vQueueCreateInternal(hwndOwner, pszPrinter, nCmdShow, lParam);
  679. }
  680. }
  681. }
  682. BOOL
  683. bPrintLibInit(
  684. VOID
  685. )
  686. /*++
  687. Routine Description:
  688. Initializes the print library.
  689. Arguments:
  690. Return Value:
  691. --*/
  692. {
  693. if( !VDSConnection::bStaticInitShutdown( ) )
  694. {
  695. return FALSE;
  696. }
  697. CAutoPtr<CCSLock> spFolderLock = new CCSLock;
  698. CAutoPtr<CCSLock> spCritSec = new CCSLock;
  699. CAutoPtr<CCSLock> spTrayLock = new CCSLock;
  700. CAutoHandleAccel shAccel = LoadAccelerators(ghInst, (LPCTSTR)MAKEINTRESOURCE(ACCEL_PRINTQUEUE));
  701. if( !shAccel || !spCritSec || !spFolderLock || !spTrayLock )
  702. {
  703. DBGMSG( DBG_WARN, ( "bPrintLibInit: globals creation failed %d\n", GetLastError( )));
  704. return FALSE;
  705. }
  706. WNDCLASS WndClass;
  707. WndClass.lpszClassName = gszClassName;
  708. WndClass.style = 0L;
  709. WndClass.lpfnWndProc = MGenericWin::SetupWndProc;
  710. WndClass.cbClsExtra = 0;
  711. WndClass.cbWndExtra = sizeof( TPrintLib* );
  712. WndClass.hInstance = ghInst;
  713. WndClass.hIcon = NULL;
  714. WndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); // NULL
  715. WndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  716. WndClass.lpszMenuName = MAKEINTRESOURCE( MENU_PRINTQUEUE );
  717. if( !RegisterClass(&WndClass) )
  718. {
  719. DBGMSG( DBG_WARN, ( "bInitPrintLib: RegisterClass failed %d\n", GetLastError( )));
  720. return FALSE;
  721. }
  722. ZeroMemory(&WndClass, sizeof(WndClass));
  723. WndClass.lpszClassName = gszQueueCreateClassName;
  724. WndClass.lpfnWndProc = TPrintLib::lrQueueCreateWndProc;
  725. WndClass.hInstance = ghInst;
  726. if( !RegisterClass(&WndClass) )
  727. {
  728. DBGMSG( DBG_WARN, ( "bInitPrintLib: RegisterClass 2 failed %d\n", GetLastError( )));
  729. return FALSE;
  730. }
  731. // everything seems to be OK here, initialize globals & detach the local objects
  732. TFolderList::gpFolderLock = spFolderLock.Detach();
  733. gpCritSec = spCritSec.Detach();
  734. gpTrayLock = spTrayLock.Detach();
  735. ghAccel = shAccel.Detach();
  736. gcxSmIcon = GetSystemMetrics( SM_CXSMICON );
  737. gcySmIcon = GetSystemMetrics( SM_CXSMICON );
  738. return TRUE;
  739. }
  740. /********************************************************************
  741. Private helper routines.
  742. ********************************************************************/
  743. BOOL
  744. TPrintLib::
  745. bGetSingleton(
  746. TRefLock<TPrintLib> &RefLock
  747. )
  748. /*++
  749. Routine Description:
  750. Retrieves the singleton object, and stores it in the global.
  751. If the singleton has not been created, then it is instantiated
  752. here.
  753. Must be called in the critical section.
  754. Arguments:
  755. Return Value:
  756. TRUE = success (singleton exists), FALSE = fail.
  757. Revision History:
  758. Lazar Ivanov Jul-2000 (rewrite)
  759. --*/
  760. {
  761. BOOL bRet = FALSE;
  762. // must be in the global CS.
  763. CCSLock::Locker lock(*gpCritSec);
  764. if( lock )
  765. {
  766. if( !_pPrintLib )
  767. {
  768. //
  769. // Initialize _pPrintLib
  770. //
  771. TPrintLib *pPrintLib = new TPrintLib();
  772. if( pPrintLib )
  773. {
  774. //
  775. // We don't want to hold weak reference while initializing.
  776. //
  777. pPrintLib->vIncRef();
  778. //
  779. // Perform the real initialization - create the UI thread, etc...
  780. //
  781. if( pPrintLib->bInitialize() && VALID_PTR(pPrintLib) )
  782. {
  783. //
  784. // Everything seems to be OK at this point.
  785. //
  786. _pPrintLib = pPrintLib;
  787. RefLock.vAcquire(_pPrintLib);
  788. }
  789. else
  790. {
  791. //
  792. // Something has failed, nobody (except us) should be holding ref to
  793. // pPrintLib at this point, so it will automatically go away when we dec
  794. // ref it.
  795. //
  796. DBGMSG(DBG_WARN, ("PrintLib.bCreateSingleton: Abnormal termination %d %d\n", pPrintLib, GetLastError()));
  797. }
  798. //
  799. // Release our own ref lock
  800. //
  801. pPrintLib->vDecRefDelete();
  802. }
  803. }
  804. else
  805. {
  806. //
  807. // Just acquire a ref to the object
  808. //
  809. RefLock.vAcquire(_pPrintLib);
  810. }
  811. // this would our measure for success
  812. bRet = (NULL != _pPrintLib);
  813. }
  814. else
  815. {
  816. // unable to enter the global CS -- this could be only
  817. // in the case where there is a contention and
  818. // the kernel is unable to allocate the semaphore
  819. SetLastError(ERROR_OUTOFMEMORY);
  820. }
  821. return bRet;
  822. }
  823. STATUS
  824. TPrintLib::
  825. xMessagePump(
  826. IN TPrintLib *pPrintLib
  827. )
  828. /*++
  829. Routine Description:
  830. Main message pump. The printer windows are architected slightly
  831. differently than regular explorer windows: there is a single UI
  832. thread, and multiple worker threads. This has two main advantages:
  833. 1. Fewer threads even when many printer queues are open (assuming
  834. NT->NT connections).
  835. 2. The UI virtually never hangs.
  836. It has this slight disadvantage that sometimes the main UI thread
  837. is busy (e.g., inserting 2,000 print jobs may take a few seconds)
  838. in which all print UI windows are hung.
  839. Arguments:
  840. None.
  841. Return Value:
  842. Win32 status code.
  843. --*/
  844. {
  845. MSG msg;
  846. TStatusB bSuccess;
  847. bSuccess DBGNOCHK = TRUE;
  848. //
  849. // Ensure that functions that must execute in the UI thread don't
  850. // execute elsewhere.
  851. //
  852. SINGLETHREAD(UIThread);
  853. //
  854. // Create our window to listen for Queue Creates.
  855. //
  856. pPrintLib->_hwndQueueCreate = CreateWindowEx(
  857. 0,
  858. gszQueueCreateClassName,
  859. gszQueueCreateClassName,
  860. WS_OVERLAPPED,
  861. 0,
  862. 0,
  863. 0,
  864. 0,
  865. NULL,
  866. NULL,
  867. ghInst,
  868. NULL );
  869. if( !pPrintLib->_hwndQueueCreate ){
  870. bSuccess DBGCHK = FALSE;
  871. }
  872. SetEvent( pPrintLib->_hEventInit );
  873. if( !bSuccess ){
  874. return 0;
  875. }
  876. while( GetMessage( &msg, NULL, 0, 0 )){
  877. if( !TranslateAccelerator( ghwndActive,
  878. ghAccel,
  879. &msg )){
  880. TranslateMessage( &msg );
  881. DispatchMessage( &msg );
  882. }
  883. }
  884. return (STATUS)msg.wParam;
  885. }
  886. VOID
  887. TPrintLib::
  888. vRefZeroed(
  889. VOID
  890. )
  891. /*++
  892. Routine Description:
  893. Virtual definition from class MRefCom.
  894. The reference count has reached zero; we should cleanup the
  895. object. Immediately zero out gpPrintLib then post a message
  896. instructing it to destory itself.
  897. Arguments:
  898. Return Value:
  899. --*/
  900. {
  901. TPrintLib *pToDelete = NULL;
  902. {
  903. // must be in the global CS
  904. CCSLock::Locker lock(*gpCritSec);
  905. if (lock)
  906. {
  907. //
  908. // we need to make this additional check here since
  909. // somebody might have acquired a reference to us while
  910. // waiting in the critical section in which case we
  911. // shouldn't go away.
  912. //
  913. if (0 == _cRef)
  914. {
  915. //
  916. // mark ourselves for destruction and zero out
  917. // the global pointer.
  918. //
  919. pToDelete = this;
  920. _pPrintLib = NULL;
  921. SINGLETHREADRESET(UIThread);
  922. }
  923. }
  924. else
  925. {
  926. //
  927. // unable to enter the global CS -- this could be only
  928. // in the case where there is a contention and
  929. // the kernel is unable to allocate the semaphore.
  930. //
  931. // not much really we can do in this case except
  932. // laving the object alive even if its RefCount
  933. // is zero (i.e. leaking the object)
  934. //
  935. SetLastError(ERROR_OUTOFMEMORY);
  936. }
  937. }
  938. if (pToDelete)
  939. {
  940. //
  941. // initiate shutdown. this may take a while since
  942. // it will require shutting down the background threads,
  943. // clear all pending work items, etc...
  944. //
  945. pToDelete->hDestroy();
  946. }
  947. }
  948. HRESULT
  949. TPrintLib::
  950. hDestroy(
  951. VOID
  952. )
  953. /*++
  954. Routine Description:
  955. call when the object is marked for destruction
  956. no further access beyond this point.
  957. Arguments:
  958. None
  959. Return Value:
  960. Standard COM error handling
  961. --*/
  962. {
  963. if (bValid())
  964. {
  965. PostMessage(_hwndQueueCreate, WM_DESTROY_REQUEST, 0, 0);
  966. //
  967. // We can't immediately delete ourselves because we may have
  968. // pending work items. Notify TThreadM that it should start
  969. // shutdown, then zero the global out so that no one else will
  970. // try to use us.
  971. //
  972. // When TThreadM has shut down the object will automatically go away.
  973. //
  974. TThreadM::vDelete();
  975. // the object shouldn't be accessed beyond this point because
  976. // Unlock is decrementing the internal refcount and the object
  977. // goes away. it may not go away immediately and wait some pending
  978. // work items to finish, but in general from now on we should
  979. // consider ourselves dead.
  980. TThreadM::Unlock();
  981. }
  982. return S_OK;
  983. }
  984. /********************************************************************
  985. Safe Thread class
  986. ********************************************************************/
  987. HANDLE
  988. TSafeThread::
  989. Create(
  990. LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes
  991. DWORD dwStackSize, // initial thread stack size, in bytes
  992. LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
  993. LPVOID lpParameter, // argument for new thread
  994. DWORD dwCreationFlags, // creation flags
  995. LPDWORD lpThreadId // pointer to returned thread identifier
  996. )
  997. {
  998. HANDLE hThread = NULL;
  999. //
  1000. // Construct the thread info class.
  1001. //
  1002. CAutoPtr<TSafeThreadInfo> spThreadInfo = new TSafeThreadInfo;
  1003. CAutoHandleNT shEventReady = CreateEvent(NULL, FALSE, FALSE, NULL);
  1004. if( spThreadInfo && shEventReady )
  1005. {
  1006. //
  1007. // Store the thread information.
  1008. //
  1009. spThreadInfo->lpStartAddress = lpStartAddress;
  1010. spThreadInfo->lpParameter = lpParameter;
  1011. spThreadInfo->hInstance = ghInst;
  1012. spThreadInfo->hEventReady = shEventReady;
  1013. //
  1014. // Start the thread to our routine.
  1015. //
  1016. DWORD dwThreadId;
  1017. hThread = CreateThread( NULL,
  1018. 0,
  1019. (LPTHREAD_START_ROUTINE)TSafeThread::Start,
  1020. static_cast<TSafeThreadInfo*>(spThreadInfo),
  1021. 0,
  1022. &dwThreadId );
  1023. if( hThread )
  1024. {
  1025. //
  1026. // If success wait for the thread to start properly
  1027. //
  1028. WaitForSingleObject( shEventReady, INFINITE );
  1029. //
  1030. // The thread will adopt this object, so we release the ownership.
  1031. //
  1032. spThreadInfo.Detach();
  1033. }
  1034. }
  1035. return hThread;
  1036. }
  1037. DWORD WINAPI
  1038. TSafeThread::
  1039. Start(
  1040. PVOID pVoid
  1041. )
  1042. {
  1043. TSafeThreadInfo *pThreadInfo = (TSafeThreadInfo*)pVoid;
  1044. HINSTANCE hLibrary = NULL;
  1045. //
  1046. // If valid instance passed then load the library again.
  1047. //
  1048. if( pThreadInfo->hInstance ){
  1049. //
  1050. // Get the DLL name to do the load.
  1051. //
  1052. TCHAR szDllName[MAX_PATH+1];
  1053. if( SUCCEEDED( SafeGetModuleFileName( pThreadInfo->hInstance, szDllName, MAX_PATH ) ) ) {
  1054. DBGMSG( DBG_THREADM, ("Loading library " TSTR "\n", szDllName ) );
  1055. hLibrary = LoadLibrary( szDllName );
  1056. }
  1057. }
  1058. DBGMSG( DBG_THREADM, ("Thread Starting %x\n", pThreadInfo->lpStartAddress ) );
  1059. if( pThreadInfo->hEventReady )
  1060. {
  1061. SetEvent( pThreadInfo->hEventReady );
  1062. }
  1063. pThreadInfo->lpStartAddress( pThreadInfo->lpParameter );
  1064. DBGMSG( DBG_THREADM, ("Thread ending %x\n", pThreadInfo->lpStartAddress ) );
  1065. //
  1066. // Ensure we release the thread info.
  1067. //
  1068. delete pThreadInfo;
  1069. //
  1070. // If library was loaded then unload and exit the thread.
  1071. //
  1072. if( hLibrary ){
  1073. DBGMSG( DBG_THREADM, ("Releasing library \n") );
  1074. FreeLibraryAndExitThread( hLibrary, 0 );
  1075. }
  1076. return TRUE;
  1077. }