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.

993 lines
24 KiB

  1. /*++
  2. Copyright (c) 1990 - 1995 Microsoft Corporation
  3. Module Name:
  4. msgbox.c
  5. Abstract:
  6. This module provides all the public exported APIs relating to Printer
  7. management for the Local Print Providor
  8. LocalAddPrinterConnection
  9. LocalDeletePrinterConnection
  10. LocalPrinterMessageBox
  11. Author:
  12. Dave Snipp (DaveSn) 15-Mar-1991
  13. Revision History:
  14. --*/
  15. #define NOMINMAX
  16. #include <precomp.h>
  17. #pragma hdrstop
  18. #include "winsta.h"
  19. #define WINSTATION_PRINTER_MESSAGE_TIMEOUT (5*60)
  20. HANDLE WinStaDllHandle = NULL;
  21. PWINSTATION_SEND_MESSAGEW pWinStationSendMessage = NULL;
  22. PINIPRINTER
  23. FindPrinterAnywhere(
  24. LPTSTR pName,
  25. DWORD SpoolerType
  26. )
  27. /*++
  28. Routine Description:
  29. Search for a printer name in all pIniSpoolers.
  30. Arguments:
  31. pName - Name of printer to search for.
  32. SpoolerType - Type of spooler the printer should reside in.
  33. Return Value:
  34. PINIPRINTER - Found pIniPrinter
  35. NULL - Not found.
  36. --*/
  37. {
  38. LPCTSTR pszLocalName;
  39. PINIPRINTER pIniPrinter;
  40. PINISPOOLER pIniSpooler = FindSpoolerByName( pName,
  41. &pszLocalName );
  42. SplInSem();
  43. if( pIniSpooler &&
  44. (( pIniPrinter = FindPrinter( pszLocalName, pIniSpooler )) ||
  45. ( pIniPrinter = FindPrinterShare( pszLocalName, pIniSpooler )))){
  46. if( pIniPrinter->pIniSpooler->SpoolerFlags & SpoolerType ){
  47. return pIniPrinter;
  48. }
  49. }
  50. return NULL;
  51. }
  52. BOOL
  53. LocalAddPrinterConnection(
  54. LPWSTR pName
  55. )
  56. {
  57. //
  58. // Allow us to make clustered connections to local printers
  59. // (they appear to be remote).
  60. //
  61. BOOL bReturn = FALSE;
  62. EnterSplSem();
  63. if( FindPrinterAnywhere( pName, SPL_TYPE_CLUSTER )){
  64. bReturn = TRUE;
  65. }
  66. LeaveSplSem();
  67. SetLastError(ERROR_INVALID_NAME);
  68. return bReturn;
  69. }
  70. BOOL
  71. LocalDeletePrinterConnection(
  72. LPWSTR pName
  73. )
  74. {
  75. //
  76. // Allow us to remove clustered connections to local printers
  77. // (they appear to be remote).
  78. //
  79. BOOL bReturn = FALSE;
  80. EnterSplSem();
  81. if( FindPrinterAnywhere( pName, SPL_TYPE_CLUSTER )){
  82. bReturn = TRUE;
  83. }
  84. LeaveSplSem();
  85. SetLastError(ERROR_INVALID_NAME);
  86. return bReturn;
  87. }
  88. DWORD
  89. LocalPrinterMessageBox(
  90. HANDLE hPrinter,
  91. DWORD Error,
  92. HWND hWnd,
  93. LPWSTR pText,
  94. LPWSTR pCaption,
  95. DWORD dwType
  96. )
  97. {
  98. //
  99. // Always fail this call. It's completely bogus and shouldn't be
  100. // supported. The router always passes us a bad handle anyway, so
  101. // we will always return invalid handle.
  102. //
  103. SetLastError(ERROR_INVALID_HANDLE);
  104. return FALSE;
  105. }
  106. BOOL
  107. UpdateJobStatus(
  108. PSPOOL pSpool,
  109. DWORD Error
  110. )
  111. /*++
  112. Routine Description:
  113. Update job status based on Error.
  114. Arguments:
  115. pSpool - Handle of session.
  116. Error - Error returned from port monitor.
  117. Return Value:
  118. TRUE - Job is still valid.
  119. FALSE - Job is pending deletion.
  120. --*/
  121. {
  122. DWORD dwJobStatus;
  123. PINIJOB pIniJob = NULL;
  124. if (pSpool->pIniJob)
  125. pIniJob = pSpool->pIniJob;
  126. else if (pSpool->pIniPort)
  127. pIniJob = pSpool->pIniPort->pIniJob;
  128. if (pIniJob) {
  129. EnterSplSem();
  130. dwJobStatus = pIniJob->Status;
  131. switch (Error) {
  132. #ifdef _HYDRA_
  133. case ERROR_BAD_DEV_TYPE:
  134. case ERROR_INVALID_NAME:
  135. //
  136. // If we have a problem with the port name, we will not find
  137. // a WinStation to put the message on. So kill the job to
  138. // prevent the spooler from looping.
  139. //
  140. pSpool->Status |= SPOOL_STATUS_CANCELLED;
  141. pIniJob->Status |= JOB_PENDING_DELETION;
  142. // Release any thread waiting on LocalSetPort
  143. SetPortErrorEvent(pIniJob->pIniPort);
  144. // Release any thread waiting on SeekPrinter
  145. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  146. SetLastError(ERROR_PRINT_CANCELLED);
  147. LeaveSplSem();
  148. SplOutSem();
  149. return FALSE;
  150. #endif
  151. case ERROR_OUT_OF_PAPER:
  152. if( !( pIniJob->Status & JOB_PAPEROUT )){
  153. pIniJob->Status |= JOB_PAPEROUT;
  154. pIniJob->pIniPrinter->cErrorOutOfPaper++;
  155. }
  156. break;
  157. case ERROR_NOT_READY:
  158. if( !( pIniJob->Status & JOB_OFFLINE )){
  159. pIniJob->Status |= JOB_OFFLINE;
  160. pIniJob->pIniPrinter->cErrorNotReady++;
  161. }
  162. break;
  163. default:
  164. if( !( pIniJob->Status & JOB_ERROR )){
  165. pIniJob->Status |= JOB_ERROR;
  166. pIniJob->pIniPrinter->cJobError++;
  167. }
  168. pIniJob->pIniPrinter->dwLastError = Error;
  169. // Release any thread waiting on SeekPrinter
  170. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  171. break;
  172. }
  173. if( dwJobStatus != pIniJob->Status ){
  174. SetPrinterChange(pIniJob->pIniPrinter,
  175. pIniJob,
  176. NVJobStatus,
  177. PRINTER_CHANGE_SET_JOB,
  178. pIniJob->pIniPrinter->pIniSpooler );
  179. }
  180. LeaveSplSem();
  181. if(( pIniJob->Status & JOB_REMOTE ) &&
  182. pIniJob->pIniPrinter->pIniSpooler->bEnableNetPopups) {
  183. if (!(pIniJob->Status & JOB_NOTIFICATION_SENT)) {
  184. SendJobAlert(pIniJob);
  185. pIniJob->Status |= JOB_NOTIFICATION_SENT;
  186. }
  187. MyMessageBeep( MB_ICONEXCLAMATION,
  188. pIniJob->pIniPrinter->pIniSpooler );
  189. }
  190. }
  191. return TRUE;
  192. }
  193. DWORD
  194. MyMessageBox(
  195. HWND hWnd,
  196. PSPOOL pSpool,
  197. DWORD Error,
  198. LPWSTR pText,
  199. LPWSTR pCaption,
  200. DWORD dwType
  201. )
  202. {
  203. PINIJOB pIniJob = NULL;
  204. LPWSTR pErrorString, pDocumentName;
  205. HANDLE hToken;
  206. WCHAR szUnnamed[80];
  207. DWORD dwJobStatus;
  208. #ifdef _HYDRA_
  209. DWORD SessionId = DetermineJobSessionId(pSpool);
  210. #endif
  211. if (pSpool->pIniJob)
  212. pIniJob = pSpool->pIniJob;
  213. else if (pSpool->pIniPort)
  214. pIniJob = pSpool->pIniPort->pIniJob;
  215. if (pIniJob) {
  216. if (pText) {
  217. #ifdef _HYDRA_
  218. Error = WinStationMessageBox(SessionId, hWnd, pText, pCaption, dwType);
  219. #else
  220. Error = MessageBox(hWnd, pText, pCaption, dwType);
  221. #endif
  222. } else {
  223. pErrorString = Error == ERROR_NOT_READY ||
  224. Error == ERROR_OUT_OF_PAPER ||
  225. Error == ERROR_DEVICE_REINITIALIZATION_NEEDED ||
  226. Error == ERROR_DEVICE_REQUIRES_CLEANING ||
  227. Error == ERROR_DEVICE_DOOR_OPEN ||
  228. Error == ERROR_DEVICE_NOT_CONNECTED ? GetErrorString(Error) : NULL;
  229. hToken = RevertToPrinterSelf();
  230. pDocumentName = pIniJob->pDocument;
  231. if (!pDocumentName) {
  232. *szUnnamed = L'\0';
  233. LoadString( hInst, IDS_UNNAMED, szUnnamed,
  234. sizeof szUnnamed / sizeof *szUnnamed );
  235. pDocumentName = szUnnamed;
  236. }
  237. if (pSpool->pIniPort) {
  238. #ifdef _HYDRA_
  239. Error = WinStationMessage(SessionId,
  240. NULL,
  241. #else
  242. Error = Message(NULL,
  243. #endif
  244. MB_ICONSTOP | MB_RETRYCANCEL | MB_SETFOREGROUND,
  245. IDS_LOCALSPOOLER,
  246. IDS_ERROR_WRITING_TO_PORT,
  247. pDocumentName,
  248. pSpool->pIniPort->pName,
  249. pErrorString ? pErrorString : szNull);
  250. } else {
  251. #ifdef _HYDRA_
  252. Error = WinStationMessage(SessionId,
  253. NULL,
  254. #else
  255. Error = Message(NULL,
  256. #endif
  257. MB_ICONSTOP | MB_RETRYCANCEL | MB_SETFOREGROUND,
  258. IDS_LOCALSPOOLER,
  259. IDS_ERROR_WRITING_TO_DISK,
  260. pDocumentName,
  261. pErrorString ? pErrorString : szNull);
  262. }
  263. ImpersonatePrinterClient(hToken);
  264. FreeSplStr(pErrorString);
  265. }
  266. } else {
  267. PWCHAR pPrinterName = NULL;
  268. //
  269. // There is no pIniJob or pIniPort, so we can't be very informative:
  270. //
  271. pErrorString = Error == ERROR_NOT_READY ||
  272. Error == ERROR_OUT_OF_PAPER ||
  273. Error == ERROR_DEVICE_REINITIALIZATION_NEEDED ||
  274. Error == ERROR_DEVICE_REQUIRES_CLEANING ||
  275. Error == ERROR_DEVICE_DOOR_OPEN ||
  276. Error == ERROR_DEVICE_NOT_CONNECTED ? GetErrorString(Error) : NULL;
  277. if (pSpool->pIniPrinter)
  278. pPrinterName = pSpool->pIniPrinter->pName;
  279. if (!pPrinterName) {
  280. *szUnnamed = L'\0';
  281. LoadString( hInst, IDS_UNNAMED, szUnnamed,
  282. COUNTOF( szUnnamed ));
  283. pPrinterName = szUnnamed;
  284. }
  285. #ifdef _HYDRA_
  286. Error = WinStationMessage(SessionId,
  287. NULL,
  288. #else
  289. Error = Message(NULL,
  290. #endif
  291. MB_ICONSTOP | MB_RETRYCANCEL | MB_SETFOREGROUND,
  292. IDS_LOCALSPOOLER,
  293. IDS_ERROR_WRITING_GENERAL,
  294. pSpool->pIniPrinter->pName,
  295. pErrorString ? pErrorString : szNull);
  296. FreeSplStr(pErrorString);
  297. }
  298. if (Error == IDCANCEL) {
  299. EnterSplSem();
  300. pSpool->Status |= SPOOL_STATUS_CANCELLED;
  301. if (pIniJob) {
  302. pIniJob->Status |= JOB_PENDING_DELETION;
  303. // Release any thread waiting on LocalSetPort
  304. SetPortErrorEvent(pIniJob->pIniPort);
  305. pIniJob->dwAlert |= JOB_NO_ALERT;
  306. // Release any thread waiting on SeekPrinter
  307. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  308. }
  309. LeaveSplSem();
  310. SplOutSem();
  311. SetLastError(ERROR_PRINT_CANCELLED);
  312. }
  313. return Error;
  314. }
  315. // Exclusively for use of the following routines. This is done so we would not have
  316. // to store LastError in PSPOOL.
  317. typedef struct _AUTORETRYTHDINFO {
  318. PSPOOL pSpool;
  319. DWORD LastError;
  320. } AUTORETRYTHDINFO;
  321. typedef AUTORETRYTHDINFO *PAUTORETRYTHDINFO;
  322. // ------------------------------------------------------------------------
  323. // SpoolerBMThread
  324. //
  325. // Thread start up routine for the spooler error message box thread. Exit
  326. // code is the return ID from MessageBox.
  327. //
  328. // ------------------------------------------------------------------------
  329. DWORD
  330. WINAPI
  331. SpoolerMBThread(
  332. PAUTORETRYTHDINFO pThdInfo
  333. )
  334. {
  335. DWORD rc;
  336. rc = MyMessageBox( NULL, pThdInfo->pSpool, pThdInfo->LastError, NULL, NULL, 0 );
  337. FreeSplMem( pThdInfo );
  338. return rc;
  339. }
  340. #define _ONE_SECOND 1000 // in milliseconds
  341. #define SPOOL_WRITE_RETRY_INTERVAL_IN_SECOND 5 // seconds
  342. // ------------------------------------------------------------------------
  343. // PromptWriteError
  344. //
  345. // we'll start a seperate thread to bring up
  346. // the message box while we'll (secretly) automatically retry on this
  347. // current thread, until user has chosen to retry or cancel. Call the error UI
  348. // on the main thread if printing direct.
  349. //
  350. // ------------------------------------------------------------------------
  351. DWORD
  352. PromptWriteError(
  353. PSPOOL pSpool,
  354. PHANDLE phThread,
  355. PDWORD pdwThreadId
  356. )
  357. {
  358. DWORD Error = GetLastError();
  359. DWORD dwExitCode;
  360. DWORD dwWaitCount = 0;
  361. SplOutSem();
  362. if( !UpdateJobStatus( pSpool, Error )){
  363. return IDCANCEL;
  364. }
  365. //
  366. // If the spooler doesn't have popup retry messageboxes enabled, then
  367. // just sleep and return.
  368. //
  369. if( !pSpool->pIniSpooler->bEnableRetryPopups ){
  370. Sleep( SPOOL_WRITE_RETRY_INTERVAL_IN_SECOND * _ONE_SECOND );
  371. return IDRETRY;
  372. }
  373. // start a seperate thread to display the message box
  374. // so we can continue to retry here
  375. // or simply sleep for 5 seconds if we have already done so
  376. if( !*phThread ) {
  377. // start a thread to bring up the message box
  378. PAUTORETRYTHDINFO pThdInfo;
  379. pThdInfo = (PAUTORETRYTHDINFO)AllocSplMem( sizeof(AUTORETRYTHDINFO));
  380. if ( pThdInfo == NULL ) {
  381. DBGMSG( DBG_WARNING, ("PromptWriteError failed to allocate AUTORETRYTHDINFO %d\n", GetLastError() ));
  382. goto _DoItOnCurrentThread;
  383. }
  384. pThdInfo->pSpool = pSpool;
  385. pThdInfo->LastError = Error;
  386. if (!(*phThread = CreateThread(NULL, 0,
  387. (LPTHREAD_START_ROUTINE)SpoolerMBThread,
  388. pThdInfo, 0, pdwThreadId))) {
  389. DBGMSG(DBG_WARNING, ("PromptWriteError: CreateThread Failed.\n"));
  390. FreeSplMem( pThdInfo );
  391. goto _DoItOnCurrentThread;
  392. }
  393. }
  394. while (1) {
  395. // we've already started a MB thread, check if user has terminated
  396. // the message box
  397. if (GetExitCodeThread( *phThread, &dwExitCode) && (dwExitCode != STILL_ACTIVE)) {
  398. // if the thread has been terminated, find out the exit code
  399. // which is the return ID from MessageBox, then close the
  400. // thread handle.
  401. CloseHandle( *phThread );
  402. *phThread = 0;
  403. return dwExitCode;
  404. }
  405. if (dwWaitCount++ >= SPOOL_WRITE_RETRY_INTERVAL_IN_SECOND)
  406. break;
  407. Sleep(_ONE_SECOND);
  408. }
  409. return IDRETRY;
  410. _DoItOnCurrentThread:
  411. return MyMessageBox(NULL, pSpool, Error, NULL, NULL, 0 );
  412. }
  413. #ifdef _HYDRA_
  414. DWORD
  415. DetermineJobSessionId(
  416. PSPOOL pSpool
  417. )
  418. /*++
  419. Routine Description:
  420. Determine which session to notify for the current job.
  421. Arguments:
  422. pSpool - Open spooler handle
  423. Return Value:
  424. SessionId to send notification message to.
  425. --*/
  426. {
  427. PINIJOB pIniJob = NULL;
  428. if (pSpool->pIniJob)
  429. pIniJob = pSpool->pIniJob;
  430. else if (pSpool->pIniPort)
  431. pIniJob = pSpool->pIniPort->pIniJob;
  432. if( pIniJob ) return( pIniJob->SessionId );
  433. return( pSpool->SessionId );
  434. }
  435. int
  436. WinStationMessageBox(
  437. DWORD SessionId,
  438. HWND hWnd,
  439. LPCWSTR lpText,
  440. LPCWSTR lpCaption,
  441. UINT uType
  442. )
  443. /*++
  444. Routine Description:
  445. Displays a message on the WinStation named by SessionId.
  446. If any problems in actually displaying the message, wait for the
  447. the message box timeout interval before returning. This prevents
  448. a spin in the spooler attempting to retry the print job without a
  449. message box to block the thread.
  450. Arguments:
  451. SessionId - ID of session to display the message on.
  452. Return Value:
  453. Result of MessageBox().
  454. --*/
  455. {
  456. UINT uOldErrorMode;
  457. DWORD MsgLength, CaptionLength, Response;
  458. BOOL Result;
  459. va_list vargs;
  460. //
  461. // Standard NT is always SessionId == 0.
  462. // On Hydra, the system console is always SessionId == 0.
  463. //
  464. if( SessionId == 0 ) {
  465. return( MessageBox( hWnd, lpText, lpCaption, uType ) );
  466. }
  467. //
  468. // If its not SessionId == 0, then we must deliver
  469. // the message to a session connected on a Hydra
  470. // server. Non-Hydra will not ever allocate a
  471. // SessionId != 0.
  472. //
  473. // On failure, we send the message to the console.
  474. //
  475. if( pWinStationSendMessage == NULL ) {
  476. uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  477. WinStaDllHandle = LoadLibraryW(L"winsta.dll");
  478. SetErrorMode(uOldErrorMode);
  479. if( WinStaDllHandle == NULL ) {
  480. return( MessageBox( hWnd, lpText, lpCaption, uType ) );
  481. }
  482. pWinStationSendMessage = (PWINSTATION_SEND_MESSAGEW)GetProcAddress(
  483. WinStaDllHandle,
  484. "WinStationSendMessageW"
  485. );
  486. if( pWinStationSendMessage == NULL ) {
  487. return( MessageBox( hWnd, lpText, lpCaption, uType ) );
  488. }
  489. }
  490. CaptionLength = (wcslen( lpCaption ) + 1) * sizeof(WCHAR);
  491. MsgLength = (wcslen( lpText ) + 1) * sizeof(WCHAR);
  492. // Send the message to the WinStation and wait for a response
  493. Result = pWinStationSendMessage(
  494. SERVERNAME_CURRENT,
  495. SessionId,
  496. (LPWSTR)lpCaption,
  497. CaptionLength,
  498. (LPWSTR)lpText,
  499. MsgLength,
  500. uType,
  501. WINSTATION_PRINTER_MESSAGE_TIMEOUT,
  502. &Response,
  503. FALSE
  504. );
  505. if( Result ) {
  506. // If not an expected response, wait to prevent spinning
  507. if( (Response != IDTIMEOUT) &&
  508. (Response != IDOK) &&
  509. (Response != IDCANCEL) &&
  510. (Response != IDRETRY) &&
  511. (Response != IDIGNORE) &&
  512. (Response != IDYES) &&
  513. (Response != IDNO) ) {
  514. // Sleep to prevent a spin
  515. Sleep( WINSTATION_PRINTER_MESSAGE_TIMEOUT*1000);
  516. }
  517. return( Response );
  518. }
  519. else {
  520. // Sleep to prevent a spin
  521. Sleep( WINSTATION_PRINTER_MESSAGE_TIMEOUT*1000);
  522. return( 0 );
  523. }
  524. }
  525. int
  526. WinStationMessage(
  527. DWORD SessionId,
  528. HWND hWnd,
  529. DWORD Type,
  530. int CaptionID,
  531. int TextID,
  532. ...
  533. )
  534. /*++
  535. Routine Description:
  536. Displays a message on the WinStation named by SessionId. This takes
  537. the message text and caption from the resource file.
  538. If any problems in actually display the message, wait for the
  539. the message box timeout interval before returning. This prevents
  540. a spin in the spooler attempting to retry the print job without a
  541. message box to block the thread.
  542. Arguments:
  543. SessionId - ID of session to display the message on.
  544. Return Value:
  545. Result of MessageBox().
  546. --*/
  547. {
  548. UINT uOldErrorMode;
  549. WCHAR MsgText[512];
  550. WCHAR MsgFormat[256];
  551. WCHAR MsgCaption[40];
  552. DWORD MsgLength, CaptionLength, Response;
  553. BOOL Result;
  554. va_list vargs;
  555. if( ( LoadString( hInst, TextID, MsgFormat,
  556. sizeof MsgFormat / sizeof *MsgFormat ) > 0 )
  557. && ( LoadString( hInst, CaptionID, MsgCaption,
  558. sizeof MsgCaption / sizeof *MsgCaption ) > 0 ) )
  559. {
  560. va_start( vargs, TextID );
  561. _vsntprintf( MsgText, COUNTOF(MsgText)-1, MsgFormat, vargs );
  562. MsgText[COUNTOF(MsgText)-1] = 0;
  563. va_end( vargs );
  564. if( SessionId == 0 ) {
  565. return( MessageBox( hWnd, MsgText, MsgCaption, Type ) );
  566. }
  567. if( pWinStationSendMessage == NULL ) {
  568. uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  569. WinStaDllHandle = LoadLibraryW(L"winsta.dll");
  570. SetErrorMode(uOldErrorMode);
  571. if( WinStaDllHandle == NULL ) {
  572. return( MessageBox( hWnd, MsgText, MsgCaption, Type ) );
  573. }
  574. pWinStationSendMessage = (PWINSTATION_SEND_MESSAGEW)GetProcAddress(
  575. WinStaDllHandle,
  576. "WinStationSendMessageW"
  577. );
  578. if( pWinStationSendMessage == NULL ) {
  579. return( MessageBox( hWnd, MsgText, MsgCaption, Type ) );
  580. }
  581. }
  582. CaptionLength = (wcslen( MsgCaption ) + 1) * sizeof(WCHAR);
  583. MsgLength = (wcslen( MsgText ) + 1) * sizeof(WCHAR);
  584. // Send the message to the WinStation and wait for a response
  585. Result = pWinStationSendMessage(
  586. SERVERNAME_CURRENT,
  587. SessionId,
  588. MsgCaption,
  589. CaptionLength,
  590. MsgText,
  591. MsgLength,
  592. Type, // Style
  593. WINSTATION_PRINTER_MESSAGE_TIMEOUT,
  594. &Response,
  595. FALSE // DoNotWait
  596. );
  597. if( Result ) {
  598. // If not an expected response, wait to prevent spinning
  599. if( (Response != IDTIMEOUT) &&
  600. (Response != IDOK) &&
  601. (Response != IDCANCEL) &&
  602. (Response != IDRETRY) &&
  603. (Response != IDIGNORE) &&
  604. (Response != IDYES) &&
  605. (Response != IDNO) ) {
  606. // Sleep to prevent a spin
  607. Sleep( WINSTATION_PRINTER_MESSAGE_TIMEOUT*1000);
  608. }
  609. return( Response );
  610. }
  611. else {
  612. // Sleep to prevent a spin
  613. Sleep( WINSTATION_PRINTER_MESSAGE_TIMEOUT*1000);
  614. return( 0 );
  615. }
  616. }
  617. else {
  618. // Sleep to prevent a spin
  619. Sleep( WINSTATION_PRINTER_MESSAGE_TIMEOUT*1000);
  620. return 0;
  621. }
  622. }
  623. #endif
  624. DWORD
  625. LclIsSessionZero (
  626. IN HANDLE hPrinter,
  627. IN DWORD JobId,
  628. OUT BOOL *pIsSessionZero
  629. )
  630. /*++
  631. Routine Description:
  632. Determines if the Job was submitted in Session 0.
  633. Arguments:
  634. hPrinter - printer handle
  635. JobId - Job ID
  636. pResponse - TRUE if the Job was submitted in Session0
  637. Return Value:
  638. Last Error
  639. --*/
  640. {
  641. DWORD dwRetValue = ERROR_SUCCESS;
  642. DWORD SessionId = -1;
  643. PSPOOL pSpool = (PSPOOL)hPrinter;
  644. if (pSpool && JobId && pIsSessionZero)
  645. {
  646. SessionId = GetJobSessionId(pSpool, JobId);
  647. }
  648. if(SessionId == -1)
  649. {
  650. dwRetValue = ERROR_INVALID_PARAMETER;
  651. }
  652. else
  653. {
  654. *pIsSessionZero = (SessionId == 0);
  655. }
  656. return dwRetValue;
  657. }
  658. BOOL
  659. LclPromptUIPerSessionUser(
  660. IN HANDLE hPrinter,
  661. IN DWORD JobId,
  662. IN PSHOWUIPARAMS pUIParams,
  663. OUT DWORD *pResponse
  664. )
  665. /*++
  666. Routine Description:
  667. Pops TS Message Box in the Session that created the Job.
  668. Arguments:
  669. hPrinter - printer handle
  670. JobId - Job ID
  671. pUIParams - UI Parameters
  672. pResponse - user's response
  673. Return Value:
  674. TRUE if it was able to show the UI
  675. --*/
  676. {
  677. PSPOOL pSpool = (PSPOOL)hPrinter;
  678. DWORD SessionId = -1;
  679. PINIJOB pIniJob = NULL;
  680. DWORD dwReturnVal = 0;
  681. DWORD MessageLength;
  682. DWORD TitleLength;
  683. BOOL bRetValue = FALSE;
  684. if (pSpool && JobId && pUIParams && pResponse)
  685. {
  686. SessionId = GetJobSessionId(pSpool, JobId);
  687. }
  688. if(SessionId == -1)
  689. {
  690. SetLastError(ERROR_INVALID_PARAMETER);
  691. }
  692. else
  693. {
  694. switch (pUIParams->UIType)
  695. {
  696. case kMessageBox:
  697. {
  698. if (pUIParams->MessageBoxParams.cbSize == sizeof(MESSAGEBOX_PARAMS) &&
  699. pUIParams->MessageBoxParams.pTitle &&
  700. pUIParams->MessageBoxParams.pMessage &&
  701. InitializeMessageBoxFunction())
  702. {
  703. TitleLength = (wcslen(pUIParams->MessageBoxParams.pTitle) + 1) * sizeof(WCHAR);
  704. MessageLength = (wcslen(pUIParams->MessageBoxParams.pMessage) + 1) * sizeof(WCHAR);
  705. bRetValue = pWinStationSendMessage(
  706. SERVERNAME_CURRENT,
  707. SessionId,
  708. pUIParams->MessageBoxParams.pTitle,
  709. TitleLength,
  710. pUIParams->MessageBoxParams.pMessage,
  711. MessageLength,
  712. pUIParams->MessageBoxParams.Style,
  713. pUIParams->MessageBoxParams.dwTimeout,
  714. pResponse,
  715. !pUIParams->MessageBoxParams.bWait);
  716. }
  717. else
  718. {
  719. SetLastError(ERROR_INVALID_PARAMETER);
  720. }
  721. }
  722. default:
  723. {
  724. SetLastError(ERROR_INVALID_PARAMETER);
  725. }
  726. }
  727. }
  728. return bRetValue;
  729. }
  730. BOOL
  731. InitializeMessageBoxFunction(
  732. )
  733. /*++
  734. Routine Description:
  735. Returns the address of WinStationSendMessageW exported by winsta.dll.
  736. WTSSendMessage could have been used instead of doing this.
  737. Arguments:
  738. None.
  739. Return Value:
  740. The address of WinStationSendMessageW.
  741. --*/
  742. {
  743. UINT uOldErrorMode;
  744. if (!pWinStationSendMessage)
  745. {
  746. if (WinStaDllHandle == NULL)
  747. {
  748. uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  749. WinStaDllHandle = LoadLibraryW(L"winsta.dll");
  750. SetErrorMode(uOldErrorMode);
  751. }
  752. if(WinStaDllHandle != NULL)
  753. {
  754. pWinStationSendMessage = (PWINSTATION_SEND_MESSAGEW)GetProcAddress(
  755. WinStaDllHandle,
  756. "WinStationSendMessageW"
  757. );
  758. }
  759. }
  760. return !!pWinStationSendMessage;
  761. }