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.

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