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.
681 lines
14 KiB
681 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
libutil.c
|
|
|
|
Abstract:
|
|
|
|
Utility functions
|
|
|
|
Environment:
|
|
|
|
Windows NT printer drivers
|
|
|
|
Revision History:
|
|
|
|
08/13/96 -davidx-
|
|
Added CopyString functions and moved SPRINTF functions.
|
|
|
|
08/13/96 -davidx-
|
|
Added devmode conversion routine and spooler API wrapper functions.
|
|
|
|
03/13/96 -davidx-
|
|
Created it.
|
|
|
|
--*/
|
|
|
|
#include "lib.h"
|
|
|
|
//
|
|
// Digit characters used for converting numbers to ASCII
|
|
//
|
|
|
|
const CHAR gstrDigitString[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
//
|
|
// Variable to control the amount of debug messages generated
|
|
//
|
|
|
|
#if DBG
|
|
|
|
INT giDebugLevel = DBG_WARNING;
|
|
|
|
#endif
|
|
|
|
|
|
DWORD
|
|
HashKeyword(
|
|
LPCSTR pKeywordStr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a hash value for the given string.
|
|
|
|
Arguments:
|
|
|
|
pKeywordStr - The string to generate the hash value for,
|
|
single byte ANSI null terminated.
|
|
|
|
Return Value:
|
|
|
|
Hash value.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPBYTE pbuf = (LPBYTE) pKeywordStr;
|
|
DWORD dwHashValue = 0;
|
|
|
|
//
|
|
// Note that only the last 32 characters of the keyword string are significant.
|
|
//
|
|
|
|
while (*pbuf)
|
|
dwHashValue = (dwHashValue << 1) ^ *pbuf++;
|
|
|
|
return(dwHashValue);
|
|
}
|
|
|
|
|
|
|
|
PTSTR
|
|
DuplicateString(
|
|
IN LPCTSTR ptstrSrc
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make a duplicate of the specified character string
|
|
|
|
Arguments:
|
|
|
|
ptstrSrc - Specifies the source string to be duplicated
|
|
|
|
Return Value:
|
|
|
|
Pointer to the duplicated string, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR ptstrDest;
|
|
INT iSize;
|
|
|
|
if (ptstrSrc == NULL)
|
|
return NULL;
|
|
|
|
iSize = SIZE_OF_STRING(ptstrSrc);
|
|
|
|
if (ptstrDest = MemAlloc(iSize))
|
|
CopyMemory(ptstrDest, ptstrSrc, iSize);
|
|
else
|
|
ERR(("Couldn't duplicate string: %ws\n", ptstrSrc));
|
|
|
|
return ptstrDest;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CopyStringW(
|
|
OUT PWSTR pwstrDest,
|
|
IN PCWSTR pwstrSrc,
|
|
IN INT iDestSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy Unicode string from source to destination
|
|
|
|
Arguments:
|
|
|
|
pwstrDest - Points to the destination buffer
|
|
pwstrSrc - Points to source string
|
|
iDestSize - Size of destination buffer (in characters)
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
If the source string is shorter than the destination buffer,
|
|
unused chars in the destination buffer is filled with NUL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR pwstrEnd;
|
|
|
|
ASSERT(pwstrDest && pwstrSrc && iDestSize > 0);
|
|
pwstrEnd = pwstrDest + (iDestSize - 1);
|
|
|
|
while ((pwstrDest < pwstrEnd) && ((*pwstrDest++ = *pwstrSrc++) != NUL))
|
|
NULL;
|
|
|
|
while (pwstrDest <= pwstrEnd)
|
|
*pwstrDest++ = NUL;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CopyStringA(
|
|
OUT PSTR pstrDest,
|
|
IN PCSTR pstrSrc,
|
|
IN INT iDestSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy ANSI string from source to destination
|
|
|
|
Arguments:
|
|
|
|
pstrDest - Points to the destination buffer
|
|
pstrSrc - Points to source string
|
|
iDestSize - Size of destination buffer (in characters)
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
If the source string is shorter than the destination buffer,
|
|
unused chars in the destination buffer is filled with NUL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstrEnd;
|
|
|
|
ASSERT(pstrDest && pstrSrc && iDestSize > 0);
|
|
pstrEnd = pstrDest + (iDestSize - 1);
|
|
|
|
while ((pstrDest < pstrEnd) && (*pstrDest++ = *pstrSrc++) != NUL)
|
|
NULL;
|
|
|
|
while (pstrDest <= pstrEnd)
|
|
*pstrDest++ = NUL;
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
MyGetPrinter(
|
|
IN HANDLE hPrinter,
|
|
IN DWORD dwLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for GetPrinter spooler API
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Identifies the printer in question
|
|
dwLevel - Specifies the level of PRINTER_INFO_x structure requested
|
|
|
|
Return Value:
|
|
|
|
Pointer to a PRINTER_INFO_x structure, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pv = NULL;
|
|
DWORD dwBytesNeeded;
|
|
|
|
if (!GetPrinter(hPrinter, dwLevel, NULL, 0, &dwBytesNeeded) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pv = MemAlloc(dwBytesNeeded)) &&
|
|
GetPrinter(hPrinter, dwLevel, pv, dwBytesNeeded, &dwBytesNeeded))
|
|
{
|
|
return pv;
|
|
}
|
|
|
|
ERR(("GetPrinter failed: %d\n", GetLastError()));
|
|
MemFree(pv);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
MyEnumForms(
|
|
IN HANDLE hPrinter,
|
|
IN DWORD dwLevel,
|
|
OUT PDWORD pdwFormsReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for EnumForms spooler API
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Identifies the printer in question
|
|
dwLevel - Specifies the level of FORM_INFO_x structure requested
|
|
pdwFormsReturned - Returns the number of FORM_INFO_x structures enumerated
|
|
|
|
Return Value:
|
|
|
|
Pointer to an array of FORM_INFO_x structures,
|
|
NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pv = NULL;
|
|
DWORD dwBytesNeeded;
|
|
|
|
if (!EnumForms(hPrinter, dwLevel, NULL, 0, &dwBytesNeeded, pdwFormsReturned) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pv = MemAlloc(dwBytesNeeded)) &&
|
|
EnumForms(hPrinter, dwLevel, pv, dwBytesNeeded, &dwBytesNeeded, pdwFormsReturned))
|
|
{
|
|
return pv;
|
|
}
|
|
|
|
ERR(("EnumForms failed: %d\n", GetLastError()));
|
|
MemFree(pv);
|
|
*pdwFormsReturned = 0;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
#ifndef KERNEL_MODE
|
|
|
|
PVOID
|
|
MyGetForm(
|
|
IN HANDLE hPrinter,
|
|
IN PTSTR ptstrFormName,
|
|
IN DWORD dwLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for GetForm spooler API
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Identifies the printer in question
|
|
ptstrFormName - Specifies the name of interested form
|
|
dwLevel - Specifies the level of FORM_INFO_x structure requested
|
|
|
|
Return Value:
|
|
|
|
Pointer to a FORM_INFO_x structures, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pv = NULL;
|
|
DWORD cb;
|
|
|
|
if (!GetForm(hPrinter, ptstrFormName, dwLevel, NULL, 0, &cb) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pv = MemAlloc(cb)) &&
|
|
GetForm(hPrinter, ptstrFormName, dwLevel, pv, cb, &cb))
|
|
{
|
|
return pv;
|
|
}
|
|
|
|
ERR(("GetForm failed: %d\n", GetLastError()));
|
|
MemFree(pv);
|
|
return NULL;
|
|
}
|
|
|
|
#endif // !KERNEL_MODE
|
|
|
|
|
|
|
|
PVOID
|
|
MyGetPrinterDriver(
|
|
IN HANDLE hPrinter,
|
|
IN HDEV hDev,
|
|
IN DWORD dwLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for GetPrinterDriver spooler API
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Identifies the printer in question
|
|
hDev - GDI handle to current printer device context
|
|
dwLevel - Specifies the level of DRIVER_INFO_x structure requested
|
|
|
|
Return Value:
|
|
|
|
Pointer to a DRIVER_INFO_x structure, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
#if !defined(WINNT_40) || !defined(KERNEL_MODE)
|
|
|
|
PVOID pv = NULL;
|
|
DWORD dwBytesNeeded;
|
|
|
|
if (!GetPrinterDriver(hPrinter, NULL, dwLevel, NULL, 0, &dwBytesNeeded) &&
|
|
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
|
(pv = MemAlloc(dwBytesNeeded)) &&
|
|
GetPrinterDriver(hPrinter, NULL, dwLevel, pv, dwBytesNeeded, &dwBytesNeeded))
|
|
{
|
|
return pv;
|
|
}
|
|
|
|
ERR(("GetPrinterDriver failed: %d\n", GetLastError()));
|
|
MemFree(pv);
|
|
|
|
#else // WINNT_40 && KERNEL_MODE
|
|
|
|
PDRIVER_INFO_3 pDriverInfo3 = NULL;
|
|
|
|
ASSERT(hDev != NULL);
|
|
|
|
if (hDev)
|
|
{
|
|
//
|
|
// hDev is available, so we can use Eng-calls to get driver_info_3 fields.
|
|
//
|
|
|
|
PWSTR pwstrDriverFile, pwstrDataFile;
|
|
INT iDriverNameSize, iDataNameSize;
|
|
PWSTR pwstrDepFiles;
|
|
DWORD dwDepSize, dwDepSizeWithPath;
|
|
PTSTR ptstrDriverDir = NULL;
|
|
|
|
//
|
|
// EngGetPrinterDriver is not available on NT4. So we'll fake a
|
|
// DRIVER_INFO_3 structure and fill in pDriverPath and pDataFile fields.
|
|
//
|
|
|
|
pwstrDriverFile = EngGetDriverName(hDev);
|
|
pwstrDataFile = EngGetPrinterDataFileName(hDev);
|
|
|
|
if (!pwstrDriverFile || !pwstrDataFile)
|
|
{
|
|
RIP(("Driver and/or data filename is NULL\n"));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// The pDependentFiles field is currently only used by PS driver.
|
|
//
|
|
|
|
pwstrDepFiles = PtstrGetPrinterDataString(hPrinter, REGVAL_DEPFILES, &dwDepSize);
|
|
|
|
if (pwstrDepFiles && !BVerifyMultiSZ(pwstrDepFiles, dwDepSize))
|
|
{
|
|
RIP(("Dependent file list is not in MULTI_SZ format\n"));
|
|
MemFree(pwstrDepFiles);
|
|
pwstrDepFiles = NULL;
|
|
}
|
|
|
|
if (pwstrDepFiles && ((ptstrDriverDir = PtstrGetDriverDirectory((LPCTSTR)pwstrDriverFile)) == NULL))
|
|
{
|
|
RIP(("Can't get driver directory from driver file name\n"));
|
|
MemFree(pwstrDepFiles);
|
|
pwstrDepFiles = NULL;
|
|
}
|
|
|
|
iDriverNameSize = SIZE_OF_STRING(pwstrDriverFile);
|
|
iDataNameSize = SIZE_OF_STRING(pwstrDataFile);
|
|
|
|
if (pwstrDepFiles == NULL)
|
|
dwDepSizeWithPath = 0;
|
|
else
|
|
dwDepSizeWithPath = dwDepSize + DwCountStringsInMultiSZ((LPCTSTR)pwstrDepFiles)
|
|
* _tcslen(ptstrDriverDir) * sizeof(TCHAR);
|
|
|
|
pDriverInfo3 = MemAllocZ(sizeof(DRIVER_INFO_3) + iDriverNameSize+iDataNameSize+dwDepSizeWithPath);
|
|
|
|
if (pDriverInfo3 == NULL)
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
MemFree(pwstrDepFiles);
|
|
MemFree(ptstrDriverDir);
|
|
return NULL;
|
|
}
|
|
|
|
pDriverInfo3->cVersion = 3;
|
|
pDriverInfo3->pDriverPath = (PWSTR) ((PBYTE) pDriverInfo3 + sizeof(DRIVER_INFO_3));
|
|
pDriverInfo3->pDataFile = (PWSTR) ((PBYTE) pDriverInfo3->pDriverPath + iDriverNameSize);
|
|
|
|
CopyMemory(pDriverInfo3->pDriverPath, pwstrDriverFile, iDriverNameSize);
|
|
CopyMemory(pDriverInfo3->pDataFile, pwstrDataFile, iDataNameSize);
|
|
|
|
if (pwstrDepFiles)
|
|
{
|
|
PTSTR ptstrSrc, ptstrDest;
|
|
INT iDirLen;
|
|
|
|
ptstrSrc = pwstrDepFiles;
|
|
ptstrDest = pDriverInfo3->pDependentFiles = (PWSTR) ((PBYTE) pDriverInfo3->pDataFile + iDataNameSize);
|
|
|
|
iDirLen = _tcslen(ptstrDriverDir);
|
|
|
|
while (*ptstrSrc)
|
|
{
|
|
INT iNameLen;
|
|
|
|
//
|
|
// Copy the driver dir path (the last char is '\')
|
|
//
|
|
|
|
CopyMemory(ptstrDest, ptstrDriverDir, iDirLen * sizeof(TCHAR));
|
|
ptstrDest += iDirLen;
|
|
|
|
//
|
|
// Copy the dependent file name
|
|
//
|
|
|
|
iNameLen = _tcslen(ptstrSrc);
|
|
CopyMemory(ptstrDest, ptstrSrc, iNameLen * sizeof(TCHAR));
|
|
ptstrDest += iNameLen + 1;
|
|
|
|
ptstrSrc += iNameLen + 1;
|
|
}
|
|
}
|
|
|
|
MemFree(pwstrDepFiles);
|
|
MemFree(ptstrDriverDir);
|
|
|
|
return((PVOID)pDriverInfo3);
|
|
}
|
|
|
|
#endif // WINNT_40 && KERNEL_MODE
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
VGetSpoolerEmfCaps(
|
|
IN HANDLE hPrinter,
|
|
OUT PBOOL pbNupOption,
|
|
OUT PBOOL pbReversePrint,
|
|
IN DWORD cbOut,
|
|
OUT PVOID pSplCaps
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Figure out what EMF features (such as N-up and reverse-order printing)
|
|
the spooler can support
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to the current printer
|
|
pbNupOption - Whether spooler supports N-up
|
|
pbReversePrint - Whether spooler supports reverse-order printing
|
|
cbOut - size in byte of output buffer pointed by pSplCaps
|
|
pSplCaps - Get all spooler caps
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
#define REGVAL_EMFCAPS TEXT("PrintProcCaps_EMF")
|
|
|
|
{
|
|
PVOID pvData;
|
|
DWORD dwSize, dwType, dwStatus;
|
|
|
|
if (pbNupOption)
|
|
*pbNupOption = FALSE;
|
|
|
|
if (pbReversePrint)
|
|
*pbReversePrint = FALSE;
|
|
|
|
#if !defined(WINNT_40)
|
|
|
|
pvData = NULL;
|
|
dwStatus = GetPrinterData(hPrinter, REGVAL_EMFCAPS, &dwType, NULL, 0, &dwSize);
|
|
|
|
if ((dwStatus == ERROR_MORE_DATA || dwStatus == ERROR_SUCCESS) &&
|
|
(dwSize >= sizeof(PRINTPROCESSOR_CAPS_1)) &&
|
|
(pvData = MemAlloc(dwSize)) &&
|
|
(GetPrinterData(hPrinter, REGVAL_EMFCAPS, &dwType, pvData, dwSize, &dwSize) == ERROR_SUCCESS))
|
|
{
|
|
PPRINTPROCESSOR_CAPS_1 pEmfCaps = pvData;
|
|
|
|
if (pbNupOption)
|
|
*pbNupOption = (pEmfCaps->dwNupOptions & ~1) != 0;
|
|
|
|
if (pbReversePrint)
|
|
*pbReversePrint = (pEmfCaps->dwPageOrderFlags & REVERSE_PRINT) != 0;
|
|
|
|
if (pSplCaps)
|
|
{
|
|
CopyMemory(pSplCaps,
|
|
pEmfCaps,
|
|
min(cbOut, sizeof(PRINTPROCESSOR_CAPS_1)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR(("GetPrinterData PrintProcCaps_EMF failed: %d\n", dwStatus));
|
|
}
|
|
|
|
MemFree(pvData);
|
|
|
|
#endif // !WINNT_40
|
|
}
|
|
|
|
|
|
|
|
PCSTR
|
|
StripDirPrefixA(
|
|
IN PCSTR pstrFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Strip the directory prefix off a filename (ANSI version)
|
|
|
|
Arguments:
|
|
|
|
pstrFilename - Pointer to filename string
|
|
|
|
Return Value:
|
|
|
|
Pointer to the last component of a filename (without directory prefix)
|
|
|
|
--*/
|
|
|
|
{
|
|
PCSTR pstr;
|
|
|
|
if (pstr = strrchr(pstrFilename, PATH_SEPARATOR))
|
|
return pstr + 1;
|
|
|
|
return pstrFilename;
|
|
}
|
|
|
|
|
|
#if !defined(KERNEL_MODE) || defined(USERMODE_DRIVER)
|
|
|
|
|
|
PVOID
|
|
MemRealloc(
|
|
IN PVOID pvOldMem,
|
|
IN DWORD cbOld,
|
|
IN DWORD cbNew
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change the size of a specified memory block. The size can increase
|
|
or decrease.
|
|
|
|
Arguments:
|
|
|
|
pvOldMem - Pointer to the old memory block to be reallocated.
|
|
cbOld - old size in bytes of the memory block
|
|
cbNew - new size in bytes of the reallocated memory block
|
|
|
|
Return Value:
|
|
|
|
If succeeds, it returns pointer to the reallocated memory block.
|
|
Otherwise, it returns NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID pvNewMem;
|
|
|
|
if (!(pvNewMem = MemAlloc(cbNew)))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
if (pvOldMem)
|
|
{
|
|
if (cbOld)
|
|
{
|
|
CopyMemory(pvNewMem, pvOldMem, min(cbOld, cbNew));
|
|
}
|
|
|
|
MemFree(pvOldMem);
|
|
}
|
|
|
|
return pvNewMem;
|
|
}
|
|
|
|
#endif // !KERNEL_MODE || USERMODE_DRIVER
|