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.

1262 lines
38 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. EmulatePrinter.cpp
  5. Abstract:
  6. This is a general purpose shim to fix all problems we have seen
  7. that are remotely connected with printers. The shim fixes the
  8. following:
  9. 1) Apps call EnumPrinters passing only PRINTER_ENUM_LOCAL but expect to see
  10. network printers as well. For some reason Win9x enumerates network
  11. printers as well when this API is called with only PRINTER_ENUM_LOCAL set.
  12. 2) Apps call EnumPrinters passing only PRINTER_ENUM_DEFAULT. This works
  13. properly in win98, however the option does not exist in w2k. This
  14. API performs the equivalent.
  15. 3) EnumPrinters Level 5 is not really supported on NT. This API calls
  16. Level 2 and munges the data into a level 5 structure.
  17. 4) Win9x ignores pDefault parameter for OpenPrinter. Some native Win9x apps
  18. are unaware about this and assume it is safe to use PRINTER_ALL_ACCESS
  19. value for DesiredAccess flag, member of pDefault parameter, to open either
  20. local printer or remote printer server. But Windows NT requires
  21. SERVER_ALL_ACCESS set for this flag to access remote printer server.
  22. To emulate Win9x behavior, we override pDefault with NULL value.
  23. 5) If an app calls one of several print APIs with a NULL printer name,
  24. looks up and supplies the default printer name, or derives it from other params.
  25. 6) Verifies a correct handle was passed to SetPrinter. Win98 does this
  26. at the start and if its a bad handle never uses the passed Information,
  27. however w2k does not check the handle till after looking at the information.
  28. This can cause an error if Level is 2 and the print buffer is null due to
  29. a missing check in SetPrinterA. (note: this was fixed in whistler).
  30. 7) Verifies that the stack is correct after the proc set in SetAbortProc is
  31. called.
  32. 8) Verifies that an initialized DEVMODEA has been passed to ResetDCA.
  33. 9) Checks GetProfileStringA for a WINDOWS DEVICE (i.e. printer). If one is
  34. requested then make sure the string is not being truncated, if it is then
  35. save the full printer name for later use.
  36. 10) Checks for a -1 in the nFromPage for PrintDlgA and corrects it to a zero.
  37. Note: the OS should handle this as per the manual, however print team no-fixed it
  38. as too risky to change since -1 is a special value in their code.
  39. Notes:
  40. This is a general purpose shim. This code from this shim was originally
  41. in two seperate shims enumnetworkprinters and handlenullprintername.
  42. Also added another SHIM EmulateStartPage to this.
  43. History:
  44. 11/08/00 mnikkel created
  45. 12/07/00 prashkud Added StartPage to this.
  46. 01/25/01 mnikkel Removed W routines, they were causing problems
  47. and were not needed.
  48. 02/07/01 mnikkel Added check for too long a string, removed fixed printer
  49. name sizes.
  50. 02/27/2001 robkenny Converted to use tcs.h
  51. 05/21/2001 mnikkel Added PrintDlgA check
  52. 09/13/2001 mnikkel Changed so that level 5 data being created from Level 2
  53. data is only done on win2k. Level 5 data was fixed for XP.
  54. Also added check so shim works with printers shared out on
  55. win9X while running on XP.
  56. 12/15/2001 mnikkel Corrected bug in shim where default printer flag was not
  57. being set in enumprintersa.
  58. --*/
  59. #include "precomp.h"
  60. #include <commdlg.h>
  61. // This file and its assumptions need to be verified on an Intl version
  62. #include "LegalStr.h" // BUGBUG
  63. IMPLEMENT_SHIM_BEGIN(EmulatePrinter)
  64. #include "ShimHookMacro.h"
  65. #define MAX_PRINTER_NAME 221
  66. #define MAX_DRIVERPORT_NAME 50
  67. APIHOOK_ENUM_BEGIN
  68. APIHOOK_ENUM_ENTRY(DocumentPropertiesA)
  69. APIHOOK_ENUM_ENTRY(OpenPrinterA)
  70. APIHOOK_ENUM_ENTRY(SetPrinterA)
  71. APIHOOK_ENUM_ENTRY(CreateDCA)
  72. APIHOOK_ENUM_ENTRY(ResetDCA)
  73. APIHOOK_ENUM_ENTRY(EnumPrintersA)
  74. APIHOOK_ENUM_ENTRY(GetProfileStringA)
  75. APIHOOK_ENUM_ENTRY(SetAbortProc)
  76. APIHOOK_ENUM_ENTRY(StartPage)
  77. APIHOOK_ENUM_ENTRY(DeviceCapabilitiesA)
  78. APIHOOK_ENUM_ENTRY(AddPrinterConnectionA)
  79. APIHOOK_ENUM_ENTRY(DeletePrinterConnectionA)
  80. APIHOOK_ENUM_ENTRY(PrintDlgA)
  81. APIHOOK_ENUM_END
  82. typedef int (WINAPI *_pfn_SetAbortProc)( HDC hdc, ABORTPROC lpAbortProc );
  83. LPSTR g_pszFullPrinterName = NULL;
  84. LPSTR g_pszPartialPrinterName = NULL;
  85. CRITICAL_SECTION g_critSec;
  86. BOOL g_bWin2k = FALSE;
  87. /*++
  88. These functions munge data from a Level 2 Information structure
  89. into a Level 5 information structure.
  90. --*/
  91. BOOL
  92. MungeInfo2TOInfo5_A(
  93. PRINTER_INFO_2A* pInfo2,
  94. DWORD cbBuf,
  95. DWORD dwInfo2Returned,
  96. PRINTER_INFO_5A* pInfo5,
  97. LPDWORD pcbNeeded,
  98. LPDWORD pcbReturned)
  99. {
  100. DWORD dwStringBufferSize = 0;
  101. LPSTR lpStringBuffer = NULL;
  102. // First calculate buffer size needed
  103. for (DWORD i = 0; i < dwInfo2Returned; i++)
  104. {
  105. if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
  106. !(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
  107. pInfo2[i].pServerName != NULL &&
  108. pInfo2[i].pShareName != NULL)
  109. {
  110. if (pInfo2[i].pServerName)
  111. {
  112. dwStringBufferSize += strlen(pInfo2[i].pServerName) + 1;
  113. }
  114. if (pInfo2[i].pPrinterName)
  115. {
  116. dwStringBufferSize += strlen(pInfo2[i].pPrinterName) + 1;
  117. }
  118. if (pInfo2[i].pShareName)
  119. {
  120. dwStringBufferSize += strlen(pInfo2[i].pShareName) + 1;
  121. }
  122. }
  123. else
  124. {
  125. if (pInfo2[i].pPrinterName)
  126. {
  127. dwStringBufferSize += strlen(pInfo2[i].pPrinterName) + 1;
  128. }
  129. if (pInfo2[i].pPortName)
  130. {
  131. dwStringBufferSize += strlen(pInfo2[i].pPortName) + 1;
  132. }
  133. }
  134. }
  135. // set the buffer size needed
  136. *pcbNeeded = dwInfo2Returned * sizeof(PRINTER_INFO_5A)
  137. + dwStringBufferSize;
  138. // verify that buffer passed in is big enough.
  139. if (cbBuf < *pcbNeeded)
  140. {
  141. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  142. return FALSE;
  143. }
  144. // Allocate the Level 5 information structure
  145. lpStringBuffer = ((LPSTR) pInfo5)
  146. + dwInfo2Returned * sizeof(PRINTER_INFO_5A);
  147. // Munge the Level 2 information into the Level 5 structure
  148. for (i = 0; i < dwInfo2Returned; i++)
  149. {
  150. if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
  151. !(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
  152. pInfo2[i].pServerName != NULL &&
  153. pInfo2[i].pShareName != NULL)
  154. {
  155. // Copy over strings
  156. strcpy( lpStringBuffer, pInfo2[i].pPrinterName );
  157. pInfo5[i].pPrinterName = lpStringBuffer;
  158. lpStringBuffer += strlen(pInfo2[i].pPrinterName) + 1;
  159. strcpy( lpStringBuffer, pInfo2[i].pServerName );
  160. strcat( lpStringBuffer, "\\" );
  161. strcat( lpStringBuffer, pInfo2[i].pShareName );
  162. pInfo5[i].pPortName = lpStringBuffer;
  163. lpStringBuffer += strlen(pInfo2[i].pServerName) + strlen(pInfo2[i].pShareName) + 2;
  164. pInfo5[i].Attributes = pInfo2[i].Attributes;
  165. pInfo5[i].DeviceNotSelectedTimeout = 15000; // Use defaults here
  166. pInfo5[i].TransmissionRetryTimeout = 45000; // Use defaults here
  167. }
  168. else
  169. {
  170. // Copy over strings
  171. strcpy( lpStringBuffer, pInfo2[i].pPrinterName );
  172. pInfo5[i].pPrinterName = lpStringBuffer;
  173. lpStringBuffer += strlen(pInfo2[i].pPrinterName) + 1;
  174. strcpy( lpStringBuffer, pInfo2[i].pPortName );
  175. pInfo5[i].pPortName = lpStringBuffer;
  176. lpStringBuffer += strlen(pInfo2[i].pPortName) + 1;
  177. pInfo5[i].Attributes = pInfo2[i].Attributes;
  178. pInfo5[i].DeviceNotSelectedTimeout = 15000; // Use defaults here
  179. pInfo5[i].TransmissionRetryTimeout = 45000; // Use defaults here
  180. }
  181. }
  182. // Set the number of structures munged
  183. *pcbReturned = dwInfo2Returned;
  184. return TRUE;
  185. }
  186. /*++
  187. Our Callback routine for SetAbortProc, this routine
  188. verifies that the stack is correct.
  189. --*/
  190. DWORD g_dwGuardNum = 0xABCD8765;
  191. DWORD g_dwFailed = 0;
  192. BOOL CALLBACK
  193. AbortProcHook(
  194. ABORTPROC pfnOld, // address of old ABORTPROC
  195. HDC hdc, // handle to DC
  196. int iError // error value
  197. )
  198. {
  199. DWORD dwRet= 0;
  200. // Flag to track whether the stack was corrected.
  201. g_dwFailed = 0;
  202. // Push a Guard number on the stack, call their
  203. // abort procedure, then pop the stack till we
  204. // find our guard number
  205. __asm
  206. {
  207. push ebx
  208. push ecx
  209. push g_dwGuardNum
  210. push iError
  211. push hdc
  212. call pfnOld ; make call to their abort proc
  213. mov ecx,16
  214. loc1:
  215. dec ecx
  216. pop ebx
  217. cmp ebx, g_dwGuardNum
  218. jne loc1
  219. cmp ecx, 15
  220. jz loc2
  221. mov g_dwFailed, 1
  222. loc2:
  223. pop ecx
  224. pop ebx
  225. mov dwRet, eax
  226. }
  227. if (g_dwFailed)
  228. {
  229. LOGN( eDbgLevelError, "[AbortProcHook] Fixing incorrect calling convention for AbortProc");
  230. }
  231. return (BOOL) dwRet;
  232. }
  233. /*++
  234. This stub function looks up the device name if pDeviceName is NULL
  235. --*/
  236. LONG
  237. APIHOOK(DocumentPropertiesA)(
  238. HWND hWnd,
  239. HANDLE hPrinter,
  240. LPSTR pDeviceName,
  241. PDEVMODEA pDevModeOutput,
  242. PDEVMODEA pDevModeInput,
  243. DWORD fMode
  244. )
  245. {
  246. LONG lRet = -1;
  247. PRINTER_INFO_2A *pPrinterInfo2A = NULL;
  248. // if they didn't supply a device name, we need to supply it.
  249. if (!pDeviceName) {
  250. LOGN( eDbgLevelError, "[DocumentPropertiesW] App passed NULL for pDeviceName.");
  251. if (hPrinter) {
  252. DWORD dwSizeNeeded = 0;
  253. DWORD dwSizeUsed = 0;
  254. // get the size
  255. GetPrinterA(hPrinter, 2, NULL, 0, &dwSizeNeeded);
  256. if (dwSizeNeeded != 0) {
  257. // allocate memory for the info
  258. pPrinterInfo2A = (PRINTER_INFO_2A*) malloc(dwSizeNeeded);
  259. if (pPrinterInfo2A) {
  260. // get the info
  261. if (GetPrinterA(hPrinter, 2, (LPBYTE)pPrinterInfo2A, dwSizeNeeded, &dwSizeUsed)) {
  262. pDeviceName = pPrinterInfo2A->pPrinterName;
  263. }
  264. }
  265. }
  266. }
  267. }
  268. if (!pDeviceName) {
  269. DPFN( eDbgLevelError, "[DocumentPropertiesA] Unable to gather correct pDeviceName."
  270. "Problem not fixed.\n");
  271. }
  272. lRet = ORIGINAL_API(DocumentPropertiesA)(
  273. hWnd,
  274. hPrinter,
  275. pDeviceName,
  276. pDevModeOutput,
  277. pDevModeInput,
  278. fMode
  279. );
  280. if (pPrinterInfo2A) {
  281. free(pPrinterInfo2A);
  282. }
  283. return lRet;
  284. }
  285. /*++
  286. These functions handle the case of EnumPrinters being called with the
  287. PRINTER_ENUM_DEFAULT flag.
  288. --*/
  289. BOOL
  290. EnumDefaultPrinterA(
  291. PRINTER_INFO_2A* pInfo2,
  292. LPBYTE pPrinterEnum,
  293. DWORD cbBuf,
  294. DWORD Level,
  295. PRINTER_INFO_5A* pInfo5,
  296. LPDWORD pcbNeeded,
  297. LPDWORD pcbReturned
  298. )
  299. {
  300. LPSTR pszName = NULL;
  301. DWORD dwSize = 0;
  302. HANDLE hPrinter = NULL;
  303. BOOL bRet= FALSE;
  304. DWORD dwInfo2Needed = 0;
  305. DWORD dwDummy;
  306. BOOL bDefaultFail = TRUE;
  307. *pcbNeeded = 0;
  308. *pcbReturned = 0;
  309. // get the default printer name
  310. if (GetDefaultPrinterA(NULL, &dwSize) < 1)
  311. {
  312. // Now that we have the right size, allocate a buffer
  313. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  314. {
  315. pszName = (LPSTR) ShimMalloc( dwSize );
  316. if (pszName)
  317. {
  318. // Now get the default printer with the right buffer size.
  319. if (GetDefaultPrinterA( pszName, &dwSize ) > 0 )
  320. {
  321. if ( OpenPrinterA( pszName, &hPrinter, NULL ) )
  322. {
  323. bDefaultFail = FALSE;
  324. }
  325. }
  326. ShimFree(pszName);
  327. }
  328. }
  329. }
  330. if ( bDefaultFail )
  331. {
  332. SetLastError(ERROR_INVALID_NAME);
  333. return FALSE;
  334. }
  335. // Printer Level 5 is not really supported on win2k.
  336. // We'll call Level 2 and munge the data into a level 5 structure.
  337. if ( g_bWin2k &&
  338. Level == 5 &&
  339. pcbNeeded != NULL &&
  340. pcbReturned != NULL)
  341. {
  342. LOGN( eDbgLevelError, "[EnumPrintersA] EnumPrintersA called with Level 5 set."
  343. " Fixing up Level 5 information.");
  344. // get the size needed for the info2 data
  345. if (GetPrinterA(hPrinter, 2, NULL, 0, &dwInfo2Needed) == 0 &&
  346. GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  347. {
  348. pInfo2 = (PRINTER_INFO_2A *) ShimMalloc( dwInfo2Needed );
  349. // get the info2 data and munge into level 5 structure
  350. if (pInfo2 &&
  351. GetPrinterA(hPrinter, 2, (LPBYTE)pInfo2, dwInfo2Needed, &dwDummy))
  352. {
  353. bRet= MungeInfo2TOInfo5_A( pInfo2, cbBuf, 1, pInfo5, pcbNeeded, pcbReturned);
  354. }
  355. if (pInfo2)
  356. ShimFree(pInfo2);
  357. }
  358. }
  359. // Not win2k or not Level 5 so just get info
  360. else
  361. {
  362. *pcbReturned = 1;
  363. bRet = GetPrinterA(hPrinter, Level, pPrinterEnum, cbBuf, pcbNeeded);
  364. }
  365. // Close the printer
  366. ClosePrinter(hPrinter);
  367. return bRet;
  368. }
  369. /*++
  370. These stub functions check for PRINTER_ENUM_DEFAULT, PRINTER_ENUM_LOCAL
  371. and Level 5 information structures.
  372. --*/
  373. BOOL
  374. APIHOOK(EnumPrintersA)(
  375. DWORD Flags,
  376. LPSTR Name,
  377. DWORD Level,
  378. LPBYTE pPrinterEnum,
  379. DWORD cbBuf,
  380. LPDWORD pcbNeeded,
  381. LPDWORD pcbReturned
  382. )
  383. {
  384. BOOL bRet = FALSE;
  385. DWORD dwInfo2Needed = 0;
  386. DWORD dwInfo2Returned = 0;
  387. DWORD dwDummy;
  388. PRINTER_INFO_2A* pInfo2 = NULL;
  389. PRINTER_INFO_5A* pInfo5 = (PRINTER_INFO_5A *) pPrinterEnum;
  390. // Win2k doesn't handle DEFAULT case like win98 did, so we get
  391. // to do it for them.
  392. if (Flags == PRINTER_ENUM_DEFAULT )
  393. {
  394. LOGN( eDbgLevelError, "[EnumPrintersA] Called with PRINTER_ENUM_DEFAULT flag."
  395. " Providing Default printer.");
  396. bRet = EnumDefaultPrinterA(
  397. pInfo2,
  398. pPrinterEnum,
  399. cbBuf,
  400. Level,
  401. pInfo5,
  402. pcbNeeded,
  403. pcbReturned);
  404. return bRet;
  405. }
  406. // For LOCAL also add in CONNECTIONS
  407. if (Flags == PRINTER_ENUM_LOCAL)
  408. {
  409. LOGN( eDbgLevelInfo, "[EnumPrintersA] Called only for "
  410. "PRINTER_ENUM_LOCAL. Adding PRINTER_ENUM_CONNECTIONS\n");
  411. Flags = (PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL);
  412. }
  413. // Printer Level 5 is not really supported on win2k.
  414. // We'll call Level 2 and munge the data into a level 5 structure.
  415. if ( g_bWin2k &&
  416. Level == 5 &&
  417. pcbNeeded != NULL &&
  418. pcbReturned != NULL)
  419. {
  420. // get the size needed for the info2 data
  421. ORIGINAL_API(EnumPrintersA)(Flags,
  422. Name,
  423. 2,
  424. NULL,
  425. 0,
  426. &dwInfo2Needed,
  427. &dwInfo2Returned);
  428. if (dwInfo2Needed > 0)
  429. {
  430. // Printers found, get the info2 data and convert it to info5
  431. pInfo2 = (PRINTER_INFO_2A *) ShimMalloc( dwInfo2Needed );
  432. if (pInfo2 &&
  433. ORIGINAL_API(EnumPrintersA)(Flags,
  434. Name,
  435. 2,
  436. (LPBYTE) pInfo2,
  437. dwInfo2Needed,
  438. &dwDummy,
  439. &dwInfo2Returned) )
  440. {
  441. bRet = MungeInfo2TOInfo5_A( pInfo2,
  442. cbBuf,
  443. dwInfo2Returned,
  444. pInfo5,
  445. pcbNeeded,
  446. pcbReturned);
  447. }
  448. if(pInfo2)
  449. ShimFree( pInfo2 );
  450. }
  451. }
  452. else
  453. {
  454. bRet = ORIGINAL_API(EnumPrintersA)(Flags,
  455. Name,
  456. Level,
  457. pPrinterEnum,
  458. cbBuf,
  459. pcbNeeded,
  460. pcbReturned);
  461. }
  462. // For level 2 and level 5 there are some win95 only attributes
  463. // that need to be emulated.
  464. if ( (Level == 2 || Level == 5) &&
  465. bRet &&
  466. pPrinterEnum != NULL )
  467. {
  468. DWORD dwSize;
  469. GetDefaultPrinterA(NULL, &dwSize);
  470. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  471. {
  472. // Now that we have the right size, allocate a buffer
  473. LPSTR pszName = (LPSTR) ShimMalloc( dwSize );
  474. if (pszName)
  475. {
  476. // Now get the default printer with the right buffer size.
  477. if (GetDefaultPrinterA( pszName, &dwSize ) > 0 )
  478. {
  479. if (Level == 2)
  480. {
  481. if (strcmp( pszName, ((PRINTER_INFO_2A*)pPrinterEnum)->pPrinterName) == 0)
  482. {
  483. ((PRINTER_INFO_2A*)pPrinterEnum)->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
  484. }
  485. }
  486. else
  487. {
  488. if (strcmp( pszName, ((PRINTER_INFO_5A*)pPrinterEnum)->pPrinterName) == 0)
  489. ((PRINTER_INFO_5A*)pPrinterEnum)->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
  490. }
  491. }
  492. ShimFree(pszName);
  493. }
  494. }
  495. }
  496. return bRet;
  497. }
  498. /*++
  499. These stub functions substitute the default printer if the pPrinterName is NULL,
  500. also they set pDefault to NULL to emulate win9x behavior
  501. --*/
  502. BOOL
  503. APIHOOK(OpenPrinterA)(
  504. LPSTR pPrinterName,
  505. LPHANDLE phPrinter,
  506. LPPRINTER_DEFAULTSA pDefault
  507. )
  508. {
  509. LPSTR pszName = NULL;
  510. DWORD dwSize;
  511. BOOL bDefaultFail = TRUE;
  512. BOOL bRet;
  513. if (!pPrinterName)
  514. {
  515. LOGN( eDbgLevelError, "[OpenPrinterA] App passed NULL for pPrinterName, using default printer.");
  516. // get the default printer name
  517. if (GetDefaultPrinterA(NULL, &dwSize) < 1)
  518. {
  519. // Now that we have the right size, allocate a buffer
  520. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  521. {
  522. pszName = (LPSTR) ShimMalloc( dwSize );
  523. if (pszName)
  524. {
  525. // Now get the default printer with the right buffer size.
  526. if (GetDefaultPrinterA( pszName, &dwSize ) > 0 )
  527. {
  528. pPrinterName = pszName;
  529. bDefaultFail = FALSE;
  530. }
  531. }
  532. }
  533. }
  534. if ( bDefaultFail )
  535. {
  536. DPFN( eDbgLevelError, "[OpenPrinterA] Unable to gather default pPrinterName.\n");
  537. }
  538. }
  539. else
  540. {
  541. if ( pPrinterName && g_pszPartialPrinterName && g_pszFullPrinterName &&
  542. 0 == strcmp(g_pszPartialPrinterName, pPrinterName) )
  543. {
  544. pPrinterName = g_pszFullPrinterName;
  545. }
  546. }
  547. if ( pPrinterName )
  548. {
  549. DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: pPrinterName: %s\n", pPrinterName);
  550. }
  551. DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: pDefault: %x\n", pDefault);
  552. DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: overriding pDefault with NULL value\n");
  553. bRet = ORIGINAL_API(OpenPrinterA)(
  554. pPrinterName,
  555. phPrinter,
  556. NULL);
  557. ShimFree(pszName);
  558. return bRet;
  559. }
  560. /*++
  561. This stub function checks to see if the app is asking for the default printer
  562. string. If it is it will be returned as follows:
  563. PrinterName, Driver, Port
  564. On Win9x, if the printer is a network printer, Port is \\server\share and
  565. local printers are Port: (ex. LPT1:).
  566. On Win2k, if the printer is a network printer, Port is NeXX: and local printers
  567. are Port: .
  568. We must query EnumPrinters in order to emulate Win9x. Note: If the printer
  569. name is to large for the input buffer we trim it and keep track of the full
  570. name for later us in other printer APIs.
  571. --*/
  572. DWORD
  573. APIHOOK(GetProfileStringA)(
  574. LPCSTR lpAppName, // section name
  575. LPCSTR lpKeyName, // key name
  576. LPCSTR lpDefault, // default string
  577. LPSTR lpReturnedString, // destination buffer
  578. DWORD nSize // size of destination buffer
  579. )
  580. {
  581. if ( lpAppName &&
  582. lpKeyName &&
  583. 0 == _stricmp(lpAppName, "windows") &&
  584. 0 == _stricmp(lpKeyName, "device" ) )
  585. {
  586. LPSTR pszProfileString = NULL;
  587. DWORD dwSize = 0;
  588. BOOL bDefaultFail = TRUE;
  589. DWORD dwProfileStringLen;
  590. LPSTR pszPrinterDriver = NULL;
  591. LPSTR pszPrinterPort = NULL;
  592. LPSTR pszName = NULL;
  593. DWORD dwPrinterNameSize = 0;
  594. DWORD dwDelimiterCount = 0;
  595. LPSTR pszPtr;
  596. // get the default printer name
  597. if (GetDefaultPrinterA(NULL, &dwSize) <1)
  598. {
  599. // Now that we have the right size, allocate a buffer
  600. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  601. {
  602. pszName = (LPSTR) ShimMalloc( dwSize );
  603. if (pszName)
  604. {
  605. // Now get the default printer with the right buffer size.
  606. if (GetDefaultPrinterA( pszName, &dwSize ) > 0 )
  607. {
  608. bDefaultFail = FALSE;
  609. }
  610. }
  611. }
  612. }
  613. ShimFree(pszName);
  614. if ( bDefaultFail )
  615. {
  616. // Unable to get the default printername. Fall back to the max
  617. // size printer name that the UI allows plus a pad for the driver
  618. // and port names.
  619. dwSize = MAX_PRINTER_NAME + MAX_DRIVERPORT_NAME;
  620. }
  621. else
  622. {
  623. // We have the printer name size add in a pad for the driver and
  624. // port names.
  625. dwSize += MAX_DRIVERPORT_NAME;
  626. }
  627. // Allocate the string
  628. pszProfileString = (LPSTR) ShimMalloc(dwSize);
  629. // Retrieve the profile string
  630. dwProfileStringLen = ORIGINAL_API(GetProfileStringA)( lpAppName,
  631. lpKeyName,
  632. lpDefault,
  633. pszProfileString,
  634. dwSize );
  635. if (dwProfileStringLen != NULL)
  636. {
  637. // Find the first and second commas.
  638. pszPtr = pszProfileString;
  639. while (*pszPtr)
  640. {
  641. if (*pszPtr == ',')
  642. {
  643. dwDelimiterCount++;
  644. if (dwDelimiterCount == 1 && *(pszPtr+1))
  645. {
  646. pszPrinterDriver= pszPtr;
  647. }
  648. else if (dwDelimiterCount == 2 && *(pszPtr+1))
  649. {
  650. pszPrinterPort = pszPtr + 1;
  651. break;
  652. }
  653. }
  654. pszPtr++;
  655. }
  656. DPFN( eDbgLevelError, "[GetProfileStringA] Profilestring <%s>\n Driver <%s>\n Port <%s>", pszProfileString, pszPrinterDriver,pszPrinterPort);
  657. // Check to see if this is a network printer
  658. if ( strstr(pszProfileString, ",Ne") )
  659. {
  660. PRINTER_INFO_2A* pInfo2 = NULL;
  661. DWORD dwInfo2Needed = 0;
  662. DWORD dwInfo2Returned = 0;
  663. DWORD dwDummy = 0;
  664. DWORD i;
  665. BOOL bEnumPrintersSuccess;
  666. BOOL bDefaultFound = FALSE;
  667. // Get the size of the Level 2 structure needed.
  668. bEnumPrintersSuccess = EnumPrintersA( PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL,
  669. NULL,
  670. 2,
  671. NULL,
  672. 0,
  673. &dwInfo2Needed,
  674. &dwInfo2Returned );
  675. // Get the Level 2 Info structure for the printer.
  676. pInfo2 = (PRINTER_INFO_2A *) ShimMalloc( dwInfo2Needed );
  677. bEnumPrintersSuccess = EnumPrintersA( PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL,
  678. NULL,
  679. 2,
  680. (LPBYTE) pInfo2,
  681. dwInfo2Needed,
  682. &dwDummy,
  683. &dwInfo2Returned );
  684. if (bEnumPrintersSuccess)
  685. {
  686. // Search for default printer in PRINTER_INFO_2 array
  687. for (i = 0; i < dwInfo2Returned; i++)
  688. {
  689. if (0 != strstr(pszProfileString, pInfo2[i].pPrinterName))
  690. {
  691. bDefaultFound = TRUE;
  692. break;
  693. }
  694. }
  695. if ( bDefaultFound )
  696. {
  697. // Double check that this is a network printer and does not have
  698. // local attribute
  699. if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
  700. !(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
  701. pInfo2[i].pServerName != NULL &&
  702. pInfo2[i].pShareName != NULL)
  703. {
  704. // Modify the Port to conform with Win9x standards.
  705. LOGN( eDbgLevelInfo, "[GetProfileStringA] Altering default printer string returned by GetProfileStringA.\n");
  706. DPFN( eDbgLevelInfo, "[GetProfileStringA] Old: %s\n", pszProfileString);
  707. strcpy(pszPrinterPort, pInfo2[i].pServerName);
  708. strcat(pszPrinterPort, "\\");
  709. strcat(pszPrinterPort, pInfo2[i].pShareName);
  710. }
  711. else
  712. {
  713. // Just copy in the port
  714. strcpy( pszPrinterPort, pInfo2[i].pPortName );
  715. }
  716. dwProfileStringLen = strlen(pszProfileString);
  717. }
  718. }
  719. ShimFree(pInfo2);
  720. }
  721. // If the size they give is big enough, then return.
  722. dwPrinterNameSize = pszPrinterDriver - pszProfileString;
  723. if ( dwProfileStringLen < nSize )
  724. {
  725. strcpy( lpReturnedString, pszProfileString );
  726. DPFN( eDbgLevelInfo, "[GetProfileStringA] Default Printer: %s Size: %d\n",
  727. lpReturnedString, strlen(lpReturnedString));
  728. return strlen(lpReturnedString);
  729. }
  730. // Modify the printer name and keep a global of the original if the printer
  731. // name causes the profile string output buffer to overflow.
  732. // If the size we need to reduce it by is greater than the size of
  733. // the printer name we're screwed, pass through.
  734. dwPrinterNameSize -= (dwProfileStringLen - nSize);
  735. if ( dwPrinterNameSize > 0 )
  736. {
  737. DPFN( eDbgLevelInfo, "[GetProfileStringA] Reducing printer name by %d characters.\n",
  738. dwProfileStringLen - nSize );
  739. LOGN( eDbgLevelInfo, "[GetProfileStringA] Reducing printer name by %d characters.\n",
  740. dwProfileStringLen - nSize );
  741. // copy in the truncated printer name.
  742. strncpy( lpReturnedString, pszProfileString, dwPrinterNameSize );
  743. strcat( lpReturnedString, pszPrinterDriver );
  744. EnterCriticalSection(&g_critSec);
  745. // Release any previous allocations.
  746. if (g_pszPartialPrinterName && g_pszFullPrinterName)
  747. {
  748. ShimFree(g_pszPartialPrinterName);
  749. ShimFree(g_pszFullPrinterName);
  750. }
  751. // save the partial and full printer names for later use.
  752. g_pszPartialPrinterName = (LPSTR)ShimMalloc(dwPrinterNameSize+1);
  753. strncpy( g_pszPartialPrinterName, pszProfileString, dwPrinterNameSize );
  754. g_pszPartialPrinterName[dwPrinterNameSize] = '\0';
  755. g_pszFullPrinterName = (LPSTR)ShimMalloc(pszPrinterDriver-pszProfileString+1);
  756. strncpy( g_pszFullPrinterName, pszProfileString, (pszPrinterDriver - pszProfileString) );
  757. g_pszFullPrinterName[pszPrinterDriver - pszProfileString] = '\0';
  758. LeaveCriticalSection(&g_critSec);
  759. DPFN( eDbgLevelInfo, "[GetProfileStringA] Partial: %s Full: %s\n",
  760. g_pszPartialPrinterName, g_pszFullPrinterName );
  761. DPFN( eDbgLevelInfo, "[GetProfileStringA] New: %s Size: %d\n",
  762. lpReturnedString, strlen(lpReturnedString));
  763. // return the modified string size.
  764. return strlen(lpReturnedString);
  765. }
  766. }
  767. }
  768. // Either an error occurred or its not asking for default printer.
  769. // pass through.
  770. return ORIGINAL_API(GetProfileStringA)( lpAppName,
  771. lpKeyName,
  772. lpDefault,
  773. lpReturnedString,
  774. nSize);
  775. }
  776. /*++
  777. This stub function pulls the device name from the DEVMODE if pszDevice is NULL
  778. and the DC is not for DISPLAY
  779. --*/
  780. HDC
  781. APIHOOK(CreateDCA)(
  782. LPCSTR pszDriver,
  783. LPCSTR pszDevice,
  784. LPCSTR pszPort,
  785. CONST DEVMODEA *pdm
  786. )
  787. {
  788. // if they've used a NULL device, but included a printer devmode,
  789. // fill in the device name from the printer devmode
  790. if (!pszDevice && pdm && (!pszDriver || _stricmp(pszDriver, "DISPLAY") != 0)) {
  791. LOGN( eDbgLevelError, "[CreateDCA] App passed NULL for pszDevice. Fixing.");
  792. pszDevice = (LPCSTR)pdm->dmDeviceName;
  793. }
  794. return ORIGINAL_API(CreateDCA)(
  795. pszDriver,
  796. pszDevice,
  797. pszPort,
  798. pdm
  799. );
  800. }
  801. /*++
  802. This stub function verifies that ResetDCA hasn't been handed an
  803. uninitialized InitData.
  804. --*/
  805. HDC
  806. APIHOOK(ResetDCA)(
  807. HDC hdc,
  808. CONST DEVMODEA *lpInitData
  809. )
  810. {
  811. // Sanity checks to make sure we aren't getting garbage
  812. // or bad values.
  813. if ( lpInitData &&
  814. lpInitData->dmSize > sizeof( DEVMODEA ) ||
  815. ( lpInitData->dmSpecVersion != 0x401 &&
  816. lpInitData->dmSpecVersion != 0x400 &&
  817. lpInitData->dmSpecVersion != 0x320 ) )
  818. {
  819. LOGN( eDbgLevelError, "[ResetDCA] App passed bad DEVMODE structure, nulling.");
  820. return ORIGINAL_API(ResetDCA)( hdc, NULL );
  821. }
  822. return ORIGINAL_API(ResetDCA)( hdc, lpInitData );
  823. }
  824. /*++
  825. These stub functions verify that SetPrinter has a valid handle
  826. before proceeding.
  827. --*/
  828. BOOL
  829. APIHOOK(SetPrinterA)(
  830. HANDLE hPrinter, // handle to printer object
  831. DWORD Level, // information level
  832. LPBYTE pPrinter, // printer data buffer
  833. DWORD Command // printer-state command
  834. )
  835. {
  836. BOOL bRet;
  837. if (hPrinter == NULL)
  838. {
  839. LOGN( eDbgLevelError, "[SetPrinterA] Called with null handle.");
  840. if (pPrinter == NULL)
  841. LOGN( eDbgLevelError, "[SetPrinterA] Called with null printer data buffer.");
  842. return FALSE;
  843. }
  844. else if (pPrinter == NULL)
  845. {
  846. LOGN( eDbgLevelError, "[SetPrinterA] Called with null printer data buffer.");
  847. return FALSE;
  848. }
  849. bRet= ORIGINAL_API(SetPrinterA)(
  850. hPrinter,
  851. Level,
  852. pPrinter,
  853. Command);
  854. DPFN( eDbgLevelSpew, "[SetPrinterA] Level= %d Command= %d Ret= %d\n",
  855. Level, Command, bRet );
  856. return bRet;
  857. }
  858. /*++
  859. This routine hooks the SetAbortProc and replaces their
  860. callback with ours.
  861. --*/
  862. int
  863. APIHOOK(SetAbortProc)(
  864. HDC hdc, // handle to DC
  865. ABORTPROC lpAbortProc // abort function
  866. )
  867. {
  868. lpAbortProc = (ABORTPROC) HookCallback(lpAbortProc, AbortProcHook);
  869. return ORIGINAL_API(SetAbortProc)( hdc, lpAbortProc );
  870. }
  871. /*++
  872. When apps start printing, they set a viewport
  873. on the printDC. They then call StartPage which has a different behaviour
  874. on 9x and WinNT. On 9x, a next call to StartPage resets the DC attributes
  875. to the default values.However on NT, the next call to StartPage does not
  876. reset the DC attributes.
  877. So, on 9x all subsequent output setup and drawing calls are carried
  878. out with a (0,0) viewport but on NT the viewport is leftover from its
  879. initial call. Since some apps(eg. Quicken 2000 and 2001) expect the API
  880. setting the (0,0) viewport,the result will be that the text and the
  881. lines are clipped on the left and top of the page.
  882. Here we hook StartPage and call SetViewportOrgEx(hdc, 0, 0, NULL) to
  883. set the viewport to (0,0) on every call to StartPage to emulate
  884. the 9x behaviour.
  885. --*/
  886. BOOL
  887. APIHOOK(StartPage)(
  888. HDC hdc
  889. )
  890. {
  891. if (SetViewportOrgEx(hdc, 0, 0, NULL))
  892. {
  893. // We have now made the device point(viewport) map to (0, 0).
  894. LOGN( eDbgLevelInfo, "[StartPage] Setting the device point map to (0,0).");
  895. }
  896. else
  897. {
  898. LOGN( eDbgLevelError, "[StartPage] Unable to set device point map to (0,0)."
  899. "Failed in a call to SetViewportOrgEx");
  900. }
  901. return ORIGINAL_API(StartPage)(hdc);
  902. }
  903. /*++
  904. This stub function verifies that DeviceCapabilities is using a correct
  905. printer name.
  906. --*/
  907. DWORD
  908. APIHOOK(DeviceCapabilitiesA)(
  909. LPCSTR pDevice,
  910. LPCSTR pPort,
  911. WORD fwCapability,
  912. LPSTR pOutput,
  913. CONST DEVMODE *pDevMode
  914. )
  915. {
  916. DWORD dwRet;
  917. if ( pDevice && g_pszPartialPrinterName && g_pszFullPrinterName &&
  918. 0 == strcmp(g_pszPartialPrinterName, pDevice) )
  919. {
  920. pDevice = g_pszFullPrinterName;
  921. }
  922. dwRet= ORIGINAL_API(DeviceCapabilitiesA)( pDevice,
  923. pPort,
  924. fwCapability,
  925. pOutput,
  926. pDevMode );
  927. if ( pDevice && pPort )
  928. {
  929. DPFN( eDbgLevelSpew, "[DeviceCapabilitiesA] pDevice= %s pPort= %s fwC= %d Out= %x RC= %d\n",
  930. pDevice, pPort, fwCapability, pOutput, dwRet );
  931. }
  932. else
  933. {
  934. DPFN( eDbgLevelSpew, "[DeviceCapabilitiesA] fwC= %d Out= %x RC= %d\n",
  935. fwCapability, pOutput, dwRet );
  936. }
  937. return dwRet;
  938. }
  939. /*++
  940. This stub function verifies that AddPrinterConnection is using a correct
  941. printer name.
  942. --*/
  943. BOOL
  944. APIHOOK(AddPrinterConnectionA)(
  945. LPSTR pName
  946. )
  947. {
  948. if ( pName && g_pszPartialPrinterName && g_pszFullPrinterName &&
  949. 0 == strcmp(g_pszPartialPrinterName, pName) )
  950. {
  951. pName = g_pszFullPrinterName;
  952. }
  953. return ORIGINAL_API(AddPrinterConnectionA)( pName );
  954. }
  955. /*++
  956. This stub function verifies that DeletePrinterConnection is using a correct
  957. printer name.
  958. --*/
  959. BOOL
  960. APIHOOK(DeletePrinterConnectionA)(
  961. LPSTR pName
  962. )
  963. {
  964. if ( pName && g_pszPartialPrinterName && g_pszFullPrinterName &&
  965. 0 == strcmp(g_pszPartialPrinterName, pName) )
  966. {
  967. pName = g_pszFullPrinterName;
  968. }
  969. return ORIGINAL_API(DeletePrinterConnectionA)( pName );
  970. }
  971. /*++
  972. This stub function verifies that PrintDlgA is using a correct
  973. nFromPage and nToPage.
  974. --*/
  975. BOOL
  976. APIHOOK(PrintDlgA)(
  977. LPPRINTDLG lppd
  978. )
  979. {
  980. // check nFromPage and nToPage for legal values.
  981. if ( lppd )
  982. {
  983. DPFN( eDbgLevelSpew, "[PrintDlgA] nFromPage = %d nToPage = %d",
  984. lppd->nFromPage, lppd->nToPage);
  985. if ( lppd->nFromPage == 0xffff )
  986. {
  987. lppd->nFromPage = 0;
  988. DPFN( eDbgLevelInfo, "[PrintDlgA] Setting nFromPage to 0." );
  989. }
  990. if ( lppd->nToPage == 0xffff)
  991. {
  992. lppd->nToPage = lppd->nFromPage;
  993. DPFN( eDbgLevelInfo, "[PrintDlgA] Setting nToPage to %d.", lppd->nFromPage );
  994. }
  995. }
  996. return ORIGINAL_API(PrintDlgA)(lppd);
  997. }
  998. /*++
  999. Register hooked functions
  1000. --*/
  1001. BOOL
  1002. NOTIFY_FUNCTION(
  1003. DWORD fdwReason)
  1004. {
  1005. if (fdwReason == DLL_PROCESS_ATTACH)
  1006. {
  1007. OSVERSIONINFOEX osvi;
  1008. BOOL bOsVersionInfoEx;
  1009. InitializeCriticalSection(&g_critSec);
  1010. // Check to see if we are under win2k
  1011. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  1012. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  1013. bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
  1014. if(bOsVersionInfoEx)
  1015. {
  1016. if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT &&
  1017. osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
  1018. {
  1019. g_bWin2k = TRUE;
  1020. }
  1021. }
  1022. }
  1023. return TRUE;
  1024. }
  1025. HOOK_BEGIN
  1026. CALL_NOTIFY_FUNCTION
  1027. APIHOOK_ENTRY(WINSPOOL.DRV, DocumentPropertiesA);
  1028. APIHOOK_ENTRY(WINSPOOL.DRV, OpenPrinterA);
  1029. APIHOOK_ENTRY(WINSPOOL.DRV, SetPrinterA);
  1030. APIHOOK_ENTRY(WINSPOOL.DRV, EnumPrintersA);
  1031. APIHOOK_ENTRY(WINSPOOL.DRV, OpenPrinterA);
  1032. APIHOOK_ENTRY(WINSPOOL.DRV, DeviceCapabilitiesA);
  1033. APIHOOK_ENTRY(WINSPOOL.DRV, AddPrinterConnectionA);
  1034. APIHOOK_ENTRY(WINSPOOL.DRV, DeletePrinterConnectionA);
  1035. APIHOOK_ENTRY(COMDLG32.DLL, PrintDlgA);
  1036. APIHOOK_ENTRY(KERNEL32.DLL,GetProfileStringA);
  1037. APIHOOK_ENTRY(GDI32.DLL, CreateDCA);
  1038. APIHOOK_ENTRY(GDI32.DLL, ResetDCA);
  1039. APIHOOK_ENTRY(GDI32.DLL, SetAbortProc);
  1040. APIHOOK_ENTRY(GDI32.DLL, StartPage);
  1041. HOOK_END
  1042. IMPLEMENT_SHIM_END