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.

797 lines
21 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. All rights reserved.
  4. Module Name:
  5. defprn.c
  6. Abstract:
  7. Default printer.
  8. Author:
  9. Steve Kiraly (SteveKi) 06-Feb-1997
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. #include "client.h"
  15. #include "defprn.h"
  16. //
  17. // The buffer size needed to hold the maximum printer name.
  18. //
  19. enum { kPrinterBufMax_ = MAX_UNC_PRINTER_NAME + 1 };
  20. /*++
  21. Name:
  22. IsPrinterDefault
  23. Description:
  24. The IsPrinterDefault function checks if the specified
  25. printer is the default printer. If the printer name
  26. specified is NULL or the NULL string then it returns
  27. success if there is a default printer.
  28. Arguments:
  29. pszPrinter - Pointer a zero terminated string that
  30. contains the printer name or NULL or the
  31. NULL string.
  32. Return Value:
  33. If the function succeeds, the return value is nonzero.
  34. If the function fails, the return value is zero. To get
  35. extended error information, call GetLastError.
  36. Remarks:
  37. If a NULL is passed as the printer name this function
  38. will indicate if there is any default printer set.
  39. --*/
  40. BOOL
  41. IsPrinterDefaultW(
  42. IN LPCTSTR pszPrinter
  43. )
  44. {
  45. BOOL bRetval = FALSE;
  46. DWORD dwDefaultSize = kPrinterBufMax_;
  47. PTSTR pszDefault = NULL;
  48. pszDefault = AllocMem(dwDefaultSize * sizeof(TCHAR));
  49. if (pszDefault)
  50. {
  51. //
  52. // Get the default printer.
  53. //
  54. bRetval = GetDefaultPrinterW( pszDefault, &dwDefaultSize );
  55. if( bRetval )
  56. {
  57. if( pszPrinter && *pszPrinter )
  58. {
  59. //
  60. // Check for a match.
  61. //
  62. bRetval = !_tcsicmp( pszDefault, pszPrinter ) ? TRUE : FALSE;
  63. }
  64. else
  65. {
  66. bRetval = TRUE;
  67. }
  68. }
  69. }
  70. else
  71. {
  72. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  73. }
  74. FreeMem(pszDefault);
  75. return bRetval;
  76. }
  77. /*++
  78. Name:
  79. GetDefaultPrinter
  80. Description:
  81. The GetDefaultPrinter function retrieves the printer
  82. name of the current default printer.
  83. Arguments:
  84. pBuffer - Points to a buffer to receive the null-terminated
  85. character string containing the default printer name.
  86. This parameter may be null if the caller want the size of
  87. default printer name.
  88. pcchBuffer - Points to a variable that specifies the maximum size,
  89. in characters, of the buffer. This value should be
  90. large enough to contain 2 + INTERNET_MAX_HOST_NAME_LENGTH
  91. + 1 MAX_PATH + 1 characters.
  92. Return Value:
  93. If the function succeeds, the return value is nonzero and
  94. the variable pointed to by the pnSize parameter contains the
  95. number of characters copied to the destination buffer,
  96. including the terminating null character.
  97. If the function fails, the return value is zero. To get extended
  98. error information, call GetLastError.
  99. Notes:
  100. If this function fails with a last error of ERROR_INSUFFICIENT_BUFFER
  101. the variable pointed to by pcchBuffer is returned with the number of
  102. characters needed to hold the printer name including the
  103. terminating null character.
  104. --*/
  105. BOOL
  106. GetDefaultPrinterW(
  107. IN LPTSTR pszBuffer,
  108. IN LPDWORD pcchBuffer
  109. )
  110. {
  111. BOOL bRetval = FALSE;
  112. LPTSTR psz = NULL;
  113. UINT uLen = 0;
  114. PTSTR pszDefault = NULL;
  115. UINT cchDefault = kPrinterBufMax_+MAX_PATH;
  116. //
  117. // Validate the size parameter.
  118. //
  119. if( !pcchBuffer )
  120. {
  121. SetLastError( ERROR_INVALID_PARAMETER );
  122. return bRetval;
  123. }
  124. //
  125. // Allocate the temp default printer buffer from the heap.
  126. //
  127. pszDefault = AllocMem(cchDefault * sizeof(TCHAR));
  128. if (!pszDefault)
  129. {
  130. DBGMSG( DBG_TRACE,( "GetDefaultPrinter: Not enough memory to allocate default printer buffer.\n" ) );
  131. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  132. return bRetval;
  133. }
  134. //
  135. // Get the devices key, which is the default device or printer.
  136. //
  137. if( DefPrnGetProfileString( szWindows, szDevice, pszDefault, cchDefault ) )
  138. {
  139. //
  140. // The string is returned in the form.
  141. // "printer_name,winspool,Ne00:" now convert it to
  142. // printer_name
  143. //
  144. psz = _tcschr( pszDefault, TEXT( ',' ));
  145. //
  146. // Set the comma to a null.
  147. //
  148. if( psz )
  149. {
  150. *psz = 0;
  151. //
  152. // Check if the return buffer has enough room for the printer name.
  153. //
  154. uLen = _tcslen( pszDefault );
  155. if( uLen < *pcchBuffer && pszBuffer )
  156. {
  157. //
  158. // Copy the default printer name to the prvided buffer.
  159. //
  160. _tcscpy( pszBuffer, pszDefault );
  161. bRetval = TRUE;
  162. DBGMSG( DBG_TRACE,( "GetDefaultPrinter: Success " TSTR "\n", pszBuffer ) );
  163. }
  164. else
  165. {
  166. DBGMSG( DBG_WARN,( "GetDefaultPrinter: buffer too small.\n" ) );
  167. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  168. }
  169. //
  170. // Return back the size of the default printer name.
  171. //
  172. *pcchBuffer = uLen + 1;
  173. }
  174. else
  175. {
  176. DBGMSG( DBG_WARN,( "GetDefaultPrinter: comma not found in printer name in devices section.\n" ) );
  177. SetLastError( ERROR_INVALID_NAME );
  178. }
  179. }
  180. else
  181. {
  182. DBGMSG( DBG_TRACE,( "GetDefaultPrinter: failed with %d Last error %d.\n", bRetval, GetLastError() ) );
  183. DBGMSG( DBG_TRACE,( "GetDefaultPrinter: No default printer.\n" ) );
  184. SetLastError( ERROR_FILE_NOT_FOUND );
  185. }
  186. //
  187. // Release any allocated memory, note FreeMem deals with a NULL pointer.
  188. //
  189. FreeMem(pszDefault);
  190. return bRetval;
  191. }
  192. /*++
  193. Name:
  194. SetDefaultPrinter
  195. Description:
  196. The SetDefaultPrinter function set the printer name to
  197. be used as the default printer.
  198. Arguments:
  199. pPrinter - Points to a null-terminated character string
  200. that specifies the name of the default printer.
  201. This parameter may be NULL or the NULL string in
  202. which case this function will set the first printer
  203. enumerated from the print sub system as the default
  204. printer, if a default printer does not already exists.
  205. Return Value:
  206. If the function succeeds, the return value is nonzero.
  207. If the function fails, the return value is zero. To get extended
  208. error information, call GetLastError.
  209. --*/
  210. BOOL
  211. SetDefaultPrinterW(
  212. IN LPCTSTR pszPrinter
  213. )
  214. {
  215. PTSTR pszDefault = NULL;
  216. PTSTR pszAnyPrinter = NULL;
  217. PTSTR pszBuffer = NULL;
  218. UINT cchDefault = kPrinterBufMax_;
  219. UINT cchAnyPrinter = kPrinterBufMax_;
  220. BOOL bRetval = FALSE;
  221. //
  222. // This calculation is large to accomodate the max printer name
  223. // plus the comma plus the processor name and port name.
  224. //
  225. UINT cchBuffer = kPrinterBufMax_+kPrinterBufMax_+1;
  226. //
  227. // Avoid broadcasts as much as possible. See if the printer
  228. // is already the default, and don't do anything if it is.
  229. //
  230. if( IsPrinterDefaultW( pszPrinter ) )
  231. {
  232. DBGMSG( DBG_TRACE, ( "SetDefaultPrinter: " TSTR " already the default printer.\n", pszPrinter ));
  233. bRetval = TRUE;
  234. goto Cleanup;
  235. }
  236. //
  237. // Allocate the temp default printer buffer from the heap.
  238. //
  239. pszDefault = AllocMem(cchDefault * sizeof(TCHAR));
  240. pszAnyPrinter = AllocMem(cchAnyPrinter * sizeof(TCHAR));
  241. pszBuffer = AllocMem(cchBuffer * sizeof(TCHAR));
  242. if (!pszDefault || !pszAnyPrinter || !pszBuffer)
  243. {
  244. DBGMSG( DBG_TRACE,( "SetDefaultPrinter: Not enough memory for temp buffers.\n" ) );
  245. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  246. goto Cleanup;
  247. }
  248. //
  249. // If the printer name was not specified, get any printer from the devices section.
  250. //
  251. if( !pszPrinter || !*pszPrinter )
  252. {
  253. //
  254. // A printer name was not specified i.e. a NULL name or NULL string was passed then fetch
  255. // the first printer from the devices section and make this the default printer.
  256. //
  257. if( !DefPrnGetProfileString( szDevices, NULL, pszAnyPrinter, cchAnyPrinter ) )
  258. {
  259. DBGMSG( DBG_WARN, ( "SetDefaultPrinter: DefPrnGetProfileString failed, last error %d any printer not available.\n", GetLastError() ) );
  260. SetLastError( ERROR_INVALID_PRINTER_NAME );
  261. goto Cleanup;
  262. }
  263. else
  264. {
  265. pszPrinter = pszAnyPrinter;
  266. }
  267. }
  268. else
  269. {
  270. //
  271. // If the given name is not in the devices list then this function may have been passed
  272. // either a local share name a fully qualified local printer name a fully qualified
  273. // printer share name
  274. //
  275. if( !DefPrnGetProfileString( szDevices, pszPrinter, pszDefault, cchDefault ) )
  276. {
  277. //
  278. // Get the actual printer name, see bGetActualPrinterName for details.
  279. //
  280. if( bGetActualPrinterName( pszPrinter, pszAnyPrinter, &cchAnyPrinter ))
  281. {
  282. //
  283. // Point to the actual printer name.
  284. //
  285. pszPrinter = pszAnyPrinter;
  286. //
  287. // Avoid broadcasts as much as possible. See if the printer
  288. // is already the default, and don't do anything if it is.
  289. //
  290. if( IsPrinterDefaultW( pszPrinter ) )
  291. {
  292. DBGMSG( DBG_TRACE, ( "SetDefaultPrinter: " TSTR " already the default printer.\n", pszPrinter ));
  293. bRetval = TRUE;
  294. goto Cleanup;
  295. }
  296. }
  297. else
  298. {
  299. DBGMSG( DBG_WARN, ( "SetDefaultPrinter: bGetActualPrinterName failed, last error %d " TSTR "\n", GetLastError(), pszPrinter ) );
  300. //
  301. // bGetActualPrinterName sets the last error on failure.
  302. //
  303. goto Cleanup;
  304. }
  305. }
  306. }
  307. //
  308. // Get the default string and check if the provided printer name is valid.
  309. //
  310. if( !DefPrnGetProfileString( szDevices, pszPrinter, pszDefault, cchDefault ) )
  311. {
  312. DBGMSG( DBG_WARN, ( "SetDefaultPrinter: DefPrnGetProfileString failed, last error %d " TSTR " not in devices section.\n", GetLastError(), pszPrinter ) );
  313. SetLastError( ERROR_INVALID_PRINTER_NAME );
  314. goto Cleanup;
  315. }
  316. //
  317. // Build the default printer string. This call should not fail since we have allocated
  318. // pszBuffer to a size that should contain the printer name plus the comma plus the port name.
  319. //
  320. if (StrNCatBuff( pszBuffer,
  321. cchBuffer,
  322. pszPrinter,
  323. szComma,
  324. pszDefault,
  325. NULL ) != ERROR_SUCCESS)
  326. {
  327. //
  328. // Set the last error to some error value, however, it cannot be set to ERROR_INSUFFICIENT_BUFFER
  329. // this error code implies the caller provided buffer is too small. StrNCatBuff would only fail because
  330. // because of some internal error or the registry was hacked with a really large printer name.
  331. //
  332. DBGMSG( DBG_ERROR, ( "SetDefaultPrinter: Buffer size too small, this should not fail.\n" ) );
  333. SetLastError( ERROR_INVALID_PARAMETER );
  334. goto Cleanup;
  335. }
  336. //
  337. // Set the default printer string in the registry.
  338. //
  339. if( !DefPrnWriteProfileString( szWindows, szDevice, pszBuffer ) )
  340. {
  341. DBGMSG( DBG_WARN, ( "SetDefaultPrinter: WriteProfileString failed, last error %d.\n", GetLastError() ) );
  342. SetLastError( ERROR_CANTWRITE );
  343. goto Cleanup;
  344. }
  345. //
  346. // Tell the world and make everyone flash.
  347. //
  348. SendNotifyMessage( HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)szWindows );
  349. bRetval = TRUE;
  350. DBGMSG( DBG_TRACE, ( "SetDefaultPrinter: Success " TSTR "\n", pszBuffer ) );
  351. Cleanup:
  352. //
  353. // Release any allocated memory, note FreeMem deals with a NULL pointer.
  354. //
  355. FreeMem(pszDefault);
  356. FreeMem(pszAnyPrinter);
  357. FreeMem(pszBuffer);
  358. return bRetval;
  359. }
  360. /*++
  361. Name:
  362. bGetActualPrinterName
  363. Description:
  364. This routine converts the given printer name or printer name alias to
  365. the actual printer name.
  366. Arguments:
  367. pszPrinter - Points to a null-terminated character string
  368. that specifies the name of printer.
  369. pszBuffer - pointer to a buffer that recieves the actual
  370. printer name on return if this function is successful.
  371. pcchBuffer - Points to a variable that specifies the maximum size,
  372. in characters of pszBuffer on input, on output this
  373. argument contains the number of characters copied into
  374. pszBuffer, not including the NULL terminator.
  375. Return Value:
  376. If the function succeeds, the return value TRUE
  377. If the function fails, the return value is FALSE. Use GetLastError to
  378. get extended error information.
  379. --*/
  380. BOOL
  381. bGetActualPrinterName(
  382. IN LPCTSTR pszPrinter,
  383. IN LPTSTR pszBuffer,
  384. IN OUT UINT *pcchBuffer
  385. )
  386. {
  387. HANDLE hPrinter = NULL;
  388. BOOL bStatus = FALSE;
  389. SPLASSERT( pszPrinter );
  390. SPLASSERT( pszBuffer );
  391. SPLASSERT( pcchBuffer );
  392. //
  393. // Open the printer for default access, all we need is read.
  394. //
  395. bStatus = OpenPrinter( (LPTSTR)pszPrinter, &hPrinter, NULL );
  396. if (bStatus)
  397. {
  398. DWORD cbNeeded = 0;
  399. DWORD cbReturned = 0;
  400. PRINTER_INFO_4 *pInfo = NULL;
  401. //
  402. // Get the printer info 4 size.
  403. //
  404. bStatus = GetPrinter( hPrinter, 4, NULL, 0, &cbNeeded );
  405. if (!bStatus && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  406. {
  407. //
  408. // Allocate the printer info 4 buffer.
  409. //
  410. pInfo = (PRINTER_INFO_4 *)LocalAlloc( LMEM_FIXED, cbNeeded );
  411. if (pInfo)
  412. {
  413. //
  414. // Get the printer name and attributes to determine if this printer is a local
  415. // or a remote printer connection.
  416. //
  417. bStatus = GetPrinter( hPrinter, 4, (LPBYTE)pInfo, cbNeeded, &cbReturned );
  418. if (bStatus)
  419. {
  420. DBGMSG( DBG_TRACE, ( "bGetActualPrinterName: Name: " TSTR " Actual: " TSTR "\n", pszPrinter, pInfo->pPrinterName ) );
  421. //
  422. // Get the printer name, the spooler will strip the local-server
  423. // name off the full printer name.
  424. //
  425. // Given: Result:
  426. // printer printer
  427. // sharename printer
  428. // \\local-server\printer printer
  429. // \\local-server\sharename printer
  430. // \\remote-server\printer \\remote-server\printer
  431. // \\remote-server\sharename \\remote-server\printer
  432. //
  433. pszPrinter = pInfo->pPrinterName;
  434. //
  435. // If we have a valid printer name and the provided buffer is
  436. // large enought to hold the printer name then copy the
  437. // actual printer name to the provided buffer.
  438. //
  439. bStatus = !!pszPrinter;
  440. if (bStatus)
  441. {
  442. UINT uLength = _tcslen( pszPrinter );
  443. //
  444. // Verify there is enough room in the buffer.
  445. //
  446. if (uLength < *pcchBuffer)
  447. {
  448. //
  449. // Copy the printer name to the provided buffer.
  450. //
  451. _tcscpy( pszBuffer, pszPrinter );
  452. }
  453. else
  454. {
  455. bStatus = FALSE;
  456. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  457. }
  458. //
  459. // Return the real length of the printer name
  460. // not including the null terminator.
  461. //
  462. *pcchBuffer = uLength;
  463. }
  464. }
  465. LocalFree( pInfo );
  466. }
  467. }
  468. ClosePrinter( hPrinter );
  469. }
  470. return bStatus;
  471. }
  472. /*++
  473. Name:
  474. DefPrnGetProfileString
  475. Description:
  476. Get the specified string from the users profile. The uses profile is located
  477. in the current users hive in the following registry path.
  478. HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion
  479. Arguments:
  480. pKey - pointer to key name to open.
  481. pValue - pointer to value to open, may be NULL
  482. pReturnedString - pointer to buffer where to store string
  483. nSize - size of the buffer in characters
  484. Return Value:
  485. If the function succeeds, the return value is TRUE.
  486. If the function fails, the return value is FALSE. Use GetLastError to
  487. get extended error information.
  488. --*/
  489. BOOL
  490. DefPrnGetProfileString(
  491. IN PCWSTR pKey,
  492. IN PCWSTR pValue,
  493. IN PWSTR pReturnedString,
  494. IN DWORD nSize
  495. )
  496. {
  497. DWORD Retval = ERROR_SUCCESS;
  498. HKEY hUser = NULL;
  499. HKEY hKey = NULL;
  500. PCWSTR pPath = NULL;
  501. DWORD cbSize = 0;
  502. //
  503. // Do some basic parameter validation.
  504. //
  505. Retval = pKey && pReturnedString ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
  506. //
  507. // Build the full registry path.
  508. //
  509. if (Retval == ERROR_SUCCESS)
  510. {
  511. cbSize = nSize * sizeof(*pReturnedString);
  512. Retval = StrCatAlloc(&pPath, gszUserProfileRegPath, szSlash, pKey, NULL);
  513. }
  514. //
  515. // Open the current user key, handle the case we are running in an inpersonating thread.
  516. //
  517. if (Retval == ERROR_SUCCESS)
  518. {
  519. Retval = RegOpenCurrentUser(KEY_READ, &hUser);
  520. }
  521. //
  522. // Open the full registry path.
  523. //
  524. if (Retval == ERROR_SUCCESS)
  525. {
  526. Retval = RegOpenKeyEx(hUser, pPath, 0, KEY_READ, &hKey);
  527. }
  528. //
  529. // Read the value, in the case the value name is null we get the name of the
  530. // first named value. Note if there is no named values the RegEnumValue api
  531. // will return success because it is returning the name if the unnamed value.
  532. // In this case we fail the call since no data was returned.
  533. //
  534. if (Retval == ERROR_SUCCESS)
  535. {
  536. if (!pValue)
  537. {
  538. Retval = RegEnumValue(hKey, 0, pReturnedString, &nSize, NULL, NULL, NULL, NULL);
  539. if (Retval == ERROR_SUCCESS && !*pReturnedString)
  540. {
  541. Retval = ERROR_NO_DATA;
  542. }
  543. }
  544. else
  545. {
  546. Retval = RegQueryValueEx(hKey, pValue, NULL, NULL, (PBYTE)pReturnedString, &cbSize);
  547. }
  548. }
  549. //
  550. // Clean up all allocated resources.
  551. //
  552. FreeSplMem((PWSTR)pPath);
  553. if (hKey)
  554. {
  555. RegCloseKey(hKey);
  556. }
  557. if (hUser)
  558. {
  559. RegCloseKey(hUser);
  560. }
  561. if (Retval != ERROR_SUCCESS)
  562. {
  563. SetLastError(Retval);
  564. }
  565. return Retval == ERROR_SUCCESS;
  566. }
  567. /*++
  568. Name:
  569. DefPrnWriteProfileString
  570. Description:
  571. Writes the specified string to the users profile.
  572. Arguments:
  573. pKey - pointer to key name to open.
  574. pValue - pointer to value to write to, may be NULL
  575. pString - pointer to string to write
  576. Return Value:
  577. If the function succeeds, the return value is TRUE.
  578. If the function fails, the return value is FALSE. Use GetLastError to
  579. get extended error information.
  580. --*/
  581. BOOL
  582. DefPrnWriteProfileString(
  583. IN PCWSTR pKey,
  584. IN PCWSTR pValue,
  585. IN PCWSTR pString
  586. )
  587. {
  588. DWORD Retval = ERROR_SUCCESS;
  589. DWORD nSize = 0;
  590. HKEY hUser = NULL;
  591. HKEY hKey = NULL;
  592. PCWSTR pPath = NULL;
  593. //
  594. // Do some basic parameter validation.
  595. //
  596. Retval = pKey && pString ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
  597. //
  598. // Build the full registry path.
  599. //
  600. if (Retval == ERROR_SUCCESS)
  601. {
  602. nSize = (wcslen(pString) + 1) * sizeof(*pString);
  603. Retval = StrCatAlloc(&pPath, gszUserProfileRegPath, szSlash, pKey, NULL);
  604. }
  605. //
  606. // Open the current user key, handle the case we are running in an inpersonating thread.
  607. //
  608. if (Retval == ERROR_SUCCESS)
  609. {
  610. Retval = RegOpenCurrentUser(KEY_WRITE, &hUser);
  611. }
  612. //
  613. // Open the full registry path.
  614. //
  615. if (Retval == ERROR_SUCCESS)
  616. {
  617. Retval= RegOpenKeyEx(hUser, pPath, 0, KEY_WRITE, &hKey);
  618. }
  619. //
  620. // Set the string value data.
  621. //
  622. if (Retval == ERROR_SUCCESS)
  623. {
  624. Retval = RegSetValueEx(hKey, pValue, 0, REG_SZ, (LPBYTE)pString, nSize);
  625. }
  626. //
  627. // Clean up all allocated resources.
  628. //
  629. FreeSplMem((PWSTR)pPath);
  630. if (hKey)
  631. {
  632. RegCloseKey(hKey);
  633. }
  634. if (hUser)
  635. {
  636. RegCloseKey(hUser);
  637. }
  638. if (Retval != ERROR_SUCCESS)
  639. {
  640. SetLastError(Retval);
  641. }
  642. return Retval == ERROR_SUCCESS;
  643. }