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.
1378 lines
33 KiB
1378 lines
33 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
prnevent.c
|
|
|
|
Abstract:
|
|
|
|
This file handles the DrvPrinterEvent spooler API.
|
|
|
|
Environment:
|
|
|
|
Win32 subsystem, DriverUI module, user mode
|
|
|
|
Revision History:
|
|
|
|
08/30/00 -fengy-
|
|
Added DrvDocumentEvent support.
|
|
|
|
02/13/97 -davidx-
|
|
Implement OEM plugin support.
|
|
|
|
02/06/97 -davidx-
|
|
Rewrote it to use common data management functions.
|
|
|
|
02/04/97 -davidx-
|
|
Reorganize driver UI to separate ps and uni DLLs.
|
|
|
|
07/17/96 -amandan-
|
|
Modified for common UI and shared binary data
|
|
|
|
05/20/96 -davidx-
|
|
Created it.
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
//
|
|
// DOCUMENTEVENT_QUERYFILTER is introduced in Whistler.
|
|
// We need to define it if it's not defined (on Win2K)
|
|
// so our code can be built with Win2K DDK.
|
|
//
|
|
|
|
#ifndef DOCUMENTEVENT_QUERYFILTER
|
|
|
|
#define DOCUMENTEVENT_QUERYFILTER 14
|
|
|
|
#endif
|
|
|
|
//
|
|
// Private APIs exported by the spooler for printer drivers and port monitors.
|
|
// These must be kept in sync with winsplp.h.
|
|
//
|
|
|
|
typedef HANDLE (*LPREVERTTOPRINTERSELF)(VOID);
|
|
typedef BOOL (*LPIMPERSONATEPRINTERCLIENT)(HANDLE);
|
|
|
|
//
|
|
// Forward and external function declarations
|
|
//
|
|
|
|
BOOL BInitOrUpgradePrinterProperties(PCOMMONINFO);
|
|
VOID DeleteFontIntallerFile(HANDLE);
|
|
|
|
PTSTR
|
|
GetBinaryFileName(
|
|
PTSTR ptstrDataFileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function generates a binary file name from the data file name.
|
|
|
|
Arguments:
|
|
|
|
ptstrDataFileName specifies the data file name
|
|
|
|
Return Value:
|
|
|
|
TRUE for success and FALSE for failure
|
|
|
|
Note:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
INT iLen;
|
|
PTSTR ptstrFileName, ptstrExtension;
|
|
PTSTR ptstrBinaryExt, ptstrFileExt;
|
|
|
|
ptstrFileName = ptstrExtension = NULL;
|
|
|
|
#ifdef UNIDRV
|
|
|
|
ptstrBinaryExt = BUD_FILENAME_EXT;
|
|
ptstrFileExt = GPD_FILENAME_EXT;
|
|
|
|
#else
|
|
|
|
ptstrBinaryExt = BPD_FILENAME_EXT;
|
|
ptstrFileExt = PPD_FILENAME_EXT;
|
|
|
|
#endif
|
|
|
|
iLen = _tcslen(ptstrDataFileName);
|
|
|
|
if ((ptstrExtension = _tcsrchr(ptstrDataFileName, TEXT('.'))) == NULL ||
|
|
_tcsicmp(ptstrExtension, ptstrFileExt) != EQUAL_STRING)
|
|
{
|
|
ptstrExtension = ptstrDataFileName + iLen;
|
|
iLen += _tcslen(ptstrBinaryExt);
|
|
}
|
|
|
|
if (ptstrFileName = MemAlloc((iLen + 1) * sizeof(TCHAR)))
|
|
{
|
|
StringCchCopyW(ptstrFileName, iLen + 1, ptstrDataFileName);
|
|
|
|
//
|
|
// The first if-block ensures that (ptstrExtension - ptstrDataFileName) is
|
|
// non-negative, and (iLen + 1) is greater than (ptstrExtension - ptstrDataFileName).
|
|
//
|
|
StringCchCopyW(ptstrFileName + (ptstrExtension - ptstrDataFileName),
|
|
(iLen + 1) - (ptstrExtension - ptstrDataFileName),
|
|
ptstrBinaryExt);
|
|
}
|
|
|
|
return ptstrFileName;
|
|
}
|
|
|
|
BOOL
|
|
DrvDriverEvent(
|
|
DWORD dwDriverEvent,
|
|
DWORD dwLevel,
|
|
LPBYTE pDriverInfo,
|
|
LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles the DrvDriverEvent spooler API
|
|
|
|
Arguments:
|
|
|
|
dwDriverEvent specifies the event
|
|
dwLevel level of DRIVER_INFO_*
|
|
pDriverInfo pointer to DRIVER_INFO_*
|
|
lParam event specific parameters.
|
|
|
|
Return Value:
|
|
|
|
TRUE for success and FALSE for failure
|
|
|
|
Note:
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bResult = TRUE;
|
|
|
|
#ifndef WINNT_40
|
|
|
|
PDRIVER_INFO_3 pDriverInfo3 = (PDRIVER_INFO_3)pDriverInfo;
|
|
PTSTR ptstrFileName;
|
|
|
|
if ((dwLevel != 3) || (pDriverInfo3 == NULL) || (pDriverInfo3->pDataFile == NULL))
|
|
return FALSE;
|
|
|
|
switch (dwDriverEvent)
|
|
{
|
|
case DRIVER_EVENT_INITIALIZE:
|
|
break;
|
|
|
|
case DRIVER_EVENT_DELETE:
|
|
|
|
//
|
|
// Need to delete the binary data generated by the parsers
|
|
//
|
|
|
|
ptstrFileName = GetBinaryFileName(pDriverInfo3->pDataFile);
|
|
|
|
if (ptstrFileName)
|
|
{
|
|
DeleteFile(ptstrFileName);
|
|
MemFree(ptstrFileName);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the OEM to handle DrvDriverEvent
|
|
//
|
|
|
|
{
|
|
PFN_OEMDriverEvent pfnOEMDriverEvent;
|
|
POEM_PLUGINS pOemPlugins;
|
|
POEM_PLUGIN_ENTRY pOemEntry;
|
|
DWORD dwOemCount;
|
|
|
|
if (! (pOemPlugins = PGetOemPluginInfo(NULL, pDriverInfo3->pConfigFile, pDriverInfo3)) ||
|
|
! BLoadOEMPluginModules(pOemPlugins))
|
|
{
|
|
ERR(("DrvDriverEvent, Cannot load OEM plugins: %d\n", GetLastError()));
|
|
if (pOemPlugins)
|
|
{
|
|
VFreeOemPluginInfo(pOemPlugins);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
dwOemCount = pOemPlugins->dwCount;
|
|
pOemEntry = pOemPlugins->aPlugins;
|
|
|
|
//
|
|
// call OEMDriverEvent entrypoint for each plugin
|
|
//
|
|
|
|
for (; dwOemCount--; pOemEntry++)
|
|
{
|
|
if (pOemEntry->hInstance == NULL)
|
|
continue;
|
|
|
|
if (HAS_COM_INTERFACE(pOemEntry))
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = HComOEMDriverEvent(pOemEntry,
|
|
dwDriverEvent,
|
|
dwLevel,
|
|
pDriverInfo,
|
|
lParam);
|
|
|
|
if (hr == E_NOTIMPL)
|
|
continue;
|
|
|
|
bResult = SUCCEEDED(hr);
|
|
|
|
}
|
|
else
|
|
{
|
|
if ((pfnOEMDriverEvent = GET_OEM_ENTRYPOINT(pOemEntry, OEMDriverEvent)) &&
|
|
!pfnOEMDriverEvent(dwDriverEvent, dwLevel, pDriverInfo, lParam))
|
|
{
|
|
ERR(("OEMDriverEvent failed for '%ws': %d\n",
|
|
CURRENT_OEM_MODULE_NAME(pOemEntry),
|
|
GetLastError()));
|
|
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pOemPlugins)
|
|
VFreeOemPluginInfo(pOemPlugins);
|
|
}
|
|
|
|
#endif // WINNT_40
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
DrvDocumentEvent
|
|
|
|
Routine Description:
|
|
|
|
Handle certain events associated with printing a document.
|
|
|
|
Although our core driver doesn't do anything for any events,
|
|
this function allows OEM plugins to add their event handling.
|
|
|
|
Arguments:
|
|
|
|
hPrinter - printer handle
|
|
hdc - device contect handle
|
|
iEsc - escape code identifying the event to be handled
|
|
cbIn - size in bytes of the array pointed to by pbIn
|
|
pbIn - pointer to a ULONG array, whose usage depends on iEsc
|
|
cbOut - only used as cbOutput parameter for ExtEscape
|
|
pbOut - pointer to an output buffer, whose usage depends on iEsc
|
|
|
|
Return Value:
|
|
|
|
DOCUMENTEVENT_FAILURE - iEsc event is supported but a failure occurred
|
|
DOCUMENTEVENT_SUCCESS - iEsc event is handled successfully
|
|
DOCUMENTEVENT_UNSUPPORTED - iEsc event is not supported
|
|
|
|
Last Error:
|
|
|
|
None
|
|
|
|
--*/
|
|
INT
|
|
DrvDocumentEvent(
|
|
HANDLE hPrinter,
|
|
HDC hdc,
|
|
int iEsc,
|
|
ULONG cbIn,
|
|
|
|
#ifdef WINNT_40
|
|
PULONG pbIn,
|
|
#else
|
|
PVOID pbIn,
|
|
#endif
|
|
|
|
ULONG cbOut,
|
|
|
|
#ifdef WINNT_40
|
|
PULONG pbOut
|
|
#else
|
|
PVOID pbOut
|
|
#endif
|
|
)
|
|
{
|
|
POEM_PLUGINS pOemPlugins = NULL;
|
|
PDRIVER_INFO_3 pDriverInfo3 = NULL;
|
|
INT iReturn;
|
|
|
|
if ((pDriverInfo3 = MyGetPrinterDriver(hPrinter, NULL, 3)) == NULL)
|
|
{
|
|
ERR(("Cannot get printer driver info: %d\n", GetLastError()));
|
|
iReturn = DOCUMENTEVENT_FAILURE;
|
|
goto docevent_exit;
|
|
}
|
|
|
|
if (!(pOemPlugins = PGetOemPluginInfo(hPrinter,
|
|
pDriverInfo3->pConfigFile,
|
|
pDriverInfo3)) ||
|
|
!BLoadOEMPluginModules(pOemPlugins))
|
|
{
|
|
ERR(("Cannot get OEM plugin info: %d\n", GetLastError()));
|
|
iReturn = DOCUMENTEVENT_FAILURE;
|
|
goto docevent_exit;
|
|
}
|
|
|
|
if (pOemPlugins->dwCount)
|
|
{
|
|
POEM_PLUGIN_ENTRY pOemEntry = pOemPlugins->aPlugins;
|
|
DWORD cOemCount = pOemPlugins->dwCount;
|
|
INT iResult;
|
|
BOOL bOEMDocEventOK = FALSE;
|
|
|
|
for ( ; cOemCount--; pOemEntry++)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pOemEntry->hInstance == NULL ||
|
|
!HAS_COM_INTERFACE(pOemEntry))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hr = HComOEMDocumentEvent(pOemEntry,
|
|
hPrinter,
|
|
hdc,
|
|
iEsc,
|
|
cbIn,
|
|
(PVOID)pbIn,
|
|
cbOut,
|
|
(PVOID)pbOut,
|
|
&iResult);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bOEMDocEventOK = TRUE;
|
|
|
|
#ifndef WINNT_40
|
|
|
|
//
|
|
// DOCUMENTEVENT_QUERYFILTER is introduced in Whistler.
|
|
//
|
|
|
|
if (iEsc == DOCUMENTEVENT_QUERYFILTER)
|
|
{
|
|
//
|
|
// At most one plugin is allowed to handle the event
|
|
// DOCUMENTEVENT_QUERYFILTER, and the filter it specifies
|
|
// will be used by spooler.
|
|
//
|
|
// For all other events, we will call every plugin so
|
|
// each will have the chance to perform its tasks.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
#endif // !WINNT_40
|
|
}
|
|
}
|
|
|
|
if (bOEMDocEventOK)
|
|
{
|
|
//
|
|
// At least one plugin handled the event successfully, so
|
|
// use the return value specified by the plugin(s).
|
|
//
|
|
|
|
iReturn = iResult;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// None of the plugins handled the event successfully.
|
|
//
|
|
|
|
iReturn = DOCUMENTEVENT_UNSUPPORTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There is no plugin.
|
|
//
|
|
|
|
iReturn = DOCUMENTEVENT_UNSUPPORTED;
|
|
}
|
|
|
|
docevent_exit:
|
|
|
|
if (pDriverInfo3)
|
|
{
|
|
MemFree(pDriverInfo3);
|
|
}
|
|
|
|
if (pOemPlugins)
|
|
{
|
|
VFreeOemPluginInfo(pOemPlugins);
|
|
}
|
|
|
|
//
|
|
// If there is no plugin, or none of the plugins handles DocumentEvent
|
|
// successfully, we return DOCUMENTEVENT_UNSUPPORTED since our driver
|
|
// doesn't do anything for DrvDocumentEvent. When spooler sees this
|
|
// return value for DOCUMENTEVENT_CREATEDCPRE, it will decide not to
|
|
// make any more event calls to the driver.
|
|
//
|
|
// If the event is handled successfully by the plugins, we will return
|
|
// the return value specified by the plugin(s).
|
|
//
|
|
|
|
return iReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DrvPrinterEvent(
|
|
LPWSTR pPrinterName,
|
|
INT DriverEvent,
|
|
DWORD Flags,
|
|
LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles the DrvPrinterEvent spooler API
|
|
|
|
Arguments:
|
|
|
|
pPrinterName name of device
|
|
DriverEvent specifies the event
|
|
Flags bits flag
|
|
lParam event specific parameters.
|
|
|
|
Return Value:
|
|
|
|
TRUE for success and FALSE for failure
|
|
|
|
Note:
|
|
|
|
--*/
|
|
|
|
{
|
|
LPREVERTTOPRINTERSELF pRevertToPrinterSelf;
|
|
LPIMPERSONATEPRINTERCLIENT pImpersonatePrinterClient;
|
|
HINSTANCE hSpoolss = NULL;
|
|
HANDLE hToken = NULL;
|
|
PCOMMONINFO pci = NULL;
|
|
HANDLE hPrinter = NULL;
|
|
BOOL bResult = TRUE;
|
|
CACHEDFILE CachedFile;
|
|
|
|
VERBOSE(("Entering DrvPrinterEvent: %d ...\n", DriverEvent));
|
|
|
|
switch (DriverEvent)
|
|
{
|
|
case PRINTER_EVENT_CACHE_REFRESH:
|
|
|
|
//
|
|
// Open a handle to the printer connection
|
|
//
|
|
|
|
if (! OpenPrinter(pPrinterName, &hPrinter, NULL))
|
|
{
|
|
ERR(("OpenPrinter '%ws' failed: %d\n", pPrinterName, GetLastError()));
|
|
hPrinter = NULL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Prepare to copy cached driver files from the server, if any
|
|
//
|
|
|
|
#ifdef PSCRIPT
|
|
_BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_NTFFILENAME);
|
|
#else
|
|
_BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_FONTFILENAME);
|
|
#endif
|
|
|
|
//
|
|
// Load spoolss.dll and get address of functions:
|
|
// RevertToPrinterSelf - switch to spooler's security context
|
|
// ImpersonatePrinterClient - switch to current user's security context
|
|
//
|
|
|
|
if (! (hSpoolss = LoadLibrary(TEXT("spoolss.dll"))) ||
|
|
! (pRevertToPrinterSelf = (LPREVERTTOPRINTERSELF)
|
|
GetProcAddress(hSpoolss, "RevertToPrinterSelf")) ||
|
|
! (pImpersonatePrinterClient = (LPIMPERSONATEPRINTERCLIENT)
|
|
GetProcAddress(hSpoolss, "ImpersonatePrinterClient")))
|
|
{
|
|
ERR(("Couldn't load spoolss.dll: %d\n", GetLastError()));
|
|
|
|
if (hSpoolss != NULL)
|
|
FreeLibrary(hSpoolss);
|
|
|
|
_VDisposeCachedFileInfo(&CachedFile);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Switch to spooler security context so that we can create
|
|
// binary printer description data file in the driver directory
|
|
//
|
|
// When we call to load raw printer description data, the parser
|
|
// will check its cache. If no binary data file exists or existing
|
|
// binary data file is out of date, the parser will regenerate
|
|
// an up-to-date binary data file.
|
|
//
|
|
|
|
hToken = pRevertToPrinterSelf();
|
|
pci = PLoadCommonInfo(hPrinter, pPrinterName, 0);
|
|
|
|
|
|
//
|
|
// Copy cached driver file from the server
|
|
//
|
|
|
|
_BCopyCachedFile(pci, &CachedFile);
|
|
_VDisposeCachedFileInfo(&CachedFile);
|
|
|
|
if (hToken)
|
|
{
|
|
if (!(bResult = pImpersonatePrinterClient(hToken)))
|
|
{
|
|
ERR(("PrinterEvent-ImpersonatePrinterClient failed: %d\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hSpoolss);
|
|
break;
|
|
|
|
case PRINTER_EVENT_INITIALIZE:
|
|
|
|
//
|
|
// Open a printer with administrator privilege, and
|
|
// process OEM plugin configuration information
|
|
//
|
|
|
|
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_ADMIN|FLAG_INIT_PRINTER);
|
|
|
|
if (pci == NULL)
|
|
break;
|
|
|
|
//
|
|
// Initialize default printer-sticky properties in registry
|
|
// Add printer forms to the spooler's forms database
|
|
//
|
|
|
|
(VOID) BInitOrUpgradePrinterProperties(pci);
|
|
|
|
#ifndef WINNT_40
|
|
|
|
VNotifyDSOfUpdate(pci->hPrinter);
|
|
|
|
#endif // !WINNT_40
|
|
|
|
|
|
break;
|
|
|
|
case PRINTER_EVENT_ADD_CONNECTION:
|
|
|
|
//
|
|
// Fix the bug where when NT5 client connects to NT4 server, the server registry
|
|
// doesn't have the REGVAL_INIDATA entry. Calling PLoadCommonInfo with
|
|
// FLAG_PROCESS_INIFILE will write REGVAL_INIDATA to NT4 registry.
|
|
//
|
|
|
|
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL|FLAG_PROCESS_INIFILE);
|
|
break;
|
|
|
|
#ifdef UNIDRV
|
|
|
|
case PRINTER_EVENT_DELETE:
|
|
case PRINTER_EVENT_DELETE_CONNECTION:
|
|
|
|
//
|
|
// Delete font installer file
|
|
//
|
|
|
|
//
|
|
// Open a handle to the printer
|
|
//
|
|
|
|
if (! OpenPrinter(pPrinterName, &hPrinter, NULL))
|
|
{
|
|
ERR(("OpenPrinter '%ws' failed: %d\n", pPrinterName, GetLastError()));
|
|
hPrinter = NULL;
|
|
break;
|
|
}
|
|
|
|
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL);
|
|
|
|
DeleteFontIntallerFile(hPrinter);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL);
|
|
break;
|
|
}
|
|
|
|
if (pci != NULL)
|
|
{
|
|
if (bResult)
|
|
{
|
|
PFN_OEMPrinterEvent pfnOEMPrinterEvent;
|
|
|
|
//
|
|
// call OEMPrinterEvent entrypoint for each plugin
|
|
//
|
|
|
|
FOREACH_OEMPLUGIN_LOOP(pci)
|
|
|
|
if (HAS_COM_INTERFACE(pOemEntry))
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = HComOEMPrinterEvent(pOemEntry,
|
|
pPrinterName,
|
|
DriverEvent,
|
|
Flags,
|
|
lParam);
|
|
|
|
if (hr == E_NOTIMPL)
|
|
continue;
|
|
|
|
bResult = SUCCEEDED(hr);
|
|
|
|
}
|
|
else
|
|
{
|
|
if ((pfnOEMPrinterEvent = GET_OEM_ENTRYPOINT(pOemEntry, OEMPrinterEvent)) &&
|
|
!pfnOEMPrinterEvent(pPrinterName, DriverEvent, Flags, lParam))
|
|
{
|
|
ERR(("OEMPrinterEvent failed for '%ws': %d\n",
|
|
CURRENT_OEM_MODULE_NAME(pOemEntry),
|
|
GetLastError()));
|
|
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
|
|
END_OEMPLUGIN_LOOP
|
|
}
|
|
VFreeCommonInfo(pci);
|
|
}
|
|
else
|
|
bResult = FALSE;
|
|
|
|
if (hPrinter != NULL)
|
|
ClosePrinter(hPrinter);
|
|
|
|
return (bResult);
|
|
}
|
|
|
|
|
|
BOOL
|
|
BInitOrUpgradePrinterData(
|
|
PCOMMONINFO pci
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the driver's printer-sticky property data
|
|
or upgrade it to current version if it already exists
|
|
|
|
Arguments:
|
|
|
|
pci - Points to basic printer info
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwSize;
|
|
BOOL bResult = TRUE;
|
|
|
|
//
|
|
// If the printer property data already exists in the registry
|
|
// and it's at least as big as the current PRINTERDATA, then
|
|
// we assume it's ok and there is no need to upgrade it.
|
|
//
|
|
|
|
if (!BGetPrinterDataDWord(pci->hPrinter, REGVAL_PRINTER_DATA_SIZE, &dwSize) ||
|
|
dwSize < sizeof(PRINTERDATA))
|
|
{
|
|
//
|
|
// Otherwise, upgrade the existing printer property data in the registry
|
|
// or save a copy of the default printer property data to registry.
|
|
//
|
|
|
|
bResult = BFillCommonInfoPrinterData(pci) &&
|
|
BSavePrinterProperties(pci->hPrinter, pci->pRawData,
|
|
pci->pPrinterData, sizeof(PRINTERDATA));
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BAddOrUpgradePrinterForms(
|
|
PCOMMONINFO pci
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add printer specific forms to the spooler's forms database
|
|
|
|
Arguments:
|
|
|
|
pci - Points to basic printer info
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PPAGESIZE pPageSize;
|
|
FORM_INFO_1 FormInfo1;
|
|
DWORD dwIndex, dwChecksum32, dwForm;
|
|
PFEATURE pFeature;
|
|
WCHAR awchBuf[CCHPAPERNAME];
|
|
|
|
//
|
|
// If forms has already been added and printer description
|
|
// data hasn't changed, we don't need to do anything
|
|
//
|
|
|
|
if (BGetPrinterDataDWord(pci->hPrinter, REGVAL_FORMS_ADDED, &dwChecksum32) &&
|
|
dwChecksum32 == pci->pRawData->dwChecksum32)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (pci->pSplForms == NULL)
|
|
pci->pSplForms = MyEnumForms(pci->hPrinter, 1, &pci->dwSplForms);
|
|
|
|
//
|
|
// Get pointer to PageSize feature
|
|
//
|
|
|
|
if ((pFeature = GET_PREDEFINED_FEATURE(pci->pUIInfo, GID_PAGESIZE)) == NULL)
|
|
{
|
|
WARNING(("No paper size supported\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&FormInfo1, sizeof(FormInfo1));
|
|
FormInfo1.Flags = FORM_PRINTER;
|
|
FormInfo1.pName = awchBuf;
|
|
|
|
//
|
|
// Go through each printer form
|
|
//
|
|
|
|
for (dwIndex=0; dwIndex < pFeature->Options.dwCount; dwIndex++)
|
|
{
|
|
pPageSize = PGetIndexedOption(pci->pUIInfo, pFeature, dwIndex);
|
|
ASSERT(pPageSize != NULL);
|
|
|
|
//
|
|
// Ignore the custom page size option
|
|
//
|
|
|
|
if (pPageSize->dwPaperSizeID == DMPAPER_USER ||
|
|
pPageSize->dwPaperSizeID == DMPAPER_CUSTOMSIZE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pPageSize->szPaperSize.cx <= 0 ||
|
|
pPageSize->szPaperSize.cy <= 0)
|
|
{
|
|
ERR(("Paper size is too small\n"));
|
|
continue;
|
|
}
|
|
|
|
if (! LOAD_STRING_PAGESIZE_NAME(pci, pPageSize, awchBuf, CCHPAPERNAME))
|
|
{
|
|
ERR(("Cannot get paper name\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if the paper name is already in the forms database.
|
|
// If it's already in the database as a
|
|
//
|
|
|
|
for (dwForm=0; dwForm < pci->dwSplForms; dwForm++)
|
|
{
|
|
if (pci->pSplForms[dwForm].Flags == FORM_USER &&
|
|
wcscmp(pci->pSplForms[dwForm].pName, awchBuf) == EQUAL_STRING)
|
|
{
|
|
VERBOSE(("Delete user/driver rdefined form: %ws\n", awchBuf));
|
|
DeleteForm(pci->hPrinter, awchBuf);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Page size:
|
|
// remember that FORM_INFO_1 uses micron units while
|
|
// PAGESIZE.szPaperSize are in Master units.
|
|
//
|
|
|
|
FormInfo1.Size.cx = MASTER_UNIT_TO_MICRON(pPageSize->szPaperSize.cx,
|
|
pci->pUIInfo->ptMasterUnits.x);
|
|
|
|
FormInfo1.Size.cy = MASTER_UNIT_TO_MICRON(pPageSize->szPaperSize.cy,
|
|
pci->pUIInfo->ptMasterUnits.y);
|
|
|
|
//
|
|
// Imageable area:
|
|
// for driver-defined forms, all margins should be set to 0.
|
|
//
|
|
|
|
FormInfo1.ImageableArea.left =
|
|
FormInfo1.ImageableArea.top = 0;
|
|
FormInfo1.ImageableArea.right = FormInfo1.Size.cx;
|
|
FormInfo1.ImageableArea.bottom = FormInfo1.Size.cy;
|
|
|
|
//
|
|
// We'll try to add the form first. If that fails,
|
|
// we assume the form is already there and try to
|
|
// update the form with the new info.
|
|
//
|
|
|
|
(VOID) AddForm(pci->hPrinter, 1, (PBYTE) &FormInfo1);
|
|
}
|
|
|
|
(VOID) BSetPrinterDataDWord(pci->hPrinter,
|
|
REGVAL_FORMS_ADDED,
|
|
pci->pRawData->dwChecksum32);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BInitOrUpgradePrinterProperties(
|
|
PCOMMONINFO pci
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize or upgrade printer property information in the registry
|
|
|
|
Arguments:
|
|
|
|
pci - Points to basic printer info
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bResult;
|
|
|
|
//
|
|
// Handle PRINTERDATA structure in registry
|
|
//
|
|
|
|
bResult = BInitOrUpgradePrinterData(pci);
|
|
|
|
//
|
|
// Handle driver-defined forms in the spooler's database
|
|
//
|
|
|
|
if (! BAddOrUpgradePrinterForms(pci))
|
|
bResult = FALSE;
|
|
|
|
#ifdef PSCRIPT
|
|
|
|
//
|
|
// pscript specific initializations
|
|
//
|
|
|
|
// Save model-specific NTF filename in registry for NT4 compatibility
|
|
|
|
if (! BUpdateModelNtfFilename(pci))
|
|
bResult = FALSE;
|
|
|
|
#ifdef WINNT_40
|
|
|
|
// Also save the current user locale too.
|
|
|
|
if (! BUpdateVMErrorMessageID(pci))
|
|
bResult = FALSE;
|
|
|
|
#endif // WINNT_40
|
|
|
|
#endif // PSCRIPT
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
PWSTR
|
|
PGetFileDirectory(
|
|
PWSTR pServerName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the name of the directory used by the font downloader
|
|
to store NTF information about downloaded fonts
|
|
|
|
Arguments:
|
|
|
|
pServerName - Name of the print server
|
|
|
|
Return Value:
|
|
|
|
Pointer to the directory used for storing NTF information
|
|
about downloaded fonts, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR p = NULL, pDir = NULL;
|
|
DWORD cbNeeded = 0, cbSize = 0;
|
|
static WCHAR wszDir[] = FONTDIR;
|
|
|
|
//
|
|
// Get the printer driver directory path
|
|
//
|
|
|
|
if (GetPrinterDriverDirectory(pServerName, NULL, 1, NULL, 0, &cbNeeded) ||
|
|
GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
|
|
(pDir = MemAlloc(cbSize = (cbNeeded + sizeof(wszDir)))) == NULL ||
|
|
!GetPrinterDriverDirectory(pServerName, NULL, 1, (PBYTE) pDir, cbNeeded, &cbNeeded))
|
|
{
|
|
ERR(("GetPrinterDriverDirectory failed: %d\n", GetLastError()));
|
|
MemFree(pDir);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Replace the last component of the directory path (which should be w32...)
|
|
// with \psfont\
|
|
//
|
|
|
|
if (p = wcsrchr(pDir, TEXT(PATH_SEPARATOR)))
|
|
StringCchCopyW(p, cbSize / sizeof(WCHAR) - (p - pDir), wszDir);
|
|
else
|
|
{
|
|
WARNING(("Driver directory is not fully-qualified: %ws\n", pDir));
|
|
StringCchCatW(pDir, cbSize / sizeof(WCHAR), wszDir);
|
|
}
|
|
|
|
return pDir;
|
|
}
|
|
|
|
|
|
|
|
PWSTR
|
|
PConcatFilename(
|
|
PWSTR pDir,
|
|
PWSTR pFilename
|
|
)
|
|
|
|
{
|
|
PWSTR pBasename;
|
|
DWORD cbSize = 0;
|
|
|
|
//
|
|
// Strip any directory prefix from the input filename
|
|
//
|
|
|
|
if (pBasename = wcsrchr(pFilename, TEXT(PATH_SEPARATOR)))
|
|
pBasename++;
|
|
else
|
|
pBasename = pFilename;
|
|
|
|
//
|
|
// Concatenate the input directory with the base filename
|
|
//
|
|
|
|
if (!(pFilename = MemAlloc(cbSize = (SIZE_OF_STRING(pDir) + SIZE_OF_STRING(pBasename)))))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
StringCchCopyW(pFilename, cbSize / sizeof(WCHAR), pDir);
|
|
StringCchCatW(pFilename, cbSize / sizeof(WCHAR), pBasename);
|
|
|
|
return pFilename;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
_BPrepareToCopyCachedFile(
|
|
HANDLE hPrinter,
|
|
PCACHEDFILE pCachedFile,
|
|
PWSTR pRegKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepare to copy files from the server during a printer-connection
|
|
cache refresh event
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to the printer connection
|
|
pCachedFile - Buffer to store information about cached file
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
Note:
|
|
|
|
We assume this function is called from within the spooler process
|
|
and with current user's security context. Specifically, we must
|
|
be able to access the server's print$ share at this point.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPRINTER_INFO_2 pPrinterInfo2 = NULL;
|
|
PWSTR pRemoteFilename;
|
|
DWORD dwSize;
|
|
|
|
ZeroMemory(pCachedFile, sizeof(CACHEDFILE));
|
|
pCachedFile->hRemoteFile = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Find out the name of the file to copy
|
|
//
|
|
#if !defined(PSCRIPT)
|
|
pCachedFile->pFilename = PtstrGetPrinterDataString(hPrinter, pRegKey, &dwSize);
|
|
#else
|
|
return TRUE;
|
|
#endif
|
|
|
|
if (pCachedFile->pFilename == NULL || *pCachedFile->pFilename == NUL)
|
|
return TRUE;
|
|
|
|
//
|
|
// Get the remote NTF filename on the server
|
|
//
|
|
// NOTE: We're really like to use level 4 here. But due to bug in the
|
|
// spooler, GetPrinter level 4 doesn't work for printer connections.
|
|
//
|
|
|
|
if (! (pPrinterInfo2 = MyGetPrinter(hPrinter, 2)) ||
|
|
! pPrinterInfo2->pServerName ||
|
|
! (pCachedFile->pRemoteDir = PGetFileDirectory(pPrinterInfo2->pServerName)) ||
|
|
! (pCachedFile->pLocalDir = PGetFileDirectory(NULL)) ||
|
|
! (pRemoteFilename = PConcatFilename(pCachedFile->pRemoteDir, pCachedFile->pFilename)))
|
|
{
|
|
goto exit_prepare_copyfile;
|
|
}
|
|
|
|
pCachedFile->hRemoteFile = CreateFile(pRemoteFilename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
|
|
NULL);
|
|
|
|
MemFree(pRemoteFilename);
|
|
|
|
exit_prepare_copyfile:
|
|
|
|
MemFree(pPrinterInfo2);
|
|
|
|
if (pCachedFile->hRemoteFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ERR(("Couldn't open remote NTF/FontInfo file: %d\n", GetLastError()));
|
|
_VDisposeCachedFileInfo(pCachedFile);
|
|
}
|
|
|
|
return (pCachedFile->hRemoteFile != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
_BCopyCachedFile(
|
|
PCOMMONINFO pci,
|
|
PCACHEDFILE pCachedFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy files from the server during printer-connection cache refresh event
|
|
|
|
Arguments:
|
|
|
|
pci - Points to basic printer information
|
|
pCachedFile - Points to information about cached file
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
Note:
|
|
|
|
We assume this function is called from within the spooler process
|
|
and with system's security context. Specifically, we must
|
|
be able to write into local machines' printer driver directory.
|
|
|
|
--*/
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
{
|
|
HANDLE hLocalFile;
|
|
PWSTR pLocalFilename = NULL;
|
|
PVOID pBuffer = NULL;
|
|
BOOL bResult = FALSE;
|
|
DWORD dwCount;
|
|
|
|
//
|
|
// We don't have any file to copy
|
|
//
|
|
|
|
if (pCachedFile->hRemoteFile == INVALID_HANDLE_VALUE)
|
|
return TRUE;
|
|
|
|
//
|
|
// Get the name for the local copy of the NTF file
|
|
// and allocate temporary buffer
|
|
//
|
|
|
|
ASSERT(BUFFER_SIZE >= MAX_PATH * sizeof(WCHAR));
|
|
|
|
if (! (pLocalFilename = PConcatFilename(pCachedFile->pLocalDir, pCachedFile->pFilename)) ||
|
|
! (pBuffer = MemAlloc(BUFFER_SIZE)))
|
|
{
|
|
goto exit_copyfile;
|
|
}
|
|
|
|
// Make sure the local directory is created
|
|
|
|
(VOID) CreateDirectory(pCachedFile->pLocalDir, NULL);
|
|
|
|
for (dwCount=0; dwCount < 2; dwCount++)
|
|
{
|
|
hLocalFile = CreateFile(pLocalFilename,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_FLAG_SEQUENTIAL_SCAN | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
|
|
NULL);
|
|
|
|
if (hLocalFile != INVALID_HANDLE_VALUE)
|
|
break;
|
|
|
|
if (dwCount == 0)
|
|
{
|
|
//
|
|
// If this is our first try, then attempt to move
|
|
// the existing file to a temporary file and set
|
|
// it to be delete-on-reboot.
|
|
//
|
|
|
|
#ifdef PSCRIPT
|
|
if (! GetTempFileName(pCachedFile->pLocalDir, L"NTF", 0, pBuffer) ||
|
|
#else
|
|
if (! GetTempFileName(pCachedFile->pLocalDir, L"FON", 0, pBuffer) ||
|
|
#endif
|
|
! MoveFileEx(pLocalFilename, pBuffer, MOVEFILE_REPLACE_EXISTING) ||
|
|
! MoveFileEx(pBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hLocalFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
while (ReadFile(pCachedFile->hRemoteFile, pBuffer, BUFFER_SIZE, &dwCount, NULL))
|
|
{
|
|
//
|
|
// Have we reached end-of-file?
|
|
//
|
|
|
|
if (dwCount == 0)
|
|
{
|
|
bResult = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (! WriteFile(hLocalFile, pBuffer, dwCount, &dwCount, NULL))
|
|
break;
|
|
}
|
|
|
|
CloseHandle(hLocalFile);
|
|
|
|
//
|
|
// If file copying failed, be sure to delete the temporary file
|
|
//
|
|
|
|
if (! bResult)
|
|
DeleteFile(pLocalFilename);
|
|
}
|
|
|
|
exit_copyfile:
|
|
|
|
MemFree(pLocalFilename);
|
|
MemFree(pBuffer);
|
|
|
|
if (! bResult)
|
|
ERR(("Couldn't copy remote NTF/FontInfo file: %d\n", GetLastError()));
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
_VDisposeCachedFileInfo(
|
|
PCACHEDFILE pCachedFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up after copying files from the server
|
|
during printer-connection cache refresh
|
|
|
|
Arguments:
|
|
|
|
pCachedFile - Points to information about cached file
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
if (pCachedFile->hRemoteFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(pCachedFile->hRemoteFile);
|
|
|
|
MemFree(pCachedFile->pFilename);
|
|
MemFree(pCachedFile->pRemoteDir);
|
|
MemFree(pCachedFile->pLocalDir);
|
|
|
|
ZeroMemory(pCachedFile, sizeof(CACHEDFILE));
|
|
pCachedFile->hRemoteFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef UNIDRV
|
|
|
|
VOID
|
|
DeleteFontIntallerFile(
|
|
HANDLE hPrinter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete font installer file when printer is deleted
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to printer
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR pFilename = NULL;
|
|
PWSTR pLocalDir = NULL;
|
|
PWSTR pLocalFilename = NULL;
|
|
|
|
pFilename = PtstrGetPrinterDataString(hPrinter, REGVAL_FONTFILENAME, NULL);
|
|
|
|
if (!pFilename)
|
|
return;
|
|
|
|
if (!*pFilename)
|
|
goto exit_deletefile;
|
|
|
|
if (!(pLocalDir = PGetFileDirectory(NULL)))
|
|
goto exit_deletefile;
|
|
|
|
if (!(pLocalFilename = PConcatFilename(pLocalDir, pFilename)))
|
|
goto exit_deletefile;
|
|
|
|
DeleteFile(pLocalFilename);
|
|
|
|
exit_deletefile:
|
|
|
|
MemFree(pFilename);
|
|
MemFree(pLocalDir);
|
|
MemFree(pLocalFilename);
|
|
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|