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.

1343 lines
41 KiB

  1. /*++
  2. Copyright (c) 2000-2002 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. 02/20/2002 mnikkel Major cleanup to remove buffer overrun possibilities.
  59. Added check for No printer in GetProfileString routine
  60. --*/
  61. #include "precomp.h"
  62. #include <commdlg.h>
  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. CString g_csFullPrinterName("");
  84. CString g_csPartialPrinterName("");
  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. // Sanity check, should not occur.
  103. if (pInfo2 == NULL || pInfo5 == NULL)
  104. {
  105. return FALSE;
  106. }
  107. // First calculate buffer size needed
  108. for (DWORD i = 0; i < dwInfo2Returned; i++)
  109. {
  110. if (pInfo2[i].pPrinterName)
  111. {
  112. dwStringBufferSize += strlen(pInfo2[i].pPrinterName);
  113. }
  114. dwStringBufferSize++;
  115. if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
  116. !(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
  117. pInfo2[i].pServerName != NULL &&
  118. pInfo2[i].pShareName != NULL)
  119. {
  120. dwStringBufferSize += strlen(pInfo2[i].pServerName) + 1;
  121. dwStringBufferSize += strlen(pInfo2[i].pShareName) + 1;
  122. }
  123. else
  124. {
  125. if (pInfo2[i].pPortName)
  126. {
  127. dwStringBufferSize += strlen(pInfo2[i].pPortName);
  128. }
  129. dwStringBufferSize++;
  130. }
  131. }
  132. // set the buffer size needed
  133. *pcbNeeded = dwInfo2Returned * sizeof(PRINTER_INFO_5A)
  134. + dwStringBufferSize;
  135. // verify that buffer passed in is big enough.
  136. if (cbBuf < *pcbNeeded)
  137. {
  138. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  139. return FALSE;
  140. }
  141. // Allocate the Level 5 information structure
  142. lpStringBuffer = ((LPSTR) pInfo5)
  143. + dwInfo2Returned * sizeof(PRINTER_INFO_5A);
  144. // Munge the Level 2 information into the Level 5 structure
  145. for (i = 0; i < dwInfo2Returned; i++)
  146. {
  147. // Copy the printername from level 2 to level 5 structure.
  148. if (pInfo2[i].pPrinterName)
  149. {
  150. if (StringCchCopyA( lpStringBuffer, cbBuf, pInfo2[i].pPrinterName) != S_OK)
  151. {
  152. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  153. return FALSE;
  154. }
  155. }
  156. else
  157. {
  158. if (StringCchCopyA( lpStringBuffer, cbBuf, "") != S_OK)
  159. {
  160. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  161. return FALSE;
  162. }
  163. }
  164. pInfo5[i].pPrinterName = lpStringBuffer;
  165. lpStringBuffer += strlen(pInfo2[i].pPrinterName) + 1;
  166. // Copy in attributes to level 5 structure and set defaults
  167. pInfo5[i].Attributes = pInfo2[i].Attributes;
  168. pInfo5[i].DeviceNotSelectedTimeout = 15000; // Use defaults here
  169. pInfo5[i].TransmissionRetryTimeout = 45000; // Use defaults here
  170. // Check for a network printer
  171. if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
  172. !(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
  173. pInfo2[i].pServerName != NULL &&
  174. pInfo2[i].pShareName != NULL)
  175. {
  176. // For network printer create the win98 style port name
  177. if (StringCchCopyA( lpStringBuffer, cbBuf, pInfo2[i].pServerName) != S_OK ||
  178. StringCchCatA( lpStringBuffer, cbBuf, "\\" ) != S_OK ||
  179. StringCchCatA( lpStringBuffer, cbBuf, pInfo2[i].pShareName) != S_OK)
  180. {
  181. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  182. return FALSE;
  183. }
  184. }
  185. else
  186. {
  187. // Not network printer, just copy in port name
  188. if (pInfo2[i].pPortName)
  189. {
  190. if (StringCchCopyA( lpStringBuffer, cbBuf, pInfo2[i].pPortName) != S_OK)
  191. {
  192. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  193. return FALSE;
  194. }
  195. }
  196. else
  197. {
  198. if (StringCchCopyA( lpStringBuffer, cbBuf, "") != S_OK)
  199. {
  200. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  201. return FALSE;
  202. }
  203. }
  204. }
  205. pInfo5[i].pPortName = lpStringBuffer;
  206. lpStringBuffer += strlen(pInfo2[i].pPortName) + 1;
  207. }
  208. // Set the number of structures munged
  209. *pcbReturned = dwInfo2Returned;
  210. return TRUE;
  211. }
  212. /*++
  213. Our Callback routine for SetAbortProc, this routine
  214. verifies that the stack is correct.
  215. --*/
  216. DWORD g_dwGuardNum = 0xABCD8765;
  217. DWORD g_dwFailed = 0;
  218. BOOL CALLBACK
  219. AbortProcHook(
  220. ABORTPROC pfnOld, // address of old ABORTPROC
  221. HDC hdc, // handle to DC
  222. int iError // error value
  223. )
  224. {
  225. DWORD dwRet= 0;
  226. // Flag to track whether the stack was corrected.
  227. g_dwFailed = 0;
  228. // Push a Guard number on the stack, call their
  229. // abort procedure, then pop the stack till we
  230. // find our guard number
  231. __asm
  232. {
  233. push ebx
  234. push ecx
  235. push g_dwGuardNum
  236. push iError
  237. push hdc
  238. call pfnOld ; make call to their abort proc
  239. mov ecx,16
  240. loc1:
  241. dec ecx
  242. pop ebx
  243. cmp ebx, g_dwGuardNum
  244. jne loc1
  245. cmp ecx, 15
  246. jz loc2
  247. mov g_dwFailed, 1
  248. loc2:
  249. pop ecx
  250. pop ebx
  251. mov dwRet, eax
  252. }
  253. if (g_dwFailed)
  254. {
  255. LOGN( eDbgLevelError, "[AbortProcHook] Fixing incorrect calling convention for AbortProc");
  256. }
  257. return (BOOL) dwRet;
  258. }
  259. /*++
  260. This stub function looks up the device name if pDeviceName is NULL
  261. --*/
  262. LONG
  263. APIHOOK(DocumentPropertiesA)(
  264. HWND hWnd,
  265. HANDLE hPrinter,
  266. LPSTR pDeviceName,
  267. PDEVMODEA pDevModeOutput,
  268. PDEVMODEA pDevModeInput,
  269. DWORD fMode
  270. )
  271. {
  272. LONG lRet = -1;
  273. PRINTER_INFO_2A *pPrinterInfo2A = NULL;
  274. // if they didn't supply a device name, we need to supply it.
  275. if (!pDeviceName)
  276. {
  277. LOGN( eDbgLevelError, "[DocumentPropertiesW] App passed NULL for pDeviceName.");
  278. if (hPrinter)
  279. {
  280. DWORD dwSizeNeeded = 0;
  281. DWORD dwSizeUsed = 0;
  282. // get the size
  283. GetPrinterA(hPrinter, 2, NULL, 0, &dwSizeNeeded);
  284. if (dwSizeNeeded != 0)
  285. {
  286. // allocate memory for the info
  287. pPrinterInfo2A = (PRINTER_INFO_2A*) malloc(dwSizeNeeded);
  288. if (pPrinterInfo2A) {
  289. // get the info
  290. if (GetPrinterA(hPrinter, 2, (LPBYTE)pPrinterInfo2A, dwSizeNeeded, &dwSizeUsed))
  291. {
  292. pDeviceName = pPrinterInfo2A->pPrinterName;
  293. }
  294. }
  295. }
  296. }
  297. }
  298. if (!pDeviceName) {
  299. DPFN( eDbgLevelError, "[DocumentPropertiesA] Unable to gather correct pDeviceName."
  300. "Problem not fixed.\n");
  301. }
  302. lRet = ORIGINAL_API(DocumentPropertiesA)(
  303. hWnd,
  304. hPrinter,
  305. pDeviceName,
  306. pDevModeOutput,
  307. pDevModeInput,
  308. fMode
  309. );
  310. if (pPrinterInfo2A) {
  311. free(pPrinterInfo2A);
  312. }
  313. return lRet;
  314. }
  315. /*++
  316. These functions handle the case of EnumPrinters being called with the
  317. PRINTER_ENUM_DEFAULT flag.
  318. --*/
  319. BOOL
  320. EnumDefaultPrinterA(
  321. PRINTER_INFO_2A* pInfo2,
  322. LPBYTE pPrinterEnum,
  323. DWORD cbBuf,
  324. DWORD Level,
  325. PRINTER_INFO_5A* pInfo5,
  326. LPDWORD pcbNeeded,
  327. LPDWORD pcbReturned
  328. )
  329. {
  330. LPSTR pszName = NULL;
  331. DWORD dwSize = 0;
  332. HANDLE hPrinter = NULL;
  333. BOOL bRet= FALSE;
  334. DWORD dwInfo2Needed = 0;
  335. DWORD dwDummy;
  336. BOOL bDefaultFail = TRUE;
  337. *pcbNeeded = 0;
  338. *pcbReturned = 0;
  339. // get the default printer name
  340. if (GetDefaultPrinterA(NULL, &dwSize) < 1)
  341. {
  342. // Now that we have the right size, allocate a buffer
  343. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  344. {
  345. pszName = (LPSTR) malloc(dwSize);
  346. if (pszName)
  347. {
  348. // Now get the default printer with the right buffer size.
  349. if (GetDefaultPrinterA(pszName, &dwSize) > 0)
  350. {
  351. if (OpenPrinterA(pszName, &hPrinter, NULL))
  352. {
  353. bDefaultFail = FALSE;
  354. }
  355. }
  356. free(pszName);
  357. }
  358. }
  359. }
  360. if (bDefaultFail)
  361. {
  362. SetLastError(ERROR_INVALID_NAME);
  363. return FALSE;
  364. }
  365. // Printer Level 5 is not really supported on win2k.
  366. // We'll call Level 2 and munge the data into a level 5 structure.
  367. if ( g_bWin2k &&
  368. Level == 5 &&
  369. pcbNeeded != NULL &&
  370. pcbReturned != NULL)
  371. {
  372. LOGN(eDbgLevelError, "[EnumPrintersA] EnumPrintersA called with Level 5 set."
  373. " Fixing up Level 5 information.");
  374. // get the size needed for the info2 data
  375. if (GetPrinterA(hPrinter, 2, NULL, 0, &dwInfo2Needed) == 0 &&
  376. GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  377. {
  378. pInfo2 = (PRINTER_INFO_2A *) malloc(dwInfo2Needed);
  379. // get the info2 data and munge into level 5 structure
  380. if (pInfo2 &&
  381. GetPrinterA(hPrinter, 2, (LPBYTE)pInfo2, dwInfo2Needed, &dwDummy))
  382. {
  383. bRet= MungeInfo2TOInfo5_A(pInfo2, cbBuf, 1, pInfo5, pcbNeeded, pcbReturned);
  384. }
  385. if (pInfo2)
  386. {
  387. free(pInfo2);
  388. }
  389. }
  390. }
  391. // Not win2k or not Level 5 so just get info
  392. else
  393. {
  394. *pcbReturned = 1;
  395. bRet = GetPrinterA(hPrinter, Level, pPrinterEnum, cbBuf, pcbNeeded);
  396. }
  397. // Close the printer
  398. ClosePrinter(hPrinter);
  399. return bRet;
  400. }
  401. /*++
  402. These stub functions check for PRINTER_ENUM_DEFAULT, PRINTER_ENUM_LOCAL
  403. and Level 5 information structures.
  404. --*/
  405. BOOL
  406. APIHOOK(EnumPrintersA)(
  407. DWORD Flags,
  408. LPSTR Name,
  409. DWORD Level,
  410. LPBYTE pPrinterEnum,
  411. DWORD cbBuf,
  412. LPDWORD pcbNeeded,
  413. LPDWORD pcbReturned
  414. )
  415. {
  416. BOOL bRet = FALSE;
  417. DWORD dwInfo2Needed = 0;
  418. DWORD dwInfo2Returned = 0;
  419. DWORD dwDummy;
  420. PRINTER_INFO_2A* pInfo2 = NULL;
  421. PRINTER_INFO_5A* pInfo5 = (PRINTER_INFO_5A *) pPrinterEnum;
  422. // Win2k doesn't handle DEFAULT case like win98 did, so we get
  423. // to do it for them.
  424. if (Flags == PRINTER_ENUM_DEFAULT )
  425. {
  426. LOGN(eDbgLevelError, "[EnumPrintersA] Called with PRINTER_ENUM_DEFAULT flag."
  427. " Providing Default printer.");
  428. bRet = EnumDefaultPrinterA(
  429. pInfo2,
  430. pPrinterEnum,
  431. cbBuf,
  432. Level,
  433. pInfo5,
  434. pcbNeeded,
  435. pcbReturned);
  436. return bRet;
  437. }
  438. // For LOCAL also add in CONNECTIONS
  439. if (Flags == PRINTER_ENUM_LOCAL)
  440. {
  441. LOGN( eDbgLevelInfo, "[EnumPrintersA] Called only for "
  442. "PRINTER_ENUM_LOCAL. Adding PRINTER_ENUM_CONNECTIONS\n");
  443. Flags = (PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL);
  444. }
  445. // Printer Level 5 is not really supported on win2k.
  446. // We'll call Level 2 and munge the data into a level 5 structure.
  447. if (g_bWin2k &&
  448. Level == 5 &&
  449. pcbNeeded != NULL &&
  450. pcbReturned != NULL)
  451. {
  452. // get the size needed for the info2 data
  453. ORIGINAL_API(EnumPrintersA)(Flags,
  454. Name,
  455. 2,
  456. NULL,
  457. 0,
  458. &dwInfo2Needed,
  459. &dwInfo2Returned);
  460. if (dwInfo2Needed > 0)
  461. {
  462. // Printers found, get the info2 data and convert it to info5
  463. pInfo2 = (PRINTER_INFO_2A *) malloc(dwInfo2Needed);
  464. if (pInfo2 &&
  465. ORIGINAL_API(EnumPrintersA)(Flags,
  466. Name,
  467. 2,
  468. (LPBYTE) pInfo2,
  469. dwInfo2Needed,
  470. &dwDummy,
  471. &dwInfo2Returned) )
  472. {
  473. bRet = MungeInfo2TOInfo5_A( pInfo2,
  474. cbBuf,
  475. dwInfo2Returned,
  476. pInfo5,
  477. pcbNeeded,
  478. pcbReturned);
  479. }
  480. if(pInfo2)
  481. {
  482. free(pInfo2);
  483. }
  484. }
  485. }
  486. else
  487. {
  488. bRet = ORIGINAL_API(EnumPrintersA)(Flags,
  489. Name,
  490. Level,
  491. pPrinterEnum,
  492. cbBuf,
  493. pcbNeeded,
  494. pcbReturned);
  495. }
  496. // For level 2 and level 5 there are some win95 only attributes
  497. // that need to be emulated.
  498. if ( (Level == 2 || Level == 5) &&
  499. bRet &&
  500. pPrinterEnum != NULL )
  501. {
  502. DWORD dwSize;
  503. GetDefaultPrinterA(NULL, &dwSize);
  504. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  505. {
  506. // Now that we have the right size, allocate a buffer
  507. LPSTR pszName = (LPSTR) malloc(dwSize);
  508. if (pszName)
  509. {
  510. // Now get the default printer with the right buffer size.
  511. if (GetDefaultPrinterA( pszName, &dwSize ) > 0)
  512. {
  513. if (Level == 2)
  514. {
  515. if (strcmp( pszName, ((PRINTER_INFO_2A*)pPrinterEnum)->pPrinterName) == 0)
  516. {
  517. ((PRINTER_INFO_2A*)pPrinterEnum)->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
  518. }
  519. }
  520. else
  521. {
  522. if (strcmp( pszName, ((PRINTER_INFO_5A*)pPrinterEnum)->pPrinterName) == 0)
  523. ((PRINTER_INFO_5A*)pPrinterEnum)->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
  524. }
  525. }
  526. free(pszName);
  527. }
  528. }
  529. }
  530. return bRet;
  531. }
  532. /*++
  533. These stub functions substitute the default printer if the pPrinterName is NULL,
  534. also they set pDefault to NULL to emulate win9x behavior
  535. --*/
  536. BOOL
  537. APIHOOK(OpenPrinterA)(
  538. LPSTR pPrinterName,
  539. LPHANDLE phPrinter,
  540. LPPRINTER_DEFAULTSA pDefault
  541. )
  542. {
  543. LPSTR pszName = NULL;
  544. DWORD dwSize;
  545. BOOL bDefaultFail = TRUE;
  546. BOOL bRet;
  547. if (!pPrinterName)
  548. {
  549. LOGN(eDbgLevelError, "[OpenPrinterA] App passed NULL for pPrinterName, using default printer.");
  550. // get the default printer name
  551. if (GetDefaultPrinterA(NULL, &dwSize) < 1)
  552. {
  553. // Now that we have the right size, allocate a buffer
  554. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  555. {
  556. pszName = (LPSTR) malloc(dwSize);
  557. if (pszName)
  558. {
  559. // Now get the default printer with the right buffer size.
  560. if (GetDefaultPrinterA( pszName, &dwSize ) > 0)
  561. {
  562. pPrinterName = pszName;
  563. bDefaultFail = FALSE;
  564. }
  565. }
  566. }
  567. }
  568. if (bDefaultFail)
  569. {
  570. DPFN(eDbgLevelError, "[OpenPrinterA] Unable to gather default pPrinterName.\n");
  571. }
  572. }
  573. else
  574. {
  575. CSTRING_TRY
  576. {
  577. if (pPrinterName &&
  578. !g_csPartialPrinterName.IsEmpty() &&
  579. !g_csFullPrinterName.IsEmpty())
  580. {
  581. CString csTemp(pPrinterName);
  582. if (0 == g_csPartialPrinterName.Compare(csTemp))
  583. {
  584. pPrinterName = g_csFullPrinterName.GetAnsi();
  585. }
  586. }
  587. }
  588. CSTRING_CATCH
  589. {
  590. // Do nothing
  591. }
  592. }
  593. if (pPrinterName)
  594. {
  595. DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: pPrinterName: %s\n", pPrinterName);
  596. }
  597. DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: pDefault: %x\n", pDefault);
  598. DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: overriding pDefault with NULL value\n");
  599. bRet = ORIGINAL_API(OpenPrinterA)(
  600. pPrinterName,
  601. phPrinter,
  602. NULL);
  603. if (pszName)
  604. {
  605. free(pszName);
  606. }
  607. return bRet;
  608. }
  609. /*++
  610. This stub function checks to see if the app is asking for the default printer
  611. string. If it is it will be returned as follows:
  612. PrinterName, Driver, Port
  613. On Win9x, if the printer is a network printer, Port is \\server\share and
  614. local printers are Port: (ex. LPT1:).
  615. On Win2k, if the printer is a network printer, Port is NeXX: and local printers
  616. are Port: .
  617. We must query EnumPrinters in order to emulate Win9x. Note: If the printer
  618. name is to large for the input buffer we trim it and keep track of the full
  619. name for later us in other printer APIs.
  620. --*/
  621. DWORD
  622. APIHOOK(GetProfileStringA)(
  623. LPCSTR lpAppName, // section name
  624. LPCSTR lpKeyName, // key name
  625. LPCSTR lpDefault, // default string
  626. LPSTR lpReturnedString, // destination buffer
  627. DWORD nSize // size of destination buffer
  628. )
  629. {
  630. LPSTR pszProfileString = NULL;
  631. if ( lpAppName &&
  632. lpKeyName &&
  633. 0 == _stricmp(lpAppName, "windows") &&
  634. 0 == _stricmp(lpKeyName, "device" ) )
  635. {
  636. DWORD dwSize = MAX_PRINTER_NAME + MAX_DRIVERPORT_NAME;
  637. DWORD dwProfileStringLen = 0;
  638. CSTRING_TRY
  639. {
  640. // loop until we have a large enough buffer
  641. do
  642. {
  643. if (pszProfileString != NULL)
  644. {
  645. free(pszProfileString);
  646. dwSize += MAX_PATH;
  647. }
  648. // Allocate the string
  649. pszProfileString = (LPSTR) malloc(dwSize);
  650. if (pszProfileString == NULL)
  651. {
  652. DPFN( eDbgLevelSpew, "[GetProfileStringA] Unable to allocate memory. Passing through.");
  653. //drop through if malloc fails
  654. goto DropThrough;
  655. }
  656. // Retrieve the profile string
  657. dwProfileStringLen = ORIGINAL_API(GetProfileStringA)( lpAppName,
  658. lpKeyName,
  659. lpDefault,
  660. pszProfileString,
  661. dwSize );
  662. // exit out if the size gets over 8000 so we don't loop forever
  663. // if buffer is not large enough dwProfileStringLen will be dwSize - 1 since
  664. // neither lpAppName nor lpKeyName are NULL
  665. } while (dwProfileStringLen == dwSize-1 && dwSize < 8000);
  666. // Zero length profile string, drop through
  667. if (dwProfileStringLen == 0 || dwSize >= 8000)
  668. {
  669. DPFN( eDbgLevelSpew, "[GetProfileStringA] Bad profile string. Passing through.");
  670. goto DropThrough;
  671. }
  672. // separate out the printer, driver and port name.
  673. CStringToken csOrig(pszProfileString, L",");
  674. CString csPrinter;
  675. CString csDriver;
  676. CString csPort;
  677. csOrig.GetToken(csPrinter);
  678. csOrig.GetToken(csDriver);
  679. csOrig.GetToken(csPort);
  680. // If the printer, driver or the port are null, drop through.
  681. if (csPrinter.IsEmpty() || csDriver.IsEmpty() || csPort.IsEmpty())
  682. {
  683. DPFN( eDbgLevelSpew, "[GetProfileStringA] Printer, Driver or Port were null. Passing through.");
  684. // Null printerdriver or printerport, drop through
  685. goto DropThrough;
  686. }
  687. DPFN( eDbgLevelError, "[GetProfileStringA] Printer <%S>\n Driver <%S>\n Port <%S>",
  688. csPrinter.Get(), csDriver.Get(), csPort.Get());
  689. // Check to see if this is a network printer
  690. if (0 == csPort.ComparePart(L"Ne", 0, 2))
  691. {
  692. PRINTER_INFO_2A* pInfo2 = NULL;
  693. DWORD dwInfo2Needed = 0;
  694. DWORD dwInfo2Returned = 0;
  695. DWORD dwDummy = 0;
  696. DWORD i = 0;
  697. BOOL bEnumPrintersSuccess = FALSE;
  698. BOOL bDefaultFound = FALSE;
  699. // Get the size of the Level 2 structure needed.
  700. bEnumPrintersSuccess = EnumPrintersA( PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL,
  701. NULL,
  702. 2,
  703. NULL,
  704. 0,
  705. &dwInfo2Needed,
  706. &dwInfo2Returned );
  707. // Get the Level 2 Info structure for the printer.
  708. pInfo2 = (PRINTER_INFO_2A *) malloc(dwInfo2Needed);
  709. bEnumPrintersSuccess = EnumPrintersA( PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL,
  710. NULL,
  711. 2,
  712. (LPBYTE) pInfo2,
  713. dwInfo2Needed,
  714. &dwDummy,
  715. &dwInfo2Returned );
  716. if (bEnumPrintersSuccess)
  717. {
  718. // Search for default printer in PRINTER_INFO_2 array
  719. for (i = 0; i < dwInfo2Returned; i++)
  720. {
  721. CString csTemp(pInfo2[i].pPrinterName);
  722. if (0 == csPrinter.Compare(csTemp))
  723. {
  724. bDefaultFound = TRUE;
  725. break;
  726. }
  727. }
  728. // Default printer was found
  729. if (bDefaultFound)
  730. {
  731. // Double check that this is a network printer and does not have
  732. // local attribute
  733. if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
  734. !(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
  735. pInfo2[i].pServerName != NULL &&
  736. pInfo2[i].pShareName != NULL)
  737. {
  738. // Modify the Port to conform with Win9x standards.
  739. LOGN( eDbgLevelInfo, "[GetProfileStringA] Altering default printer string returned by GetProfileStringA.\n");
  740. DPFN( eDbgLevelInfo, "[GetProfileStringA] Old: %s\n", pszProfileString);
  741. csPort = CString(pInfo2[i].pServerName) + L"\\" + CString(pInfo2[i].pShareName);
  742. }
  743. else
  744. {
  745. if (pInfo2[i].pPortName == NULL)
  746. {
  747. // Bad portname, pass through
  748. goto DropThrough;
  749. }
  750. // Just copy in the port
  751. csPort = CString(pInfo2[i].pPortName);
  752. }
  753. }
  754. }
  755. free(pInfo2);
  756. }
  757. // Create a profile string based off the modifed strings.
  758. CString csProfile(csPrinter);
  759. csProfile += L"," + csDriver + L"," + csPort;
  760. dwProfileStringLen = csProfile.GetLength()+1;
  761. // If the size they give is big enough, then return.
  762. if (dwProfileStringLen <= nSize)
  763. {
  764. StringCchCopyA( lpReturnedString, nSize, csProfile.GetAnsi());
  765. DPFN( eDbgLevelInfo, "[GetProfileStringA] Default Printer: %s Size: %d\n",
  766. lpReturnedString, strlen(lpReturnedString));
  767. return strlen(lpReturnedString);
  768. }
  769. // Modify the printer name and keep a global of the original if the printer
  770. // name causes the profile string output buffer to overflow.
  771. // If the size we need to reduce it by is greater than the size of
  772. // the printer name we're screwed, pass through.
  773. DWORD dwPrinterNameSize = csPrinter.GetLength() - (dwProfileStringLen - nSize);
  774. if (dwPrinterNameSize > 0)
  775. {
  776. DPFN( eDbgLevelInfo, "[GetProfileStringA] Reducing printer name by %d characters.\n",
  777. dwProfileStringLen - nSize );
  778. LOGN( eDbgLevelInfo, "[GetProfileStringA] Reducing printer name by %d characters.\n",
  779. dwProfileStringLen - nSize );
  780. EnterCriticalSection(&g_critSec);
  781. // save the partial and full printer names for later use.
  782. g_csPartialPrinterName = csPrinter.Left(dwPrinterNameSize);
  783. g_csFullPrinterName = csPrinter;
  784. // Create new profile string based off of partial printer name
  785. StringCchCopyA( lpReturnedString, nSize, g_csPartialPrinterName.GetAnsi());
  786. StringCchCatA( lpReturnedString, nSize, ",");
  787. StringCchCatA( lpReturnedString, nSize, csDriver.GetAnsi());
  788. StringCchCatA( lpReturnedString, nSize, ",");
  789. StringCchCatA( lpReturnedString, nSize, csPort.GetAnsi());
  790. LeaveCriticalSection(&g_critSec);
  791. DPFN( eDbgLevelInfo, "[GetProfileStringA] Partial: %s\n Full: %s\n",
  792. g_csPartialPrinterName.GetAnsi(), g_csFullPrinterName.GetAnsi() );
  793. DPFN( eDbgLevelInfo, "[GetProfileStringA] New: %s Size: %d\n",
  794. lpReturnedString, strlen(lpReturnedString));
  795. // return the modified string size.
  796. return strlen(lpReturnedString);
  797. }
  798. }
  799. CSTRING_CATCH
  800. {
  801. // Do nothing, just drop through.
  802. }
  803. }
  804. DropThrough:
  805. // Either an error occurred or its not asking for default printer.
  806. // pass through.
  807. return ORIGINAL_API(GetProfileStringA)(lpAppName,
  808. lpKeyName,
  809. lpDefault,
  810. lpReturnedString,
  811. nSize);
  812. }
  813. /*++
  814. This stub function pulls the device name from the DEVMODE if pszDevice is NULL
  815. and the DC is not for DISPLAY
  816. --*/
  817. HDC
  818. APIHOOK(CreateDCA)(
  819. LPCSTR pszDriver,
  820. LPCSTR pszDevice,
  821. LPCSTR pszPort,
  822. CONST DEVMODEA *pdm
  823. )
  824. {
  825. // if they've used a NULL device, but included a printer devmode,
  826. // fill in the device name from the printer devmode
  827. if (!pszDevice && pdm && (!pszDriver || _stricmp(pszDriver, "DISPLAY") != 0)) {
  828. LOGN( eDbgLevelError, "[CreateDCA] App passed NULL for pszDevice. Fixing.");
  829. pszDevice = (LPCSTR)pdm->dmDeviceName;
  830. }
  831. return ORIGINAL_API(CreateDCA)(
  832. pszDriver,
  833. pszDevice,
  834. pszPort,
  835. pdm
  836. );
  837. }
  838. /*++
  839. This stub function verifies that ResetDCA hasn't been handed an
  840. uninitialized InitData.
  841. --*/
  842. HDC
  843. APIHOOK(ResetDCA)(
  844. HDC hdc,
  845. CONST DEVMODEA *lpInitData
  846. )
  847. {
  848. // Sanity checks to make sure we aren't getting garbage
  849. // or bad values.
  850. if (lpInitData &&
  851. (lpInitData->dmSize > sizeof( DEVMODEA ) ||
  852. ( lpInitData->dmSpecVersion != 0x401 &&
  853. lpInitData->dmSpecVersion != 0x400 &&
  854. lpInitData->dmSpecVersion != 0x320 ) ) )
  855. {
  856. LOGN( eDbgLevelError, "[ResetDCA] App passed bad DEVMODE structure, nulling.");
  857. return ORIGINAL_API(ResetDCA)( hdc, NULL );
  858. }
  859. return ORIGINAL_API(ResetDCA)( hdc, lpInitData );
  860. }
  861. /*++
  862. These stub functions verify that SetPrinter has a valid handle
  863. before proceeding.
  864. --*/
  865. BOOL
  866. APIHOOK(SetPrinterA)(
  867. HANDLE hPrinter, // handle to printer object
  868. DWORD Level, // information level
  869. LPBYTE pPrinter, // printer data buffer
  870. DWORD Command // printer-state command
  871. )
  872. {
  873. BOOL bRet;
  874. if (hPrinter == NULL)
  875. {
  876. LOGN(eDbgLevelError, "[SetPrinterA] Called with null handle.");
  877. if (pPrinter == NULL)
  878. LOGN( eDbgLevelError, "[SetPrinterA] Called with null printer data buffer.");
  879. return FALSE;
  880. }
  881. else if (pPrinter == NULL)
  882. {
  883. LOGN(eDbgLevelError, "[SetPrinterA] Called with null printer data buffer.");
  884. return FALSE;
  885. }
  886. bRet= ORIGINAL_API(SetPrinterA)(
  887. hPrinter,
  888. Level,
  889. pPrinter,
  890. Command);
  891. DPFN( eDbgLevelSpew, "[SetPrinterA] Level= %d Command= %d Ret= %d\n",
  892. Level, Command, bRet );
  893. return bRet;
  894. }
  895. /*++
  896. This routine hooks the SetAbortProc and replaces their
  897. callback with ours.
  898. --*/
  899. int
  900. APIHOOK(SetAbortProc)(
  901. HDC hdc, // handle to DC
  902. ABORTPROC lpAbortProc // abort function
  903. )
  904. {
  905. lpAbortProc = (ABORTPROC) HookCallback(lpAbortProc, AbortProcHook);
  906. return ORIGINAL_API(SetAbortProc)( hdc, lpAbortProc );
  907. }
  908. /*++
  909. When apps start printing, they set a viewport
  910. on the printDC. They then call StartPage which has a different behaviour
  911. on 9x and WinNT. On 9x, a next call to StartPage resets the DC attributes
  912. to the default values.However on NT, the next call to StartPage does not
  913. reset the DC attributes.
  914. So, on 9x all subsequent output setup and drawing calls are carried
  915. out with a (0,0) viewport but on NT the viewport is leftover from its
  916. initial call. Since some apps(eg. Quicken 2000 and 2001) expect the API
  917. setting the (0,0) viewport,the result will be that the text and the
  918. lines are clipped on the left and top of the page.
  919. Here we hook StartPage and call SetViewportOrgEx(hdc, 0, 0, NULL) to
  920. set the viewport to (0,0) on every call to StartPage to emulate
  921. the 9x behaviour.
  922. --*/
  923. BOOL
  924. APIHOOK(StartPage)(
  925. HDC hdc
  926. )
  927. {
  928. if (SetViewportOrgEx(hdc, 0, 0, NULL))
  929. {
  930. // We have now made the device point(viewport) map to (0, 0).
  931. LOGN(eDbgLevelInfo, "[StartPage] Setting the device point map to (0,0).");
  932. }
  933. else
  934. {
  935. LOGN(eDbgLevelError, "[StartPage] Unable to set device point map to (0,0)."
  936. "Failed in a call to SetViewportOrgEx");
  937. }
  938. return ORIGINAL_API(StartPage)(hdc);
  939. }
  940. /*++
  941. This stub function verifies that DeviceCapabilities is using a correct
  942. printer name.
  943. --*/
  944. DWORD
  945. APIHOOK(DeviceCapabilitiesA)(
  946. LPCSTR pDevice,
  947. LPCSTR pPort,
  948. WORD fwCapability,
  949. LPSTR pOutput,
  950. CONST DEVMODE *pDevMode
  951. )
  952. {
  953. DWORD dwRet;
  954. CSTRING_TRY
  955. {
  956. if ( pDevice &&
  957. !g_csPartialPrinterName.IsEmpty() &&
  958. !g_csFullPrinterName.IsEmpty())
  959. {
  960. CString csTemp(pDevice);
  961. if (0 == g_csPartialPrinterName.Compare(csTemp))
  962. {
  963. pDevice = g_csFullPrinterName.GetAnsi();
  964. }
  965. }
  966. }
  967. CSTRING_CATCH
  968. {
  969. // Do nothing
  970. }
  971. dwRet= ORIGINAL_API(DeviceCapabilitiesA)( pDevice,
  972. pPort,
  973. fwCapability,
  974. pOutput,
  975. pDevMode );
  976. if (pDevice && pPort)
  977. {
  978. DPFN( eDbgLevelSpew, "[DeviceCapabilitiesA] pDevice= %s pPort= %s fwC= %d Out= %x RC= %d\n",
  979. pDevice, pPort, fwCapability, pOutput, dwRet );
  980. }
  981. else
  982. {
  983. DPFN( eDbgLevelSpew, "[DeviceCapabilitiesA] fwC= %d Out= %x RC= %d\n",
  984. fwCapability, pOutput, dwRet );
  985. }
  986. return dwRet;
  987. }
  988. /*++
  989. This stub function verifies that AddPrinterConnection is using a correct
  990. printer name.
  991. --*/
  992. BOOL
  993. APIHOOK(AddPrinterConnectionA)(
  994. LPSTR pName
  995. )
  996. {
  997. CSTRING_TRY
  998. {
  999. if (pName &&
  1000. !g_csPartialPrinterName.IsEmpty() &&
  1001. !g_csFullPrinterName.IsEmpty())
  1002. {
  1003. CString csTemp(pName);
  1004. if (0 == g_csPartialPrinterName.Compare(csTemp))
  1005. {
  1006. pName = g_csFullPrinterName.GetAnsi();
  1007. }
  1008. }
  1009. }
  1010. CSTRING_CATCH
  1011. {
  1012. // Do nothing
  1013. }
  1014. return ORIGINAL_API(AddPrinterConnectionA)(pName);
  1015. }
  1016. /*++
  1017. This stub function verifies that DeletePrinterConnection is using a correct
  1018. printer name.
  1019. --*/
  1020. BOOL
  1021. APIHOOK(DeletePrinterConnectionA)(
  1022. LPSTR pName
  1023. )
  1024. {
  1025. CSTRING_TRY
  1026. {
  1027. if (pName &&
  1028. !g_csPartialPrinterName.IsEmpty() &&
  1029. !g_csFullPrinterName.IsEmpty())
  1030. {
  1031. CString csTemp(pName);
  1032. if (0 == g_csPartialPrinterName.Compare(csTemp))
  1033. {
  1034. pName = g_csFullPrinterName.GetAnsi();
  1035. }
  1036. }
  1037. }
  1038. CSTRING_CATCH
  1039. {
  1040. // Do nothing
  1041. }
  1042. return ORIGINAL_API(DeletePrinterConnectionA)(pName);
  1043. }
  1044. /*++
  1045. This stub function verifies that PrintDlgA is using a correct
  1046. nFromPage and nToPage.
  1047. --*/
  1048. BOOL
  1049. APIHOOK(PrintDlgA)(
  1050. LPPRINTDLG lppd
  1051. )
  1052. {
  1053. // check nFromPage and nToPage for legal values.
  1054. if (lppd)
  1055. {
  1056. DPFN(eDbgLevelSpew, "[PrintDlgA] nFromPage = %d nToPage = %d",
  1057. lppd->nFromPage, lppd->nToPage);
  1058. if (lppd->nFromPage == 0xffff)
  1059. {
  1060. lppd->nFromPage = 0;
  1061. DPFN( eDbgLevelInfo, "[PrintDlgA] Setting nFromPage to 0." );
  1062. }
  1063. if (lppd->nToPage == 0xffff)
  1064. {
  1065. lppd->nToPage = lppd->nFromPage;
  1066. DPFN( eDbgLevelInfo, "[PrintDlgA] Setting nToPage to %d.", lppd->nFromPage );
  1067. }
  1068. }
  1069. return ORIGINAL_API(PrintDlgA)(lppd);
  1070. }
  1071. /*++
  1072. Register hooked functions
  1073. --*/
  1074. BOOL
  1075. NOTIFY_FUNCTION(
  1076. DWORD fdwReason)
  1077. {
  1078. if (fdwReason == DLL_PROCESS_ATTACH)
  1079. {
  1080. OSVERSIONINFOEX osvi;
  1081. BOOL bOsVersionInfoEx;
  1082. if (!InitializeCriticalSectionAndSpinCount(&g_critSec,0x80000000))
  1083. {
  1084. return FALSE;
  1085. }
  1086. // Check to see if we are under win2k
  1087. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  1088. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  1089. bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
  1090. if(bOsVersionInfoEx)
  1091. {
  1092. if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT &&
  1093. osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
  1094. {
  1095. g_bWin2k = TRUE;
  1096. }
  1097. }
  1098. }
  1099. return TRUE;
  1100. }
  1101. HOOK_BEGIN
  1102. CALL_NOTIFY_FUNCTION
  1103. APIHOOK_ENTRY(WINSPOOL.DRV, DocumentPropertiesA);
  1104. APIHOOK_ENTRY(WINSPOOL.DRV, OpenPrinterA);
  1105. APIHOOK_ENTRY(WINSPOOL.DRV, SetPrinterA);
  1106. APIHOOK_ENTRY(WINSPOOL.DRV, EnumPrintersA);
  1107. APIHOOK_ENTRY(WINSPOOL.DRV, OpenPrinterA);
  1108. APIHOOK_ENTRY(WINSPOOL.DRV, DeviceCapabilitiesA);
  1109. APIHOOK_ENTRY(WINSPOOL.DRV, AddPrinterConnectionA);
  1110. APIHOOK_ENTRY(WINSPOOL.DRV, DeletePrinterConnectionA);
  1111. APIHOOK_ENTRY(COMDLG32.DLL, PrintDlgA);
  1112. APIHOOK_ENTRY(KERNEL32.DLL,GetProfileStringA);
  1113. APIHOOK_ENTRY(GDI32.DLL, CreateDCA);
  1114. APIHOOK_ENTRY(GDI32.DLL, ResetDCA);
  1115. APIHOOK_ENTRY(GDI32.DLL, SetAbortProc);
  1116. APIHOOK_ENTRY(GDI32.DLL, StartPage);
  1117. HOOK_END
  1118. IMPLEMENT_SHIM_END