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.

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