Windows2003-3790/windows/appcompat/shims/layer/emulateprinter.cpp
2020-09-30 16:53:55 +02:00

1344 lines
41 KiB
C++

/*++
Copyright (c) 2000-2002 Microsoft Corporation
Module Name:
EmulatePrinter.cpp
Abstract:
This is a general purpose shim to fix all problems we have seen
that are remotely connected with printers. The shim fixes the
following:
1) Apps call EnumPrinters passing only PRINTER_ENUM_LOCAL but expect to see
network printers as well. For some reason Win9x enumerates network
printers as well when this API is called with only PRINTER_ENUM_LOCAL set.
2) Apps call EnumPrinters passing only PRINTER_ENUM_DEFAULT. This works
properly in win98, however the option does not exist in w2k. This
API performs the equivalent.
3) EnumPrinters Level 5 is not really supported on NT. This API calls
Level 2 and munges the data into a level 5 structure.
4) Win9x ignores pDefault parameter for OpenPrinter. Some native Win9x apps
are unaware about this and assume it is safe to use PRINTER_ALL_ACCESS
value for DesiredAccess flag, member of pDefault parameter, to open either
local printer or remote printer server. But Windows NT requires
SERVER_ALL_ACCESS set for this flag to access remote printer server.
To emulate Win9x behavior, we override pDefault with NULL value.
5) If an app calls one of several print APIs with a NULL printer name,
looks up and supplies the default printer name, or derives it from other params.
6) Verifies a correct handle was passed to SetPrinter. Win98 does this
at the start and if its a bad handle never uses the passed Information,
however w2k does not check the handle till after looking at the information.
This can cause an error if Level is 2 and the print buffer is null due to
a missing check in SetPrinterA. (note: this was fixed in whistler).
7) Verifies that the stack is correct after the proc set in SetAbortProc is
called.
8) Verifies that an initialized DEVMODEA has been passed to ResetDCA.
9) Checks GetProfileStringA for a WINDOWS DEVICE (i.e. printer). If one is
requested then make sure the string is not being truncated, if it is then
save the full printer name for later use.
10) Checks for a -1 in the nFromPage for PrintDlgA and corrects it to a zero.
Note: the OS should handle this as per the manual, however print team no-fixed it
as too risky to change since -1 is a special value in their code.
Notes:
This is a general purpose shim. This code from this shim was originally
in two seperate shims enumnetworkprinters and handlenullprintername.
Also added another SHIM EmulateStartPage to this.
History:
11/08/00 mnikkel created
12/07/00 prashkud Added StartPage to this.
01/25/01 mnikkel Removed W routines, they were causing problems
and were not needed.
02/07/01 mnikkel Added check for too long a string, removed fixed printer
name sizes.
02/27/2001 robkenny Converted to use tcs.h
05/21/2001 mnikkel Added PrintDlgA check
09/13/2001 mnikkel Changed so that level 5 data being created from Level 2
data is only done on win2k. Level 5 data was fixed for XP.
Also added check so shim works with printers shared out on
win9X while running on XP.
12/15/2001 mnikkel Corrected bug in shim where default printer flag was not
being set in enumprintersa.
02/20/2002 mnikkel Major cleanup to remove buffer overrun possibilities.
Added check for No printer in GetProfileString routine
--*/
#include "precomp.h"
#include <commdlg.h>
IMPLEMENT_SHIM_BEGIN(EmulatePrinter)
#include "ShimHookMacro.h"
#define MAX_PRINTER_NAME 221
#define MAX_DRIVERPORT_NAME 50
APIHOOK_ENUM_BEGIN
APIHOOK_ENUM_ENTRY(DocumentPropertiesA)
APIHOOK_ENUM_ENTRY(OpenPrinterA)
APIHOOK_ENUM_ENTRY(SetPrinterA)
APIHOOK_ENUM_ENTRY(CreateDCA)
APIHOOK_ENUM_ENTRY(ResetDCA)
APIHOOK_ENUM_ENTRY(EnumPrintersA)
APIHOOK_ENUM_ENTRY(GetProfileStringA)
APIHOOK_ENUM_ENTRY(SetAbortProc)
APIHOOK_ENUM_ENTRY(StartPage)
APIHOOK_ENUM_ENTRY(DeviceCapabilitiesA)
APIHOOK_ENUM_ENTRY(AddPrinterConnectionA)
APIHOOK_ENUM_ENTRY(DeletePrinterConnectionA)
APIHOOK_ENUM_ENTRY(PrintDlgA)
APIHOOK_ENUM_END
typedef int (WINAPI *_pfn_SetAbortProc)(HDC hdc, ABORTPROC lpAbortProc);
CString g_csFullPrinterName("");
CString g_csPartialPrinterName("");
CRITICAL_SECTION g_critSec;
BOOL g_bWin2k = FALSE;
/*++
These functions munge data from a Level 2 Information structure
into a Level 5 information structure.
--*/
BOOL
MungeInfo2TOInfo5_A(
PRINTER_INFO_2A* pInfo2,
DWORD cbBuf,
DWORD dwInfo2Returned,
PRINTER_INFO_5A* pInfo5,
LPDWORD pcbNeeded,
LPDWORD pcbReturned)
{
DWORD dwStringBufferSize = 0;
LPSTR lpStringBuffer = NULL;
// Sanity check, should not occur.
if (pInfo2 == NULL || pInfo5 == NULL)
{
return FALSE;
}
// First calculate buffer size needed
for (DWORD i = 0; i < dwInfo2Returned; i++)
{
if (pInfo2[i].pPrinterName)
{
dwStringBufferSize += strlen(pInfo2[i].pPrinterName);
}
dwStringBufferSize++;
if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
!(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
pInfo2[i].pServerName != NULL &&
pInfo2[i].pShareName != NULL)
{
dwStringBufferSize += strlen(pInfo2[i].pServerName) + 1;
dwStringBufferSize += strlen(pInfo2[i].pShareName) + 1;
}
else
{
if (pInfo2[i].pPortName)
{
dwStringBufferSize += strlen(pInfo2[i].pPortName);
}
dwStringBufferSize++;
}
}
// set the buffer size needed
*pcbNeeded = dwInfo2Returned * sizeof(PRINTER_INFO_5A)
+ dwStringBufferSize;
// verify that buffer passed in is big enough.
if (cbBuf < *pcbNeeded)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
// Allocate the Level 5 information structure
lpStringBuffer = ((LPSTR) pInfo5)
+ dwInfo2Returned * sizeof(PRINTER_INFO_5A);
// Munge the Level 2 information into the Level 5 structure
for (i = 0; i < dwInfo2Returned; i++)
{
// Copy the printername from level 2 to level 5 structure.
if (pInfo2[i].pPrinterName)
{
if (StringCchCopyA( lpStringBuffer, cbBuf, pInfo2[i].pPrinterName) != S_OK)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
}
else
{
if (StringCchCopyA( lpStringBuffer, cbBuf, "") != S_OK)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
}
pInfo5[i].pPrinterName = lpStringBuffer;
lpStringBuffer += strlen(pInfo2[i].pPrinterName) + 1;
// Copy in attributes to level 5 structure and set defaults
pInfo5[i].Attributes = pInfo2[i].Attributes;
pInfo5[i].DeviceNotSelectedTimeout = 15000; // Use defaults here
pInfo5[i].TransmissionRetryTimeout = 45000; // Use defaults here
// Check for a network printer
if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
!(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
pInfo2[i].pServerName != NULL &&
pInfo2[i].pShareName != NULL)
{
// For network printer create the win98 style port name
if (StringCchCopyA( lpStringBuffer, cbBuf, pInfo2[i].pServerName) != S_OK ||
StringCchCatA( lpStringBuffer, cbBuf, "\\" ) != S_OK ||
StringCchCatA( lpStringBuffer, cbBuf, pInfo2[i].pShareName) != S_OK)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
}
else
{
// Not network printer, just copy in port name
if (pInfo2[i].pPortName)
{
if (StringCchCopyA( lpStringBuffer, cbBuf, pInfo2[i].pPortName) != S_OK)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
}
else
{
if (StringCchCopyA( lpStringBuffer, cbBuf, "") != S_OK)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
}
}
pInfo5[i].pPortName = lpStringBuffer;
lpStringBuffer += strlen(pInfo2[i].pPortName) + 1;
}
// Set the number of structures munged
*pcbReturned = dwInfo2Returned;
return TRUE;
}
/*++
Our Callback routine for SetAbortProc, this routine
verifies that the stack is correct.
--*/
DWORD g_dwGuardNum = 0xABCD8765;
DWORD g_dwFailed = 0;
BOOL CALLBACK
AbortProcHook(
ABORTPROC pfnOld, // address of old ABORTPROC
HDC hdc, // handle to DC
int iError // error value
)
{
DWORD dwRet= 0;
// Flag to track whether the stack was corrected.
g_dwFailed = 0;
// Push a Guard number on the stack, call their
// abort procedure, then pop the stack till we
// find our guard number
__asm
{
push ebx
push ecx
push g_dwGuardNum
push iError
push hdc
call pfnOld ; make call to their abort proc
mov ecx,16
loc1:
dec ecx
pop ebx
cmp ebx, g_dwGuardNum
jne loc1
cmp ecx, 15
jz loc2
mov g_dwFailed, 1
loc2:
pop ecx
pop ebx
mov dwRet, eax
}
if (g_dwFailed)
{
LOGN( eDbgLevelError, "[AbortProcHook] Fixing incorrect calling convention for AbortProc");
}
return (BOOL) dwRet;
}
/*++
This stub function looks up the device name if pDeviceName is NULL
--*/
LONG
APIHOOK(DocumentPropertiesA)(
HWND hWnd,
HANDLE hPrinter,
LPSTR pDeviceName,
PDEVMODEA pDevModeOutput,
PDEVMODEA pDevModeInput,
DWORD fMode
)
{
LONG lRet = -1;
PRINTER_INFO_2A *pPrinterInfo2A = NULL;
// if they didn't supply a device name, we need to supply it.
if (!pDeviceName)
{
LOGN( eDbgLevelError, "[DocumentPropertiesW] App passed NULL for pDeviceName.");
if (hPrinter)
{
DWORD dwSizeNeeded = 0;
DWORD dwSizeUsed = 0;
// get the size
GetPrinterA(hPrinter, 2, NULL, 0, &dwSizeNeeded);
if (dwSizeNeeded != 0)
{
// allocate memory for the info
pPrinterInfo2A = (PRINTER_INFO_2A*) malloc(dwSizeNeeded);
if (pPrinterInfo2A) {
// get the info
if (GetPrinterA(hPrinter, 2, (LPBYTE)pPrinterInfo2A, dwSizeNeeded, &dwSizeUsed))
{
pDeviceName = pPrinterInfo2A->pPrinterName;
}
}
}
}
}
if (!pDeviceName) {
DPFN( eDbgLevelError, "[DocumentPropertiesA] Unable to gather correct pDeviceName."
"Problem not fixed.\n");
}
lRet = ORIGINAL_API(DocumentPropertiesA)(
hWnd,
hPrinter,
pDeviceName,
pDevModeOutput,
pDevModeInput,
fMode
);
if (pPrinterInfo2A) {
free(pPrinterInfo2A);
}
return lRet;
}
/*++
These functions handle the case of EnumPrinters being called with the
PRINTER_ENUM_DEFAULT flag.
--*/
BOOL
EnumDefaultPrinterA(
PRINTER_INFO_2A* pInfo2,
LPBYTE pPrinterEnum,
DWORD cbBuf,
DWORD Level,
PRINTER_INFO_5A* pInfo5,
LPDWORD pcbNeeded,
LPDWORD pcbReturned
)
{
LPSTR pszName = NULL;
DWORD dwSize = 0;
HANDLE hPrinter = NULL;
BOOL bRet= FALSE;
DWORD dwInfo2Needed = 0;
DWORD dwDummy;
BOOL bDefaultFail = TRUE;
*pcbNeeded = 0;
*pcbReturned = 0;
// get the default printer name
if (GetDefaultPrinterA(NULL, &dwSize) < 1)
{
// Now that we have the right size, allocate a buffer
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pszName = (LPSTR) malloc(dwSize);
if (pszName)
{
// Now get the default printer with the right buffer size.
if (GetDefaultPrinterA(pszName, &dwSize) > 0)
{
if (OpenPrinterA(pszName, &hPrinter, NULL))
{
bDefaultFail = FALSE;
}
}
free(pszName);
}
}
}
if (bDefaultFail)
{
SetLastError(ERROR_INVALID_NAME);
return FALSE;
}
// Printer Level 5 is not really supported on win2k.
// We'll call Level 2 and munge the data into a level 5 structure.
if ( g_bWin2k &&
Level == 5 &&
pcbNeeded != NULL &&
pcbReturned != NULL)
{
LOGN(eDbgLevelError, "[EnumPrintersA] EnumPrintersA called with Level 5 set."
" Fixing up Level 5 information.");
// get the size needed for the info2 data
if (GetPrinterA(hPrinter, 2, NULL, 0, &dwInfo2Needed) == 0 &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pInfo2 = (PRINTER_INFO_2A *) malloc(dwInfo2Needed);
// get the info2 data and munge into level 5 structure
if (pInfo2 &&
GetPrinterA(hPrinter, 2, (LPBYTE)pInfo2, dwInfo2Needed, &dwDummy))
{
bRet= MungeInfo2TOInfo5_A(pInfo2, cbBuf, 1, pInfo5, pcbNeeded, pcbReturned);
}
if (pInfo2)
{
free(pInfo2);
}
}
}
// Not win2k or not Level 5 so just get info
else
{
*pcbReturned = 1;
bRet = GetPrinterA(hPrinter, Level, pPrinterEnum, cbBuf, pcbNeeded);
}
// Close the printer
ClosePrinter(hPrinter);
return bRet;
}
/*++
These stub functions check for PRINTER_ENUM_DEFAULT, PRINTER_ENUM_LOCAL
and Level 5 information structures.
--*/
BOOL
APIHOOK(EnumPrintersA)(
DWORD Flags,
LPSTR Name,
DWORD Level,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcbReturned
)
{
BOOL bRet = FALSE;
DWORD dwInfo2Needed = 0;
DWORD dwInfo2Returned = 0;
DWORD dwDummy;
PRINTER_INFO_2A* pInfo2 = NULL;
PRINTER_INFO_5A* pInfo5 = (PRINTER_INFO_5A *) pPrinterEnum;
// Win2k doesn't handle DEFAULT case like win98 did, so we get
// to do it for them.
if (Flags == PRINTER_ENUM_DEFAULT )
{
LOGN(eDbgLevelError, "[EnumPrintersA] Called with PRINTER_ENUM_DEFAULT flag."
" Providing Default printer.");
bRet = EnumDefaultPrinterA(
pInfo2,
pPrinterEnum,
cbBuf,
Level,
pInfo5,
pcbNeeded,
pcbReturned);
return bRet;
}
// For LOCAL also add in CONNECTIONS
if (Flags == PRINTER_ENUM_LOCAL)
{
LOGN( eDbgLevelInfo, "[EnumPrintersA] Called only for "
"PRINTER_ENUM_LOCAL. Adding PRINTER_ENUM_CONNECTIONS\n");
Flags = (PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL);
}
// Printer Level 5 is not really supported on win2k.
// We'll call Level 2 and munge the data into a level 5 structure.
if (g_bWin2k &&
Level == 5 &&
pcbNeeded != NULL &&
pcbReturned != NULL)
{
// get the size needed for the info2 data
ORIGINAL_API(EnumPrintersA)(Flags,
Name,
2,
NULL,
0,
&dwInfo2Needed,
&dwInfo2Returned);
if (dwInfo2Needed > 0)
{
// Printers found, get the info2 data and convert it to info5
pInfo2 = (PRINTER_INFO_2A *) malloc(dwInfo2Needed);
if (pInfo2 &&
ORIGINAL_API(EnumPrintersA)(Flags,
Name,
2,
(LPBYTE) pInfo2,
dwInfo2Needed,
&dwDummy,
&dwInfo2Returned) )
{
bRet = MungeInfo2TOInfo5_A( pInfo2,
cbBuf,
dwInfo2Returned,
pInfo5,
pcbNeeded,
pcbReturned);
}
if(pInfo2)
{
free(pInfo2);
}
}
}
else
{
bRet = ORIGINAL_API(EnumPrintersA)(Flags,
Name,
Level,
pPrinterEnum,
cbBuf,
pcbNeeded,
pcbReturned);
}
// For level 2 and level 5 there are some win95 only attributes
// that need to be emulated.
if ( (Level == 2 || Level == 5) &&
bRet &&
pPrinterEnum != NULL )
{
DWORD dwSize;
GetDefaultPrinterA(NULL, &dwSize);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// Now that we have the right size, allocate a buffer
LPSTR pszName = (LPSTR) malloc(dwSize);
if (pszName)
{
// Now get the default printer with the right buffer size.
if (GetDefaultPrinterA( pszName, &dwSize ) > 0)
{
if (Level == 2)
{
if (strcmp( pszName, ((PRINTER_INFO_2A*)pPrinterEnum)->pPrinterName) == 0)
{
((PRINTER_INFO_2A*)pPrinterEnum)->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
}
}
else
{
if (strcmp( pszName, ((PRINTER_INFO_5A*)pPrinterEnum)->pPrinterName) == 0)
((PRINTER_INFO_5A*)pPrinterEnum)->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
}
}
free(pszName);
}
}
}
return bRet;
}
/*++
These stub functions substitute the default printer if the pPrinterName is NULL,
also they set pDefault to NULL to emulate win9x behavior
--*/
BOOL
APIHOOK(OpenPrinterA)(
LPSTR pPrinterName,
LPHANDLE phPrinter,
LPPRINTER_DEFAULTSA pDefault
)
{
LPSTR pszName = NULL;
DWORD dwSize;
BOOL bDefaultFail = TRUE;
BOOL bRet;
if (!pPrinterName)
{
LOGN(eDbgLevelError, "[OpenPrinterA] App passed NULL for pPrinterName, using default printer.");
// get the default printer name
if (GetDefaultPrinterA(NULL, &dwSize) < 1)
{
// Now that we have the right size, allocate a buffer
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pszName = (LPSTR) malloc(dwSize);
if (pszName)
{
// Now get the default printer with the right buffer size.
if (GetDefaultPrinterA( pszName, &dwSize ) > 0)
{
pPrinterName = pszName;
bDefaultFail = FALSE;
}
}
}
}
if (bDefaultFail)
{
DPFN(eDbgLevelError, "[OpenPrinterA] Unable to gather default pPrinterName.\n");
}
}
else
{
CSTRING_TRY
{
if (pPrinterName &&
!g_csPartialPrinterName.IsEmpty() &&
!g_csFullPrinterName.IsEmpty())
{
CString csTemp(pPrinterName);
if (0 == g_csPartialPrinterName.Compare(csTemp))
{
pPrinterName = g_csFullPrinterName.GetAnsi();
}
}
}
CSTRING_CATCH
{
// Do nothing
}
}
if (pPrinterName)
{
DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: pPrinterName: %s\n", pPrinterName);
}
DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: pDefault: %x\n", pDefault);
DPFN( eDbgLevelInfo, "[OpenPrinterA] APIHOOK(OpenPrinterA: overriding pDefault with NULL value\n");
bRet = ORIGINAL_API(OpenPrinterA)(
pPrinterName,
phPrinter,
NULL);
if (pszName)
{
free(pszName);
}
return bRet;
}
/*++
This stub function checks to see if the app is asking for the default printer
string. If it is it will be returned as follows:
PrinterName, Driver, Port
On Win9x, if the printer is a network printer, Port is \\server\share and
local printers are Port: (ex. LPT1:).
On Win2k, if the printer is a network printer, Port is NeXX: and local printers
are Port: .
We must query EnumPrinters in order to emulate Win9x. Note: If the printer
name is to large for the input buffer we trim it and keep track of the full
name for later us in other printer APIs.
--*/
DWORD
APIHOOK(GetProfileStringA)(
LPCSTR lpAppName, // section name
LPCSTR lpKeyName, // key name
LPCSTR lpDefault, // default string
LPSTR lpReturnedString, // destination buffer
DWORD nSize // size of destination buffer
)
{
LPSTR pszProfileString = NULL;
if ( lpAppName &&
lpKeyName &&
0 == _stricmp(lpAppName, "windows") &&
0 == _stricmp(lpKeyName, "device" ) )
{
DWORD dwSize = MAX_PRINTER_NAME + MAX_DRIVERPORT_NAME;
DWORD dwProfileStringLen = 0;
CSTRING_TRY
{
// loop until we have a large enough buffer
do
{
if (pszProfileString != NULL)
{
free(pszProfileString);
dwSize += MAX_PATH;
}
// Allocate the string
pszProfileString = (LPSTR) malloc(dwSize);
if (pszProfileString == NULL)
{
DPFN( eDbgLevelSpew, "[GetProfileStringA] Unable to allocate memory. Passing through.");
//drop through if malloc fails
goto DropThrough;
}
// Retrieve the profile string
dwProfileStringLen = ORIGINAL_API(GetProfileStringA)( lpAppName,
lpKeyName,
lpDefault,
pszProfileString,
dwSize );
// exit out if the size gets over 8000 so we don't loop forever
// if buffer is not large enough dwProfileStringLen will be dwSize - 1 since
// neither lpAppName nor lpKeyName are NULL
} while (dwProfileStringLen == dwSize-1 && dwSize < 8000);
// Zero length profile string, drop through
if (dwProfileStringLen == 0 || dwSize >= 8000)
{
DPFN( eDbgLevelSpew, "[GetProfileStringA] Bad profile string. Passing through.");
goto DropThrough;
}
// separate out the printer, driver and port name.
CStringToken csOrig(pszProfileString, L",");
CString csPrinter;
CString csDriver;
CString csPort;
csOrig.GetToken(csPrinter);
csOrig.GetToken(csDriver);
csOrig.GetToken(csPort);
// If the printer, driver or the port are null, drop through.
if (csPrinter.IsEmpty() || csDriver.IsEmpty() || csPort.IsEmpty())
{
DPFN( eDbgLevelSpew, "[GetProfileStringA] Printer, Driver or Port were null. Passing through.");
// Null printerdriver or printerport, drop through
goto DropThrough;
}
DPFN( eDbgLevelError, "[GetProfileStringA] Printer <%S>\n Driver <%S>\n Port <%S>",
csPrinter.Get(), csDriver.Get(), csPort.Get());
// Check to see if this is a network printer
if (0 == csPort.ComparePart(L"Ne", 0, 2))
{
PRINTER_INFO_2A* pInfo2 = NULL;
DWORD dwInfo2Needed = 0;
DWORD dwInfo2Returned = 0;
DWORD dwDummy = 0;
DWORD i = 0;
BOOL bEnumPrintersSuccess = FALSE;
BOOL bDefaultFound = FALSE;
// Get the size of the Level 2 structure needed.
bEnumPrintersSuccess = EnumPrintersA( PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL,
NULL,
2,
NULL,
0,
&dwInfo2Needed,
&dwInfo2Returned );
// Get the Level 2 Info structure for the printer.
pInfo2 = (PRINTER_INFO_2A *) malloc(dwInfo2Needed);
bEnumPrintersSuccess = EnumPrintersA( PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL,
NULL,
2,
(LPBYTE) pInfo2,
dwInfo2Needed,
&dwDummy,
&dwInfo2Returned );
if (bEnumPrintersSuccess)
{
// Search for default printer in PRINTER_INFO_2 array
for (i = 0; i < dwInfo2Returned; i++)
{
CString csTemp(pInfo2[i].pPrinterName);
if (0 == csPrinter.Compare(csTemp))
{
bDefaultFound = TRUE;
break;
}
}
// Default printer was found
if (bDefaultFound)
{
// Double check that this is a network printer and does not have
// local attribute
if (pInfo2[i].Attributes & PRINTER_ATTRIBUTE_NETWORK &&
!(pInfo2[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) &&
pInfo2[i].pServerName != NULL &&
pInfo2[i].pShareName != NULL)
{
// Modify the Port to conform with Win9x standards.
LOGN( eDbgLevelInfo, "[GetProfileStringA] Altering default printer string returned by GetProfileStringA.\n");
DPFN( eDbgLevelInfo, "[GetProfileStringA] Old: %s\n", pszProfileString);
csPort = CString(pInfo2[i].pServerName) + L"\\" + CString(pInfo2[i].pShareName);
}
else
{
if (pInfo2[i].pPortName == NULL)
{
// Bad portname, pass through
goto DropThrough;
}
// Just copy in the port
csPort = CString(pInfo2[i].pPortName);
}
}
}
free(pInfo2);
}
// Create a profile string based off the modifed strings.
CString csProfile(csPrinter);
csProfile += L"," + csDriver + L"," + csPort;
dwProfileStringLen = csProfile.GetLength()+1;
// If the size they give is big enough, then return.
if (dwProfileStringLen <= nSize)
{
StringCchCopyA( lpReturnedString, nSize, csProfile.GetAnsi());
DPFN( eDbgLevelInfo, "[GetProfileStringA] Default Printer: %s Size: %d\n",
lpReturnedString, strlen(lpReturnedString));
return strlen(lpReturnedString);
}
// Modify the printer name and keep a global of the original if the printer
// name causes the profile string output buffer to overflow.
// If the size we need to reduce it by is greater than the size of
// the printer name we're screwed, pass through.
DWORD dwPrinterNameSize = csPrinter.GetLength() - (dwProfileStringLen - nSize);
if (dwPrinterNameSize > 0)
{
DPFN( eDbgLevelInfo, "[GetProfileStringA] Reducing printer name by %d characters.\n",
dwProfileStringLen - nSize );
LOGN( eDbgLevelInfo, "[GetProfileStringA] Reducing printer name by %d characters.\n",
dwProfileStringLen - nSize );
EnterCriticalSection(&g_critSec);
// save the partial and full printer names for later use.
g_csPartialPrinterName = csPrinter.Left(dwPrinterNameSize);
g_csFullPrinterName = csPrinter;
// Create new profile string based off of partial printer name
StringCchCopyA( lpReturnedString, nSize, g_csPartialPrinterName.GetAnsi());
StringCchCatA( lpReturnedString, nSize, ",");
StringCchCatA( lpReturnedString, nSize, csDriver.GetAnsi());
StringCchCatA( lpReturnedString, nSize, ",");
StringCchCatA( lpReturnedString, nSize, csPort.GetAnsi());
LeaveCriticalSection(&g_critSec);
DPFN( eDbgLevelInfo, "[GetProfileStringA] Partial: %s\n Full: %s\n",
g_csPartialPrinterName.GetAnsi(), g_csFullPrinterName.GetAnsi() );
DPFN( eDbgLevelInfo, "[GetProfileStringA] New: %s Size: %d\n",
lpReturnedString, strlen(lpReturnedString));
// return the modified string size.
return strlen(lpReturnedString);
}
}
CSTRING_CATCH
{
// Do nothing, just drop through.
}
}
DropThrough:
// Either an error occurred or its not asking for default printer.
// pass through.
return ORIGINAL_API(GetProfileStringA)(lpAppName,
lpKeyName,
lpDefault,
lpReturnedString,
nSize);
}
/*++
This stub function pulls the device name from the DEVMODE if pszDevice is NULL
and the DC is not for DISPLAY
--*/
HDC
APIHOOK(CreateDCA)(
LPCSTR pszDriver,
LPCSTR pszDevice,
LPCSTR pszPort,
CONST DEVMODEA *pdm
)
{
// if they've used a NULL device, but included a printer devmode,
// fill in the device name from the printer devmode
if (!pszDevice && pdm && (!pszDriver || _stricmp(pszDriver, "DISPLAY") != 0)) {
LOGN( eDbgLevelError, "[CreateDCA] App passed NULL for pszDevice. Fixing.");
pszDevice = (LPCSTR)pdm->dmDeviceName;
}
return ORIGINAL_API(CreateDCA)(
pszDriver,
pszDevice,
pszPort,
pdm
);
}
/*++
This stub function verifies that ResetDCA hasn't been handed an
uninitialized InitData.
--*/
HDC
APIHOOK(ResetDCA)(
HDC hdc,
CONST DEVMODEA *lpInitData
)
{
// Sanity checks to make sure we aren't getting garbage
// or bad values.
if (lpInitData &&
(lpInitData->dmSize > sizeof( DEVMODEA ) ||
( lpInitData->dmSpecVersion != 0x401 &&
lpInitData->dmSpecVersion != 0x400 &&
lpInitData->dmSpecVersion != 0x320 ) ) )
{
LOGN( eDbgLevelError, "[ResetDCA] App passed bad DEVMODE structure, nulling.");
return ORIGINAL_API(ResetDCA)( hdc, NULL );
}
return ORIGINAL_API(ResetDCA)( hdc, lpInitData );
}
/*++
These stub functions verify that SetPrinter has a valid handle
before proceeding.
--*/
BOOL
APIHOOK(SetPrinterA)(
HANDLE hPrinter, // handle to printer object
DWORD Level, // information level
LPBYTE pPrinter, // printer data buffer
DWORD Command // printer-state command
)
{
BOOL bRet;
if (hPrinter == NULL)
{
LOGN(eDbgLevelError, "[SetPrinterA] Called with null handle.");
if (pPrinter == NULL)
LOGN( eDbgLevelError, "[SetPrinterA] Called with null printer data buffer.");
return FALSE;
}
else if (pPrinter == NULL)
{
LOGN(eDbgLevelError, "[SetPrinterA] Called with null printer data buffer.");
return FALSE;
}
bRet= ORIGINAL_API(SetPrinterA)(
hPrinter,
Level,
pPrinter,
Command);
DPFN( eDbgLevelSpew, "[SetPrinterA] Level= %d Command= %d Ret= %d\n",
Level, Command, bRet );
return bRet;
}
/*++
This routine hooks the SetAbortProc and replaces their
callback with ours.
--*/
int
APIHOOK(SetAbortProc)(
HDC hdc, // handle to DC
ABORTPROC lpAbortProc // abort function
)
{
lpAbortProc = (ABORTPROC) HookCallback(lpAbortProc, AbortProcHook);
return ORIGINAL_API(SetAbortProc)( hdc, lpAbortProc );
}
/*++
When apps start printing, they set a viewport
on the printDC. They then call StartPage which has a different behaviour
on 9x and WinNT. On 9x, a next call to StartPage resets the DC attributes
to the default values.However on NT, the next call to StartPage does not
reset the DC attributes.
So, on 9x all subsequent output setup and drawing calls are carried
out with a (0,0) viewport but on NT the viewport is leftover from its
initial call. Since some apps(eg. Quicken 2000 and 2001) expect the API
setting the (0,0) viewport,the result will be that the text and the
lines are clipped on the left and top of the page.
Here we hook StartPage and call SetViewportOrgEx(hdc, 0, 0, NULL) to
set the viewport to (0,0) on every call to StartPage to emulate
the 9x behaviour.
--*/
BOOL
APIHOOK(StartPage)(
HDC hdc
)
{
if (SetViewportOrgEx(hdc, 0, 0, NULL))
{
// We have now made the device point(viewport) map to (0, 0).
LOGN(eDbgLevelInfo, "[StartPage] Setting the device point map to (0,0).");
}
else
{
LOGN(eDbgLevelError, "[StartPage] Unable to set device point map to (0,0)."
"Failed in a call to SetViewportOrgEx");
}
return ORIGINAL_API(StartPage)(hdc);
}
/*++
This stub function verifies that DeviceCapabilities is using a correct
printer name.
--*/
DWORD
APIHOOK(DeviceCapabilitiesA)(
LPCSTR pDevice,
LPCSTR pPort,
WORD fwCapability,
LPSTR pOutput,
CONST DEVMODE *pDevMode
)
{
DWORD dwRet;
CSTRING_TRY
{
if ( pDevice &&
!g_csPartialPrinterName.IsEmpty() &&
!g_csFullPrinterName.IsEmpty())
{
CString csTemp(pDevice);
if (0 == g_csPartialPrinterName.Compare(csTemp))
{
pDevice = g_csFullPrinterName.GetAnsi();
}
}
}
CSTRING_CATCH
{
// Do nothing
}
dwRet= ORIGINAL_API(DeviceCapabilitiesA)( pDevice,
pPort,
fwCapability,
pOutput,
pDevMode );
if (pDevice && pPort)
{
DPFN( eDbgLevelSpew, "[DeviceCapabilitiesA] pDevice= %s pPort= %s fwC= %d Out= %x RC= %d\n",
pDevice, pPort, fwCapability, pOutput, dwRet );
}
else
{
DPFN( eDbgLevelSpew, "[DeviceCapabilitiesA] fwC= %d Out= %x RC= %d\n",
fwCapability, pOutput, dwRet );
}
return dwRet;
}
/*++
This stub function verifies that AddPrinterConnection is using a correct
printer name.
--*/
BOOL
APIHOOK(AddPrinterConnectionA)(
LPSTR pName
)
{
CSTRING_TRY
{
if (pName &&
!g_csPartialPrinterName.IsEmpty() &&
!g_csFullPrinterName.IsEmpty())
{
CString csTemp(pName);
if (0 == g_csPartialPrinterName.Compare(csTemp))
{
pName = g_csFullPrinterName.GetAnsi();
}
}
}
CSTRING_CATCH
{
// Do nothing
}
return ORIGINAL_API(AddPrinterConnectionA)(pName);
}
/*++
This stub function verifies that DeletePrinterConnection is using a correct
printer name.
--*/
BOOL
APIHOOK(DeletePrinterConnectionA)(
LPSTR pName
)
{
CSTRING_TRY
{
if (pName &&
!g_csPartialPrinterName.IsEmpty() &&
!g_csFullPrinterName.IsEmpty())
{
CString csTemp(pName);
if (0 == g_csPartialPrinterName.Compare(csTemp))
{
pName = g_csFullPrinterName.GetAnsi();
}
}
}
CSTRING_CATCH
{
// Do nothing
}
return ORIGINAL_API(DeletePrinterConnectionA)(pName);
}
/*++
This stub function verifies that PrintDlgA is using a correct
nFromPage and nToPage.
--*/
BOOL
APIHOOK(PrintDlgA)(
LPPRINTDLG lppd
)
{
// check nFromPage and nToPage for legal values.
if (lppd)
{
DPFN(eDbgLevelSpew, "[PrintDlgA] nFromPage = %d nToPage = %d",
lppd->nFromPage, lppd->nToPage);
if (lppd->nFromPage == 0xffff)
{
lppd->nFromPage = 0;
DPFN( eDbgLevelInfo, "[PrintDlgA] Setting nFromPage to 0." );
}
if (lppd->nToPage == 0xffff)
{
lppd->nToPage = lppd->nFromPage;
DPFN( eDbgLevelInfo, "[PrintDlgA] Setting nToPage to %d.", lppd->nFromPage );
}
}
return ORIGINAL_API(PrintDlgA)(lppd);
}
/*++
Register hooked functions
--*/
BOOL
NOTIFY_FUNCTION(
DWORD fdwReason)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;
if (!InitializeCriticalSectionAndSpinCount(&g_critSec,0x80000000))
{
return FALSE;
}
// Check to see if we are under win2k
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
if(bOsVersionInfoEx)
{
if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT &&
osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
{
g_bWin2k = TRUE;
}
}
}
return TRUE;
}
HOOK_BEGIN
CALL_NOTIFY_FUNCTION
APIHOOK_ENTRY(WINSPOOL.DRV, DocumentPropertiesA);
APIHOOK_ENTRY(WINSPOOL.DRV, OpenPrinterA);
APIHOOK_ENTRY(WINSPOOL.DRV, SetPrinterA);
APIHOOK_ENTRY(WINSPOOL.DRV, EnumPrintersA);
APIHOOK_ENTRY(WINSPOOL.DRV, OpenPrinterA);
APIHOOK_ENTRY(WINSPOOL.DRV, DeviceCapabilitiesA);
APIHOOK_ENTRY(WINSPOOL.DRV, AddPrinterConnectionA);
APIHOOK_ENTRY(WINSPOOL.DRV, DeletePrinterConnectionA);
APIHOOK_ENTRY(COMDLG32.DLL, PrintDlgA);
APIHOOK_ENTRY(KERNEL32.DLL,GetProfileStringA);
APIHOOK_ENTRY(GDI32.DLL, CreateDCA);
APIHOOK_ENTRY(GDI32.DLL, ResetDCA);
APIHOOK_ENTRY(GDI32.DLL, SetAbortProc);
APIHOOK_ENTRY(GDI32.DLL, StartPage);
HOOK_END
IMPLEMENT_SHIM_END