windows-nt/Source/XPSP1/NT/printscan/print/spooler/spoolss/client/winspool.c

11305 lines
308 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990-1996 Microsoft Corporation
All rights reserved
Module Name:
Winspool.c
Abstract:
Bulk of winspool.drv code
Author:
Environment:
User Mode -Win32
Revision History:
mattfe april 14 94 added caching to writeprinter
mattfe jan 95 Add SetAllocFailCount api
13-Jun-1996 Thu 15:07:16 updated -by- Daniel Chou (danielc)
Make PrinterProperties call PrinterPropertySheets and
DocumentProperties call DocumentPropertySheets
SWilson Dec 1996 - added GetPrinterDataEx, SetPrinterDataEx, EnumPrinterDataEx,
EnumPrinterKey, DeletePrinterDataEx, and DeletePrinterKey
khaleds Feb 2000 - Added DocumentPropertiesThunk,
AddPortWThunk,
CongigurePortWThunk,
DeleteProtWThunk,
DeviceCapabilitesWThunk,
PrinterPropertiesWThunk,
DocmentEvenThunk,
SpoolerPrinterEventThunk
Renamed the above native functions from xx to xxNative
Khaleds Mar 2000 - Added SendRecvBidiData
Khaleds Mar 2001 - Fix for WritePrinter
LazarI - Oct-30-2000 added GetCurrentThreadLastPopup & fixed StartDocDlgW
--*/
#include "precomp.h"
#pragma hdrstop
#include "client.h"
#include "winsprlp.h"
#include "pfdlg.h"
#include "splwow64.h"
#include "drvsetup.h"
MODULE_DEBUG_INIT( DBG_ERROR, DBG_ERROR );
HANDLE hInst = NULL;
CRITICAL_SECTION ProcessHndlCS;
HANDLE hSurrogateProcess;
WndHndlList* GWndHndlList=NULL;
LPWSTR szEnvironment = LOCAL_ENVIRONMENT;
LPWSTR szIA64Environment = L"Windows IA64";
HANDLE hShell32 = INVALID_HANDLE_VALUE;
// pointer to the start of the list containing the driver file handles
PDRVLIBNODE pStartDrvLib = NULL;
CRITICAL_SECTION ListAccessSem;
DWORD gcClientICHandle = 0;
#define DM_MATCH( dm, sp ) ((((sp)+50)/100-dm)<15&&(((sp)+50)/100-dm)>-15)
#define DM_PAPER_WL (DM_PAPERWIDTH | DM_PAPERLENGTH)
#define JOB_CANCEL_CHECK_INTERVAL 2000 // 2 seconds
#define MAX_RETRY_INVALID_HANDLE 2 // 2 retries
LONG
CallCommonPropertySheetUI(
HWND hWndOwner,
PFNPROPSHEETUI pfnPropSheetUI,
LPARAM lParam,
LPDWORD pResult
)
/*++
Routine Description:
This function dymically load the compstui.dll and call its entry
Arguments:
pfnPropSheetUI - Pointer to callback function
lParam - lParam for the pfnPropSheetUI
pResult - pResult for the CommonPropertySheetUI
Return Value:
LONG - as describe in compstui.h
Author:
01-Nov-1995 Wed 13:11:19 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
HINSTANCE hInstCPSUI;
FARPROC pProc;
LONG Result = ERR_CPSUI_GETLASTERROR;
//
// ONLY need to call the ANSI version of LoadLibrary
//
if ((hInstCPSUI = LoadLibraryA(szCompstuiDll)) &&
(pProc = GetProcAddress(hInstCPSUI, szCommonPropertySheetUIW))) {
RpcTryExcept {
Result = (LONG)((*pProc)(hWndOwner, pfnPropSheetUI, lParam, pResult));
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Result = ERR_CPSUI_GETLASTERROR;
} RpcEndExcept
}
if (hInstCPSUI) {
FreeLibrary(hInstCPSUI);
}
return(Result);
}
// Simple for Now !!!
DWORD
TranslateExceptionCode(
DWORD ExceptionCode
)
{
switch (ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_OVERFLOW:
case EXCEPTION_PRIV_INSTRUCTION:
case ERROR_NOACCESS:
case RPC_S_INVALID_BOUND:
return ERROR_INVALID_PARAMETER;
break;
default:
return ExceptionCode;
}
}
BOOL
EnumPrintersW(
DWORD Flags,
LPWSTR Name,
DWORD Level,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
FieldInfo *pFieldInfo;
switch (Level) {
case STRESSINFOLEVEL:
pFieldInfo = PrinterInfoStressFields;
cbStruct = sizeof(PRINTER_INFO_STRESS);
break;
case 1:
pFieldInfo = PrinterInfo1Fields;
cbStruct = sizeof(PRINTER_INFO_1);
break;
case 2:
pFieldInfo = PrinterInfo2Fields;
cbStruct = sizeof(PRINTER_INFO_2);
break;
case 4:
pFieldInfo = PrinterInfo4Fields;
cbStruct = sizeof(PRINTER_INFO_4);
break;
case 5:
pFieldInfo = PrinterInfo5Fields;
cbStruct = sizeof(PRINTER_INFO_5);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pPrinterEnum)
memset(pPrinterEnum, 0, cbBuf);
if (ReturnValue = RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrinterEnum) {
ReturnValue = MarshallUpStructuresArray(pPrinterEnum, *pcReturned, pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
ResetPrinterW(
HANDLE hPrinter,
LPPRINTER_DEFAULTS pDefault
)
{
BOOL ReturnValue = FALSE;
DEVMODE_CONTAINER DevModeContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwFlags = 0;
LPWSTR pDatatype = NULL;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FlushBuffer(pSpool, NULL);
if( !UpdatePrinterDefaults( pSpool, NULL, pDefault )){
goto Done;
}
if (pDefault && pDefault->pDatatype) {
if (pDefault->pDatatype == (LPWSTR)-1) {
pDatatype = NULL;
dwFlags |= RESET_PRINTER_DATATYPE;
} else {
pDatatype = pDefault->pDatatype;
}
} else {
pDatatype = NULL;
}
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
if( pDefault ){
if (pDefault->pDevMode == (LPDEVMODE)-1) {
dwFlags |= RESET_PRINTER_DEVMODE;
} else if( bValidDevModeW( pDefault->pDevMode )){
DevModeContainer.cbBuf = pDefault->pDevMode->dmSize +
pDefault->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pDefault->pDevMode;
}
}
do {
RpcTryExcept {
if (ReturnValue = RpcResetPrinterEx(pSpool->hPrinter,
pDatatype, &DevModeContainer,
dwFlags
)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
SetJobW(
HANDLE hPrinter,
DWORD JobId,
DWORD Level,
LPBYTE pJob,
DWORD Command
)
{
BOOL ReturnValue = FALSE;
GENERIC_CONTAINER GenericContainer;
GENERIC_CONTAINER *pGenericContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
switch (Level) {
case 0:
break;
case 1:
case 2:
case 3:
if (!pJob) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
do {
RpcTryExcept {
if (pJob) {
GenericContainer.Level = Level;
GenericContainer.pData = pJob;
pGenericContainer = &GenericContainer;
} else
pGenericContainer = NULL;
if (bLoadedBySpooler && fpYSetJob && pSpool->hSplPrinter) {
ReturnValue = (*fpYSetJob)(pSpool->hSplPrinter,
JobId,
(JOB_CONTAINER *)pGenericContainer,
Command,
NATIVE_CALL);
}
else {
ReturnValue = RpcSetJob(pSpool->hPrinter,
JobId,
(JOB_CONTAINER *)pGenericContainer,
Command);
}
if (ReturnValue != ERROR_SUCCESS) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
GetJobW(
HANDLE hPrinter,
DWORD JobId,
DWORD Level,
LPBYTE pJob,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue = FALSE;
FieldInfo *pFieldInfo;
SIZE_T cbStruct;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FlushBuffer(pSpool, NULL);
switch (Level) {
case 1:
pFieldInfo = JobInfo1Fields;
cbStruct = sizeof(JOB_INFO_1);
break;
case 2:
pFieldInfo = JobInfo2Fields;
cbStruct = sizeof(JOB_INFO_2);
break;
case 3:
pFieldInfo = JobInfo3Fields;
cbStruct = sizeof(JOB_INFO_3);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
do {
RpcTryExcept {
if (pJob)
memset(pJob, 0, cbBuf);
if (ReturnValue = RpcGetJob(pSpool->hPrinter, JobId, Level, pJob, cbBuf,
pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = MarshallUpStructure(pJob, pFieldInfo, cbStruct, RPC_CALL);
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
EnumJobsW(
HANDLE hPrinter,
DWORD FirstJob,
DWORD NoJobs,
DWORD Level,
LPBYTE pJob,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue = FALSE;
DWORD i, cbStruct;
FieldInfo *pFieldInfo;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FlushBuffer(pSpool, NULL);
switch (Level) {
case 1:
pFieldInfo = JobInfo1Fields;
cbStruct = sizeof(JOB_INFO_1);
break;
case 2:
pFieldInfo = JobInfo2Fields;
cbStruct = sizeof(JOB_INFO_2);
break;
case 3:
pFieldInfo = JobInfo3Fields;
cbStruct = sizeof(JOB_INFO_3);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
do {
RpcTryExcept {
if (pJob)
memset(pJob, 0, cbBuf);
if (ReturnValue = RpcEnumJobs(pSpool->hPrinter, FirstJob, NoJobs, Level, pJob,
cbBuf, pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = MarshallUpStructuresArray(pJob, *pcReturned, pFieldInfo, cbStruct, RPC_CALL);
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
HANDLE
AddPrinterW(
LPWSTR pName,
DWORD Level,
LPBYTE pPrinter
)
{
DWORD ReturnValue;
PRINTER_CONTAINER PrinterContainer;
DEVMODE_CONTAINER DevModeContainer;
SECURITY_CONTAINER SecurityContainer;
HANDLE hPrinter;
PSPOOL pSpool = NULL;
PVOID pNewSecurityDescriptor = NULL;
SECURITY_DESCRIPTOR_CONTROL SecurityDescriptorControl = 0;
PPRINTER_INFO_2 pPrinterInfo = (PPRINTER_INFO_2)pPrinter;
switch (Level) {
case 2:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return NULL;
}
if ( !pPrinter ) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
PrinterContainer.Level = Level;
PrinterContainer.PrinterInfo.pPrinterInfo1 = (PPRINTER_INFO_1)pPrinter;
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
SecurityContainer.cbBuf = 0;
SecurityContainer.pSecurity = NULL;
if (Level == 2) {
//
// If valid (non-NULL and properly formatted), then update the
// global DevMode (not per-user).
//
if( bValidDevModeW( pPrinterInfo->pDevMode )){
DevModeContainer.cbBuf = pPrinterInfo->pDevMode->dmSize +
pPrinterInfo->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pPrinterInfo->pDevMode;
}
if (pPrinterInfo->pSecurityDescriptor) {
DWORD sedlen = 0;
//
// We must construct a self relative security descriptor from
// whatever we get as input: If we get an Absolute SD we should
// convert it to a self-relative one. (this is a given) and we
// should also convert any self -relative input SD into a a new
// self relative security descriptor; this will take care of
// any holes in the Dacl or the Sacl in the self-relative sd
//
pNewSecurityDescriptor = BuildInputSD(
pPrinterInfo->pSecurityDescriptor,
&sedlen);
if (pNewSecurityDescriptor) {
SecurityContainer.cbBuf = sedlen;
SecurityContainer.pSecurity = pNewSecurityDescriptor;
}
}
}
RpcTryExcept {
if (ReturnValue = RpcAddPrinter(pName,
(PPRINTER_CONTAINER)&PrinterContainer,
(PDEVMODE_CONTAINER)&DevModeContainer,
(PSECURITY_CONTAINER)&SecurityContainer,
&hPrinter)) {
SetLastError(ReturnValue);
hPrinter = FALSE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
hPrinter = FALSE;
} RpcEndExcept
if (hPrinter) {
WCHAR szFullPrinterName[MAX_UNC_PRINTER_NAME];
szFullPrinterName[0] = 0;
pSpool = AllocSpool();
if( pPrinterInfo->pServerName ){
wcscpy( szFullPrinterName, pPrinterInfo->pServerName );
wcscat( szFullPrinterName, L"\\" );
}
wcscat( szFullPrinterName, pPrinterInfo->pPrinterName );
if ( pSpool &&
UpdatePrinterDefaults( pSpool, szFullPrinterName, NULL ) &&
( !DevModeContainer.pDevMode ||
WriteCurDevModeToRegistry(pPrinterInfo->pPrinterName,
(LPDEVMODEW)DevModeContainer.pDevMode)) ) {
pSpool->hPrinter = hPrinter;
//
// Update the access.
//
pSpool->Default.DesiredAccess = PRINTER_ALL_ACCESS;
} else {
RpcDeletePrinter(hPrinter);
RpcClosePrinter(&hPrinter);
FreeSpool(pSpool);
pSpool = NULL;
}
}
//
// Free Memory allocated for the SecurityDescriptor
//
if (pNewSecurityDescriptor) {
LocalFree(pNewSecurityDescriptor);
}
//
// Some apps check for last error instead of return value
// and report failures even if the return handle is not NULL.
// For success case, set last error to ERROR_SUCCESS.
//
if (pSpool) {
SetLastError(ERROR_SUCCESS);
}
return pSpool;
}
BOOL
DeletePrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FlushBuffer(pSpool, NULL);
do {
RpcTryExcept {
if (ReturnValue = RpcDeletePrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
DeleteCurDevModeFromRegistry(pSpool->pszPrinter);
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
SpoolerPrinterEventNative(
LPWSTR pName,
INT PrinterEvent,
DWORD Flags,
LPARAM lParam
)
/*++
//
// Some printer drivers, like the FAX driver want to do per client
// initialization at the time a connection is established
// For example in the FAX case they want to push up UI to get all
// the client info - Name, Number etc.
// Or they might want to run Setup, in initialize some other components
// Thus on a successful conenction we call into the Printer Drivers UI
// DLL to give them this oportunity
//
// mattfe may 1 96
--*/
{
BOOL ReturnValue = FALSE;
HANDLE hPrinter;
HANDLE hModule;
INT_FARPROC pfn;
if (OpenPrinter((LPWSTR)pName, &hPrinter, NULL)) {
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = (INT_FARPROC)GetProcAddress(hModule, "DrvPrinterEvent")) {
try {
ReturnValue = (*pfn)( pName, PrinterEvent, Flags, lParam );
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
}
RefCntUnloadDriver(hModule, TRUE);
}
ClosePrinter(hPrinter);
}
return ReturnValue;
}
BOOL
SpoolerPrinterEventThunk(
LPWSTR pName,
INT PrinterEvent,
DWORD Flags,
LPARAM lParam
)
/*++
//
// Some printer drivers, like the FAX driver want to do per client
// initialization at the time a connection is established
// For example in the FAX case they want to push up UI to get all
// the client info - Name, Number etc.
// Or they might want to run Setup, in initialize some other components
// Thus on a successful conenction we call into the Printer Drivers UI
// DLL to give them this oportunity
--*/
{
BOOL ReturnValue = FALSE;
HANDLE hPrinter;
HANDLE hModule;
DWORD dwRet = ERROR_SUCCESS;
INT_FARPROC pfn;
RpcTryExcept
{
if((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS)
{
if((ReturnValue = RPCSplWOW64SpoolerPrinterEvent(pName,
PrinterEvent,
Flags,
lParam,
&dwRet))==FALSE)
{
SetLastError(dwRet);
}
}
else
{
SetLastError(dwRet);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
return ReturnValue;
}
BOOL
SpoolerPrinterEvent(
LPWSTR pName,
INT PrinterEvent,
DWORD Flags,
LPARAM lParam
)
{
if(RunInWOW64())
{
return(SpoolerPrinterEventThunk(pName,
PrinterEvent,
Flags,
lParam));
}
else
{
return(SpoolerPrinterEventNative(pName,
PrinterEvent,
Flags,
lParam));
}
}
VOID
CopyFileEventForAKey(
HANDLE hPrinter,
LPWSTR pszPrinterName,
LPWSTR pszModule,
LPWSTR pszKey,
DWORD dwEvent
)
{
DWORD dwNeeded, dwVersion = 0;
HMODULE hModule = NULL;
LPDRIVER_INFO_5 pDriverInfo5 = NULL;
WCHAR szPath[MAX_PATH];
LPWSTR psz;
BOOL (*pfn)(LPWSTR, LPWSTR, DWORD);
BOOL bAllocBuffer = FALSE, bLoadedDriver = FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC];
pDriverInfo5 = (LPDRIVER_INFO_5) btBuffer;
if (!pszModule || !*pszModule) {
goto CleanUp;
}
// Get the Driver config file name
if (!GetPrinterDriverW(hPrinter, NULL, 5, (LPBYTE) pDriverInfo5,
MAX_STATIC_ALLOC, &dwNeeded)) {
if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pDriverInfo5 = (LPDRIVER_INFO_5)LocalAlloc(LMEM_FIXED, dwNeeded))) {
bAllocBuffer = TRUE;
if (!GetPrinterDriverW(hPrinter, NULL, 5,
(LPBYTE)pDriverInfo5, dwNeeded, &dwNeeded)) {
goto CleanUp;
}
} else goto CleanUp;
}
// If module name is the same as the config file, use reference counting
wcscpy(szPath, pDriverInfo5->pConfigFile);
// Get the pointer to just the file name
psz = wcsrchr(szPath, L'\\');
if (psz && !_wcsicmp(pszModule, (psz+1))) {
if (hModule = RefCntLoadDriver(szPath,
LOAD_WITH_ALTERED_SEARCH_PATH,
pDriverInfo5->dwConfigVersion,
TRUE)) {
bLoadedDriver = TRUE;
}
}
if (!hModule) {
hModule = LoadLibraryEx(pszModule, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}
if (!hModule) {
if (GetLastError() != ERROR_MOD_NOT_FOUND) {
// Fail the call
goto CleanUp;
}
// The module could not be found in users path check if it is there
// in the printer driver directory
dwNeeded = (DWORD) (psz - szPath + wcslen(pszModule) + 1);
if (dwNeeded > MAX_PATH) {
// Sanity check for file name size
goto CleanUp;
}
wcscpy(psz, pszModule);
hModule = LoadLibraryEx(szPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}
// Call the SpoolerCopyFileEvent export
if (hModule &&
((FARPROC)pfn = GetProcAddress(hModule, "SpoolerCopyFileEvent"))) {
pfn(pszPrinterName, pszKey, dwEvent);
}
CleanUp:
if (bAllocBuffer) {
LocalFree(pDriverInfo5);
}
// Use reference counting for config file and FreeLibrary for others
if (hModule) {
if (bLoadedDriver) {
RefCntUnloadDriver(hModule, TRUE);
} else {
FreeLibrary(hModule);
}
}
}
VOID
DoCopyFileEventForAllKeys(
LPWSTR pszPrinterName,
DWORD dwEvent
)
{
DWORD dwLastError, dwNeeded, dwType;
LPWSTR pszBuf = NULL, psz, pszSubKey, pszModule;
HANDLE hPrinter;
WCHAR szKey[MAX_PATH];
BOOL bAllocBufferEnum = FALSE, bAllocBufferGPD = FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC], btBufferGPD[MAX_STATIC_ALLOC];
pszBuf = (LPTSTR) btBuffer;
ZeroMemory(pszBuf, MAX_STATIC_ALLOC);
if ( !OpenPrinter(pszPrinterName, &hPrinter, NULL) )
return;
dwLastError = EnumPrinterKeyW(hPrinter,
L"CopyFiles",
pszBuf,
MAX_STATIC_ALLOC,
&dwNeeded);
//
// If CopyFiles key is not found there is nothing to do
//
if ( dwLastError != ERROR_SUCCESS )
goto Cleanup;
if (dwNeeded > MAX_STATIC_ALLOC) {
if (pszBuf = (LPWSTR) LocalAlloc(LPTR, dwNeeded)) {
bAllocBufferEnum = TRUE;
if (EnumPrinterKeyW(hPrinter,
L"CopyFiles",
pszBuf,
dwNeeded,
&dwNeeded) != ERROR_SUCCESS) {
goto Cleanup;
}
} else goto Cleanup;
}
for ( psz = (LPWSTR) pszBuf ; *psz ; psz += wcslen(psz) + 1 ) {
if ( wcslen(psz) + wcslen(L"CopyFiles") + 2
> sizeof(szKey)/sizeof(szKey[0]) )
continue;
wcscpy(szKey, L"CopyFiles\\");
wcscat(szKey, psz);
bAllocBufferGPD = FALSE;
pszModule = (LPTSTR) btBufferGPD;
ZeroMemory(pszModule, MAX_STATIC_ALLOC);
dwLastError = GetPrinterDataExW(hPrinter,
szKey,
L"Module",
&dwType,
(LPBYTE) pszModule,
MAX_STATIC_ALLOC,
&dwNeeded);
if (dwLastError != ERROR_SUCCESS) {
continue;
}
if (dwNeeded > MAX_STATIC_ALLOC) {
if (pszModule = (LPWSTR) LocalAlloc(LPTR, dwNeeded)) {
bAllocBufferGPD = TRUE;
dwLastError = GetPrinterDataExW(hPrinter,
szKey,
L"Module",
&dwType,
(LPBYTE) pszModule,
MAX_STATIC_ALLOC,
&dwNeeded);
if (dwLastError != ERROR_SUCCESS) {
LocalFree((LPBYTE)pszModule);
continue;
}
} else continue;
}
CopyFileEventForAKey(hPrinter, pszPrinterName, pszModule,
szKey, dwEvent);
if (bAllocBufferGPD) {
LocalFree((LPBYTE)pszModule);
}
}
Cleanup:
ClosePrinter(hPrinter);
if (bAllocBufferEnum) {
LocalFree((LPBYTE)pszBuf);
}
return;
}
BOOL
AddPrinterConnectionW(
LPWSTR pName
)
{
BOOL ReturnValue;
HANDLE hPrinter, hModule;
FARPROC pfn;
RpcTryExcept {
if (ReturnValue = RpcAddPrinterConnection(pName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue=FALSE;
} RpcEndExcept
if ( ReturnValue ) {
SpoolerPrinterEvent( pName, PRINTER_EVENT_ADD_CONNECTION, 0, (LPARAM)NULL );
DoCopyFileEventForAllKeys(pName, COPYFILE_EVENT_ADD_PRINTER_CONNECTION);
}
return ReturnValue;
}
BOOL
DeletePrinterConnectionW(
LPWSTR pName
)
{
BOOL ReturnValue;
DWORD LastError;
SpoolerPrinterEvent( pName, PRINTER_EVENT_DELETE_CONNECTION, 0, (LPARAM)NULL );
DoCopyFileEventForAllKeys(pName, COPYFILE_EVENT_DELETE_PRINTER_CONNECTION);
RpcTryExcept {
if (LastError = RpcDeletePrinterConnection(pName)) {
SetLastError(LastError);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue=FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
SetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD Command
)
{
BOOL ReturnValue = FALSE;
PRINTER_CONTAINER PrinterContainer;
DEVMODE_CONTAINER DevModeContainer;
SECURITY_CONTAINER SecurityContainer;
PPRINTER_INFO_2 pPrinterInfo2;
PPRINTER_INFO_3 pPrinterInfo3;
PRINTER_INFO_6 PrinterInfo6;
PSPOOL pSpool = (PSPOOL)hPrinter;
PVOID pNewSecurityDescriptor = NULL;
DWORD sedlen = 0;
PDEVMODE pDevModeWow = NULL;
DWORD dwSize = 0;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
SecurityContainer.cbBuf = 0;
SecurityContainer.pSecurity = NULL;
switch (Level) {
case STRESSINFOLEVEL:
//
// Internally we treat the Level 0, Command PRINTER_CONTROL_SET_STATUS
// as Level 6 since level 0 could be STRESS_INFO (for rpc)
//
if ( Command == PRINTER_CONTROL_SET_STATUS ) {
PrinterInfo6.dwStatus = (DWORD)(ULONG_PTR)pPrinter;
pPrinter = (LPBYTE)&PrinterInfo6;
Command = 0;
Level = 6;
}
break;
case 2:
pPrinterInfo2 = (PPRINTER_INFO_2)pPrinter;
if (pPrinterInfo2 == NULL) {
DBGMSG(DBG_TRACE, ("Error: SetPrinter pPrinterInfo2 is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
//
// If valid (non-NULL and properly formatted), then update the
// per-user DevMode. Note that we don't remove the per-user DevMode
// if this is NULL--client should user INFO_LEVEL_9 instead.
//
if( bValidDevModeW( pPrinterInfo2->pDevMode )){
//
// We won't setup the container, since setting a DevMode
// with INFO_2 doesn't change the global default.
// Use INFO_8 instead.
//
pDevModeWow = pPrinterInfo2->pDevMode;
DevModeContainer.cbBuf = pPrinterInfo2->pDevMode->dmSize +
pPrinterInfo2->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pPrinterInfo2->pDevMode;
}
if (pPrinterInfo2->pSecurityDescriptor) {
//
// We must construct a self relative security descriptor from
// whatever we get as input: If we get an Absolute SD we should
// convert it to a self-relative one. (this is a given) and we
// should also convert any self -relative input SD into a a new
// self relative security descriptor; this will take care of
// any holes in the Dacl or the Sacl in the self-relative sd
//
pNewSecurityDescriptor = BuildInputSD(pPrinterInfo2->pSecurityDescriptor,
&sedlen);
if (pNewSecurityDescriptor) {
SecurityContainer.cbBuf = sedlen;
SecurityContainer.pSecurity = pNewSecurityDescriptor;
}
}
break;
case 3:
pPrinterInfo3 = (PPRINTER_INFO_3)pPrinter;
if (pPrinterInfo3 == NULL) {
DBGMSG(DBG_TRACE, ("Error: SetPrinter pPrinterInfo3 is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
if (pPrinterInfo3->pSecurityDescriptor) {
//
// We must construct a self relative security descriptor from
// whatever we get as input: If we get an Absolute SD we should
// convert it to a self-relative one. (this is a given) and we
// should also convert any self -relative input SD into a a new
// self relative security descriptor; this will take care of
// any holes in the Dacl or the Sacl in the self-relative sd
//
pNewSecurityDescriptor = BuildInputSD(pPrinterInfo3->pSecurityDescriptor,
&sedlen);
if (pNewSecurityDescriptor) {
SecurityContainer.cbBuf = sedlen;
SecurityContainer.pSecurity = pNewSecurityDescriptor;
}
}
break;
case 4:
case 5:
if ( pPrinter == NULL ) {
DBGMSG(DBG_TRACE,("Error SetPrinter pPrinter is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
break;
case 6:
if ( pPrinter == NULL ) {
DBGMSG(DBG_TRACE,("Error SetPrinter pPrinter is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
break;
case 7:
if ( pPrinter == NULL ) {
DBGMSG(DBG_TRACE,("Error SetPrinter pPrinter is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
break;
case 8:
{
PPRINTER_INFO_8 pPrinterInfo8;
//
// Global DevMode
//
pPrinterInfo8 = (PPRINTER_INFO_8)pPrinter;
if( !pPrinterInfo8 || !bValidDevModeW( pPrinterInfo8->pDevMode )){
DBGMSG(DBG_TRACE,("Error SetPrinter 8 pPrinter\n"));
SetLastError( ERROR_INVALID_PARAMETER );
goto Done;
}
pDevModeWow = pPrinterInfo8->pDevMode;
DevModeContainer.cbBuf = pPrinterInfo8->pDevMode->dmSize +
pPrinterInfo8->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pPrinterInfo8->pDevMode;
break;
}
case 9:
{
PPRINTER_INFO_9 pPrinterInfo9;
//
// Per-user DevMode
//
pPrinterInfo9 = (PPRINTER_INFO_9)pPrinter;
//
// Update the per-user DevMode if it's a valid DevMode,
// or it is NULL, which indicates that the per-user DevMode
// should be removed.
//
if( !pPrinterInfo9 ||
( pPrinterInfo9->pDevMode &&
!bValidDevModeW( pPrinterInfo9->pDevMode ))){
DBGMSG(DBG_TRACE,("Error SetPrinter 9 pPrinter\n"));
SetLastError( ERROR_INVALID_PARAMETER );
goto Done;
}
if( pPrinterInfo9->pDevMode ){
pDevModeWow = pPrinterInfo9->pDevMode;
DevModeContainer.cbBuf = pPrinterInfo9->pDevMode->dmSize +
pPrinterInfo9->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pPrinterInfo9->pDevMode;
}
break;
}
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
PrinterContainer.Level = Level;
PrinterContainer.PrinterInfo.pPrinterInfo1 = (PPRINTER_INFO_1)pPrinter;
do {
RpcTryExcept {
if (ReturnValue = RpcSetPrinter(
pSpool->hPrinter,
(PPRINTER_CONTAINER)&PrinterContainer,
(PDEVMODE_CONTAINER)&DevModeContainer,
(PSECURITY_CONTAINER)&SecurityContainer,
Command)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
//
// Need to write DevMode to registry so that dos apps doing
// ExtDeviceMode can pick up the new devmode
//
if( ReturnValue && pDevModeWow ){
if( !WriteCurDevModeToRegistry( pSpool->pszPrinter,
pDevModeWow )){
DBGMSG( DBG_WARN,
( "Write wow DevMode failed: %d\n", GetLastError( )));
}
//
// Per-user DevMode is handled in the client's spoolsv process.
//
}
//
// Did we allocate memory for a new self-relative SD?
// If we did, let's free it.
//
if (pNewSecurityDescriptor) {
LocalFree(pNewSecurityDescriptor);
}
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
GetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue = FALSE;
FieldInfo *pFieldInfo;
SIZE_T cbStruct;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
switch (Level) {
case STRESSINFOLEVEL:
pFieldInfo = PrinterInfoStressFields;
cbStruct = sizeof(PRINTER_INFO_STRESS);
break;
case 1:
pFieldInfo = PrinterInfo1Fields;
cbStruct = sizeof(PRINTER_INFO_1);
break;
case 2:
pFieldInfo = PrinterInfo2Fields;
cbStruct = sizeof(PRINTER_INFO_2);
break;
case 3:
pFieldInfo = PrinterInfo3Fields;
cbStruct = sizeof(PRINTER_INFO_3);
break;
case 4:
pFieldInfo = PrinterInfo4Fields;
cbStruct = sizeof(PRINTER_INFO_4);
break;
case 5:
pFieldInfo = PrinterInfo5Fields;
cbStruct = sizeof(PRINTER_INFO_5);
break;
case 6:
pFieldInfo = PrinterInfo6Fields;
cbStruct = sizeof(PRINTER_INFO_6);
break;
case 7:
pFieldInfo = PrinterInfo7Fields;
cbStruct = sizeof(PRINTER_INFO_7);
break;
case 8:
pFieldInfo = PrinterInfo8Fields;
cbStruct = sizeof(PRINTER_INFO_8);
break;
case 9:
pFieldInfo = PrinterInfo9Fields;
cbStruct = sizeof(PRINTER_INFO_9);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
if (pPrinter)
memset(pPrinter, 0, cbBuf);
do {
RpcTryExcept {
if (ReturnValue = RpcGetPrinter(pSpool->hPrinter, Level, pPrinter, cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrinter) {
ReturnValue = MarshallUpStructure(pPrinter, pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
GetOSVersion(
IN LPCTSTR pszServerName, OPTIONAL
OUT OSVERSIONINFO *pOSVer
)
{
DWORD dwStatus = ERROR_SUCCESS;
dwStatus = pOSVer ? S_OK : ERROR_INVALID_PARAMETER;
if (ERROR_SUCCESS == dwStatus)
{
ZeroMemory(pOSVer, sizeof(OSVERSIONINFO));
pOSVer->dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!pszServerName || !*pszServerName) // allow string to be empty?
{
dwStatus = GetVersionEx((POSVERSIONINFO) pOSVer) ? ERROR_SUCCESS : GetLastError();
}
else
{
HANDLE hPrinter = NULL;
DWORD dwNeeded = 0;
DWORD dwType = REG_BINARY;
PRINTER_DEFAULTS Defaults = { NULL, NULL, SERVER_READ };
//
// Open the server for read access.
//
dwStatus = OpenPrinter((LPTSTR) pszServerName, &hPrinter, &Defaults) ? ERROR_SUCCESS : GetLastError();
//
// Get the os version from the remote spooler.
//
if (ERROR_SUCCESS == dwStatus)
{
dwStatus = GetPrinterData(hPrinter,
SPLREG_OS_VERSION,
&dwType,
(PBYTE)pOSVer,
sizeof(OSVERSIONINFO),
&dwNeeded);
}
if (ERROR_INVALID_PARAMETER == dwStatus)
{
//
// Assume that we're on NT4 as it doesn't support SPLREG_OS_VERSION
// at it's the only OS that doesn't that could land up in this remote code path.
//
dwStatus = ERROR_SUCCESS;
pOSVer->dwMajorVersion = 4;
pOSVer->dwMinorVersion = 0;
}
if (NULL != hPrinter )
{
ClosePrinter(hPrinter);
}
}
}
SetLastError(dwStatus);
return ERROR_SUCCESS == dwStatus ? TRUE : FALSE ;
}
BOOL
AddPrinterDriverExW(
LPWSTR pName,
DWORD Level,
PBYTE lpbDriverInfo,
DWORD dwFileCopyFlags
)
{
BOOL ReturnValue;
DRIVER_CONTAINER DriverContainer;
BOOL bDefaultEnvironmentUsed = FALSE;
LPRPC_DRIVER_INFO_4W pRpcDriverInfo4 = NULL;
DRIVER_INFO_4 *pDriverInfo4 = NULL;
LPRPC_DRIVER_INFO_6W pRpcDriverInfo6 = NULL;
DRIVER_INFO_6 *pDriverInfo6 = NULL;
BOOL bShowUI = FALSE;
BOOL bMapUnknownPrinterDriverToBlockedDriver = FALSE;
OSVERSIONINFO OsVer;
LPWSTR pStr;
//
// Validate Input Parameters
//
if (!lpbDriverInfo) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
DriverContainer.Level = Level;
switch (Level) {
case 2:
if ( (((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment == NULL ) ||
(*((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment == L'\0') ) {
bDefaultEnvironmentUsed = TRUE;
((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment = szEnvironment;
}
DriverContainer.DriverInfo.Level2 = (DRIVER_INFO_2 *)lpbDriverInfo;
break;
case 3:
case 4:
//
// DRIVER_INFO_4 is 3 + pszzPreviousNames field
// We will use RPC_DRIVER_INFO_4 for both cases
//
DriverContainer.Level = Level;
if ( (((LPDRIVER_INFO_4)lpbDriverInfo)->pEnvironment == NULL ) ||
(*((LPDRIVER_INFO_4)lpbDriverInfo)->pEnvironment == L'\0') ) {
bDefaultEnvironmentUsed = TRUE;
((LPDRIVER_INFO_4)lpbDriverInfo)->pEnvironment = szEnvironment;
}
if ( !(pRpcDriverInfo4=AllocSplMem(sizeof(RPC_DRIVER_INFO_4W))) ) {
return FALSE;
}
pDriverInfo4 = (DRIVER_INFO_4 *)lpbDriverInfo;
pRpcDriverInfo4->cVersion = pDriverInfo4->cVersion;
pRpcDriverInfo4->pName = pDriverInfo4->pName;
pRpcDriverInfo4->pEnvironment = pDriverInfo4->pEnvironment;
pRpcDriverInfo4->pDriverPath = pDriverInfo4->pDriverPath;
pRpcDriverInfo4->pDataFile = pDriverInfo4->pDataFile;
pRpcDriverInfo4->pConfigFile = pDriverInfo4->pConfigFile;
pRpcDriverInfo4->pHelpFile = pDriverInfo4->pHelpFile;
pRpcDriverInfo4->pDependentFiles = pDriverInfo4->pDependentFiles;
pRpcDriverInfo4->pMonitorName = pDriverInfo4->pMonitorName;
pRpcDriverInfo4->pDefaultDataType = pDriverInfo4->pDefaultDataType;
//
// Set the char count of the mz string.
// NULL --- 0
// szNULL --- 1
// string --- number of characters in the string including the last '\0'
//
if ( pStr = pDriverInfo4->pDependentFiles ) {
while ( *pStr )
pStr += wcslen(pStr) + 1;
pRpcDriverInfo4->cchDependentFiles
= (DWORD) (pStr - pDriverInfo4->pDependentFiles + 1);
} else {
pRpcDriverInfo4->cchDependentFiles = 0;
}
pRpcDriverInfo4->cchPreviousNames = 0;
if ( Level == 4 &&
(pStr = pDriverInfo4->pszzPreviousNames) &&
*pStr ) {
pRpcDriverInfo4->pszzPreviousNames = pStr;
while ( *pStr )
pStr += wcslen(pStr) + 1;
pRpcDriverInfo4->cchPreviousNames
= (DWORD) (pStr - pDriverInfo4->pszzPreviousNames + 1);
}
DriverContainer.DriverInfo.Level4 = pRpcDriverInfo4;
break;
case 6:
DriverContainer.Level = Level;
if ( (((LPDRIVER_INFO_6)lpbDriverInfo)->pEnvironment == NULL ) ||
(*((LPDRIVER_INFO_6)lpbDriverInfo)->pEnvironment == L'\0') ) {
bDefaultEnvironmentUsed = TRUE;
((LPDRIVER_INFO_6)lpbDriverInfo)->pEnvironment = szEnvironment;
}
if ( !(pRpcDriverInfo6=AllocSplMem(sizeof(RPC_DRIVER_INFO_6W))) ) {
return FALSE;
}
pDriverInfo6 = (DRIVER_INFO_6 *)lpbDriverInfo;
pRpcDriverInfo6->cVersion = pDriverInfo6->cVersion;
pRpcDriverInfo6->pName = pDriverInfo6->pName;
pRpcDriverInfo6->pEnvironment = pDriverInfo6->pEnvironment;
pRpcDriverInfo6->pDriverPath = pDriverInfo6->pDriverPath;
pRpcDriverInfo6->pDataFile = pDriverInfo6->pDataFile;
pRpcDriverInfo6->pConfigFile = pDriverInfo6->pConfigFile;
pRpcDriverInfo6->pHelpFile = pDriverInfo6->pHelpFile;
pRpcDriverInfo6->pDependentFiles = pDriverInfo6->pDependentFiles;
pRpcDriverInfo6->pMonitorName = pDriverInfo6->pMonitorName;
pRpcDriverInfo6->pDefaultDataType = pDriverInfo6->pDefaultDataType;
pRpcDriverInfo6->ftDriverDate = pDriverInfo6->ftDriverDate;
pRpcDriverInfo6->dwlDriverVersion = pDriverInfo6->dwlDriverVersion;
pRpcDriverInfo6->pMfgName = pDriverInfo6->pszMfgName;
pRpcDriverInfo6->pOEMUrl = pDriverInfo6->pszOEMUrl;
pRpcDriverInfo6->pHardwareID = pDriverInfo6->pszHardwareID;
pRpcDriverInfo6->pProvider = pDriverInfo6->pszProvider;
//
// Set the char count of the mz string.
// NULL --- 0
// szNULL --- 1
// string --- number of characters in the string including the last '\0'
//
if ( pStr = pDriverInfo6->pDependentFiles ) {
while ( *pStr )
pStr += wcslen(pStr) + 1;
pRpcDriverInfo6->cchDependentFiles = (DWORD) (pStr - pDriverInfo6->pDependentFiles + 1);
} else {
pRpcDriverInfo6->cchDependentFiles = 0;
}
pRpcDriverInfo6->cchPreviousNames = 0;
if ( Level == 6 &&
(pStr = pDriverInfo6->pszzPreviousNames) &&
*pStr ) {
pRpcDriverInfo6->pszzPreviousNames = pStr;
while ( *pStr )
pStr += wcslen(pStr) + 1;
pRpcDriverInfo6->cchPreviousNames
= (DWORD) (pStr - pDriverInfo6->pszzPreviousNames + 1);
}
DriverContainer.DriverInfo.Level6 = pRpcDriverInfo6;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
//
// The driver path is at the same location in all of the DRIVER_INFO_X
// structures, as is the driver name. If this changes, the
// CheckForBlockedDrivers() call will have to do different things
// depending on the level.
//
SPLASSERT(Level >= 2 && Level <= 6);
//
// APD_NO_UI has no meaning at the server side, so clear it before the
// RPC call.
//
bShowUI = !(dwFileCopyFlags & APD_NO_UI);
dwFileCopyFlags &= ~APD_NO_UI;
//
// GetOSVersionEx has set last error correctly.
//
ReturnValue = GetOSVersion(pName, &OsVer);
if (!ReturnValue) {
goto Cleanup;
}
//
// If the server is Whistler or later, instruct the spooler to
// return the actual blocking code ERROR_PRINTER_DRIVER_BLOCKED or
// ERROR_PRINTER_DRIVER_WARNED.
//
// A win2k server returns ERROR_UNKNOWN_PRINTER_DRIVER for blocked
// driver, so we need to re-map this code to the correct blocking
// code.
//
if (OsVer.dwMajorVersion >= 5 && OsVer.dwMinorVersion > 0)
{
dwFileCopyFlags |= APD_RETURN_BLOCKING_STATUS_CODE;
}
else
{
//
// APD_DONT_SET_CHECKPOINT has no meaning at the server side, so clear it
// before the RPC call.
//
dwFileCopyFlags &= ~APD_DONT_SET_CHECKPOINT;
dwFileCopyFlags &= ~APD_INSTALL_WARNED_DRIVER;
if (OsVer.dwMajorVersion == 5 && OsVer.dwMinorVersion == 0)
{
bMapUnknownPrinterDriverToBlockedDriver = TRUE;
}
}
RpcTryExcept {
ReturnValue = RpcAddPrinterDriverEx(pName,
&DriverContainer,
dwFileCopyFlags);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
if (bMapUnknownPrinterDriverToBlockedDriver && (ERROR_UNKNOWN_PRINTER_DRIVER == ReturnValue))
{
ReturnValue = ERROR_PRINTER_DRIVER_BLOCKED;
}
//
// Popup UI but do not offer replacement for all cases.
//
if (bShowUI && ((ERROR_PRINTER_DRIVER_BLOCKED == ReturnValue) || (ERROR_PRINTER_DRIVER_WARNED == ReturnValue))) {
ReturnValue = ShowPrintUpgUI(ReturnValue);
//
// For warned driver and the user instructs to install it, retry it
// with APD_INSTALL_WARNED_DRIVER.
//
if ((ERROR_SUCCESS == ReturnValue)) {
dwFileCopyFlags |= APD_INSTALL_WARNED_DRIVER;
RpcTryExcept {
ReturnValue = RpcAddPrinterDriverEx(pName,
&DriverContainer,
dwFileCopyFlags);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
}
}
if (ERROR_SUCCESS != ReturnValue) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
if (bDefaultEnvironmentUsed) {
if ( Level == 2 )
((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment = NULL;
else //Level == 3
((LPDRIVER_INFO_3)lpbDriverInfo)->pEnvironment = NULL;
}
Cleanup:
FreeSplMem(pRpcDriverInfo4);
FreeSplMem(pRpcDriverInfo6);
return ReturnValue;
}
BOOL
AddDriverCatalog(
HANDLE hPrinter,
DWORD dwLevel,
VOID *pvDriverInfCatInfo,
DWORD dwCatalogCopyFlags
)
{
HRESULT hRetval = E_FAIL;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
DRIVER_INFCAT_CONTAINER DriverInfCatContainer;
hRetval = pvDriverInfCatInfo && hPrinter ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
hRetval = eProtectHandle(hPrinter, FALSE) ? S_OK : GetLastErrorAsHResult();
}
if (SUCCEEDED(hRetval))
{
switch (dwLevel)
{
case 1:
DriverInfCatContainer.dwLevel = dwLevel;
DriverInfCatContainer.DriverInfCatInfo.pDriverInfCatInfo1 = (LPRPC_DRIVER_INFCAT_INFO_1) pvDriverInfCatInfo;
break;
case 2:
DriverInfCatContainer.dwLevel = dwLevel;
DriverInfCatContainer.DriverInfCatInfo.pDriverInfCatInfo2 = (LPRPC_DRIVER_INFCAT_INFO_2) pvDriverInfCatInfo;
break;
default:
hRetval = HRESULT_FROM_WIN32(ERROR_INVALID_LEVEL);
break;
}
if (SUCCEEDED(hRetval))
{
do
{
RpcTryExcept
{
hRetval = HResultFromWin32(RpcAddDriverCatalog(pSpool->hPrinter,
&DriverInfCatContainer,
dwCatalogCopyFlags));
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
hRetval = HResultFromWin32(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
} while (FAILED(hRetval) && (HRESULT_CODE(hRetval) == ERROR_INVALID_HANDLE) &&
(cRetry++ < MAX_RETRY_INVALID_HANDLE) &&
RevalidateHandle( pSpool ));
}
vUnprotectHandle(hPrinter);
}
if (FAILED(hRetval))
{
SetLastError(HRESULT_CODE(hRetval));
}
return SUCCEEDED(hRetval);
}
BOOL
AddPrinterDriverW(
LPWSTR pName,
DWORD Level,
PBYTE lpbDriverInfo
)
{
return AddPrinterDriverExW(pName, Level, lpbDriverInfo, APD_COPY_NEW_FILES);
}
BOOL
EnumPrinterDriversW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pDriverInfo,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct;
FieldInfo *pFieldInfo;
switch (Level) {
case 1:
pFieldInfo = DriverInfo1Fields;
cbStruct = sizeof(DRIVER_INFO_1);
break;
case 2:
pFieldInfo = DriverInfo2Fields;
cbStruct = sizeof(DRIVER_INFO_2);
break;
case 3:
pFieldInfo = DriverInfo3Fields;
cbStruct = sizeof(DRIVER_INFO_3);
break;
case 4:
pFieldInfo = DriverInfo4Fields;
cbStruct = sizeof(DRIVER_INFO_4);
break;
case 5:
pFieldInfo = DriverInfo5Fields;
cbStruct = sizeof(DRIVER_INFO_5);
break;
case 6:
pFieldInfo = DriverInfo6Fields;
cbStruct = sizeof(DRIVER_INFO_6);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcEnumPrinterDrivers(pName, pEnvironment, Level,
pDriverInfo, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pDriverInfo) {
ReturnValue = MarshallUpStructuresArray(pDriverInfo, *pcReturned, pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetPrinterDriverW(
HANDLE hPrinter,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pDriverInfo,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue = FALSE;
FieldInfo *pFieldInfo;
SIZE_T cbStruct;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwMajorVersionNeeded = (DWORD)-1, dwMinorVersionNeeded = (DWORD)-1;
DWORD dwServerMajorVersion;
DWORD dwServerMinorVersion;
UINT cRetry = 0;
CALL_ROUTE Route;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
switch (Level) {
case 1:
pFieldInfo = DriverInfo1Fields;
cbStruct = sizeof(DRIVER_INFO_1);
break;
case 2:
pFieldInfo = DriverInfo2Fields;
cbStruct = sizeof(DRIVER_INFO_2);
break;
case 3:
pFieldInfo = DriverInfo3Fields;
cbStruct = sizeof(DRIVER_INFO_3);
break;
case 4:
pFieldInfo = DriverInfo4Fields;
cbStruct = sizeof(DRIVER_INFO_4);
break;
case 5:
pFieldInfo = DriverInfo5Fields;
cbStruct = sizeof(DRIVER_INFO_5);
break;
case 6:
pFieldInfo = DriverInfo6Fields;
cbStruct = sizeof(DRIVER_INFO_6);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
do {
RpcTryExcept {
if (pDriverInfo)
memset(pDriverInfo, 0, cbBuf);
if (!pEnvironment || !*pEnvironment)
pEnvironment = RunInWOW64() ? szIA64Environment : szEnvironment;
else if ( !lstrcmp(pEnvironment, cszWin95Environment) )
dwMajorVersionNeeded = dwMinorVersionNeeded = 0;
if (bLoadedBySpooler && fpYGetPrinterDriver2 && pSpool->hSplPrinter) {
ReturnValue = (*fpYGetPrinterDriver2)(pSpool->hSplPrinter,
pEnvironment,
Level, pDriverInfo, cbBuf,
pcbNeeded,
dwMajorVersionNeeded,
dwMinorVersionNeeded,
&dwServerMajorVersion,
&dwServerMinorVersion,
NATIVE_CALL
);
Route = NATIVE_CALL;
} else {
ReturnValue = RpcGetPrinterDriver2(pSpool->hPrinter,
pEnvironment,
Level, pDriverInfo, cbBuf,
pcbNeeded,
dwMajorVersionNeeded,
dwMinorVersionNeeded,
&dwServerMajorVersion,
&dwServerMinorVersion
);
Route = RPC_CALL;
}
if (ReturnValue) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pDriverInfo) {
if (!MarshallUpStructure(pDriverInfo, pFieldInfo, cbStruct, Route))
{
ReturnValue = FALSE;
break;
}
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
GetPrinterDriverDirectoryW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pDriverDirectory,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = pEnvironment = RunInWOW64() ? szIA64Environment : szEnvironment;
if (bLoadedBySpooler && fpYGetPrinterDriverDirectory) {
ReturnValue = (*fpYGetPrinterDriverDirectory)(pName, pEnvironment,
Level,
pDriverDirectory,
cbBuf, pcbNeeded,
FALSE);
} else {
ReturnValue = RpcGetPrinterDriverDirectory(pName,
pEnvironment,
Level,
pDriverDirectory,
cbBuf, pcbNeeded);
}
if (ReturnValue) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
DWORD Error;
Error = TranslateExceptionCode(RpcExceptionCode());
if (Error == RPC_S_SERVER_UNAVAILABLE)
{
ReturnValue = BuildSpoolerObjectPath(gszPrinterDriversPath,
pName,
pEnvironment,
Level,
pDriverDirectory,
cbBuf,
pcbNeeded);
}
else
{
SetLastError(Error);
ReturnValue = FALSE;
}
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrinterDriverExW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pDriverName,
DWORD dwDeleteFlag,
DWORD dwVersionNum
)
{
BOOL ReturnValue;
if (!pDriverName || !*pDriverName) {
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrinterDriverEx(pName,
pEnvironment,
pDriverName,
dwDeleteFlag,
dwVersionNum)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrinterDriverW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pDriverName
)
{
BOOL ReturnValue;
if (!pDriverName || !*pDriverName) {
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrinterDriver(pName,
pEnvironment,
pDriverName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
AddPerMachineConnectionW(
LPCWSTR pServer,
LPCWSTR pPrinterName,
LPCWSTR pPrintServer,
LPCWSTR pProvider
)
{
BOOL ReturnValue;
WCHAR DummyStr[] = L"";
if (!pPrinterName || !*pPrinterName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!pPrintServer || !*pPrintServer) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// pProvider is an optional parameter and can be NULL. Since RPC does not
// accept NULL pointers we have to pass some dummy pointer to szNULL.
if (!pProvider) {
pProvider = (LPCWSTR) DummyStr;
}
RpcTryExcept {
if (ReturnValue = RpcAddPerMachineConnection((LPWSTR) pServer,
pPrinterName,
pPrintServer,
pProvider)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePerMachineConnectionW(
LPCWSTR pServer,
LPCWSTR pPrinterName
)
{
BOOL ReturnValue;
if (!pPrinterName || !*pPrinterName) {
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RpcTryExcept {
if (ReturnValue = RpcDeletePerMachineConnection((LPWSTR) pServer,
pPrinterName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumPerMachineConnectionsW(
LPCWSTR pServer,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct, index;
FieldInfo *pFieldInfo;
pFieldInfo = PrinterInfo4Fields;
cbStruct = sizeof(PRINTER_INFO_4);
RpcTryExcept {
if (pPrinterEnum)
memset(pPrinterEnum, 0, cbBuf);
if (ReturnValue = RpcEnumPerMachineConnections((LPWSTR) pServer,
pPrinterEnum,
cbBuf,
pcbNeeded,
pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrinterEnum) {
ReturnValue = MarshallUpStructuresArray(pPrinterEnum, *pcReturned, pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
AddPrintProcessorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pPathName,
LPWSTR pPrintProcessorName
)
{
BOOL ReturnValue;
if (!pPrintProcessorName || !*pPrintProcessorName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!pPathName || !*pPathName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcAddPrintProcessor(pName, pEnvironment, pPathName,
pPrintProcessorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumPrintProcessorsW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pPrintProcessorInfo,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct;
FieldInfo *pFieldInfo;
switch (Level) {
case 1:
pFieldInfo = PrintProcessorInfo1Fields;
cbStruct = sizeof(PRINTPROCESSOR_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcEnumPrintProcessors(pName, pEnvironment, Level,
pPrintProcessorInfo, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrintProcessorInfo) {
ReturnValue = MarshallUpStructuresArray(pPrintProcessorInfo, *pcReturned,
pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetPrintProcessorDirectoryW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pPrintProcessorInfo,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = pEnvironment = RunInWOW64() ? szIA64Environment : szEnvironment;
if (ReturnValue = RpcGetPrintProcessorDirectory(pName,
pEnvironment,
Level,
pPrintProcessorInfo,
cbBuf,
pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
DWORD Error;
Error = TranslateExceptionCode(RpcExceptionCode());
if (Error == RPC_S_SERVER_UNAVAILABLE)
{
ReturnValue = BuildSpoolerObjectPath(gszPrintProcessorsPath,
pName,
pEnvironment,
Level,
pPrintProcessorInfo,
cbBuf,
pcbNeeded);
}
else
{
SetLastError(Error);
ReturnValue = FALSE;
}
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumPrintProcessorDatatypesW(
LPWSTR pName,
LPWSTR pPrintProcessorName,
DWORD Level,
LPBYTE pDatatypes,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct;
FieldInfo *pFieldInfo;
switch (Level) {
case 1:
pFieldInfo = PrintProcessorInfo1Fields;
cbStruct = sizeof(DATATYPES_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (ReturnValue = RpcEnumPrintProcessorDatatypes(pName,
pPrintProcessorName,
Level,
pDatatypes,
cbBuf,
pcbNeeded,
pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pDatatypes) {
ReturnValue = MarshallUpStructuresArray(pDatatypes, *pcReturned,
pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
DWORD
StartDocPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pDocInfo
)
{
DWORD ReturnValue = 0;
BOOL EverythingWorked = FALSE;
BOOL PrintingToFile = FALSE;
PSPOOL pSpool = (PSPOOL)hPrinter;
PDOC_INFO_1 pDocInfo1 = NULL;
PDOC_INFO_3 pDocInfo3 = NULL;
LPBYTE pBuffer = NULL;
DWORD cbBuffer = MAX_STATIC_ALLOC;
DWORD cbNeeded;
BOOL bReturn;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
if ( pSpool->Status & SPOOL_STATUS_STARTDOC ) {
SetLastError(ERROR_INVALID_PRINTER_STATE);
goto Done;
}
DBGMSG(DBG_TRACE,("Entered StartDocPrinterW client side hPrinter = %x\n", hPrinter));
// level 2 is supported on win95 and not on NT
switch (Level) {
case 1:
pDocInfo1 = (PDOC_INFO_1)pDocInfo;
break;
case 3:
pDocInfo1 = (PDOC_INFO_1)pDocInfo;
pDocInfo3 = (PDOC_INFO_3)pDocInfo;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
goto Done;
}
pBuffer = AllocSplMem(cbBuffer);
if (!pBuffer) {
goto Done;
}
try {
//
// Earlier on, if we had a non-null string, we assumed it to be
// printing to file. Print to file will not go thru the client-side
// optimization code. Now gdi is passing us pOutputFile name
// irrespective of whether it is file or not. We must determine if
// pOutputFile is really a file name
//
if (pDocInfo1->pOutputFile &&
(*(pDocInfo1->pOutputFile) != L'\0') &&
IsaFileName(pDocInfo1->pOutputFile, (LPWSTR)pBuffer, cbBuffer / sizeof(WCHAR))){
PrintingToFile = TRUE;
}
if (!PrintingToFile &&
!((Level == 3) && (pDocInfo3->dwFlags & DI_MEMORYMAP_WRITE)) &&
AddJobW(hPrinter, 1, pBuffer, cbBuffer, &cbNeeded)) {
PADDJOB_INFO_1 pAddJob = (PADDJOB_INFO_1)pBuffer;
pSpool->JobId = pAddJob->JobId;
pSpool->hFile = CreateFile(pAddJob->Path,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
if (pSpool->JobId == (DWORD)-1) {
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
QUERY_PRINT_JOB_INFO JobInfo;
Status = NtFsControlFile(pSpool->hFile, NULL, NULL, NULL,
&Iosb,
FSCTL_GET_PRINT_ID,
NULL, 0,
&JobInfo, sizeof(JobInfo));
if (NT_SUCCESS(Status)) {
pSpool->JobId = JobInfo.JobId;
}
}
ZeroMemory(pBuffer, cbBuffer);
if (!(bReturn = GetJob(hPrinter, pSpool->JobId, 1, pBuffer, cbBuffer, &cbNeeded))) {
if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
FreeSplMem(pBuffer) &&
(pBuffer = AllocSplMem(cbNeeded))) {
//
// Update the new size of our work buffer
//
cbBuffer = cbNeeded;
bReturn = GetJob(hPrinter, pSpool->JobId, 1, pBuffer, cbBuffer, &cbNeeded);
}
}
if (bReturn) {
PJOB_INFO_1 pJob = (PJOB_INFO_1)pBuffer;
pJob->pDocument = pDocInfo1->pDocName;
if (pDocInfo1->pDatatype) {
pJob->pDatatype = pDocInfo1->pDatatype;
}
pJob->Position = JOB_POSITION_UNSPECIFIED;
if (SetJob(hPrinter, pSpool->JobId,
1, (LPBYTE)pJob, 0)) {
EverythingWorked = TRUE;
}
}
}
if (!PrintingToFile && !EverythingWorked) {
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
if (CloseHandle(pSpool->hFile)) {
pSpool->hFile = INVALID_HANDLE_VALUE;
}
}
SetJob(hPrinter,pSpool->JobId, 0, NULL, JOB_CONTROL_CANCEL);
ScheduleJob(hPrinter, pSpool->JobId);
pSpool->JobId = 0;
}
}
if (EverythingWorked) {
ReturnValue = pSpool->JobId;
} else {
UINT cRetry = 0;
//
// If it's invalid datatype, fail immediately instead of trying
// StartDocPrinter.
//
if( GetLastError() == ERROR_INVALID_DATATYPE ){
ReturnValue = 0;
} else {
GENERIC_CONTAINER DocInfoContainer;
DWORD JobId;
pSpool->hFile = INVALID_HANDLE_VALUE;
pSpool->JobId = 0;
// Level 3 data is required only on the client
DocInfoContainer.Level = 1;
DocInfoContainer.pData = pDocInfo;
do {
RpcTryExcept {
if (ReturnValue = RpcStartDocPrinter(
pSpool->hPrinter,
(LPDOC_INFO_CONTAINER)&DocInfoContainer,
&JobId)) {
SetLastError(ReturnValue);
ReturnValue = 0;
} else
ReturnValue = JobId;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = 0;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
}
}
if (ReturnValue) {
pSpool->Status |= SPOOL_STATUS_STARTDOC;
}
//
// If the tray icon has not been notified, then do so now. Set
// the flag so that we won't call it multiple times.
//
if( ReturnValue && !( pSpool->Status & SPOOL_STATUS_TRAYICON_NOTIFIED )){
vUpdateTrayIcon( hPrinter, ReturnValue );
}
} except (1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
ReturnValue = 0;
}
Done:
FreeSplMem(pBuffer);
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
StartPagePrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
try {
FlushBuffer(pSpool, NULL);
RpcTryExcept {
if (ReturnValue = RpcStartPagePrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} except (1) {
SetLastError(ERROR_INVALID_HANDLE);
ReturnValue = FALSE;
}
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
FlushBuffer(
PSPOOL pSpool,
PDWORD pcbWritten
)
{
DWORD ReturnValue = TRUE;
DWORD cbWritten = 0;
SPLASSERT (pSpool != NULL);
SPLASSERT (pSpool->signature == SP_SIGNATURE);
DBGMSG(DBG_TRACE, ("FlushBuffer - pSpool %x\n",pSpool));
if (pSpool->cbBuffer) {
SPLASSERT(pSpool->pBuffer != NULL);
DBGMSG(DBG_TRACE, ("FlushBuffer - Number Cached WritePrinters before Flush %d\n", pSpool->cCacheWrite));
pSpool->cCacheWrite = 0;
pSpool->cFlushBuffers++;
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
// FileIO
ReturnValue = WriteFile( pSpool->hFile,
pSpool->pBuffer,
pSpool->cbBuffer,
&cbWritten, NULL);
DBGMSG(DBG_TRACE, ("FlushBuffer - WriteFile pSpool %x hFile %x pBuffer %x cbBuffer %d cbWritten %d\n",
pSpool, pSpool->hFile, pSpool->pBuffer, pSpool->cbBuffer, cbWritten));
} else {
// RPC IO
RpcTryExcept {
if (bLoadedBySpooler && fpYWritePrinter && pSpool->hSplPrinter) {
ReturnValue = (*fpYWritePrinter)(pSpool->hSplPrinter,
pSpool->pBuffer,
pSpool->cbBuffer,
&cbWritten,
FALSE);
} else {
ReturnValue = RpcWritePrinter(pSpool->hPrinter,
pSpool->pBuffer,
pSpool->cbBuffer,
&cbWritten);
}
if (ReturnValue) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
DBGMSG(DBG_WARNING, ("FlushBuffer - RpcWritePrinter Failed Error %d\n",GetLastError() ));
} else {
ReturnValue = TRUE;
DBGMSG(DBG_TRACE, ("FlushBuffer - RpcWritePrinter Success hPrinter %x pBuffer %x cbBuffer %x cbWritten %x\n",
pSpool->hPrinter, pSpool->pBuffer,
pSpool->cbBuffer, cbWritten));
}
//
// This routine seems messed up.
// If it doesn't flush the entire buffer, it apparently still
// returns TRUE. It correctly updates the buffer pointers
// so it doesn't send duplicate information, but it
// doesn't send back bytes written. When WritePrinter
// sees success, it assumes that all bytes have been written.
//
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
DBGMSG(DBG_WARNING, ("RpcWritePrinter Exception Error %d\n",GetLastError()));
} RpcEndExcept
}
//
// We have sent more data to the printer. If we had any bytes
// from the previous write, we have just sent part of them to the
// printer. Update the cbFlushPending count to reflect the
// sent bytes. cbWritten may be > cbFlushPending, since we
// may have sent new bytes too.
//
if (pSpool->cbFlushPending < cbWritten) {
pSpool->cbFlushPending = 0;
} else {
pSpool->cbFlushPending -= cbWritten;
}
if (pSpool->cbBuffer <= cbWritten) {
if ( pSpool->cbBuffer < cbWritten) {
DBGMSG( DBG_WARNING, ("FlushBuffer cbBuffer %d < cbWritten %d ReturnValue %x LastError %d\n",
pSpool->cbBuffer, cbWritten, ReturnValue, GetLastError() ));
}
// Successful IO
// Empty the cache buffer count
pSpool->cbBuffer = 0;
} else if ( cbWritten != 0 ) {
// Partial IO
// Adjust the buffer so it contains the data that was not
// written
SPLASSERT(pSpool->cbBuffer <= BUFFER_SIZE);
SPLASSERT(cbWritten <= BUFFER_SIZE);
SPLASSERT(pSpool->cbBuffer >= cbWritten);
DBGMSG(DBG_WARNING, ("Partial IO adjusting buffer data\n"));
MoveMemory(pSpool->pBuffer,
pSpool->pBuffer + cbWritten,
BUFFER_SIZE - cbWritten);
pSpool->cbBuffer -= cbWritten;
}
}
DBGMSG(DBG_TRACE, ("FlushBuffer returns %d\n",ReturnValue));
if (pcbWritten) {
*pcbWritten = cbWritten;
}
if(!pSpool->cOKFlushBuffers &&
ReturnValue &&
cbWritten)
{
pSpool->cOKFlushBuffers=1;
}
return ReturnValue;
}
BOOL
SeekPrinter(
HANDLE hPrinter,
LARGE_INTEGER liDistanceToMove,
PLARGE_INTEGER pliNewPointer,
DWORD dwMoveMethod,
BOOL bWritePrinter
)
{
DWORD dwReturnValue;
BOOL bReturnValue = FALSE;
PSPOOL pSpool = (PSPOOL)hPrinter;
LARGE_INTEGER liUnused;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
if( !pliNewPointer ){
pliNewPointer = &liUnused;
}
RpcTryExcept {
if (bLoadedBySpooler && fpYSeekPrinter && pSpool->hSplPrinter) {
dwReturnValue = (*fpYSeekPrinter)( pSpool->hSplPrinter,
liDistanceToMove,
pliNewPointer,
dwMoveMethod,
bWritePrinter,
FALSE );
} else {
dwReturnValue = RpcSeekPrinter( pSpool->hPrinter,
liDistanceToMove,
pliNewPointer,
dwMoveMethod,
bWritePrinter );
}
if( dwReturnValue == ERROR_SUCCESS ){
bReturnValue = TRUE;
} else {
SetLastError( dwReturnValue );
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(RpcExceptionCode());
} RpcEndExcept
vUnprotectHandle( hPrinter );
return bReturnValue;
}
BOOL
FlushPrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pcWritten,
DWORD cSleep
)
/*++
Function Description: FlushPrinter is typically used by the driver to send a burst of zeros
to the printer and introduce a delay in the i/o line to the printer.
The spooler does not schedule any job for cSleep milliseconds.
Parameters: hPrinter - printer handle
pBuf - buffer to be sent to the printer
cbBuf - size of the buffer
pcWritten - pointer to return the number of bytes written
cSleep - sleep time in milliseconds.
Return Values: TRUE if successful;
FALSE otherwise
--*/
{
DWORD dwError, cWritten, Buffer;
BOOL bReturn = FALSE;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (eProtectHandle( hPrinter, FALSE ))
{
return FALSE;
}
//
// In case the job was canceled or a printer failure
// occured before priting any part of the document, we
// just short circuit and return to prevent any unnecessary
// delays in returning to the caller.
//
if (!pSpool->cOKFlushBuffers)
{
bReturn = TRUE;
goto Done;
}
//
// Use temp variables since RPC does not take NULL pointers
//
if (!pcWritten)
{
pcWritten = &cWritten;
}
if (!pBuf)
{
if (cbBuf == 0)
{
pBuf = (LPVOID) &Buffer;
}
else
{
SetLastError(ERROR_INVALID_PARAMETER);
goto Done;
}
}
//
// Rpc to the spooler
//
RpcTryExcept {
if(bLoadedBySpooler && fpYFlushPrinter && pSpool->hSplPrinter)
{
dwError = (*fpYFlushPrinter)(pSpool->hSplPrinter,
pBuf,
cbBuf,
pcWritten,
cSleep,
FALSE);
}
else
{
dwError = RpcFlushPrinter( pSpool->hPrinter,
pBuf,
cbBuf,
pcWritten,
cSleep );
}
if (dwError == ERROR_SUCCESS)
{
bReturn = TRUE;
}
else
{
SetLastError( dwError );
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(RpcExceptionCode());
} RpcEndExcept
Done:
vUnprotectHandle( hPrinter );
return bReturn;
}
BOOL
WritePrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pcWritten
)
{
BOOL ReturnValue=TRUE, bAllocBuffer = FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC];
DWORD cb;
DWORD cbWritten = 0;
DWORD cTotalWritten = 0;
LPBYTE pBuffer = pBuf;
LPBYTE pInitialBuf = pBuf;
PSPOOL pSpool = (PSPOOL)hPrinter;
PJOB_INFO_1 pJob;
DWORD cbNeeded;
DWORD dwTickCount, dwTickCount1;
DWORD FlushPendingDataSize;
DWORD ReqTotalDataSize;
DWORD ReqToWriteDataSize = cbBuf;
DWORD NumOfCmpltWrts = 0;
DBGMSG(DBG_TRACE, ("WritePrinter - hPrinter %x pBuf %x cbBuf %d pcWritten %x\n",
hPrinter, pBuf, cbBuf, pcWritten));
if( eProtectHandle( hPrinter, FALSE ))
{
return FALSE;
}
if (pSpool && pSpool->Flushed)
{
ReturnValue = FALSE;
goto EndWritePrinter;
}
FlushPendingDataSize = pSpool->cbFlushPending;
ReqTotalDataSize = FlushPendingDataSize + ReqToWriteDataSize;
*pcWritten = 0;
if ( !(pSpool->Status & SPOOL_STATUS_STARTDOC) ) {
SetLastError(ERROR_SPL_NO_STARTDOC);
ReturnValue = FALSE;
goto EndWritePrinter;
}
// Check if local job is cancelled every JOB_CANCEL_CHECK_INTERVAL bytes
if (!pSpool->cWritePrinters) {
pSpool->dwTickCount = GetTickCount();
pSpool->dwCheckJobInterval = JOB_CANCEL_CHECK_INTERVAL;
}
if (pSpool->hFile != INVALID_HANDLE_VALUE &&
pSpool->dwTickCount + pSpool->dwCheckJobInterval < (dwTickCount = GetTickCount())) {
bAllocBuffer = FALSE;
pJob = (PJOB_INFO_1) btBuffer;
ZeroMemory(pJob, MAX_STATIC_ALLOC);
ReturnValue = GetJob((HANDLE) pSpool, pSpool->JobId, 1, (LPBYTE)pJob,
MAX_STATIC_ALLOC, &cbNeeded);
if (!ReturnValue &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pJob = (PJOB_INFO_1) AllocSplMem(cbNeeded))) {
bAllocBuffer = TRUE;
ReturnValue = GetJob(hPrinter, pSpool->JobId, 1, (LPBYTE)pJob,
cbNeeded, &cbNeeded);
}
if (ReturnValue) {
// Don't allow GetJob calls to take more than 1% pSpool->dwCheckJobInterval
dwTickCount1 = GetTickCount();
if (dwTickCount1 > dwTickCount + (pSpool->dwCheckJobInterval/100)) {
pSpool->dwCheckJobInterval *= 2;
} else if (dwTickCount1 - dwTickCount < JOB_CANCEL_CHECK_INTERVAL/100) {
pSpool->dwCheckJobInterval = JOB_CANCEL_CHECK_INTERVAL;
}
if (!pJob->pStatus && (pJob->Status & JOB_STATUS_DELETING)) {
SetLastError(ERROR_PRINT_CANCELLED);
if (bAllocBuffer) {
FreeSplMem(pJob);
}
ReturnValue = FALSE;
goto EndWritePrinter;
}
}
if (bAllocBuffer) {
FreeSplMem(pJob);
}
pSpool->dwTickCount = GetTickCount();
}
pSpool->cWritePrinters++;
// WritePrinter will cache on the client side all IO's
// into BUFFER_SIZE writes. This is done to minimize
// the number of RPC calls if the app is doing a lot of small
// sized IO's.
while (cbBuf && ReturnValue) {
// Special Case FileIO's since file system prefers large
// writes, RPC is optimal with smaller writes.
//
// RPC should manage its own buffer size. I'm not sure why we
// only do this optimization for file writes.
//
if ((pSpool->hFile != INVALID_HANDLE_VALUE) &&
(pSpool->cbBuffer == 0) &&
(cbBuf > BUFFER_SIZE)) {
ReturnValue = WriteFile(pSpool->hFile, pBuffer, cbBuf, &cbWritten, NULL);
DBGMSG(DBG_TRACE, ("WritePrinter - WriteFile pSpool %x hFile %x pBuffer %x cbBuffer %d cbWritten %d\n",
pSpool, pSpool->hFile, pBuffer, pSpool->cbBuffer, *pcWritten));
} else {
// Fill cache buffer so IO is optimal size.
SPLASSERT(pSpool->cbBuffer <= BUFFER_SIZE);
//
// cb is the amount of new data we want to put in the buffer.
// It is the min of the space remaining, and the size of the
// input buffer.
//
cb = min((BUFFER_SIZE - pSpool->cbBuffer), cbBuf);
if (cb != 0) {
if (pSpool->pBuffer == NULL) {
pSpool->pBuffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (pSpool->pBuffer == NULL) {
DBGMSG(DBG_WARNING, ("VirtualAlloc Failed to allocate 4k buffer %d\n",GetLastError()));
ReturnValue = FALSE;
goto EndWritePrinter;
}
}
CopyMemory( pSpool->pBuffer + pSpool->cbBuffer, pBuffer, cb);
pSpool->cbBuffer += cb;
cbWritten = cb;
pSpool->cCacheWrite++;
}
//
// cbWritten is the amount of new data that has been put into
// the buffer. It may not have been written to the device, but
// since it is in our buffer, the driver can assume it has been
// written (e.g., the *pcbWritten out parameter to WritePrinter
// includes this data).
//
if (pSpool->cbBuffer == BUFFER_SIZE)
{
DWORD cbPending = pSpool->cbFlushPending;
DWORD cbFlushed = 0;
ReturnValue = FlushBuffer(pSpool, &cbFlushed);
if(!NumOfCmpltWrts && ReturnValue)
{
NumOfCmpltWrts = 1;
}
if(!ReturnValue &&
(ERROR_PRINT_CANCELLED == GetLastError()) &&
pSpool->hSplPrinter &&
pSpool->cOKFlushBuffers)
{
SJobCancelInfo JobCancelInfo;
JobCancelInfo.pSpool = pSpool;
JobCancelInfo.pInitialBuf = pInitialBuf;
JobCancelInfo.pcbWritten = &cbWritten;
JobCancelInfo.pcTotalWritten = &cTotalWritten;
JobCancelInfo.NumOfCmpltWrts = NumOfCmpltWrts;
JobCancelInfo.cbFlushed = cbFlushed;
JobCancelInfo.ReqTotalDataSize = ReqTotalDataSize;
JobCancelInfo.ReqToWriteDataSize = ReqToWriteDataSize;
JobCancelInfo.FlushPendingDataSize = FlushPendingDataSize;
JobCancelInfo.ReturnValue = ReturnValue;
ReturnValue = JobCanceled(&JobCancelInfo);
}
}
}
// Update Total Byte Count after the Flush or File IO
// This is done because the IO might fail and thus
// the correct value written might have changed.
if(!pSpool->Flushed)
{
SPLASSERT(cbBuf >= cbWritten);
cbBuf -= cbWritten;
pBuffer += cbWritten;
cTotalWritten += cbWritten;
}
else
break;
}
// Return the number of bytes written.
*pcWritten = cTotalWritten;
DBGMSG(DBG_TRACE, ("WritePrinter cbWritten %d ReturnValue %d\n",*pcWritten, ReturnValue));
//
// Remember if there is a flush pending on this WritePrinter. If there
// is, then when we return, we say we've written all the bytes, but
// we really haven't since there's some left in the buffer. If the
// user cancels the next job, then we need to flush out the last
// bytes, since the driver assumes that we've written it out and
// tracks the printer state.
//
if(!pSpool->Flushed)
pSpool->cbFlushPending = pSpool->cbBuffer;
EndWritePrinter:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
EndPagePrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue = TRUE;
PSPOOL pSpool = (PSPOOL)hPrinter;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
try {
FlushBuffer(pSpool, NULL);
if( pSpool->hFile == INVALID_HANDLE_VALUE ){
RpcTryExcept {
if (ReturnValue = RpcEndPagePrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
}
} except (1) {
SetLastError(ERROR_INVALID_HANDLE);
ReturnValue = FALSE;
}
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
AbortPrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwNumWritten = 0;
DWORD dwPointer = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
//
// No longer in StartDoc mode; also resetting the tray icon notification
// flag so that upcoming StartDocPrinter/AddJobs indicate a new job.
//
pSpool->Status &= ~(SPOOL_STATUS_STARTDOC|SPOOL_STATUS_TRAYICON_NOTIFIED);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
if (pSpool->Status & SPOOL_STATUS_ADDJOB) {
// Close your handle to the .SPL file, otherwise the
// DeleteJob will fail in the Spooler
CloseSpoolFileHandles( pSpool );
if (!SetJob(hPrinter,pSpool->JobId, 0, NULL, JOB_CONTROL_DELETE)) {
DBGMSG(DBG_WARNING, ("Error: SetJob cancel returned failure with %d\n", GetLastError()));
}
ReturnValue = ScheduleJob(hPrinter, pSpool->JobId);
goto Done;
} else {
DBGMSG(DBG_WARNING, ("Error: pSpool->hFile != INVALID_HANDLE_VALUE and pSpool's status is not SPOOL_STATUS_ADDJOB\n"));
}
}
RpcTryExcept {
if (ReturnValue = RpcAbortPrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
Done:
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
ReadPrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pNoBytesRead
)
{
BOOL bReturn = FALSE;
DWORD dwStatus;
PSPOOL pSpool = (PSPOOL)hPrinter;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FlushBuffer(pSpool, NULL);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
SetLastError(ERROR_INVALID_HANDLE);
goto Done;
}
RpcTryExcept {
cbBuf = min(BUFFER_SIZE, cbBuf);
if (bLoadedBySpooler && fpYReadPrinter && pSpool->hSplPrinter) {
dwStatus = (*fpYReadPrinter)(pSpool->hSplPrinter, pBuf, cbBuf, pNoBytesRead, FALSE);
} else {
dwStatus = RpcReadPrinter(pSpool->hPrinter, pBuf, cbBuf, pNoBytesRead);
}
if (dwStatus) {
SetLastError(dwStatus);
} else {
bReturn = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
} RpcEndExcept
Done:
vUnprotectHandle( hPrinter );
return bReturn;
}
BOOL
SplReadPrinter(
HANDLE hPrinter,
LPBYTE *pBuf,
DWORD cbBuf
)
/*++
Function Description: This is an internal function used by the spooler during playback of
EMF jobs. It is called from gdi32.dll. SplReadPrinter is equivalent
to ReadPrinter in all respects except that it returns a pointer to the
buffer in pBuf. The spool file is memory mapped.
Parameters: hPrinter -- handle to the printer
pBuf -- pointer to the buffer
cbBuf -- number to bytes to read
Return Values: TRUE if sucessful (pBuf contains the required pointer)
FALSE otherwise
--*/
{
BOOL bReturn = FALSE;
DWORD dwStatus = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
// This function is to be used only internally. Hence no RPC interface is required.
if (!bLoadedBySpooler || !fpYSplReadPrinter || !pSpool->hSplPrinter) {
SetLastError(ERROR_NOT_SUPPORTED);
goto Done;
}
FlushBuffer(pSpool, NULL);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
SetLastError(ERROR_INVALID_HANDLE);
goto Done;
}
// Optimal buffer size of 4K need not be used for non RPC code paths.
dwStatus = (*fpYSplReadPrinter)(pSpool->hSplPrinter, pBuf, cbBuf, FALSE);
if (dwStatus) {
SetLastError(dwStatus);
} else {
bReturn = TRUE;
}
Done:
vUnprotectHandle( hPrinter );
return bReturn;
}
BOOL
EndDocPrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwRetryTimes;
DWORD dwNeeded;
USEROBJECTFLAGS uof;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
if (GetUserObjectInformation(GetProcessWindowStation(), UOI_FLAGS, &uof, sizeof(uof), &dwNeeded) && (WSF_VISIBLE & uof.dwFlags)) {
//
// hack: if we are in interactive window station (i.e. not in a service)
// we need to wait the tray code to startup, so we don't miss balloon
// notifications. there is still possibility of missing balloon notifications
// but very unlikely. the complete fix will come with CSR in place (i.e. in
// Blackcomb)
//
dwRetryTimes = 20;
while (dwRetryTimes--){
if (NULL == FindWindow(cszTrayListenerClassName, NULL)){
Sleep(100);
continue;
}
Sleep(100);
break;
}
}
try {
FlushBuffer(pSpool, NULL);
//
// No longer in StartDoc mode; also resetting the tray icon
// notification flag so that upcoming StartDocPrinter/AddJobs
// indicate a new job.
//
pSpool->Status &= ~(SPOOL_STATUS_STARTDOC|SPOOL_STATUS_TRAYICON_NOTIFIED);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
if (CloseHandle(pSpool->hFile)) {
pSpool->hFile = INVALID_HANDLE_VALUE;
}
ReturnValue = ScheduleJob(hPrinter, pSpool->JobId);
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
DBGMSG(DBG_TRACE, ("Exit EndDocPrinter - client side hPrinter %x\n", hPrinter));
} else {
RpcTryExcept {
if(bLoadedBySpooler && fpYEndDocPrinter && pSpool->hSplPrinter)
{
ReturnValue = (*fpYEndDocPrinter)(pSpool->hSplPrinter,FALSE);
}
else
{
ReturnValue = RpcEndDocPrinter(pSpool->hPrinter);
}
if (ReturnValue)
{
SetLastError(ReturnValue);
ReturnValue = FALSE;
}
else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
DBGMSG(DBG_TRACE, ("Exit EndDocPrinter - client side hPrinter %x\n", hPrinter));
}
} except (1) {
SetLastError(ERROR_INVALID_HANDLE);
ReturnValue = FALSE;
}
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
AddJobW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pData,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue = FALSE;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
FieldInfo *pFieldInfo;
DWORD cbStruct;
switch (Level) {
case 1:
pFieldInfo = AddJobFields;
cbStruct = sizeof(ADDJOB_INFO_1W);
break;
case 2:
case 3:
{
//
// Level 3 is meant to be used only by RDR/SRV. The spooler needs
// to know whether the job comes from RDR/SRV. See LocalScheduleJob
// in localspl.dll for details
//
//
// This is an internal call used by the server when it needs
// to submit a job with a specific machine name (used for
// netbiosless notifications, or if the user want the notification
// to go to the computer instead of the user).
//
// IN: (PADDJOB_INFO_2W)pData - points to buffer that receives the
// path and ID. On input, pData points to the computer name.
// pData->pData must not point to a string inside of the pData
// buffer, and it must be smaller than cbBuf -
// sizeof( ADDJOB_INFO_2W ). It must not be szNull or NULL.
//
PADDJOB_INFO_2W pInfo2;
pInfo2 = (PADDJOB_INFO_2W)pData;
//
// Check valid pointer and buffer.
//
if( !pInfo2 ||
!pInfo2->pData ||
!pInfo2->pData[0] ||
cbBuf < sizeof( *pInfo2 ) +
(wcslen( pInfo2->pData ) + 1) * sizeof( WCHAR )){
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
//
// Simple marshalling.
//
wcscpy( (LPWSTR)(pInfo2 + 1), pInfo2->pData );
pInfo2->pData = (LPWSTR)sizeof( *pInfo2 );
pFieldInfo = AddJob2Fields;
cbStruct = sizeof(ADDJOB_INFO_2W);
break;
}
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
try {
do {
RpcTryExcept {
if (ReturnValue = RpcAddJob(pSpool->hPrinter, Level, pData,
cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = MarshallUpStructure(pData, pFieldInfo, cbStruct, RPC_CALL);
pSpool->Status |= SPOOL_STATUS_ADDJOB;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
if( ReturnValue ){
//
// Notify the tray icon that a new job has been sent.
//
vUpdateTrayIcon( hPrinter, ((PADDJOB_INFO_1)pData)->JobId );
}
} except (1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
ReturnValue = FALSE;
}
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
ScheduleJob(
HANDLE hPrinter,
DWORD JobId
)
{
PSPOOL pSpool = (PSPOOL)hPrinter;
BOOL bReturn;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
bReturn = ScheduleJobWorker( pSpool, JobId );
vUnprotectHandle( hPrinter );
return bReturn;
}
BOOL
ScheduleJobWorker(
PSPOOL pSpool,
DWORD JobId
)
{
BOOL ReturnValue;
try {
//
// The job has been scheduled, so reset the flag that indicates
// the tray icon has been notified. Any new AddJob/StartDocPrinter/
// StartDoc events should send a new notification, since it's really
// a new job.
//
pSpool->Status &= ~SPOOL_STATUS_TRAYICON_NOTIFIED;
FlushBuffer(pSpool, NULL);
RpcTryExcept {
if (ReturnValue = RpcScheduleJob(pSpool->hPrinter, JobId)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
} except (1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
return(FALSE);
}
}
DWORD WINAPI
AsyncPrinterProperties(
PVOID pData
)
{
PrtPropsData *ThrdData = (PrtPropsData *)pData;
RpcTryExcept
{
RPCSplWOW64PrinterProperties(ThrdData->hWnd,
ThrdData->PrinterName,
ThrdData->Flag,
ThrdData->dwRet);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
return(0);
}
BOOL
PrinterPropertiesNative(
HWND hWnd,
HANDLE hPrinter
)
/*++
Routine Description:
This is main PrinterProperties entri point and will call into the
our DevicePropertySheets() for UI pop up
Arguments:
hWnd - Handle to the window parent
hPrinter - Handle to the printer interested
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE.
To get extended error information, call GetLastError.
Author:
13-Jun-1996 Thu 15:22:36 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
PRINTER_INFO_2 *pPI2 = NULL;
DEVICEPROPERTYHEADER DPHdr;
LONG Result;
DWORD cb;
DWORD dwValue = 1;
BOOL bAllocBuffer = FALSE, bReturn;
BYTE btBuffer[MAX_STATIC_ALLOC];
//
// Ensure the printer handle is valid
//
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
DPHdr.cbSize = sizeof(DPHdr);
DPHdr.hPrinter = hPrinter;
DPHdr.Flags = DPS_NOPERMISSION;
//
// Do a GetPrinter() level2 to get the printer name.
//
pPI2 = (PPRINTER_INFO_2) btBuffer;
bReturn = GetPrinter(hPrinter, 2, (LPBYTE)pPI2, MAX_STATIC_ALLOC, &cb);
if (!bReturn &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pPI2 = (PPRINTER_INFO_2)LocalAlloc(LMEM_FIXED, cb))) {
bAllocBuffer = TRUE;
bReturn = GetPrinter(hPrinter, 2, (LPBYTE)pPI2, cb, &cb);
}
//
// Set the printer name.
//
if (bReturn) {
DPHdr.pszPrinterName = pPI2->pPrinterName;
} else {
DPHdr.pszPrinterName = NULL;
}
//
// Attempt to set the printer data to determine access privilages.
//
if (SetPrinterData( hPrinter,
TEXT( "PrinterPropertiesPermission" ),
REG_DWORD,
(LPBYTE)&dwValue,
sizeof( dwValue ) ) == STATUS_SUCCESS ) {
//
// Indicate we have permissions.
//
DPHdr.Flags &= ~DPS_NOPERMISSION;
}
//
// Call Common UI to call do the and call the driver.
//
if ( CallCommonPropertySheetUI(hWnd,
(PFNPROPSHEETUI)DevicePropertySheets,
(LPARAM)&DPHdr,
(LPDWORD)&Result) < 0 ) {
Result = FALSE;
} else {
Result = TRUE;
}
if (bAllocBuffer) {
LocalFree((HLOCAL)pPI2);
}
vUnprotectHandle( hPrinter );
return Result;
}
BOOL
PrinterPropertiesThunk(
HWND hWnd,
HANDLE hPrinter
)
/*++
Routine Description:
This is main PrinterProperties entri point and will call into the
our DevicePropertySheets() for UI pop up
Arguments:
hWnd - Handle to the window parent
hPrinter - Handle to the printer interested
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE.
To get extended error information, call GetLastError.
--*/
{
PRINTER_INFO_2 *pPI2 = NULL;
DEVICEPROPERTYHEADER DPHdr;
LONG Result;
DWORD cb;
DWORD dwValue = 1;
BOOL bAllocBuffer = FALSE, bReturn;
BYTE btBuffer[MAX_STATIC_ALLOC];
DWORD dwRet;
//
// Ensure the printer handle is valid
//
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
DPHdr.cbSize = sizeof(DPHdr);
DPHdr.hPrinter = hPrinter;
DPHdr.Flags = DPS_NOPERMISSION;
//
// Do a GetPrinter() level2 to get the printer name.
//
pPI2 = (PPRINTER_INFO_2) btBuffer;
bReturn = GetPrinter(hPrinter, 2, (LPBYTE)pPI2, MAX_STATIC_ALLOC, &cb);
if (!bReturn &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pPI2 = (PPRINTER_INFO_2)LocalAlloc(LMEM_FIXED, cb))) {
bAllocBuffer = TRUE;
bReturn = GetPrinter(hPrinter, 2, (LPBYTE)pPI2, cb, &cb);
}
//
// Set the printer name.
//
if (bReturn)
{
if(pPI2->pPrinterName)
{
//
// Attempt to set the printer data to determine access privilages.
//
DWORD Flag = DPS_NOPERMISSION;
if (SetPrinterData( hPrinter,
TEXT( "PrinterPropertiesPermission" ),
REG_DWORD,
(LPBYTE)&dwValue,
sizeof( dwValue ) ) == STATUS_SUCCESS )
{
//
// Indicate we have permissions.
//
Flag &= ~DPS_NOPERMISSION;
}
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
HANDLE hUIMsgThrd = NULL;
DWORD UIMsgThrdId = 0;
PrtPropsData ThrdData;
ThrdData.hWnd = (ULONG_PTR)hWnd;
ThrdData.dwRet = &dwRet;
ThrdData.PrinterName = (LPWSTR)pPI2->pPrinterName;
ThrdData.Flag = Flag;
if(!(hUIMsgThrd = CreateThread(NULL,
INITIAL_STACK_COMMIT,
AsyncPrinterProperties,
(PVOID)&ThrdData,
0,
&UIMsgThrdId)))
{
dwRet = GetLastError();
}
//
// The following is the required message loop for processing messages
// from the UI in case we have a window handle.
//
//
if(hUIMsgThrd)
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
//
// In This message loop We should trap a User defined message
// which indicates the success or the failure of the operation
//
if(msg.message == WM_ENDPRINTERPROPERTIES)
{
Result = (LONG)msg.wParam;
if(Result == FALSE)
SetLastError((DWORD)msg.lParam);
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
Result = FALSE;
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if(hUIMsgThrd)
{
WaitForSingleObject(hUIMsgThrd,INFINITE);
CloseHandle(hUIMsgThrd);
}
}
else
{
SetLastError(dwRet);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
}
else
{
Result = FALSE;
}
}
else
{
Result = FALSE;
}
if (bAllocBuffer) {
LocalFree((HLOCAL)pPI2);
}
vUnprotectHandle( hPrinter );
return Result;
}
BOOL
PrinterProperties(
HWND hWnd,
HANDLE hPrinter
)
{
if(RunInWOW64())
{
return(PrinterPropertiesThunk(hWnd,
hPrinter));
}
else
{
return(PrinterPropertiesNative(hWnd,
hPrinter));
}
}
DWORD
GetPrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName,
LPDWORD pType,
LPBYTE pData,
DWORD nSize,
LPDWORD pcbNeeded
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
WCHAR szEMFDatatype[] = L"PrintProcCaps_EMF";
WCHAR szEMFDatatypeWithVersion[] = L"PrintProcCaps_NT EMF 1.008";
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// ReturnValue (this is just a dummy pointer).
//
if( !pData && !nSize ){
pData = (PBYTE)&ReturnValue;
}
if (!pType) {
pType = (PDWORD) &ReturnType;
}
//
// If pValueName is PrintProcCaps_datatype add the EMF version if necessary.
// This hardcoded EMF version number will have to be modified whenever GDI changes
// the version number. This change has been made for GetPrintProcessorCapabilities.
//
if (pValueName && !_wcsicmp(pValueName, szEMFDatatype)) {
pValueName = (LPWSTR) szEMFDatatypeWithVersion;
}
do {
RpcTryExcept {
ReturnValue = RpcGetPrinterData(pSpool->hPrinter, pValueName, pType,
pData, nSize, pcbNeeded);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
GetPrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName,
LPCWSTR pValueName,
LPDWORD pType,
LPBYTE pData,
DWORD nSize,
LPDWORD pcbNeeded
)
{
DWORD Key = 0;
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// ReturnValue (this is just a dummy pointer).
//
if( !pData && !nSize ){
pData = (PBYTE)&ReturnValue;
}
if (!pType) {
pType = (PDWORD) &ReturnType;
}
if (!pKeyName) {
pKeyName = (PWSTR) &Key;
}
do
{
RpcTryExcept {
ReturnValue = RpcGetPrinterDataEx( pSpool->hPrinter,
pKeyName,
pValueName,
pType,
pData,
nSize,
pcbNeeded);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while (ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle(pSpool));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
HANDLE
GetSpoolFileHandle(
HANDLE hPrinter
)
/*++
Function Description: Gets spool file handle which is used by GDI in recording EMF
data.
Parameters: hPrinter - Printer handle
Return Values: Handle to the spool file if successful
INVALID_HANDLE_VALUE otherwise
--*/
{
HANDLE hReturn = INVALID_HANDLE_VALUE;
DWORD dwAppProcessId, cbBuf, dwNeeded, dwRpcReturn;
FILE_INFO_CONTAINER FileInfoContainer;
SPOOL_FILE_INFO_1 SpoolFileInfo;
PSPOOL pSpool = (PSPOOL) hPrinter;
if (eProtectHandle(hPrinter, FALSE)) {
return hReturn;
}
if (pSpool->hSpoolFile != INVALID_HANDLE_VALUE) {
// GetSpoolFileHandle has already been called; return old handles
hReturn = pSpool->hSpoolFile;
goto CleanUp;
}
dwAppProcessId = GetCurrentProcessId();
FileInfoContainer.Level = 1;
FileInfoContainer.FileInfo.Level1 = &SpoolFileInfo;
RpcTryExcept {
dwRpcReturn = RpcGetSpoolFileInfo2(pSpool->hPrinter,
dwAppProcessId,
1,
&FileInfoContainer);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
dwRpcReturn = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
if (dwRpcReturn) {
SetLastError(dwRpcReturn);
} else {
pSpool->hSpoolFile = FileInfoContainer.FileInfo.Level1->hSpoolFile;
pSpool->dwSpoolFileAttributes = FileInfoContainer.FileInfo.Level1->dwAttributes;
hReturn = pSpool->hSpoolFile;
}
CleanUp:
vUnprotectHandle(hPrinter);
return hReturn;
}
HANDLE
CommitSpoolData(
HANDLE hPrinter,
HANDLE hSpoolFile,
DWORD cbCommit
)
/*++
Function Description: Commits cbCommit bytes in the spool file. For temporary files, a new
spool file handle is returned.
Parameters: hPrinter -- printer handle
hSpoolFile -- spool file handle (from GetSpoolFileHandle)
cbCommit -- number of bytes to commit (incremental count)
Return Values: New spool file handle for temporary spool files and
old handle for persistent files
--*/
{
HANDLE hReturn = INVALID_HANDLE_VALUE;
DWORD dwAppProcessId, dwRpcReturn;
DWORD dwNeeded, cbBuf;
HANDLE hNewSpoolFile;
FILE_INFO_CONTAINER FileInfoContainer;
SPOOL_FILE_INFO_1 SpoolFileInfo;
PSPOOL pSpool = (PSPOOL) hPrinter;
if (eProtectHandle(hPrinter, FALSE)) {
return hReturn;
}
if ((pSpool->hSpoolFile == INVALID_HANDLE_VALUE) ||
(pSpool->hSpoolFile != hSpoolFile)) {
SetLastError(ERROR_INVALID_HANDLE);
goto CleanUp;
}
dwAppProcessId = GetCurrentProcessId();
FileInfoContainer.Level = 1;
FileInfoContainer.FileInfo.Level1 = &SpoolFileInfo;
RpcTryExcept {
dwRpcReturn = RpcCommitSpoolData2(pSpool->hPrinter,
dwAppProcessId,
cbCommit,
1,
&FileInfoContainer);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
dwRpcReturn = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
if (dwRpcReturn) {
SetLastError(dwRpcReturn);
} else {
hNewSpoolFile = FileInfoContainer.FileInfo.Level1->hSpoolFile;
if (hNewSpoolFile != SPOOL_INVALID_HANDLE_VALUE_32BIT &&
hNewSpoolFile != INVALID_HANDLE_VALUE) {
CloseHandle(pSpool->hSpoolFile);
pSpool->hSpoolFile = hNewSpoolFile;
}
hReturn = pSpool->hSpoolFile;
}
CleanUp:
vUnprotectHandle(hPrinter);
return hReturn;
}
BOOL
CloseSpoolFileHandle(
HANDLE hPrinter,
HANDLE hSpoolFile
)
/*++
Function Description: Closes the client and server handles for the spool file.
Parameters: hPrinter - printer handle
hSpoolFile - spool file handle (used for consistency across APIs)
Return Values: TRUE if sucessfule; FALSE otherwise
--*/
{
BOOL bReturn = FALSE;
DWORD dwLastError = ERROR_SUCCESS;
PSPOOL pSpool = (PSPOOL) hPrinter;
if (eProtectHandle(hPrinter, FALSE)) {
return FALSE;
}
if (pSpool->hSpoolFile != hSpoolFile) {
SetLastError(ERROR_INVALID_HANDLE);
goto Done;
}
if (pSpool->hSpoolFile != INVALID_HANDLE_VALUE) {
CloseHandle(pSpool->hSpoolFile);
pSpool->hSpoolFile = INVALID_HANDLE_VALUE;
}
RpcTryExcept {
dwLastError = RpcCloseSpoolFileHandle(pSpool->hPrinter);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
dwLastError = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
if (dwLastError != ERROR_SUCCESS) {
SetLastError(dwLastError);
} else {
bReturn = TRUE;
}
Done:
vUnprotectHandle(hPrinter);
return bReturn;
}
DWORD
EnumPrinterDataW(
HANDLE hPrinter,
DWORD dwIndex, // index of value to query
LPWSTR pValueName, // address of buffer for value string
DWORD cbValueName, // size of pValueName
LPDWORD pcbValueName, // address for size of value buffer
LPDWORD pType, // address of buffer for type code
LPBYTE pData, // address of buffer for value data
DWORD cbData, // size of pData
LPDWORD pcbData // address for size of data buffer
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// a dummy pointer.
//
if (!pValueName && !cbValueName)
pValueName = (LPWSTR) &ReturnValue;
if( !pData && !cbData )
pData = (PBYTE)&ReturnValue;
if (!pType)
pType = (PDWORD) &ReturnType;
do {
RpcTryExcept {
ReturnValue = RpcEnumPrinterData( pSpool->hPrinter,
dwIndex,
pValueName,
cbValueName,
pcbValueName,
pType,
pData,
cbData,
pcbData);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
EnumPrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName, // address of key name
LPBYTE pEnumValues,
DWORD cbEnumValues,
LPDWORD pcbEnumValues,
LPDWORD pnEnumValues
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD i;
PPRINTER_ENUM_VALUES pEnumValue = (PPRINTER_ENUM_VALUES) pEnumValues;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// a dummy pointer.
//
if (!pEnumValues && !cbEnumValues)
pEnumValues = (LPBYTE) &ReturnValue;
do {
RpcTryExcept {
ReturnValue = RpcEnumPrinterDataEx(pSpool->hPrinter,
pKeyName,
pEnumValues,
cbEnumValues,
pcbEnumValues,
pnEnumValues);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
if (ReturnValue == ERROR_SUCCESS) {
if (pEnumValues) {
if (!MarshallUpStructuresArray((LPBYTE)pEnumValue, *pnEnumValues,PrinterEnumValuesFields,
sizeof(PRINTER_ENUM_VALUES), RPC_CALL) ) {
ReturnValue = GetLastError();
}
}
}
} while ( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
EnumPrinterKeyW(
HANDLE hPrinter,
LPCWSTR pKeyName, // address of key name
LPWSTR pSubkey, // address of buffer for value string
DWORD cbSubkey, // size of pValueName
LPDWORD pcbSubkey // address for size of value buffer
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// a dummy pointer.
//
if (!pSubkey && !cbSubkey)
pSubkey = (LPWSTR) &ReturnValue;
do {
RpcTryExcept {
ReturnValue = RpcEnumPrinterKey(pSpool->hPrinter,
pKeyName,
pSubkey,
cbSubkey,
pcbSubkey);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while ( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
DeletePrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
do {
RpcTryExcept {
ReturnValue = RpcDeletePrinterData(pSpool->hPrinter, pValueName);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
DeletePrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName,
LPCWSTR pValueName
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
do {
RpcTryExcept {
ReturnValue = RpcDeletePrinterDataEx(pSpool->hPrinter, pKeyName, pValueName);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
DeletePrinterKeyW(
HANDLE hPrinter,
LPCWSTR pKeyName
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
do {
RpcTryExcept {
ReturnValue = RpcDeletePrinterKey(pSpool->hPrinter, pKeyName);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
SetPrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName,
DWORD Type,
LPBYTE pData,
DWORD cbData
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
do {
RpcTryExcept {
ReturnValue = RpcSetPrinterData(pSpool->hPrinter, pValueName, Type,
pData, cbData);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
DWORD
SetPrinterDataExW(
HANDLE hPrinter,
LPCWSTR pKeyName,
LPCWSTR pValueName,
DWORD Type,
LPBYTE pData,
DWORD cbData
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return ERROR_INVALID_HANDLE;
}
if (!pKeyName)
pKeyName = L"";
do {
RpcTryExcept {
ReturnValue = RpcSetPrinterDataEx( pSpool->hPrinter,
pKeyName,
pValueName,
Type,
pData,
cbData);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
} while( ReturnValue == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
VOID
SplDriverUnloadComplete(
LPWSTR pDriverFile
)
/*++
Function Description: The information on the driver unload is set to the spooler
so that it may continue any pending upgrades.
Parameters: pDriverFile -- driver file name which was unloaded
Return Value: NONE
--*/
{
if (bLoadedBySpooler && fpYDriverUnloadComplete) {
(*fpYDriverUnloadComplete)(pDriverFile);
}
}
HANDLE
LoadNewCopy(
LPWSTR pConfigFile,
DWORD dwFlags,
DWORD dwVersion
)
/*++
Function Description: This function loads the driver file and creates a node to
maintain its reference count. It is called inside ListAccessSem.
Parameters: pConfigFile -- driver config file path
dwFlags -- flags for loading
dwVersion -- version number of the driver since reboot
Return Value: handle to the library
--*/
{
HANDLE hReturn = NULL;
PDRVLIBNODE pTmpDrvLib, pNewDrvLib = NULL;
ULONG_PTR lActCtx = 0;
BOOL bDidActivate = FALSE;
//
// Activate the empty context
//
bDidActivate = ActivateActCtx( ACTCTX_EMPTY, &lActCtx );
// Inside ListAccessSem
hReturn = LoadLibraryEx(pConfigFile, NULL, dwFlags);
if (hReturn) {
// Create a new DRVLIBNODE
if (pNewDrvLib = (PDRVLIBNODE) AllocSplMem(sizeof(DRVLIBNODE))) {
pNewDrvLib->hLib = hReturn;
pNewDrvLib->dwVersion = dwVersion;
// Initialize ref cnt to 2. This ensures that the library remains loaded
// in the normal course.
pNewDrvLib->dwNumHandles = 2;
pNewDrvLib->bArtificialIncrement = TRUE;
if (!(pNewDrvLib->pConfigFile = AllocSplStr(pConfigFile)))
{
FreeSplMem(pNewDrvLib);
pNewDrvLib = NULL;
}
}
if (!pNewDrvLib) {
// Free the library
FreeLibrary(hReturn);
hReturn = NULL;
} else {
// Add the node to the list
pNewDrvLib->pNext = pStartDrvLib;
pStartDrvLib = pNewDrvLib;
}
}
//
// Deactivate the context
//
if( bDidActivate ){
DeactivateActCtx( 0, lActCtx );
}
return hReturn;
}
PDRVLIBNODE
FindDriverNode(
LPWSTR pConfigFile,
DWORD dwVersion,
BOOL bUseVersion
)
/*++
Function Description: Searches thru the list of driver nodes to get the
required driver information. In case of version mismatch the
artificial increment on the old driver is removed.
This function is called inside the ListAccessSem
Parameters: pConfigFile -- driver config file name
dwVersion -- version number of the driver since reboot
bUseVersion -- flag to use the version number
Return Values: pDrvLibNode for the required driver, if present
--*/
{
PDRVLIBNODE pTmpDrvLib;
for (pTmpDrvLib = pStartDrvLib; pTmpDrvLib; pTmpDrvLib = pTmpDrvLib->pNext) {
if (!_wcsicmp(pConfigFile, pTmpDrvLib->pConfigFile)) {
break;
}
}
if (pTmpDrvLib && bUseVersion && (pTmpDrvLib->dwVersion != dwVersion)) {
if (pTmpDrvLib->bArtificialIncrement) {
pTmpDrvLib->bArtificialIncrement = FALSE;
if (RefCntUnloadDriver(pTmpDrvLib->hLib, FALSE)) {
pTmpDrvLib = NULL;
}
}
}
return pTmpDrvLib;
}
HANDLE
RefCntLoadDriver(
LPWSTR pConfigFile,
DWORD dwFlags,
DWORD dwVersion,
BOOL bUseVersion
)
/*++
Function Description: This function loads the driver config file. It reuses existing handles
to avoid expensive Loads and Frees. In case of a version mismatch the
original handle is freed and we load the driver again.
Parameters: pConfigFile -- driver config file name
dwFlags -- flags for loading (ignored if existing handle is returned)
dwVersion -- version of the driver file since reboot
bUseVersion -- flag to use the version number check
Return Value: handle to the library
--*/
{
HANDLE hReturn = NULL;
PDRVLIBNODE pTmpDrvLib;
if (!pConfigFile || !*pConfigFile) {
// nothing to load
return hReturn;
}
EnterCriticalSection( &ListAccessSem );
pTmpDrvLib = FindDriverNode(pConfigFile, dwVersion, bUseVersion);
// Use existing handle, if any.
if (pTmpDrvLib) {
// Increment the ref cnt for library usage;
pTmpDrvLib->dwNumHandles += 1;
hReturn = pTmpDrvLib->hLib;
} else {
// Reload the library
hReturn = LoadNewCopy(pConfigFile, dwFlags, dwVersion);
}
LeaveCriticalSection( &ListAccessSem );
return hReturn;
}
BOOL
RefCntUnloadDriver(
HANDLE hLib,
BOOL bNotifySpooler
)
/*++
Function Description: This function decrements the reference count for the library usage.
It also frees the library if the reference count falls to zero.
Parameters: hLib -- handle of the library to free
bNotifySpooler -- flag to notify the spooler of the unload
Return Value: TRUE if the driver library was freed
FALSE otherwise
--*/
{
BOOL bReturn = FALSE;
PDRVLIBNODE *ppTmpDrvLib, pTmpDrvLib;
LPWSTR pConfigFile = NULL;
EnterCriticalSection( &ListAccessSem );
for (ppTmpDrvLib = &pStartDrvLib;
pTmpDrvLib = *ppTmpDrvLib;
ppTmpDrvLib = &(pTmpDrvLib->pNext)) {
if (pTmpDrvLib->hLib == hLib) {
// Reduce the ref cnt
SPLASSERT(pTmpDrvLib->dwNumHandles > 0);
pTmpDrvLib->dwNumHandles -= 1;
// Free the library and the node if ref cnt is zero
if (pTmpDrvLib->dwNumHandles == 0) {
FreeLibrary(hLib);
*ppTmpDrvLib = pTmpDrvLib->pNext;
pConfigFile = AllocSplStr(pTmpDrvLib->pConfigFile);
FreeSplStr(pTmpDrvLib->pConfigFile);
FreeSplMem(pTmpDrvLib);
bReturn = TRUE;
}
break;
}
}
LeaveCriticalSection( &ListAccessSem );
if (bNotifySpooler && bReturn) {
SplDriverUnloadComplete(pConfigFile);
}
FreeSplStr(pConfigFile);
return bReturn;
}
BOOL
ForceUnloadDriver(
LPWSTR pConfigFile
)
/*++
Function Description: This function will remove any artificial increment on the
config file.
Parameters: pConfigFile -- driver config file name
Return Values: TRUE if the config file no longer loaded;
FALSE otherwise
--*/
{
BOOL bReturn = TRUE;
PDRVLIBNODE *ppTmpDrvLib, pTmpDrvLib;
if (!pConfigFile || !*pConfigFile) {
// nothing to unload
return bReturn;
}
EnterCriticalSection( &ListAccessSem );
pTmpDrvLib = FindDriverNode(pConfigFile, 0, FALSE);
if (pTmpDrvLib) {
if (pTmpDrvLib->bArtificialIncrement) {
pTmpDrvLib->bArtificialIncrement = FALSE;
bReturn = RefCntUnloadDriver(pTmpDrvLib->hLib, FALSE);
} else {
bReturn = FALSE;
}
} else {
bReturn = TRUE;
}
LeaveCriticalSection( &ListAccessSem );
return bReturn;
}
HANDLE
LoadPrinterDriver(
HANDLE hPrinter
)
{
PDRIVER_INFO_5 pDriverInfo;
DWORD cbNeeded, dwVersion;
HANDLE hModule=FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC];
BOOL bAllocBuffer = FALSE, bReturn;
pDriverInfo = (PDRIVER_INFO_5) btBuffer;
bReturn = GetPrinterDriverW(hPrinter, NULL, 5, (LPBYTE)pDriverInfo,
MAX_STATIC_ALLOC, &cbNeeded);
if (!bReturn &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pDriverInfo = (PDRIVER_INFO_5)LocalAlloc(LMEM_FIXED, cbNeeded))) {
bAllocBuffer = TRUE;
bReturn = GetPrinterDriverW(hPrinter, NULL, 5, (LPBYTE)pDriverInfo,
cbNeeded, &cbNeeded);
}
if (bReturn) {
hModule = RefCntLoadDriver(pDriverInfo->pConfigFile,
LOAD_WITH_ALTERED_SEARCH_PATH,
pDriverInfo->dwConfigVersion,
TRUE);
}
if (bAllocBuffer) {
LocalFree(pDriverInfo);
}
return hModule;
}
DWORD WINAPI
AsyncDocumentPropertiesW(
PVOID pData
)
{
PumpThrdData *ThrdData = (PumpThrdData *)pData;
RpcTryExcept
{
*ThrdData->Result = RPCSplWOW64DocumentProperties(ThrdData->hWnd,
ThrdData->PrinterName,
ThrdData->TouchedDevModeSize,
ThrdData->ClonedDevModeOutSize,
ThrdData->ClonedDevModeOut,
ThrdData->DevModeInSize,
ThrdData->pDevModeInput,
ThrdData->ClonedDevModeFill,
ThrdData->fMode,
ThrdData->dwRet);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
return(0);
}
LONG
DocumentPropertiesWNative(
HWND hWnd,
HANDLE hPrinter,
LPWSTR pDeviceName,
PDEVMODE pDevModeOutput,
PDEVMODE pDevModeInput,
DWORD fMode
)
/*++
Routine Description:
DocumentProperties entry point to call DocumentPropertySheets() depends on
the DM_PROMPT
Arguments:
Return Value:
Author:
13-Jun-1996 Thu 15:35:25 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
DOCUMENTPROPERTYHEADER DPHdr;
PDEVMODE pDM;
LONG Result = -1;
HANDLE hTmpPrinter = NULL;
//
// Compatibility with Win95
// Win95 allows for hPrinter to be NULL
//
if (hPrinter == NULL) {
//
// Open the printer for default access.
//
if (!OpenPrinter( pDeviceName, &hTmpPrinter, NULL )) {
hTmpPrinter = NULL;
}
} else {
hTmpPrinter = hPrinter;
}
//
// Ensure the printer handle is valid
//
if( !eProtectHandle( hTmpPrinter, FALSE )){
//
// If fMode doesn't specify DM_IN_BUFFER, then zero out
// pDevModeInput.
//
// Old 3.51 (version 1-0) drivers used to ignore the absence of
// DM_IN_BUFFER and use pDevModeInput if it was not NULL. It
// probably did this because Printman.exe was broken.
//
// If the devmode is invalid, then don't pass one in.
// This fixes MS Imager32 (which passes dmSize == 0) and
// Milestones etc. 4.5.
//
// Note: this assumes that pDevModeOutput is still the
// correct size!
//
if( !(fMode & DM_IN_BUFFER) || !bValidDevModeW( pDevModeInput )){
//
// If either are not set, make sure both are not set.
//
pDevModeInput = NULL;
fMode &= ~DM_IN_BUFFER;
}
DPHdr.cbSize = sizeof(DPHdr);
DPHdr.Reserved = 0;
DPHdr.hPrinter = hTmpPrinter;
DPHdr.pszPrinterName = pDeviceName;
if (pDevModeOutput) {
//
// Get the driver devmode size at here
//
DPHdr.pdmIn = NULL;
DPHdr.pdmOut = NULL;
DPHdr.fMode = 0;
DPHdr.cbOut = (LONG)DocumentPropertySheets(NULL, (LPARAM)&DPHdr);
} else {
DPHdr.cbOut = 0;
}
DPHdr.pdmIn = (PDEVMODE)pDevModeInput;
DPHdr.pdmOut = (PDEVMODE)pDevModeOutput;
DPHdr.fMode = fMode;
if (fMode & DM_PROMPT) {
Result = CPSUI_CANCEL;
if ((CallCommonPropertySheetUI(hWnd,
(PFNPROPSHEETUI)DocumentPropertySheets,
(LPARAM)&DPHdr,
(LPDWORD)&Result)) < 0) {
Result = -1;
} else {
Result = (Result == CPSUI_OK) ? IDOK : IDCANCEL;
}
} else {
Result = (LONG)DocumentPropertySheets(NULL, (LPARAM)&DPHdr);
}
vUnprotectHandle( hTmpPrinter );
}
if (hPrinter == NULL) {
if( hTmpPrinter ){
ClosePrinter(hTmpPrinter);
}
}
return Result;
}
LONG
DocumentPropertiesWThunk(
HWND hWnd,
HANDLE hPrinter,
LPWSTR pDeviceName,
PDEVMODE pDevModeOutput,
PDEVMODE pDevModeInput,
DWORD fMode
)
/*++
Routine Description:
DocumentProperties entry point to call DocumentPropertySheets() depends on
the DM_PROMPT
--*/
{
DOCUMENTPROPERTYHEADER DPHdr;
PDEVMODE pDM;
LONG Result = -1;
HANDLE hTmpPrinter = NULL;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (hPrinter == NULL)
{
if (!OpenPrinter( pDeviceName, &hTmpPrinter, NULL ))
{
hTmpPrinter = NULL;
}
}
else
{
hTmpPrinter = hPrinter;
}
if( !eProtectHandle( hTmpPrinter, FALSE ))
{
LPWSTR PrinterName;
MSG msg;
LONG RetVal;
DWORD dwRet = ERROR_SUCCESS;
DWORD ClonedDevModeOutSize = 0;
DWORD TouchedDevModeSize = 0;
BOOL ClonedDevModeFill = (!!(fMode & DM_OUT_BUFFER) && pDevModeOutput);
DWORD DevModeInSize = pDevModeInput ? (pDevModeInput->dmSize + pDevModeInput->dmDriverExtra) : 0;
byte **ClonedDevModeOut = NULL;
if(ClonedDevModeOut = (byte **)LocalAlloc(LPTR,sizeof(byte *)))
{
*ClonedDevModeOut = NULL;
if(pSpool)
{
PrinterName = pSpool->pszPrinter;
}
else
{
PrinterName = pDeviceName;
}
//
// If fMode doesn't specify DM_IN_BUFFER, then zero out
// pDevModeInput.
//
// Old 3.51 (version 1-0) drivers used to ignore the absence of
// DM_IN_BUFFER and use pDevModeInput if it was not NULL. It
// probably did this because Printman.exe was broken.
//
// If the devmode is invalid, then don't pass one in.
// This fixes MS Imager32 (which passes dmSize == 0) and
// Milestones etc. 4.5.
//
// Note: this assumes that pDevModeOutput is still the
// correct size!
//
if( !(fMode & DM_IN_BUFFER) || !bValidDevModeW( pDevModeInput ))
{
//
// If either are not set, make sure both are not set.
//
pDevModeInput = NULL;
DevModeInSize = 0;
fMode &= ~DM_IN_BUFFER;
}
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
(!hWnd ||
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS)))
{
HANDLE hUIMsgThrd = NULL;
DWORD UIMsgThrdId = 0;
PumpThrdData ThrdData;
ThrdData.hWnd = (ULONG_PTR)hWnd;
ThrdData.PrinterName=PrinterName;
ThrdData.TouchedDevModeSize = &TouchedDevModeSize;
ThrdData.ClonedDevModeOutSize = &ClonedDevModeOutSize;
ThrdData.ClonedDevModeOut = (byte**)ClonedDevModeOut;
ThrdData.DevModeInSize = DevModeInSize;
ThrdData.pDevModeInput = (byte*)pDevModeInput;
ThrdData.fMode = fMode;
ThrdData.fExclusionFlags = 0;
ThrdData.dwRet = &dwRet;
ThrdData.ClonedDevModeFill = ClonedDevModeFill;
ThrdData.Result = &Result;
//
// If we have a window handle , the following functions cann't
// proceed synchronasly. The reason for that is in order to show
// the UI of the driver property sheets we need to be able to dispatch
// incomming messages and process them.For this reason the following
// call would be asynchronous call and the success or failure doesn't
// in reality tell us anything more than than the async process started
// or not. We get the success of failure from the termination message.
// If we don't have a window handle, then the call is synchronous.
//
if(!(hUIMsgThrd = CreateThread(NULL,
INITIAL_STACK_COMMIT,
AsyncDocumentPropertiesW,
(PVOID)&ThrdData,
0,
&UIMsgThrdId)))
{
dwRet = GetLastError();
}
//
// The following is the required message loop for processing messages
// from the UI in case we have a window handle.
//
//
if(hUIMsgThrd && hWnd && (fMode & DM_PROMPT))
{
while (GetMessage(&msg, NULL, 0, 0))
{
//
// In This message loop We should trap a User defined message
// which indicates the success or the failure of the operation
//
if(msg.message == WM_ENDDOCUMENTPROPERTIES)
{
Result = (LONG)msg.wParam;
if(Result == -1)
SetLastError((DWORD)msg.lParam);
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
Result = -1;
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if(hUIMsgThrd)
{
WaitForSingleObject(hUIMsgThrd,INFINITE);
CloseHandle(hUIMsgThrd);
}
if(Result!=-1 && pDevModeOutput)
{
memcpy((PVOID)pDevModeOutput,(PVOID)*ClonedDevModeOut,TouchedDevModeSize);
}
if(*ClonedDevModeOut)
{
MIDL_user_free((PVOID)*ClonedDevModeOut);
}
if(ClonedDevModeOut)
{
LocalFree((PVOID) ClonedDevModeOut);
}
}
else
{
SetLastError(dwRet);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
vUnprotectHandle( hTmpPrinter );
}
else
{
SetLastError(ERROR_OUTOFMEMORY);
}
}
if (hPrinter == NULL)
{
if( hTmpPrinter )
{
ClosePrinter(hTmpPrinter);
}
}
return(Result);
}
LONG
DocumentPropertiesW(
HWND hWnd,
HANDLE hPrinter,
LPWSTR pDeviceName,
PDEVMODE pDevModeOutput,
PDEVMODE pDevModeInput,
DWORD fMode
)
{
if(RunInWOW64())
{
return(DocumentPropertiesWThunk(hWnd,
hPrinter,
pDeviceName,
pDevModeOutput,
pDevModeInput,
fMode));
}
else
{
return(DocumentPropertiesWNative(hWnd,
hPrinter,
pDeviceName,
pDevModeOutput,
pDevModeInput,
fMode));
}
}
LONG
AdvancedDocumentPropertiesW(
HWND hWnd,
HANDLE hPrinter,
LPWSTR pDeviceName,
PDEVMODE pDevModeOutput,
PDEVMODE pDevModeInput
)
/*++
Routine Description:
AdvanceDocumentProperties() will call DocumentProperties() with DM_ADVANCED
flag mode set
Arguments:
Return Value:
TRUE/FALSE
Author:
13-Jun-1996 Thu 16:00:13 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
return((DocumentPropertiesW(hWnd,
hPrinter,
pDeviceName,
pDevModeOutput,
pDevModeInput,
DM_PROMPT |
DM_MODIFY |
DM_COPY |
DM_ADVANCED) == CPSUI_OK) ? 1 : 0);
}
LONG
AdvancedSetupDialogW(
HWND hWnd,
HANDLE hInst,
LPDEVMODE pDevModeInput,
LPDEVMODE pDevModeOutput
)
{
HANDLE hPrinter;
LONG ReturnValue = -1;
if (OpenPrinterW(pDevModeInput->dmDeviceName, &hPrinter, NULL)) {
ReturnValue = AdvancedDocumentPropertiesW(hWnd, hPrinter,
pDevModeInput->dmDeviceName,
pDevModeOutput,
pDevModeInput);
ClosePrinter(hPrinter);
}
return ReturnValue;
}
int
WINAPI
DeviceCapabilitiesWNative(
LPCWSTR pDevice,
LPCWSTR pPort,
WORD fwCapability,
LPWSTR pOutput,
CONST DEVMODEW *pDevMode
)
{
HANDLE hPrinter, hModule;
int ReturnValue=-1;
INT_FARPROC pfn;
if (OpenPrinter((LPWSTR)pDevice, &hPrinter, NULL)) {
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = (INT_FARPROC)GetProcAddress(hModule, "DrvDeviceCapabilities")) {
try {
ReturnValue = (*pfn)(hPrinter, pDevice, fwCapability,
pOutput, pDevMode);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = -1;
}
}
RefCntUnloadDriver(hModule, TRUE);
}
ClosePrinter(hPrinter);
}
return ReturnValue;
}
int
WINAPI
DeviceCapabilitiesWThunk(
LPCWSTR pDevice,
LPCWSTR pPort,
WORD fwCapability,
LPWSTR pOutput,
CONST DEVMODEW *pDevMode
)
{
HANDLE hPrinter, hModule;
int ReturnValue = -1;
INT_FARPROC pfn;
LPWSTR DriverFileName;
DWORD DevModeSize;
DWORD ClonedOutputSize = 0;
BOOL ClonedOutputFill = FALSE;
DWORD dwRet = ERROR_SUCCESS;
byte **ClonedOutput = NULL;
DevModeSize = pDevMode ? (pDevMode->dmSize + pDevMode->dmDriverExtra) : 0;
ClonedOutputFill = (pOutput != NULL);
if(ClonedOutput = (byte **)LocalAlloc(LPTR,sizeof(byte *)))
{
*ClonedOutput = NULL;
RpcTryExcept
{
if((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS)
{
ReturnValue = RPCSplWOW64DeviceCapabilities((LPWSTR)pDevice,
(LPWSTR)pPort,
fwCapability,
DevModeSize,
(byte*)pDevMode,
ClonedOutputFill,
&ClonedOutputSize,
(byte**)ClonedOutput,
&dwRet);
if(ReturnValue!=-1 &&
pOutput &&
*ClonedOutput)
{
memcpy((PVOID)pOutput,(PVOID)*ClonedOutput,ClonedOutputSize);
}
if(*ClonedOutput)
{
MIDL_user_free((PVOID)*ClonedOutput);
}
}
else
{
SetLastError(dwRet);
}
if(ClonedOutput)
{
LocalFree((PVOID) ClonedOutput);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = -1;
}
RpcEndExcept
}
else
{
SetLastError(ERROR_OUTOFMEMORY);
}
return(ReturnValue);
}
int
WINAPI
DeviceCapabilitiesW(
LPCWSTR pDevice,
LPCWSTR pPort,
WORD fwCapability,
LPWSTR pOutput,
CONST DEVMODEW *pDevMode
)
{
if(RunInWOW64())
{
return(DeviceCapabilitiesWThunk(pDevice,
pPort,
fwCapability,
pOutput,
pDevMode));
}
else
{
return(DeviceCapabilitiesWNative(pDevice,
pPort,
fwCapability,
pOutput,
pDevMode));
}
}
BOOL
AddFormW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pForm
)
{
BOOL ReturnValue;
GENERIC_CONTAINER FormContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FormContainer.Level = Level;
FormContainer.pData = pForm;
do {
RpcTryExcept {
if (ReturnValue = RpcAddForm(pSpool->hPrinter,
(PFORM_CONTAINER)&FormContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
DeleteFormW(
HANDLE hPrinter,
LPWSTR pFormName
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
do {
RpcTryExcept {
if (ReturnValue = RpcDeleteForm(pSpool->hPrinter, pFormName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
GetFormW(
HANDLE hPrinter,
LPWSTR pFormName,
DWORD Level,
LPBYTE pForm,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
FieldInfo *pFieldInfo;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
SIZE_T cbStruct;
switch (Level) {
case 1:
pFieldInfo = FormInfo1Fields;
cbStruct = sizeof(FORM_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
do {
RpcTryExcept {
if (pForm)
memset(pForm, 0, cbBuf);
if (ReturnValue = RpcGetForm(pSpool->hPrinter, pFormName, Level, pForm,
cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pForm) {
ReturnValue = MarshallUpStructure(pForm, pFieldInfo, cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
SetFormW(
HANDLE hPrinter,
LPWSTR pFormName,
DWORD Level,
LPBYTE pForm
)
{
BOOL ReturnValue;
GENERIC_CONTAINER FormContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
FormContainer.Level = Level;
FormContainer.pData = pForm;
do {
RpcTryExcept {
if (ReturnValue = RpcSetForm(pSpool->hPrinter, pFormName,
(PFORM_CONTAINER)&FormContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
EnumFormsW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pForm,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct, cbStruct32;
FieldInfo *pFieldInfo;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
switch (Level) {
case 1:
pFieldInfo = FormInfo1Fields;
cbStruct = sizeof(FORM_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
do {
RpcTryExcept {
if (pForm)
memset(pForm, 0, cbBuf);
if (ReturnValue = RpcEnumForms(pSpool->hPrinter, Level, pForm, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pForm) {
ReturnValue = MarshallUpStructuresArray(pForm, *pcReturned, pFieldInfo,
cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
EnumPortsW(
LPWSTR pName,
DWORD Level,
LPBYTE pPort,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
FieldInfo *pFieldInfo;
switch (Level) {
case 1:
pFieldInfo = PortInfo1Fields;
cbStruct = sizeof(PORT_INFO_1);
break;
case 2:
pFieldInfo = PortInfo2Fields;
cbStruct = sizeof(PORT_INFO_2);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pPort)
memset(pPort, 0, cbBuf);
if (ReturnValue = RpcEnumPorts(pName, Level, pPort, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPort) {
ReturnValue = MarshallUpStructuresArray(pPort, *pcReturned, pFieldInfo,
cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumMonitorsW(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitor,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
FieldInfo *pFieldInfo;
switch (Level) {
case 1:
pFieldInfo = MonitorInfo1Fields;
cbStruct = sizeof(MONITOR_INFO_1);
break;
case 2:
pFieldInfo = MonitorInfo2Fields;
cbStruct = sizeof(MONITOR_INFO_2);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pMonitor)
memset(pMonitor, 0, cbBuf);
if (ReturnValue = RpcEnumMonitors(pName, Level, pMonitor, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pMonitor) {
ReturnValue = MarshallUpStructuresArray(pMonitor, *pcReturned, pFieldInfo,
cbStruct, RPC_CALL);
}
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
typedef struct {
LPWSTR pName;
HWND hWnd;
LPWSTR pPortName;
HANDLE Complete;
DWORD ReturnValue;
DWORD Error;
INT_FARPROC pfn;
} CONFIGUREPORT_PARAMETERS;
void
PortThread(
CONFIGUREPORT_PARAMETERS *pParam
)
{
DWORD ReturnValue;
/* It's no use setting errors here, because they're kept on a per-thread
* basis. Instead we have to pass any error code back to the calling
* thread and let him set it.
*/
RpcTryExcept {
if (ReturnValue = (*pParam->pfn)(pParam->pName, pParam->hWnd,
pParam->pPortName)) {
pParam->Error = ReturnValue;
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
pParam->Error = TranslateExceptionCode(RpcExceptionCode());
ReturnValue = FALSE;
} RpcEndExcept
pParam->ReturnValue = ReturnValue;
SetEvent(pParam->Complete);
}
BOOL
KickoffThread(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName,
INT_FARPROC pfn
)
{
CONFIGUREPORT_PARAMETERS Parameters;
HANDLE ThreadHandle;
MSG msg;
DWORD ThreadId;
if( hWnd ){
EnableWindow(hWnd, FALSE);
}
Parameters.pName = pName;
Parameters.hWnd = hWnd;
Parameters.pPortName = pPortName;
Parameters.Complete = CreateEvent(NULL, TRUE, FALSE, NULL);
Parameters.pfn = pfn;
ThreadHandle = CreateThread(NULL, INITIAL_STACK_COMMIT,
(LPTHREAD_START_ROUTINE)PortThread,
&Parameters, 0, &ThreadId);
if( ThreadHandle ){
CloseHandle(ThreadHandle);
while (MsgWaitForMultipleObjects(1, &Parameters.Complete, FALSE, INFINITE,
QS_ALLEVENTS | QS_SENDMESSAGE) == 1) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CloseHandle(Parameters.Complete);
if( hWnd ){
EnableWindow(hWnd, TRUE);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
if(!Parameters.ReturnValue)
SetLastError(Parameters.Error);
return Parameters.ReturnValue;
}
return FALSE;
}
BOOL
AddPortWNative(
LPWSTR pName,
HWND hWnd,
LPWSTR pMonitorName
)
{
DWORD dwRet;
DWORD dwSessionId;
BOOL bRet;
PMONITORUI pMonitorUI;
PMONITORUIDATA pMonitorUIData = NULL;
dwRet = GetMonitorUI(pName, pMonitorName, L"XcvMonitor", &pMonitorUI, &pMonitorUIData);
if (dwRet == ERROR_SUCCESS ||
dwRet == ERROR_INVALID_PRINT_MONITOR ||
dwRet == ERROR_INVALID_PRINTER_NAME ||
dwRet == ERROR_NOT_SUPPORTED ||
dwRet == ERROR_MOD_NOT_FOUND ||
dwRet == ERROR_UNKNOWN_PORT) {
if (dwRet == ERROR_SUCCESS) {
bRet = (*pMonitorUI->pfnAddPortUI)(pName, hWnd, pMonitorName, NULL);
dwRet = GetLastError();
} else if ((bRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId)) && dwSessionId) {
bRet = FALSE;
dwRet = ERROR_NOT_SUPPORTED;
} else {
bRet = KickoffThread(pName, hWnd, pMonitorName, RpcAddPort);
dwRet = GetLastError();
}
SetLastError(dwRet);
} else {
SetLastError(dwRet);
bRet = FALSE;
}
FreeMonitorUI(pMonitorUIData);
return bRet;
}
BOOL
AddPortWThunk(
LPWSTR pName,
HWND hWnd,
LPWSTR pMonitorName
)
{
DWORD dwRet;
DWORD dwSessionId;
BOOL bRet;
PMONITORUI pMonitorUI;
LPWSTR pMonitorUIDll = NULL;
dwRet = GetMonitorUIDll(pName,pMonitorName,L"XcvMonitor",&pMonitorUIDll);
RpcTryExcept
{
if (dwRet == ERROR_SUCCESS ||
dwRet == ERROR_INVALID_PRINT_MONITOR ||
dwRet == ERROR_INVALID_PRINTER_NAME ||
dwRet == ERROR_NOT_SUPPORTED ||
dwRet == ERROR_MOD_NOT_FOUND ||
dwRet == ERROR_UNKNOWN_PORT) {
if (dwRet == ERROR_SUCCESS)
{
MSG msg;
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
//
// The following functions cann't proceed synchronasly. The reason
// for that is in order to show the UI of the port monitor we need
// to be able to dispatch incomming messages and process them.
// For this reason the following call is an asynchronous call and the
// success or failure doesn't in reality tell us anything more than
// than the async process started or not
//
if(bRet = RPCSplWOW64AddPort((ULONG_PTR)hWnd,
pName,
pMonitorUIDll,
pMonitorName,
&dwRet))
{
//
// The following is the required message loop for processing messages
// from the UI. The window handle has to be NULL in order to process
// messages from all windows in the calling Thread , otherwise we would
// have message dispatching problems
//
while (GetMessage(&msg, NULL, 0, 0))
{
//
// In This message loop We should trap a User defined message
// which indicates the success or the failure of the operation
//
if(msg.message == WM_ENDADDPORT)
{
bRet = (BOOL)msg.wParam;
if(!bRet)
dwRet = (DWORD)msg.lParam;
else
dwRet = ERROR_SUCCESS;
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
bRet = FALSE;
SetLastError(RPC_S_SERVER_UNAVAILABLE);
PostMessage(hWnd,WM_ACTIVATEAPP,TRUE,0);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
else
{
bRet = FALSE;
}
SetLastError(dwRet);
}
else if ((bRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId)) && dwSessionId)
{
bRet = FALSE;
dwRet = ERROR_NOT_SUPPORTED;
}
else
{
bRet = KickoffThread(pName, hWnd, pMonitorName, RpcAddPort);
dwRet = GetLastError();
}
if(pMonitorUIDll)
{
FreeSplMem(pMonitorUIDll);
}
/* FreeLibrary is busting the last error, so we need to set it here
*/
SetLastError(dwRet);
}
else
{
SetLastError(dwRet);
bRet = FALSE;
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
bRet = FALSE;
}
RpcEndExcept
return(bRet);
}
BOOL
AddPortW(
LPWSTR pName,
HWND hWnd,
LPWSTR pMonitorName
)
{
if(RunInWOW64())
{
return(AddPortWThunk(pName,
hWnd,
pMonitorName));
}
else
{
return(AddPortWNative(pName,
hWnd,
pMonitorName));
}
}
BOOL
ConfigurePortWNative(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
DWORD dwRet;
DWORD dwSessionId;
BOOL bRet;
PMONITORUI pMonitorUI;
PMONITORUIDATA pMonitorUIData = NULL;
dwRet = GetMonitorUI(pName, pPortName, L"XcvPort", &pMonitorUI, &pMonitorUIData);
if (dwRet == ERROR_SUCCESS ||
dwRet == ERROR_INVALID_PRINT_MONITOR ||
dwRet == ERROR_INVALID_PRINTER_NAME ||
dwRet == ERROR_NOT_SUPPORTED ||
dwRet == ERROR_MOD_NOT_FOUND ||
dwRet == ERROR_UNKNOWN_PORT) {
if (dwRet == ERROR_SUCCESS) {
bRet = (*pMonitorUI->pfnConfigurePortUI)(pName, hWnd, pPortName);
} else if ((bRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId)) && dwSessionId) {
bRet = FALSE;
SetLastError(ERROR_NOT_SUPPORTED);
} else {
bRet = KickoffThread(pName, hWnd, pPortName, RpcConfigurePort);
}
} else {
SetLastError(dwRet);
bRet = FALSE;
}
FreeMonitorUI(pMonitorUIData);
return bRet;
}
BOOL
ConfigurePortWThunk(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
DWORD dwRet;
DWORD dwSessionId;
BOOL bRet;
PMONITORUI pMonitorUI;
LPWSTR pMonitorUIDll = NULL;
dwRet = GetMonitorUIDll(pName,pPortName,L"XcvPort",&pMonitorUIDll);
if (dwRet == ERROR_SUCCESS ||
dwRet == ERROR_INVALID_PRINT_MONITOR ||
dwRet == ERROR_INVALID_PRINTER_NAME ||
dwRet == ERROR_NOT_SUPPORTED ||
dwRet == ERROR_MOD_NOT_FOUND ||
dwRet == ERROR_UNKNOWN_PORT) {
if (dwRet == ERROR_SUCCESS)
{
RpcTryExcept
{
MSG msg;
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
//
// The following functions cann't proceed synchronasly. The reason
// for that is in order to show the UI of the port monitor we need
// to be able to dispatch incomming messages and process them.
// For this reason the following call is an asynchronous call and the
// success or failure doesn't in reality tell us anything more than
// than the async process started or not
//
if(bRet = RPCSplWOW64ConfigurePort((ULONG_PTR)hWnd,
pName,
pMonitorUIDll,
pPortName,
&dwRet))
{
//
// The following is the required message loop for processing messages
// from the UI. The window handle has to be NULL in order to process
// messages from all windows in the calling Thread , otherwise we would
// have message dispatching problems
//
while (GetMessage(&msg, NULL, 0, 0))
{
//
// In This message loop We should trap a User defined message
// which indicates the success or the failure of the operation
//
if(msg.message == WM_ENDCFGPORT)
{
bRet = (BOOL)msg.wParam;
if(!bRet)
dwRet = (DWORD)msg.lParam;
else
dwRet = ERROR_SUCCESS;
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
bRet = FALSE;
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
else
{
bRet = FALSE;
}
SetLastError(dwRet);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
bRet = FALSE;
}
RpcEndExcept
}
else if ((bRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId)) && dwSessionId) {
bRet = FALSE;
SetLastError(ERROR_NOT_SUPPORTED);
} else {
bRet = KickoffThread(pName, hWnd, pPortName, RpcConfigurePort);
}
if(pMonitorUIDll)
{
FreeSplMem(pMonitorUIDll);
}
}
else
{
SetLastError(dwRet);
bRet = FALSE;
}
return(bRet);
}
BOOL
ConfigurePortW(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
if(RunInWOW64())
{
return(ConfigurePortWThunk(pName,
hWnd,
pPortName));
}
else
{
return(ConfigurePortWNative(pName,
hWnd,
pPortName));
}
}
BOOL
DeletePortWNative(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
DWORD dwRet;
BOOL bRet;
PMONITORUI pMonitorUI;
PMONITORUIDATA pMonitorUIData = NULL;
dwRet = GetMonitorUI(pName, pPortName, L"XcvPort", &pMonitorUI, &pMonitorUIData);
if (dwRet == ERROR_SUCCESS ||
dwRet == ERROR_INVALID_PRINT_MONITOR ||
dwRet == ERROR_INVALID_PRINTER_NAME ||
dwRet == ERROR_NOT_SUPPORTED ||
dwRet == ERROR_MOD_NOT_FOUND ||
dwRet == ERROR_UNKNOWN_PORT) {
if (dwRet == ERROR_SUCCESS)
bRet = (*pMonitorUI->pfnDeletePortUI)(pName, hWnd, pPortName);
else
bRet = KickoffThread(pName, hWnd, pPortName, RpcDeletePort);
} else {
SetLastError(dwRet);
bRet = FALSE;
}
FreeMonitorUI(pMonitorUIData);
return bRet;
}
BOOL
DeletePortWThunk(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
DWORD dwRet;
BOOL bRet;
PMONITORUI pMonitorUI;
LPWSTR pMonitorUIDll = NULL;
dwRet = GetMonitorUIDll(pName,pPortName,L"XcvPort",&pMonitorUIDll);
if (dwRet == ERROR_SUCCESS ||
dwRet == ERROR_INVALID_PRINT_MONITOR ||
dwRet == ERROR_INVALID_PRINTER_NAME ||
dwRet == ERROR_NOT_SUPPORTED ||
dwRet == ERROR_MOD_NOT_FOUND ||
dwRet == ERROR_UNKNOWN_PORT) {
if (dwRet == ERROR_SUCCESS)
{
RpcTryExcept
{
MSG msg;
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
//
// The following functions cann't proceed synchronasly. The reason
// for that is in order to show the UI of the port monitor we need
// to be able to dispatch incomming messages and process them.
// For this reason the following call is an asynchronous call and the
// success or failure doesn't in reality tell us anything more than
// than the async process started or not
//
if(bRet = RPCSplWOW64DeletePort((ULONG_PTR)hWnd,
pName,
pMonitorUIDll,
pPortName,
&dwRet))
{
//
// The following is the required message loop for processing messages
// from the UI. The window handle has to be NULL in order to process
// messages from all windows in the calling Thread , otherwise we would
// have message dispatching problems
//
while (GetMessage(&msg, NULL, 0, 0))
{
//
// In This message loop We should trap a User defined message
// which indicates the success or the failure of the operation
//
if(msg.message == WM_ENDDELPORT)
{
bRet = (BOOL)msg.wParam;
if(!bRet)
dwRet = (DWORD)msg.lParam;
else
dwRet = ERROR_SUCCESS;
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
bRet = FALSE;
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
else
{
bRet = FALSE;
}
SetLastError(dwRet);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
bRet = FALSE;
}
RpcEndExcept
}
else
bRet = KickoffThread(pName, hWnd, pPortName, RpcDeletePort);
if(pMonitorUIDll)
{
FreeSplMem(pMonitorUIDll);
}
}
else
{
SetLastError(dwRet);
bRet = FALSE;
}
return(bRet);
}
BOOL
DeletePortW(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
if(RunInWOW64())
{
return(DeletePortWThunk(pName,
hWnd,
pPortName));
}
else
{
return(DeletePortWNative(pName,
hWnd,
pPortName));
}
}
DWORD
GetMonitorUI(
IN PCWSTR pszMachineName,
IN PCWSTR pszObjectName,
IN PCWSTR pszObjectType,
OUT PMONITORUI *ppMonitorUI,
OUT PMONITORUIDATA *ppMonitorUIData
)
{
DWORD ReturnValue;
DWORD dwDummy; // RPC needs an address for 'out' parameters
HANDLE hXcv = NULL;
PBYTE pOutputData = NULL;
DWORD cbOutput;
PWSTR pszServerName = NULL;
PRINTER_DEFAULTS Default;
PMONITORUI (*pfnInitializePrintMonitorUI)(VOID) = NULL;
DWORD dwStatus;
BOOL bAllocBuffer = FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC];
PMONITORUIDATA pMonitorUIData = NULL;
HRESULT hRetval;
Default.pDatatype = NULL;
Default.pDevMode = NULL;
Default.DesiredAccess = SERVER_ACCESS_ADMINISTER;
*ppMonitorUI = NULL;
*ppMonitorUIData = NULL;
if (!(pszServerName = ConstructXcvName(pszMachineName, pszObjectName, pszObjectType))) {
ReturnValue = GetLastError();
goto Done;
}
RpcTryExcept {
ReturnValue = OpenPrinter( pszServerName,
&hXcv,
&Default);
if (!ReturnValue) {
ReturnValue = GetLastError();
goto Done;
}
pOutputData = (PBYTE) btBuffer;
ZeroMemory(pOutputData, MAX_STATIC_ALLOC);
ReturnValue = RpcXcvData( ((PSPOOL)hXcv)->hPrinter,
L"MonitorUI",
(PBYTE) &dwDummy,
0,
pOutputData,
MAX_STATIC_ALLOC,
&cbOutput,
&dwStatus);
if (ReturnValue != ERROR_SUCCESS)
goto Done;
if (dwStatus != ERROR_SUCCESS) {
if (dwStatus != ERROR_INSUFFICIENT_BUFFER) {
ReturnValue = dwStatus;
goto Done;
}
if (!(pOutputData = AllocSplMem(cbOutput))) {
ReturnValue = GetLastError();
goto Done;
}
bAllocBuffer = TRUE;
ReturnValue = RpcXcvData( ((PSPOOL)hXcv)->hPrinter,
L"MonitorUI",
(PBYTE) &dwDummy,
0,
pOutputData,
cbOutput,
&cbOutput,
&dwStatus);
}
if (ReturnValue != ERROR_SUCCESS)
goto Done;
if (dwStatus != ERROR_SUCCESS) {
ReturnValue = dwStatus;
goto Done;
}
//
// Create and initialize the monitor UI data.
//
hRetval = CreateMonitorUIData(&pMonitorUIData);
if (FAILED(hRetval)) {
ReturnValue = HRESULT_CODE(hRetval);
goto Done;
}
//
// Get and activate the monitor UI context.
//
hRetval = GetMonitorUIActivationContext((PCWSTR)pOutputData, pMonitorUIData);
if (FAILED(hRetval)) {
ReturnValue = HRESULT_CODE(hRetval);
goto Done;
}
if (!(pMonitorUIData->hLibrary = LoadLibrary(pMonitorUIData->pszMonitorName))) {
ReturnValue = GetLastError();
goto Done;
}
pfnInitializePrintMonitorUI = (PMONITORUI (*)(VOID))
GetProcAddress(pMonitorUIData->hLibrary, "InitializePrintMonitorUI");
if (!pfnInitializePrintMonitorUI) {
ReturnValue = GetLastError();
goto Done;
}
*ppMonitorUI = (*pfnInitializePrintMonitorUI)();
*ppMonitorUIData = pMonitorUIData;
pMonitorUIData = NULL;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(ReturnValue = TranslateExceptionCode(RpcExceptionCode()));
} RpcEndExcept
Done:
if (bAllocBuffer) {
FreeSplMem(pOutputData);
}
if (hXcv) {
ClosePrinter(hXcv);
}
FreeSplMem(pszServerName);
FreeMonitorUI(pMonitorUIData);
return ReturnValue;
}
/*++
Routine Name:
CreateMonitorUIData
Routine Description:
This function creates and initialize the monitor UI data.
Arguments:
ppMonitorUIData - pointer where to return the monitor UI data
Returns:
An HRESULT
--*/
HRESULT
CreateMonitorUIData(
OUT MONITORUIDATA **ppMonitorUIData
)
{
HRESULT hRetval = E_FAIL;
MONITORUIDATA *pMonitorUIData = NULL;
hRetval = ppMonitorUIData ? S_OK : E_POINTER;
if (SUCCEEDED(hRetval))
{
*ppMonitorUIData = NULL;
}
if (SUCCEEDED(hRetval))
{
pMonitorUIData = AllocSplMem(sizeof(MONITORUIDATA));
hRetval = pMonitorUIData ? S_OK : E_OUTOFMEMORY;
}
//
// Initialize the monitor UI data.
//
if (SUCCEEDED(hRetval))
{
ZeroMemory(pMonitorUIData, sizeof(MONITORUIDATA));
pMonitorUIData->hActCtx = INVALID_HANDLE_VALUE;
}
//
// Everything succeeded, copy back the pointer.
//
if (SUCCEEDED(hRetval))
{
*ppMonitorUIData = pMonitorUIData;
}
return hRetval;
}
/*++
Routine Name:
FreeMonitorUI
Routine Description:
This function releases the monitor UI data. It is responsible
for releasing the monitor library as well the monitor fusion
activation context. Note this function is called in error cases
when GetMonitorUI fails so all the parameters must be checked
for validity before use.
Arguments:
pMonitorUIData - pointer to the monitor UI data created in GetMonitorUI
Returns:
Nothing.
--*/
VOID
FreeMonitorUI(
IN PMONITORUIDATA pMonitorUIData
)
{
//
// Preserve the last error.
//
DWORD dwLastError = GetLastError();
if (pMonitorUIData)
{
//
// Release the monitor library.
//
if (pMonitorUIData->hLibrary)
{
FreeLibrary(pMonitorUIData->hLibrary);
}
//
// If we have an activation cookie then deactivate this context
//
if (pMonitorUIData->bDidActivate)
{
DeactivateActCtx(0, pMonitorUIData->lActCtx);
}
//
// If we have created an activation context then release it.
//
if (pMonitorUIData->hActCtx != INVALID_HANDLE_VALUE && pMonitorUIData->hActCtx != ACTCTX_EMPTY)
{
ReleaseActCtx(pMonitorUIData->hActCtx);
}
//
// Release the monitor name
//
if (pMonitorUIData->pszMonitorName)
{
FreeSplMem(pMonitorUIData->pszMonitorName);
}
//
// Release the monitor UI data back to the heap.
//
FreeSplMem(pMonitorUIData);
}
SetLastError(dwLastError);
}
/*++
Routine Name:
GetMonitorUIActivationContext
Routine Description:
This routine gets the monitor UI activation context and then
activates the context. If the monitor does not have an activation
context in it's resource file it will activate the empty context
for compatiblity with previous version of common control.
Arguments:
pszMonitorName - pointer to the monitor name.
pMonitorUIData - pointer to the monitor UI data created in GetMonitorUI
Returns:
An HRESULT
--*/
HRESULT
GetMonitorUIActivationContext(
IN PCWSTR pszMonitorName,
IN MONITORUIDATA *pMonitorUIData
)
{
HRESULT hRetval = E_FAIL;
hRetval = pszMonitorName && pMonitorUIData ? S_OK : E_INVALIDARG;
//
// Get the monitor full name.
//
if (SUCCEEDED(hRetval))
{
hRetval = GetMonitorUIFullName(pszMonitorName, &pMonitorUIData->pszMonitorName);
}
//
// See if there is an activation context in the resouce of this
// monitor UI binary. If there is we will create this context
// else we will create the empty context for backward compatibility.
//
if (SUCCEEDED(hRetval))
{
ACTCTX act;
ZeroMemory(&act, sizeof(act));
act.cbSize = sizeof(act);
act.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
act.lpSource = pMonitorUIData->pszMonitorName;
act.lpResourceName = MAKEINTRESOURCE(ACTIVATION_CONTEXT_RESOURCE_ID);
pMonitorUIData->hActCtx = CreateActCtx(&act);
if (pMonitorUIData->hActCtx == INVALID_HANDLE_VALUE)
{
pMonitorUIData->hActCtx = ACTCTX_EMPTY;
}
hRetval = pMonitorUIData->hActCtx ? S_OK : E_FAIL;
}
//
// Activate this context.
//
if (SUCCEEDED(hRetval))
{
hRetval = ActivateActCtx(pMonitorUIData->hActCtx,
&pMonitorUIData->lActCtx) ? S_OK : GetLastErrorAsHResult();
pMonitorUIData->bDidActivate = SUCCEEDED(hRetval);
}
return hRetval;
}
/*++
Routine Name:
GetMonitorUIFullName
Routine Description:
Get's the full monitor name. XCV is currently returning just the file name
not fully qualified. However the ddk does not indicate whether a monitor
should or should not return the monitor name fully qualified or not. Hence
this routine was written. It first builds a full name then it checkes if the
name is valid and if it is not then the orginal name is assumed fully
qualified and returned to the caller.
Arguments:
pszMonitorName - pointer to the monitor name.
ppszMonitorName - pointer where to return a monitor name pointer
Returns:
An HRESULT
--*/
HRESULT
GetMonitorUIFullName(
IN PCWSTR pszMonitorName,
IN PWSTR *ppszMonitorName
)
{
HRESULT hRetval = E_FAIL;
PWSTR pszTempMonitorName = NULL;
PWSTR pszBuff = NULL;
DWORD dwRetval = ERROR_SUCCESS;
hRetval = pszMonitorName && ppszMonitorName ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hRetval))
{
*ppszMonitorName = NULL;
}
//
// Allocate a temp buffer, use the heap. Too much stack usage
// will cause stress failures.
//
if (SUCCEEDED(hRetval))
{
pszBuff = (PWSTR)AllocSplMem(MAX_PATH * sizeof(WCHAR));
hRetval = pszBuff ? S_OK : E_OUTOFMEMORY;
}
//
// We need a full path to create an activation context. Xcv
// is only returning the monitor name not the full path.
//
if (SUCCEEDED(hRetval))
{
hRetval = GetSystemDirectory(pszBuff, MAX_PATH) ? S_OK : GetLastErrorAsHResult();
}
//
// Append the monitor name to the system directory.
//
if (SUCCEEDED(hRetval))
{
dwRetval = StrCatAlloc(&pszTempMonitorName,
pszBuff,
szSlash,
pszMonitorName,
NULL);
hRetval = HRESULT_FROM_WIN32(dwRetval);
}
//
// Check to see if this is a valid name.
//
if (SUCCEEDED(hRetval))
{
hRetval = GetFileAttributes(pszTempMonitorName) != -1 ? S_OK : GetLastErrorAsHResult();
//
// Name is not valid, release the current name and
// make a copy of the name passed to us and return this
// and the full monitor name.
//
if (FAILED(hRetval))
{
FreeSplMem(pszTempMonitorName);
dwRetval = StrCatAlloc(&pszTempMonitorName,
pszMonitorName,
NULL);
hRetval = HRESULT_FROM_WIN32(dwRetval);
}
}
//
// We have a valid name return it to the caller.
//
if (SUCCEEDED(hRetval))
{
*ppszMonitorName = pszTempMonitorName;
pszTempMonitorName = NULL;
}
FreeSplMem(pszBuff);
FreeSplMem(pszTempMonitorName);
return hRetval;
}
DWORD
GetMonitorUIDll(
PCWSTR pszMachineName,
PCWSTR pszObjectName,
PCWSTR pszObjectType,
PWSTR *pMonitorUIDll
)
{
DWORD ReturnValue;
DWORD dwDummy; // RPC needs an address for 'out' parameters
HANDLE hXcv = NULL;
PBYTE pOutputData = NULL;
DWORD cbOutput;
PWSTR pszServerName = NULL;
PRINTER_DEFAULTS Default;
PMONITORUI (*pfnInitializePrintMonitorUI)(VOID) = NULL;
DWORD dwStatus;
BOOL bAllocBuffer = FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC];
Default.pDatatype = NULL;
Default.pDevMode = NULL;
Default.DesiredAccess = SERVER_ACCESS_ADMINISTER;
*pMonitorUIDll = NULL;
if (!(pszServerName = ConstructXcvName(pszMachineName, pszObjectName, pszObjectType))) {
ReturnValue = GetLastError();
goto Done;
}
RpcTryExcept {
ReturnValue = OpenPrinter( pszServerName,
&hXcv,
&Default);
if (!ReturnValue) {
ReturnValue = GetLastError();
goto Done;
}
pOutputData = (PBYTE) btBuffer;
ZeroMemory(pOutputData, MAX_STATIC_ALLOC);
ReturnValue = RpcXcvData( ((PSPOOL)hXcv)->hPrinter,
L"MonitorUI",
(PBYTE) &dwDummy,
0,
pOutputData,
MAX_STATIC_ALLOC,
&cbOutput,
&dwStatus);
if (ReturnValue != ERROR_SUCCESS)
goto Done;
if (dwStatus != ERROR_SUCCESS) {
if (dwStatus != ERROR_INSUFFICIENT_BUFFER) {
ReturnValue = dwStatus;
goto Done;
}
if (!(pOutputData = AllocSplMem(cbOutput))) {
ReturnValue = GetLastError();
goto Done;
}
bAllocBuffer = TRUE;
ReturnValue = RpcXcvData( ((PSPOOL)hXcv)->hPrinter,
L"MonitorUI",
(PBYTE) &dwDummy,
0,
pOutputData,
cbOutput,
&cbOutput,
&dwStatus);
}
else
{
cbOutput = MAX_STATIC_ALLOC;
}
if (ReturnValue != ERROR_SUCCESS)
goto Done;
if (dwStatus != ERROR_SUCCESS) {
ReturnValue = dwStatus;
goto Done;
}
if (!(*pMonitorUIDll = AllocSplMem(cbOutput))) {
ReturnValue = GetLastError();
goto Done;
}
wcscpy(*pMonitorUIDll,(LPWSTR)pOutputData);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(ReturnValue = TranslateExceptionCode(RpcExceptionCode()));
} RpcEndExcept
Done:
if (bAllocBuffer) {
FreeSplMem(pOutputData);
}
FreeSplMem(pszServerName);
if (hXcv) {
ClosePrinter(hXcv);
}
return ReturnValue;
}
HANDLE
CreatePrinterIC(
HANDLE hPrinter,
LPDEVMODEW pDevMode
)
{
HANDLE ReturnValue;
DWORD Error;
DEVMODE_CONTAINER DevModeContainer;
HANDLE hGdi;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
if( bValidDevModeW( pDevMode )){
DevModeContainer.cbBuf = pDevMode->dmSize + pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pDevMode;
} else {
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = (LPBYTE)pDevMode;
}
do {
RpcTryExcept {
if (Error = RpcCreatePrinterIC( pSpool->hPrinter,
&hGdi,
&DevModeContainer )){
SetLastError(Error);
ReturnValue = FALSE;
} else {
ReturnValue = hGdi;
InterlockedIncrement( &gcClientICHandle );
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(RpcExceptionCode());
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
PlayGdiScriptOnPrinterIC(
HANDLE hPrinterIC,
LPBYTE pIn,
DWORD cIn,
LPBYTE pOut,
DWORD cOut,
DWORD ul
)
{
BOOL ReturnValue;
RpcTryExcept {
if (ReturnValue = RpcPlayGdiScriptOnPrinterIC(hPrinterIC, pIn, cIn,
pOut, cOut, ul)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrinterIC(
HANDLE hPrinterIC
)
{
BOOL ReturnValue;
RpcTryExcept {
if (ReturnValue = RpcDeletePrinterIC(&hPrinterIC)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
InterlockedDecrement( &gcClientICHandle );
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
/****************************************************************************
* INT QueryRemoteFonts( HANDLE, PUNIVERSAL_FONT_ID, ULONG )
*
* This is a hacky version of QueryRemoteFonts that doesn't do any
* caching based on the time stamp returned by QueryFonts. Additionally,
* it uses the CreatePrinterIC/PlayGdiScriptOnDC mechanism since it was
* already in place. It may be better to eliminate CreatePrinterIC and use
* an HPRINTER instead.
*
* Note that if the user doesn't pass in a buffer large enough to hold all
* the fonts we truncate the list and copy only enough fonts for which there
* is room but will still return success. This is okay because the worst
* that can happen in this case is that we may download unecessary fonts in
* the spool stream.
*
*
* History:
* 5/25/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
INT QueryRemoteFonts(
HANDLE hPrinter,
PUNIVERSAL_FONT_ID pufi,
ULONG nBufferSize
)
{
HANDLE hPrinterIC;
PBYTE pBuf;
DWORD dwDummy,cOut;
INT iRet = -1;
hPrinterIC = CreatePrinterIC( hPrinter, NULL );
if( hPrinterIC )
{
cOut = (nBufferSize * sizeof(UNIVERSAL_FONT_ID)) + sizeof(INT);
pBuf = LocalAlloc( LMEM_FIXED, cOut );
if( pBuf )
{
// Just call PlayGdiScriptOnPrinterIC for now since the piping is in place.
// For some reason the RPC stuff doesn't like NULL pointers for pIn so we
// use &dwDummy instead;
if(PlayGdiScriptOnPrinterIC(hPrinterIC,(PBYTE) &dwDummy,
sizeof(dwDummy),pBuf,cOut, 0))
{
DWORD dwSize = *((DWORD*) pBuf );
iRet = (INT) dwSize;
SPLASSERT( iRet >= 0 );
//
// If the supplied buffer is not large enough, we truncate the data
// The caller needs to check if he needs to call again this function
// with a larger buffer
//
if( dwSize > nBufferSize )
{
dwSize = nBufferSize;
}
memcpy(pufi,pBuf+sizeof(DWORD),dwSize * sizeof(UNIVERSAL_FONT_ID));
}
LocalFree( pBuf );
}
DeletePrinterIC( hPrinterIC );
}
return(iRet);
}
DWORD
PrinterMessageBoxW(
HANDLE hPrinter,
DWORD Error,
HWND hWnd,
LPWSTR pText,
LPWSTR pCaption,
DWORD dwType
)
{
return ERROR_NOT_SUPPORTED;
}
BOOL
AddMonitorW(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitorInfo
)
{
BOOL ReturnValue;
MONITOR_CONTAINER MonitorContainer;
MONITOR_INFO_2 MonitorInfo2;
switch (Level) {
case 2:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if (pMonitorInfo)
MonitorInfo2 = *(PMONITOR_INFO_2)pMonitorInfo;
else
memset(&MonitorInfo2, 0, sizeof(MonitorInfo2));
if (!MonitorInfo2.pEnvironment || !*MonitorInfo2.pEnvironment) {
if(RunInWOW64()) {
MonitorInfo2.pEnvironment = szIA64Environment;
}
else {
MonitorInfo2.pEnvironment = szEnvironment;
}
}
MonitorContainer.Level = Level;
MonitorContainer.MonitorInfo.pMonitorInfo2 = (MONITOR_INFO_2 *)&MonitorInfo2;
RpcTryExcept {
if (ReturnValue = RpcAddMonitor(pName, &MonitorContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeleteMonitorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pMonitorName
)
{
BOOL ReturnValue;
if (!pMonitorName || !*pMonitorName) {
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeleteMonitor(pName,
pEnvironment,
pMonitorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrintProcessorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pPrintProcessorName
)
{
BOOL ReturnValue;
if (!pPrintProcessorName || !*pPrintProcessorName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrintProcessor(pName,
pEnvironment,
pPrintProcessorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
AddPrintProvidorW(
LPWSTR pName,
DWORD Level,
LPBYTE pProvidorInfo
)
{
BOOL ReturnValue;
LPWSTR pStr;
PROVIDOR_CONTAINER ProvidorContainer;
RPC_PROVIDOR_INFO_2W RpcProvidorInfo;
ProvidorContainer.Level = Level;
switch (Level) {
case 1:
ProvidorContainer.ProvidorInfo.pProvidorInfo1 = (PROVIDOR_INFO_1 *)pProvidorInfo;
break;
case 2:
RpcProvidorInfo.pOrder = ((PROVIDOR_INFO_2 *) pProvidorInfo)->pOrder;
for (pStr = RpcProvidorInfo.pOrder;
pStr && *pStr;
pStr += (wcslen(pStr) + 1)) ;
// Set the character count for the multisz string
RpcProvidorInfo.cchOrder = (DWORD) ((ULONG_PTR) (pStr - RpcProvidorInfo.pOrder + 1));
ProvidorContainer.ProvidorInfo.pRpcProvidorInfo2 = &RpcProvidorInfo;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (ReturnValue = RpcAddPrintProvidor(pName, &ProvidorContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrintProvidorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pPrintProvidorName
)
{
BOOL ReturnValue;
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrintProvidor(pName,
pEnvironment,
pPrintProvidorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
LPWSTR
IsaFileName(
LPWSTR pOutputFile,
LPWSTR FullPathName,
DWORD cchFullPathName
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
LPWSTR pFileName=NULL;
LPWSTR pFullPathName=NULL;
//
// Hack for Word20c.Win
//
if (!_wcsicmp(pOutputFile, L"FILE")) {
return NULL;
}
//
// cchFullPathName needs to be at least MAX_PATH
//
if (GetFullPathName(pOutputFile, cchFullPathName, FullPathName, &pFileName)) {
DBGMSG(DBG_TRACE, ("Fully qualified filename is %ws\n", FullPathName));
//
// Filenames containing ":" create a stream and a file on NTFS. When the file is closed,
// the stream is deleted (if DELETE_ON_CLOSE is specified) but the file remains. Not only
// that, but GetFileType will return FILE_TYPE_DISK. You can see this by printing from IE
// to a network printer. The incoming name will be something like "157.55.3.5:PASSTHRU".
// Therefore, we need to catch this case here.
//
if (pFileName && wcschr(pFileName, L':')) {
return NULL;
}
hFile = CreateFile(pOutputFile,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
hFile = CreateFile(pOutputFile,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
}
if (hFile != INVALID_HANDLE_VALUE) {
if (GetFileType(hFile) == FILE_TYPE_DISK) {
pFullPathName = FullPathName;
}
CloseHandle(hFile);
}
}
return pFullPathName;
}
BOOL IsaPortName(
PKEYDATA pKeyData,
LPWSTR pOutputFile
)
{
DWORD i = 0;
UINT uStrLen;
if (!pKeyData) {
return(FALSE);
}
for (i=0; i < pKeyData->cTokens; i++) {
//
// If FILE: is one of the ports, and the app got the port
// name from win.in (e.g., Nexx:), then we will put up the
// print to file dialog, so we're not really printing to a port.
//
if (!lstrcmpi(pKeyData->pTokens[i], szFilePort)) {
if ((!wcsncmp(pOutputFile, L"Ne", 2)) &&
(*(pOutputFile + 4) == L':')) {
return(FALSE);
} else {
continue;
}
}
if (!lstrcmpi(pKeyData->pTokens[i], pOutputFile)) {
return(TRUE);
}
}
//
// Hack for NeXY: ports
//
if (!_wcsnicmp(pOutputFile, L"Ne", 2)) {
uStrLen = wcslen( pOutputFile );
//
// Ne00: or Ne00 if app truncates it
//
if (( uStrLen == 5 ) || ( uStrLen == 4 ) ) {
// Check for two Digits
if (( pOutputFile[2] >= L'0' ) && ( pOutputFile[2] <= L'9' ) &&
( pOutputFile[3] >= L'0' ) && ( pOutputFile[3] <= L'9' )) {
//
// Check for the final : as in Ne01:,
// note some apps will truncate it.
//
if (( uStrLen == 5 ) && (pOutputFile[4] != L':')) {
return FALSE;
}
return TRUE;
}
}
}
return(FALSE);
}
BOOL HasAFilePort(PKEYDATA pKeyData)
{
DWORD i = 0;
if (!pKeyData) {
return(FALSE);
}
for (i=0; i < pKeyData->cTokens; i++) {
if (!lstrcmpi(pKeyData->pTokens[i], szFilePort)) {
return(TRUE);
}
}
return(FALSE);
}
//
// This function is trying to get the last active popup of the top
// level owner of the current thread active window.
//
HRESULT
GetCurrentThreadLastPopup(
OUT HWND *phwnd
)
{
HWND hwndOwner, hwndParent;
HRESULT hr = E_INVALIDARG;
GUITHREADINFO ti = {0};
if( phwnd )
{
hr = E_FAIL;
*phwnd = NULL;
ti.cbSize = sizeof(ti);
if( GetGUIThreadInfo(0, &ti) && ti.hwndActive )
{
*phwnd = ti.hwndActive;
// climb up to the top parent in case it's a child window...
while( hwndParent = GetParent(*phwnd) )
{
*phwnd = hwndParent;
}
// get the owner in case the top parent is owned
hwndOwner = GetWindow(*phwnd, GW_OWNER);
if( hwndOwner )
{
*phwnd = hwndOwner;
}
// get the last popup of the owner window
*phwnd = GetLastActivePopup(*phwnd);
hr = (*phwnd) ? S_OK : E_FAIL;
}
}
return hr;
}
LPWSTR
StartDocDlgW(
HANDLE hPrinter,
DOCINFO *pDocInfo
)
{
DWORD dwError = 0;
DWORD dwStatus = FALSE;
LPWSTR lpFileName = NULL;
DWORD rc = 0;
PKEYDATA pKeyData = NULL;
LPWSTR pPortNames = NULL;
WCHAR FullPathName[MAX_PATH];
WCHAR CurrentDirectory[MAX_PATH];
PKEYDATA pOutputList = NULL;
WCHAR PortNames[MAX_PATH];
DWORD i = 0;
HWND hwndParent = NULL;
#if DBG
GetCurrentDirectory(MAX_PATH, CurrentDirectory);
DBGMSG(DBG_TRACE, ("The Current Directory is %ws\n", CurrentDirectory));
#endif
if (pDocInfo) {
DBGMSG(DBG_TRACE, ("lpOutputFile is %ws\n", pDocInfo->lpszOutput ? pDocInfo->lpszOutput: L""));
}
memset(FullPathName, 0, sizeof(WCHAR)*MAX_PATH);
pPortNames = GetPrinterPortList(hPrinter);
pKeyData = CreateTokenList(pPortNames);
//
// Check for the presence of multiple ports in the lpszOutput field
// the assumed delimiter is the comma. Thus there can be no files with commas
//
if (pDocInfo && pDocInfo->lpszOutput && pDocInfo->lpszOutput[0]) {
//
// Make a copy of the pDocInfo->lpszOutput because CreateTokenList is destructive
//
wcsncpy(PortNames, pDocInfo->lpszOutput, COUNTOF(PortNames)-1);
PortNames[COUNTOF(PortNames)-1] = 0;
pOutputList = CreateTokenList(PortNames);
if (pOutputList && (pOutputList->cTokens > 1) &&
!lstrcmpi(pPortNames, pDocInfo->lpszOutput))
{
for (i= 0; i < pOutputList->cTokens; i++) {
if (!lstrcmpi(pOutputList->pTokens[i], szFilePort)) {
wcscpy((LPWSTR)pDocInfo->lpszOutput, szFilePort);
break;
}
}
if (i == pOutputList->cTokens) {
wcscpy((LPWSTR)pDocInfo->lpszOutput, pOutputList->pTokens[0]);
}
}
FreeSplMem(pOutputList);
}
if (pDocInfo && pDocInfo->lpszOutput && pDocInfo->lpszOutput[0]) {
if (IsaPortName(pKeyData, (LPWSTR)pDocInfo->lpszOutput)) {
lpFileName = NULL;
goto StartDocDlgWReturn;
}
if (IsaFileName((LPWSTR)pDocInfo->lpszOutput, FullPathName, COUNTOF(FullPathName))) {
//
// Fully Qualify the pathname for Apps like PageMaker and QuatroPro
//
if (lpFileName = LocalAlloc(LPTR, (wcslen(FullPathName)+1)*sizeof(WCHAR))) {
wcscpy(lpFileName, FullPathName);
}
goto StartDocDlgWReturn;
}
}
if ((HasAFilePort(pKeyData)) ||
(pDocInfo && pDocInfo->lpszOutput
&& (!_wcsicmp(pDocInfo->lpszOutput, L"FILE:") ||
!_wcsicmp(pDocInfo->lpszOutput, L"FILE"))))
{
//
// since we have no idea who is calling us and we want to show
// modal against the last active popup, we need to figure out
// who is the last active popup of the calling app and then specify
// as parent - GetCurrentThreadLastPopup does a little magic to
// find the appropriate window.
//
DBGMSG(DBG_TRACE, ("We returned True from has file\n"));
rc = (DWORD)DialogBoxParam( hInst,
MAKEINTRESOURCE( DLG_PRINTTOFILE ),
SUCCEEDED(GetCurrentThreadLastPopup(&hwndParent)) ? hwndParent : NULL, (DLGPROC)PrintToFileDlg,
(LPARAM)&lpFileName );
if (rc == -1) {
DBGMSG(DBG_TRACE, ("Error from DialogBoxParam- %d\n", GetLastError()));
lpFileName = (LPWSTR)-1;
goto StartDocDlgWReturn;
} else if (rc == 0) {
DBGMSG(DBG_TRACE, ("User cancelled the dialog\n"));
lpFileName = (LPWSTR)-2;
SetLastError( ERROR_CANCELLED );
goto StartDocDlgWReturn;
} else {
DBGMSG(DBG_TRACE, ("The string was successfully returned\n"));
DBGMSG(DBG_TRACE, ("The string is %ws\n", lpFileName? lpFileName: L"NULL"));
goto StartDocDlgWReturn;
}
} else {
lpFileName = (LPWSTR)NULL;
}
StartDocDlgWReturn:
FreeSplMem(pKeyData);
FreeSplStr(pPortNames);
return(lpFileName);
}
BOOL
AddPortExW(
LPWSTR pName,
DWORD Level,
LPBYTE lpBuffer,
LPWSTR lpMonitorName
)
{
DWORD ReturnValue;
PORT_CONTAINER PortContainer;
PORT_VAR_CONTAINER PortVarContainer;
PPORT_INFO_FF pPortInfoFF;
PPORT_INFO_1 pPortInfo1;
if (!lpBuffer) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
switch (Level) {
case (DWORD)-1:
pPortInfoFF = (PPORT_INFO_FF)lpBuffer;
PortContainer.Level = Level;
PortContainer.PortInfo.pPortInfoFF = (PPORT_INFO_FF)pPortInfoFF;
PortVarContainer.cbMonitorData = pPortInfoFF->cbMonitorData;
PortVarContainer.pMonitorData = pPortInfoFF->pMonitorData;
break;
case 1:
pPortInfo1 = (PPORT_INFO_1)lpBuffer;
PortContainer.Level = Level;
PortContainer.PortInfo.pPortInfo1 = (PPORT_INFO_1)pPortInfo1;
PortVarContainer.cbMonitorData = 0;
PortVarContainer.pMonitorData = NULL;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return(FALSE);
}
RpcTryExcept {
if (ReturnValue = RpcAddPortEx(pName, (LPPORT_CONTAINER)&PortContainer,
(LPPORT_VAR_CONTAINER)&PortVarContainer,
lpMonitorName
)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DevQueryPrint(
HANDLE hPrinter,
LPDEVMODE pDevMode,
DWORD *pResID
)
{
BOOL Ok = FALSE;
HANDLE hModule;
INT_FARPROC pfn;
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = (INT_FARPROC)GetProcAddress(hModule, "DevQueryPrint")) {
try {
Ok = (*pfn)(hPrinter, pDevMode, pResID);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
}
RefCntUnloadDriver(hModule, TRUE);
}
return(Ok);
}
BOOL
DevQueryPrintEx(
PDEVQUERYPRINT_INFO pDQPInfo
)
{
BOOL Ok = FALSE;
HANDLE hModule;
INT_FARPROC pfn;
if (hModule = LoadPrinterDriver(pDQPInfo->hPrinter)) {
if (pfn = (INT_FARPROC)GetProcAddress(hModule, "DevQueryPrintEx")) {
try {
Ok = (*pfn)(pDQPInfo);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
}
RefCntUnloadDriver(hModule, TRUE);
}
return(Ok);
}
BOOL
SpoolerDevQueryPrintW(
HANDLE hPrinter,
LPDEVMODE pDevMode,
DWORD *pResID,
LPWSTR pszBuffer,
DWORD cchBuffer
)
{
BOOL Ok = FALSE;
HANDLE hModule;
INT_FARPROC pfn;
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = (INT_FARPROC)GetProcAddress(hModule, "DevQueryPrintEx")) {
DEVQUERYPRINT_INFO DQPInfo;
DQPInfo.cbSize = sizeof(DQPInfo);
DQPInfo.Level = 1;
DQPInfo.hPrinter = hPrinter;
DQPInfo.pDevMode = pDevMode;
DQPInfo.pszErrorStr = (LPTSTR)pszBuffer;
DQPInfo.cchErrorStr = (WORD)cchBuffer;
DQPInfo.cchNeeded = 0;
try {
*pResID = (Ok = (*pfn)(&DQPInfo)) ? 0 : 0xDCDCDCDC;
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
} else if (pfn = (INT_FARPROC)GetProcAddress(hModule, "DevQueryPrint")) {
try {
if ((Ok = (*pfn)(hPrinter, pDevMode, pResID)) &&
(*pResID)) {
UINT cch;
*pszBuffer = L'\0';
SelectFormNameFromDevMode(hPrinter, pDevMode, pszBuffer);
if (cch = lstrlen(pszBuffer)) {
pszBuffer += cch;
*pszBuffer++ = L' ';
*pszBuffer++ = L'-';
*pszBuffer++ = L' ';
cchBuffer -= (cch + 3);
}
LoadString(hModule, *pResID, pszBuffer, cchBuffer);
}
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
}
RefCntUnloadDriver(hModule, TRUE);
}
return(Ok);
}
LPWSTR
SelectFormNameFromDevMode(
HANDLE hPrinter,
PDEVMODEW pDevModeW,
LPWSTR pFormName
)
/*++
Routine Description:
This function pick the current form associated with current devmode and
return a form name pointer
Arguments:
hPrinter - Handle to the printer object
pDevModeW - Pointer to the unicode devmode for this printer
FormName - Pointer to the formname to be filled
Return Value:
Either a pointer to the FormName passed in if we do found one form,
otherwise it return NULL to signal a failue
Author:
21-Mar-1995 Tue 16:57:51 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
DWORD cb;
DWORD cRet;
LPFORM_INFO_1 pFIBase;
LPFORM_INFO_1 pFI;
BYTE btBuffer[MAX_STATIC_ALLOC];
BOOL bAllocBuffer = FALSE, bReturn;
//
// 1. If the DM_FORMNAME is turned on, then we want to check this bit first
// because it only specific to the NT which using form. The form name
// supposed set by any NT driver but not win31 or Win95.Use the
// dmFormName only if dmPaperSize, dmPaperLength and dmPaperWidth fields
// are not set. If any of them is set then we have to find a form using
// the value in these fields.
//
if ( (pDevModeW->dmFields & DM_FORMNAME)
&& (!(pDevModeW->dmFields & (DM_PAPERSIZE |
DM_PAPERLENGTH |
DM_PAPERWIDTH))) ) {
wcscpy(pFormName, pDevModeW->dmFormName);
return(pFormName);
}
//
// For all other cases we need to get forms data base first, but we want
// to set the form name to NULL so that we can check if we found one
//
cb =
cRet = 0;
pFIBase =
pFI = NULL;
pFIBase = (LPFORM_INFO_1) btBuffer;
ZeroMemory(pFIBase, MAX_STATIC_ALLOC);
bReturn = EnumForms(hPrinter, 1, (LPBYTE)pFIBase, MAX_STATIC_ALLOC,
&cb, &cRet);
if (!bReturn &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pFIBase = (LPFORM_INFO_1)LocalAlloc(LPTR, cb))) {
bAllocBuffer = TRUE;
bReturn = EnumForms(hPrinter, 1, (LPBYTE)pFIBase, cb, &cb, &cRet);
}
if (bReturn) {
//
// 2. If user specified dmPaperSize then honor it, otherwise, it must
// be a custom form, and we will check to see if it match one of
// in the database
//
if ((pDevModeW->dmFields & DM_PAPERSIZE) &&
(pDevModeW->dmPaperSize >= DMPAPER_FIRST) &&
(pDevModeW->dmPaperSize <= (SHORT)cRet)) {
//
// We go the valid index now
//
pFI = pFIBase + (pDevModeW->dmPaperSize - DMPAPER_FIRST);
} else if ((pDevModeW->dmFields & DM_PAPER_WL) == DM_PAPER_WL) {
LPFORM_INFO_1 pFICur = pFIBase;
while (cRet--) {
if ((DM_MATCH(pDevModeW->dmPaperWidth, pFICur->Size.cx)) &&
(DM_MATCH(pDevModeW->dmPaperLength, pFICur->Size.cy))) {
//
// We found the match which has discern size differences
//
pFI = pFICur;
break;
}
pFICur++;
}
}
}
//
// If we found the form then copy the name down, otherwise set the
// formname to be NULL
//
if (pFI) {
wcscpy(pFormName, pFI->pName);
} else {
*pFormName = L'\0';
pFormName = NULL;
}
if (bAllocBuffer) {
LocalFree((HLOCAL)pFIBase);
}
return(pFormName);
}
BOOL
SetAllocFailCount(
HANDLE hPrinter,
DWORD dwFailCount,
LPDWORD lpdwAllocCount,
LPDWORD lpdwFreeCount,
LPDWORD lpdwFailCountHit
)
{
BOOL ReturnValue;
PSPOOL pSpool = hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE )){
return FALSE;
}
do {
RpcTryExcept {
if (ReturnValue = RpcSetAllocFailCount( pSpool->hPrinter,
dwFailCount,
lpdwAllocCount,
lpdwFreeCount,
lpdwFailCountHit )) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
return ReturnValue;
}
BOOL
WINAPI
EnumPrinterPropertySheets(
HANDLE hPrinter,
HWND hWnd,
LPFNADDPROPSHEETPAGE lpfnAdd,
LPARAM lParam
)
{
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
return FALSE;
}
VOID
vUpdateTrayIcon(
IN HANDLE hPrinter,
IN DWORD JobId
)
{
SHCNF_PRINTJOB_DATA JobData;
LPPRINTER_INFO_1 pPrinterInfo1;
FARPROC pfnSHChangeNotify;
PSPOOL pSpool = (PSPOOL)hPrinter;
BYTE btBuffer[MAX_PRINTER_INFO1];
SPLASSERT( JobId );
//
// Avoid sending multiple notifications by setting this flag.
// When other calls (notably StartDocPrinter) see this,
// they will avoid sending a notification.
//
pSpool->Status |= SPOOL_STATUS_TRAYICON_NOTIFIED;
if (InCSRProcess()) {
//
// We are running in CSR, don't load up shell.
//
return;
}
ZeroMemory( &JobData, sizeof( JobData ));
JobData.JobId = JobId;
//
// Get a copy of the real printer name
//
pPrinterInfo1 = (LPPRINTER_INFO_1) btBuffer;
ZeroMemory(pPrinterInfo1, MAX_PRINTER_INFO1);
if( pPrinterInfo1 ){
DWORD dwNeeded;
if( GetPrinter( hPrinter,
1,
(PBYTE)pPrinterInfo1,
MAX_PRINTER_INFO1,
&dwNeeded )){
if (hShell32 == INVALID_HANDLE_VALUE)
hShell32 = LoadLibrary( gszShell32 );
if (hShell32) {
pfnSHChangeNotify = GetProcAddress( hShell32,
"SHChangeNotify" );
if( pfnSHChangeNotify ){
(*pfnSHChangeNotify)(
SHCNE_CREATE,
SHCNF_PRINTJOB | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT,
pPrinterInfo1->pName,
&JobData );
}
}
}
}
}
INT
CallDrvDocumentEventNative(
HANDLE hPrinter,
HDC hdc,
INT iEsc,
ULONG cbIn,
PVOID pulIn,
ULONG cbOut,
PVOID pulOut
)
/*++
Routine Description:
Call DrvDocumentEvent on driver UI
Arguments:
Return Value:
-1 : DOCUMENTEVENT_FAILURE
0 : DOCUMENTEVENT_UNSUPPORTED
1 : DOCUMENTEVENT_SUCCESS
--*/
{
HANDLE hLibrary;
INT_FARPROC pfn;
INT ReturnValue=DOCUMENTEVENT_UNSUPPORTED;
PSPOOL pSpool = (PSPOOL)hPrinter;
ULONG_PTR lActCtx = 0;
BOOL bDidActivate = FALSE;
if ( hLibrary = LoadPrinterDriver( hPrinter )) {
//
// Activate the empty context, we do not check the return value.
// because this may be called for non UI document events.
//
bDidActivate = ActivateActCtx( ACTCTX_EMPTY, &lActCtx );
//
// Disable the call so we don't recurse if the
// callback calls StartPage, etc.
//
pSpool->Status &= ~SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
if( pfn = (INT_FARPROC)GetProcAddress( hLibrary, "DrvDocumentEvent")){
try {
ReturnValue = (*pfn)( hPrinter,
hdc,
iEsc,
cbIn,
pulIn,
cbOut,
pulOut);
} except(1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
ReturnValue = DOCUMENTEVENT_FAILURE;
}
//
// When driver does not export DrvDocumentEvent we leave
// this bit disabled so we will not try to load the DLL
// for future calls
//
pSpool->Status |= SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
}
//
// Deactivate the context
//
if( bDidActivate ){
DeactivateActCtx( 0, lActCtx );
}
RefCntUnloadDriver(hLibrary, TRUE);
}
return ReturnValue;
}
INT
CallDrvDocumentEventThunk(
HANDLE hPrinter,
HDC hdc,
INT iEsc,
ULONG cbIn,
PVOID pulIn,
ULONG cbOut,
PVOID pulOut
)
/*++
Routine Description:
Call DrvDocumentEvent on driver UI
Arguments:
Return Value:
-1 : DOCUMENTEVENT_FAILURE
0 : DOCUMENTEVENT_UNSUPPORTED
1 : DOCUMENTEVENT_SUCCESS
--*/
{
HANDLE hLibrary;
INT_FARPROC pfn;
INT ReturnValue=DOCUMENTEVENT_UNSUPPORTED;
DWORD dwRet = ERROR_SUCCESS;
PSPOOL pSpool = (PSPOOL)hPrinter;
LPWSTR PrinterName = pSpool->pszPrinter;
pSpool->Status &= ~SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
RpcTryExcept
{
*((PULONG_PTR)pulOut) = (ULONG_PTR)0L;
if((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS)
{
ReturnValue = RPCSplWOW64DocumentEvent(PrinterName,
(ULONG_PTR)hdc,
iEsc,
cbIn,
(LPBYTE) pulIn,
&cbOut,
(LPBYTE*) pulOut,
&dwRet);
}
else
{
SetLastError(dwRet);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = -1;
}
RpcEndExcept
pSpool->Status |= SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
return ReturnValue;
}
INT
CallDrvDocumentEvent(
HANDLE hPrinter,
HDC hdc,
INT iEsc,
ULONG cbIn,
PVOID pulIn,
ULONG cbOut,
PVOID pulOut
)
{
if(RunInWOW64())
{
return(CallDrvDocumentEventThunk(hPrinter,
hdc,
iEsc,
cbIn,
pulIn,
cbOut,
pulOut));
}
else
{
return(CallDrvDocumentEventNative(hPrinter,
hdc,
iEsc,
cbIn,
pulIn,
cbOut,
pulOut));
}
}
INT
DocumentEvent(
HANDLE hPrinter,
HDC hdc,
INT iEsc,
ULONG cbIn,
PVOID pulIn,
ULONG cbOut,
PVOID pulOut
)
/*++
Routine Description:
Allow the driver UI dll to hook specific print events.
Arguments:
Return Value:
-1 : DOCUMENTEVENT_FAILURE
0 : DOCUMENTEVENT_UNSUPPORTED
1 : DOCUMENTEVENT_SUCCESS
--*/
{
DWORD cbNeeded;
INT ReturnValue = DOCUMENTEVENT_FAILURE;
PSPOOL pSpool = (PSPOOL)hPrinter;
PDOCEVENT_FILTER pDoceventFilter = NULL;
BOOL bDocEventFilter = FALSE;
BOOL bCallDriver = TRUE;
UINT uIndex;
if( eProtectHandle( hPrinter, FALSE )){
return DOCUMENTEVENT_FAILURE;
}
if( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_CREATEDCPRE ){
if ( pSpool->pDoceventFilter ) {
FreeSplMem(pSpool->pDoceventFilter);
pSpool->pDoceventFilter = NULL;
}
//
// First we will check if the driver wants to filter the events
//
cbNeeded = sizeof(DOCEVENT_FILTER) + sizeof(DWORD) * (DOCUMENTEVENT_LAST-2);
pDoceventFilter = AllocSplMem(cbNeeded);
if ( pDoceventFilter == NULL )
goto Fail;
pDoceventFilter->cbSize = sizeof(DOCEVENT_FILTER);
pDoceventFilter->cElementsAllocated = DOCUMENTEVENT_LAST-1;
pDoceventFilter->cElementsReturned = (UINT)-1;
pDoceventFilter->cElementsNeeded = (UINT)-1;
//
// Before every CreateDC, re-enable DocumentEvent.
// If it fails on the first try, then don't try again
// until the next CreateDC.
//
pSpool->Status |= SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
ReturnValue = CallDrvDocumentEvent( hPrinter,
hdc,
DOCUMENTEVENT_QUERYFILTER,
cbIn,
pulIn,
cbNeeded,
(PVOID)pDoceventFilter);
//
// We only regard the call to be successful if the driver returned
// success _and_ modified aither cElementsReturned or cElementsNeeded.
// This is to handle the case where a driver returns success, but in
// fact does not know how to handle the call.
//
bDocEventFilter = ReturnValue == DOCUMENTEVENT_SUCCESS &&
(pDoceventFilter->cElementsReturned != (UINT)-1 ||
pDoceventFilter->cElementsNeeded != (UINT)-1);
if (pDoceventFilter->cElementsReturned == (UINT)-1)
{
pDoceventFilter->cElementsReturned = 0;
}
if (pDoceventFilter->cElementsNeeded == (UINT)-1)
{
pDoceventFilter->cElementsNeeded = 0;
}
if (bDocEventFilter) {
//
// Validity check
//
if ( pDoceventFilter->cElementsReturned > pDoceventFilter->cElementsAllocated ) {
SPLASSERT(pDoceventFilter->cElementsReturned <= pDoceventFilter->cElementsAllocated);
ReturnValue = DOCUMENTEVENT_FAILURE;
goto Fail;
//
// For drivers that are written for future OS (with new doc events)
// we still want to filter and send the doc events we support
//
// So we realloc and query
//
} else if ( pDoceventFilter->cElementsNeeded > pDoceventFilter->cElementsAllocated ) {
uIndex = pDoceventFilter->cElementsNeeded;
cbNeeded = sizeof(DOCEVENT_FILTER) + sizeof(DWORD) * (uIndex - 1);
FreeSplMem(pDoceventFilter);
ReturnValue = DOCUMENTEVENT_FAILURE;
pDoceventFilter = AllocSplMem(cbNeeded);
if ( pDoceventFilter == NULL )
goto Fail;
pDoceventFilter->cbSize = sizeof(DOCEVENT_FILTER);
pDoceventFilter->cElementsAllocated = uIndex;
ReturnValue = CallDrvDocumentEvent( hPrinter,
hdc,
DOCUMENTEVENT_QUERYFILTER,
cbIn,
pulIn,
cbNeeded,
(PVOID)pDoceventFilter);
//
// Validity check for second call
//
if ( ReturnValue == DOCUMENTEVENT_SUCCESS ) {
if ( pDoceventFilter->cElementsReturned > pDoceventFilter->cElementsAllocated ) {
SPLASSERT(pDoceventFilter->cElementsReturned <= pDoceventFilter->cElementsAllocated);
ReturnValue = DOCUMENTEVENT_FAILURE;;
goto Fail;
}
}
}
}
//
// Not supported we go to old behavior (no filtering)
//
if ( bDocEventFilter && ReturnValue == DOCUMENTEVENT_SUCCESS ) {
pSpool->pDoceventFilter = pDoceventFilter;
} else {
FreeSplMem(pDoceventFilter);
pDoceventFilter = NULL;
}
}
ReturnValue = DOCUMENTEVENT_UNSUPPORTED;
if( pSpool->Status & SPOOL_STATUS_DOCUMENTEVENT_ENABLED ){
//
// When driver supports DOCUMENTEVENT_QUERYFILTER we will
// only call events in the filter with
// DOCUMENTEVENT_CREATEDCPRE being an exception
//
// When driver does not support it (or fails it) we revert to old
// behavior and make all callbacks
//
if ( DOCUMENTEVENT_EVENT( iEsc ) != DOCUMENTEVENT_CREATEDCPRE &&
(pDoceventFilter = pSpool->pDoceventFilter) != NULL ) {
for ( uIndex = 0, bCallDriver = FALSE ;
uIndex < pDoceventFilter->cElementsReturned && !bCallDriver ;
++uIndex ) {
if ( pDoceventFilter->aDocEventCall[uIndex] == DOCUMENTEVENT_EVENT(iEsc) )
bCallDriver = TRUE;
}
}
if ( bCallDriver ) {
ReturnValue = CallDrvDocumentEvent( hPrinter,
hdc,
iEsc,
cbIn,
pulIn,
cbOut,
pulOut);
//
// Old (i.e. before DOCUMENTEVENT_QUERYFILTER) behavior is
// on DOCUMENTEVENT_CREATEDCPRE failure no more calls are made
// to the driver UI dll. We preserve the same behavior.
//
// Note that some drivers return a large positive value for a success
// code. So, ReturnValue <= DOCUMENTEVENT_UNSUPPORTED is the correct
// implementation.
//
if ( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_CREATEDCPRE &&
ReturnValue <= DOCUMENTEVENT_UNSUPPORTED )
pSpool->Status &= ~SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
}
}
//
// If it's a StartDocPost, a job was just added. Notify the
// tray icon if we haven't already.
//
if( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_STARTDOCPOST ){
if( !( pSpool->Status & SPOOL_STATUS_TRAYICON_NOTIFIED )){
//
// If we have a StartDocPost, then issue a notification so that
// the user's tray starts polling. pulIn[0] holds the JobId.
//
vUpdateTrayIcon( hPrinter, (DWORD)((PULONG_PTR)pulIn)[0] );
}
} else {
//
// If we have sent a notification, then by the next time we get a
// document event, we have completed any additional AddJobs or
// StartDocPrinters. Therefore we can reset the TRAYICON_NOTIFIED
// flag, since any more AddJobs/StartDocPrinters are really new
// jobs.
//
pSpool->Status &= ~SPOOL_STATUS_TRAYICON_NOTIFIED;
}
Fail:
if ( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_CREATEDCPRE &&
ReturnValue == DOCUMENTEVENT_FAILURE ) {
FreeSplMem(pDoceventFilter);
pSpool->Status &= ~SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
pSpool->pDoceventFilter = NULL;
}
vUnprotectHandle( hPrinter );
return ReturnValue;
}
/****************************************************************************
* INT QueryColorProfile()
*
* Returns:
*
* -1 : Printer driver does not hook color profile.
* 0 : Error.
* 1 : Success.
*
* History:
* 8/Oct/1997 by Hideyuki Nagase [hideyukn]
* Wrote it.
*****************************************************************************/
INT
QueryColorProfile(
HANDLE hPrinter,
PDEVMODEW pdevmode,
ULONG ulQueryMode,
PVOID pvProfileData,
ULONG *pcbProfileData,
FLONG *pflProfileData
)
{
INT iRet = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if( eProtectHandle( hPrinter, FALSE )){
return 0;
}
if (pSpool->Status & SPOOL_STATUS_NO_COLORPROFILE_HOOK) {
//
// DrvQueryColorProfile is not supported in Printer driver.
//
iRet = -1;
} else {
HANDLE hLibrary;
INT_FARPROC pfn;
if (hLibrary = LoadPrinterDriver( hPrinter )) {
if (pfn = (INT_FARPROC)GetProcAddress( hLibrary, "DrvQueryColorProfile" )) {
try {
//
// Call the Printer UI driver.
//
iRet = (*pfn)( hPrinter,
pdevmode,
ulQueryMode,
pvProfileData,
pcbProfileData,
pflProfileData );
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
} else {
//
// Mark this driver does not export it, so later
// we can fail without load printer driver.
//
pSpool->Status |= SPOOL_STATUS_NO_COLORPROFILE_HOOK;
//
// Tell callee it is not supported.
//
iRet = -1;
}
RefCntUnloadDriver(hLibrary, TRUE);
}
}
vUnprotectHandle( hPrinter );
return (iRet);
}
/****************************************************************************
* BOOL QuerySpoolMode( hPrinter, pflSpoolMode, puVersion )
*
* This function is called by GDI at StartDoc time when printing to an EMF.
* It tell GDI whether to embed fonts in the job as well as what version of
* EMF to generate.
*
* For now I am doing something hacky: I'm calling GetPrinterInfo to determine
* if the target is a remote machine and if so always telling GDI to embed
* fonts which don't exist on the server into spool file. Eventually this
* call will be routed to the print processor on the target machine which
* will use some UI/registry setting to determine what to do with fonts and
* set the version number correctly.
*
* History:
* 5/13/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
// !!later move this define to the appropriate header file
#define QSM_DOWNLOADFONTS 0x00000001
BOOL
QuerySpoolMode(
HANDLE hPrinter,
LONG *pflSpoolMode,
ULONG *puVersion
)
{
DWORD dwPrinterInfoSize = 0;
PRINTER_INFO_2 *pPrinterInfo2 = NULL;
BOOL bRet = FALSE, bStatus, bAllocBuffer = FALSE;
BYTE btBuffer[MAX_STATIC_ALLOC];
pPrinterInfo2 = (PPRINTER_INFO_2) btBuffer;
ZeroMemory(pPrinterInfo2, MAX_STATIC_ALLOC);
bStatus = GetPrinter(hPrinter, 2, (LPBYTE) pPrinterInfo2,
MAX_STATIC_ALLOC, &dwPrinterInfoSize);
if (!bStatus &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pPrinterInfo2 = (PRINTER_INFO_2*) LocalAlloc(LPTR,
dwPrinterInfoSize)))
{
bAllocBuffer = TRUE;
bStatus = GetPrinter(hPrinter, 2, (LPBYTE) pPrinterInfo2,
dwPrinterInfoSize, &dwPrinterInfoSize);
}
if (bStatus)
{
*puVersion = 0x00010000; // version 1.0
//
// No server means we are printing locally
//
*pflSpoolMode = ( pPrinterInfo2->pServerName == NULL ) ?
0 :
QSM_DOWNLOADFONTS;
bRet = TRUE;
}
else
{
DBGMSG( DBG_WARNING, ( "QuerySpoolMode: GetPrinter failed %d.\n", GetLastError( )));
}
if (bAllocBuffer)
{
LocalFree( pPrinterInfo2 );
}
return bRet;
}
BOOL
SetPortW(
LPWSTR pszName,
LPWSTR pszPortName,
DWORD dwLevel,
LPBYTE pPortInfo
)
{
BOOL ReturnValue;
PORT_CONTAINER PortContainer;
switch (dwLevel) {
case 3:
if ( !pPortInfo ) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
PortContainer.Level = dwLevel;
PortContainer.PortInfo.pPortInfo3 = (PPORT_INFO_3)pPortInfo;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (bLoadedBySpooler && fpYSetPort) {
ReturnValue = (*fpYSetPort)(pszName, pszPortName, &PortContainer, NATIVE_CALL);
}
else {
ReturnValue = RpcSetPort(pszName, pszPortName, &PortContainer);
}
if (ReturnValue != ERROR_SUCCESS) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
bValidDevModeW(
const DEVMODE *pDevMode
)
/*++
Routine Description:
Check whether a devmode is valid to be RPC'd across to the spooler.
Arguments:
pDevMode - DevMode to check.
Return Value:
TRUE - Devmode can be RPC'd to spooler.
FALSE - Invalid Devmode.
--*/
{
if( !pDevMode || pDevMode == (PDEVMODE)-1 ){
return FALSE;
}
if( pDevMode->dmSize < MIN_DEVMODE_SIZEW ){
//
// The only valid case is if pDevModeW is NULL. If it's
// not NULL, then a bad devmode was passed in and the
// app should fix it's code.
//
SPLASSERT( pDevMode->dmSize >= MIN_DEVMODE_SIZEW );
return FALSE;
}
return TRUE;
}
BOOL
XcvDataW(
HANDLE hPrinter,
PCWSTR pszDataName,
PBYTE pInputData,
DWORD cbInputData,
PBYTE pOutputData,
DWORD cbOutputData,
PDWORD pcbOutputNeeded,
PDWORD pdwStatus
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if (!pcbOutputNeeded){
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if( eProtectHandle( hPrinter, FALSE )){
SetLastError( ERROR_INVALID_HANDLE );
return FALSE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// a dummy pointer.
//
if (!pInputData && !cbInputData)
pInputData = (PBYTE) &ReturnValue;
if (!pOutputData && !cbOutputData)
pOutputData = (PBYTE) &ReturnValue;
do {
RpcTryExcept {
if (ReturnValue = RpcXcvData( pSpool->hPrinter,
pszDataName,
pInputData,
cbInputData,
pOutputData,
cbOutputData,
pcbOutputNeeded,
pdwStatus)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
} while( !ReturnValue &&
GetLastError() == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
if (!ReturnValue) {
DBGMSG(DBG_TRACE,("XcvData Exception: %d\n", GetLastError()));
}
vUnprotectHandle( hPrinter );
return ReturnValue;
}
PWSTR
ConstructXcvName(
PCWSTR pServerName,
PCWSTR pObjectName,
PCWSTR pObjectType
)
{
DWORD cbOutput;
PWSTR pOut;
cbOutput = pServerName ? (wcslen(pServerName) + 2)*sizeof(WCHAR) : sizeof(WCHAR); /* "\\Server\," */
cbOutput += (wcslen(pObjectType) + 2)*sizeof(WCHAR); /* "\\Server\,XcvPort _" */
cbOutput += pObjectName ? (wcslen(pObjectName))*sizeof(WCHAR) : 0; /* "\\Server\,XcvPort Object_" */
if (pOut = AllocSplMem(cbOutput)) {
if (pServerName) {
wcscpy(pOut,pServerName);
wcscat(pOut, L"\\");
}
wcscat(pOut,L",");
wcscat(pOut,pObjectType);
wcscat(pOut,L" ");
if (pObjectName)
wcscat(pOut,pObjectName);
}
return pOut;
}
HANDLE
ConnectToPrinterDlg(
IN HWND hwnd,
IN DWORD dwFlags
)
{
typedef HANDLE (WINAPI *PF_CONNECTTOPRINTERDLG)( HWND, DWORD );
PF_CONNECTTOPRINTERDLG pfConnectToPrinterDlg = NULL;
HANDLE hHandle = NULL;
HINSTANCE hLib = NULL;
hLib = LoadLibrary( szPrintUIDll );
if( hLib )
{
pfConnectToPrinterDlg = (PF_CONNECTTOPRINTERDLG)GetProcAddress( hLib, "ConnectToPrinterDlg" );
if( pfConnectToPrinterDlg )
{
hHandle = pfConnectToPrinterDlg( hwnd, dwFlags );
}
FreeLibrary( hLib );
}
return hHandle;
}
DWORD
SendRecvBidiData(
IN HANDLE hPrinter,
IN LPCWSTR pAction,
IN PBIDI_REQUEST_CONTAINER pReqData,
OUT PBIDI_RESPONSE_CONTAINER* ppResData
)
{
DWORD dwRet = ERROR_SUCCESS;
PSPOOL pSpool = (PSPOOL)hPrinter;
UINT cRetry = 0;
if( eProtectHandle( hPrinter, FALSE ))
{
dwRet = GetLastError();
}
else
{
do
{
RpcTryExcept
{
if(ppResData)
{
*ppResData = NULL;
}
dwRet = RpcSendRecvBidiData(pSpool->hPrinter,
pAction,
(PRPC_BIDI_REQUEST_CONTAINER)pReqData,
(PRPC_BIDI_RESPONSE_CONTAINER*)ppResData);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
dwRet = TranslateExceptionCode(RpcExceptionCode());
}
RpcEndExcept
} while (dwRet == ERROR_INVALID_HANDLE &&
cRetry++ < MAX_RETRY_INVALID_HANDLE &&
RevalidateHandle( pSpool ));
vUnprotectHandle( hPrinter );
}
//
// If we are trying to communicate with a downlevel router, that does
// not understand the meaning of SendRecvBidiData , we would get the
// error code: RPC_S_PROCNUM_OUT_OF_RANGE which might be converted to
// ERROR_NOT_SUPPORTED for better clearity and more consistency with
// the a genaral return error code if feature is not supported.
if(dwRet == RPC_S_PROCNUM_OUT_OF_RANGE)
{
dwRet = ERROR_NOT_SUPPORTED;
}
return (dwRet);
}
VOID
PrintUIQueueCreate(
IN HWND hWnd,
IN LPCWSTR PrinterName,
IN INT CmdShow,
IN LPARAM lParam
)
{
DWORD dwRet = ERROR_SUCCESS;
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
AllowSetForegroundWindow(RPCSplWOW64GetProcessID());
if((dwRet = RPCSplWOW64PrintUIQueueCreate((ULONG_PTR)GetForeGroundWindow(),
PrinterName,
CmdShow,
lParam)) == ERROR_SUCCESS)
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_ENDQUEUECREATE)
{
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
SetLastError(dwRet);
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
}
VOID
PrintUIPrinterPropPages(
IN HWND hWnd,
IN LPCWSTR PrinterName,
IN INT CmdShow,
IN LPARAM lParam
)
{
DWORD dwRet = ERROR_SUCCESS;
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
AllowSetForegroundWindow(RPCSplWOW64GetProcessID());
if((dwRet = RPCSplWOW64PrintUIPrinterPropPages((ULONG_PTR)GetForeGroundWindow(),
PrinterName,
CmdShow,
lParam)) == ERROR_SUCCESS)
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_ENDPRINTERPROPPAGES)
{
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
SetLastError(dwRet);
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
}
VOID
PrintUIDocumentDefaults(
IN HWND hWnd,
IN LPCWSTR PrinterName,
IN INT CmdShow,
IN LPARAM lParam
)
{
DWORD dwRet = ERROR_SUCCESS;
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
AllowSetForegroundWindow(RPCSplWOW64GetProcessID());
if((dwRet = RPCSplWOW64PrintUIDocumentDefaults((ULONG_PTR)GetForeGroundWindow(),
PrinterName,
CmdShow,
lParam)) == ERROR_SUCCESS)
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_ENDDOCUMENTDEFAULTS)
{
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
SetLastError(dwRet);
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
}
DWORD WINAPI
AsyncPrinterSetup(
PVOID pData
)
{
PrinterSetupData *pThrdData = (PrinterSetupData *)pData;
RpcTryExcept
{
RPCSplWOW64PrintUIPrinterSetup((ULONG_PTR)GetForeGroundWindow(),
pThrdData->uAction,
pThrdData->cchPrinterName,
pThrdData->PrinterNameSize,
(byte *)pThrdData->pszPrinterName,
pThrdData->pcchPrinterName,
pThrdData->pszServerName);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
return(0);
}
BOOL
PrintUIPrinterSetup(
IN HWND hWnd,
IN UINT uAction,
IN UINT cchPrinterName,
IN OUT LPWSTR pszPrinterName,
OUT UINT *pcchPrinterName,
IN LPCWSTR pszServerName
)
{
BOOL bRet = FALSE;
DWORD dwRet = ERROR_SUCCESS;
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
HANDLE hAsyncSetupThrd = NULL;
DWORD AsyncSetupThrdId = 0;
PrinterSetupData ThrdData;
AllowSetForegroundWindow(RPCSplWOW64GetProcessID());
ThrdData.hWnd = (ULONG_PTR)GetForeGroundWindow();
ThrdData.uAction = uAction;
ThrdData.cchPrinterName = cchPrinterName;
ThrdData.PrinterNameSize = cchPrinterName*2;
ThrdData.pszPrinterName = pszPrinterName;
ThrdData.pcchPrinterName = pcchPrinterName;
ThrdData.pszServerName = pszServerName;
if(!(hAsyncSetupThrd = CreateThread(NULL,
INITIAL_STACK_COMMIT,
AsyncPrinterSetup,
(PVOID)&ThrdData,
0,
&AsyncSetupThrdId)))
{
dwRet = GetLastError();
}
else
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_ENDPRINTERSETUP)
{
bRet = (BOOL)msg.wParam;
SetLastError((DWORD)msg.lParam);
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
WaitForSingleObject(hAsyncSetupThrd,INFINITE);
CloseHandle(hAsyncSetupThrd);
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
return bRet;
}
VOID
PrintUIServerPropPages(
IN HWND hWnd,
IN LPCWSTR ServerName,
IN INT CmdShow,
IN LPARAM lParam
)
{
DWORD dwRet = ERROR_SUCCESS;
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS))
{
AllowSetForegroundWindow(RPCSplWOW64GetProcessID());
if((dwRet = RPCSplWOW64PrintUIServerPropPages((ULONG_PTR)GetForeGroundWindow(),
ServerName,
CmdShow,
lParam)) == ERROR_SUCCESS)
{
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_ENDSERVERPROPPAGES)
{
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
SetLastError(dwRet);
}
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
}
DWORD WINAPI
AsyncDocumentPropertiesWrap(
PVOID pData
)
{
PumpThrdData *ThrdData = (PumpThrdData *)pData;
RpcTryExcept
{
*ThrdData->Result = RPCSplWOW64PrintUIDocumentProperties(ThrdData->hWnd,
ThrdData->PrinterName,
ThrdData->TouchedDevModeSize,
ThrdData->ClonedDevModeOutSize,
ThrdData->ClonedDevModeOut,
ThrdData->DevModeInSize,
ThrdData->pDevModeInput,
ThrdData->ClonedDevModeFill,
ThrdData->fMode,
ThrdData->fExclusionFlags,
ThrdData->dwRet);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
return(0);
}
LONG
PrintUIDocumentPropertiesWrap(
HWND hWnd, // handle to parent window
HANDLE hPrinter, // handle to printer object
LPTSTR pDeviceName, // device name
PDEVMODE pDevModeOutput, // modified device mode
PDEVMODE pDevModeInput, // original device mode
DWORD fMode, // mode options
DWORD fExclusionFlags // exclusion flags
)
{
DOCUMENTPROPERTYHEADER DPHdr;
PDEVMODE pDM;
LONG Result = -1;
HANDLE hTmpPrinter = NULL;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (hPrinter == NULL)
{
if (!OpenPrinter( pDeviceName, &hTmpPrinter, NULL ))
{
hTmpPrinter = NULL;
}
}
else
{
hTmpPrinter = hPrinter;
}
if( !eProtectHandle( hTmpPrinter, FALSE ))
{
LPWSTR PrinterName;
MSG msg;
LONG RetVal;
DWORD dwRet = ERROR_SUCCESS;
DWORD ClonedDevModeOutSize = 0;
DWORD TouchedDevModeSize = 0;
BOOL ClonedDevModeFill = (!!(fMode & DM_OUT_BUFFER) && pDevModeOutput);
DWORD DevModeInSize = pDevModeInput ? (pDevModeInput->dmSize + pDevModeInput->dmDriverExtra) : 0;
byte **ClonedDevModeOut = NULL;
if(ClonedDevModeOut = (byte **)LocalAlloc(LPTR,sizeof(byte *)))
{
*ClonedDevModeOut = NULL;
if(pSpool)
{
PrinterName = pSpool->pszPrinter;
}
else
{
PrinterName = pDeviceName;
}
//
// If fMode doesn't specify DM_IN_BUFFER, then zero out
// pDevModeInput.
//
// Old 3.51 (version 1-0) drivers used to ignore the absence of
// DM_IN_BUFFER and use pDevModeInput if it was not NULL. It
// probably did this because Printman.exe was broken.
//
// If the devmode is invalid, then don't pass one in.
// This fixes MS Imager32 (which passes dmSize == 0) and
// Milestones etc. 4.5.
//
// Note: this assumes that pDevModeOutput is still the
// correct size!
//
if( !(fMode & DM_IN_BUFFER) || !bValidDevModeW( pDevModeInput ))
{
//
// If either are not set, make sure both are not set.
//
pDevModeInput = NULL;
DevModeInSize = 0;
fMode &= ~DM_IN_BUFFER;
}
RpcTryExcept
{
if(((dwRet = ConnectToLd64In32Server(&hSurrogateProcess)) == ERROR_SUCCESS) &&
(!hWnd ||
((dwRet = AddHandleToList(hWnd)) == ERROR_SUCCESS)))
{
HANDLE hUIMsgThrd = NULL;
DWORD UIMsgThrdId = 0;
PumpThrdData ThrdData;
ThrdData.hWnd = (ULONG_PTR)hWnd;
ThrdData.PrinterName=PrinterName;
ThrdData.TouchedDevModeSize = &TouchedDevModeSize;
ThrdData.ClonedDevModeOutSize = &ClonedDevModeOutSize;
ThrdData.ClonedDevModeOut = (byte**)ClonedDevModeOut;
ThrdData.DevModeInSize = DevModeInSize;
ThrdData.pDevModeInput = (byte*)pDevModeInput;
ThrdData.fMode = fMode;
ThrdData.fExclusionFlags = fExclusionFlags;
ThrdData.dwRet = &dwRet;
ThrdData.ClonedDevModeFill = ClonedDevModeFill;
ThrdData.Result = &Result;
//
// If we have a window handle , the following functions cann't
// proceed synchronasly. The reason for that is in order to show
// the UI of the driver property sheets we need to be able to dispatch
// incomming messages and process them.For this reason the following
// call would be asynchronous call and the success or failure doesn't
// in reality tell us anything more than than the async process started
// or not. We get the success of failure from the termination message.
// If we don't have a window handle, then the call is synchronous.
//
if(!(hUIMsgThrd = CreateThread(NULL,
INITIAL_STACK_COMMIT,
AsyncDocumentPropertiesWrap,
(PVOID)&ThrdData,
0,
&UIMsgThrdId)))
{
dwRet = GetLastError();
}
//
// The following is the required message loop for processing messages
// from the UI in case we have a window handle.
//
//
if(hUIMsgThrd && hWnd)
{
while (GetMessage(&msg, NULL, 0, 0))
{
//
// In This message loop We should trap a User defined message
// which indicates the success or the failure of the operation
//
if(msg.message == WM_ENDPRINTUIDOCUMENTPROPERTIES)
{
Result = (LONG)msg.wParam;
if(Result == -1)
SetLastError((DWORD)msg.lParam);
DelHandleFromList(hWnd);
break;
}
else if(msg.message == WM_SURROGATEFAILURE)
{
//
// This means that the server process died and we have
// break from the message loop
//
Result = -1;
SetLastError(RPC_S_SERVER_UNAVAILABLE);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if(hUIMsgThrd)
{
WaitForSingleObject(hUIMsgThrd,INFINITE);
CloseHandle(hUIMsgThrd);
}
if(Result!=-1 && pDevModeOutput)
{
memcpy((PVOID)pDevModeOutput,(PVOID)*ClonedDevModeOut,TouchedDevModeSize);
}
if(*ClonedDevModeOut)
{
MIDL_user_free((PVOID)*ClonedDevModeOut);
}
if(ClonedDevModeOut)
{
LocalFree((PVOID) ClonedDevModeOut);
}
}
else
{
SetLastError(dwRet);
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
RpcEndExcept
vUnprotectHandle( hTmpPrinter );
}
else
{
SetLastError(ERROR_OUTOFMEMORY);
}
}
if (hPrinter == NULL)
{
if( hTmpPrinter )
{
ClosePrinter(hTmpPrinter);
}
}
return(Result);
}
/*++
Function Name:
MonitorRPCServerProcess
Description:
This function monitors the status of the RPC surrogate
process. The one used in loading the required 64 dlls
in a 32 bit client.This function always run in a
separate thread
Parameters:
pData : Pointer to the process handle to monitor
Return Value
Always return 0
--*/
DWORD WINAPI
MonitorRPCServerProcess(
PVOID pData
)
{
WndHndlList ListObj;
HANDLE* phProcess;
HANDLE hEvent;
LPMonitorThrdData pThrdData = (LPMonitorThrdData)pData;
ListObj.Head = 0x00000000;
ListObj.Tail = 0x00000000;
ListObj.NumOfHndls = 0;
//
// reconstruct the Data for the thread
//
hEvent = pThrdData->hEvent;
phProcess = pThrdData->hProcess;
EnterCriticalSection(&ProcessHndlCS);
{
GWndHndlList = &ListObj;
}
LeaveCriticalSection(&ProcessHndlCS);
SetEvent(hEvent);
WaitForSingleObject(*phProcess,INFINITE);
EnterCriticalSection(&ProcessHndlCS);
{
CloseHandle(*((HANDLE *)phProcess));
*((HANDLE *)phProcess) = 0x00000000;
RpcBindingFree(&hSurrogate);
//
// Release any windows which might be
// locked on a surrogate process waiting
// for its completion
//
ReleaseAndCleanupWndList();
}
LeaveCriticalSection(&ProcessHndlCS);
return(0);
}
/*++
Function Name:
ConnectToLd64In32Server
Description:
This function make sure that we retry connection to the server
in case of a very slight window where the Server terminated between
our connection and the very first call.
Parameters:
hProcess : Pointer to the process handle that retrieves
the process handle of the server
Return Value
--*/
DWORD
ConnectToLd64In32Server(
HANDLE *hProcess
)
{
DWORD RetVal = ERROR_SUCCESS;
//
// As GDI would be using the same monitoring Thread, So we spin
// only one thread.
//
if(!hProcess)
{
hProcess = &hSurrogateProcess;
}
if( (RetVal = ConnectToLd64In32ServerWorker(hProcess)) != ERROR_SUCCESS)
{
if(RetVal == RPC_S_SERVER_UNAVAILABLE || RetVal == RPC_S_CALL_FAILED_DNE)
{
RetVal = ConnectToLd64In32ServerWorker(hProcess);
}
}
return(RetVal);
}
/*++
Function Name:
ConnectToLd64In32ServerWorker
Description:
This function handles the connectivity issues with
the RPC surrogate process (the one that loads 64 bit
dlls in a 32 bit process).
Parameters:
hProcess : Pointer to the process handle that retrieves
the process handle of the server
Return Value
--*/
DWORD
ConnectToLd64In32ServerWorker(
HANDLE *hProcess
)
{
DWORD RetVal = ERROR_SUCCESS;
RPC_STATUS RpcStatus;
EnterCriticalSection(&ProcessHndlCS);
{
if(!*hProcess)
{
WCHAR* StringBinding = NULL;
STARTUPINFO StartUPInfo;
PROCESS_INFORMATION ProcessInfo;
ZeroMemory(&StartUPInfo,sizeof(STARTUPINFO));
ZeroMemory(&ProcessInfo,sizeof(PROCESS_INFORMATION));
StartUPInfo.cb = sizeof(STARTUPINFO);
RpcTryExcept
{
HANDLE hOneProcessMutex = NULL;
WCHAR SessionEndPoint[50];
DWORD CurrSessionId;
DWORD CurrProcessId = GetCurrentProcessId();
if(ProcessIdToSessionId(CurrProcessId,&CurrSessionId))
{
wsprintf(SessionEndPoint,L"%s_%x",L"splwow64",CurrSessionId);
if(!(((RpcStatus = RpcStringBindingCompose(NULL,
L"ncalrpc",
NULL,
SessionEndPoint,
NULL,
&StringBinding))==RPC_S_OK) &&
((RpcStatus = RpcBindingFromStringBinding(StringBinding,
&hSurrogate))==RPC_S_OK) &&
((RpcStatus = RpcStringFree(&StringBinding)) == RPC_S_OK)))
{
RetVal = (DWORD)RpcStatus;
}
else
{
//
// This mutex is defined as Local to be different for
// each TS session
//
if(hOneProcessMutex = CreateMutex(NULL,
FALSE,
L"Local\\WinSpl64To32Mutex"))
{
HANDLE hThread;
HANDLE hMonitorStartedEvent;
DWORD ThreadId;
DWORD i=0;
DWORD RpcRetCode;
WaitForSingleObject(hOneProcessMutex,INFINITE);
{
if(RpcMgmtIsServerListening(hSurrogate) == RPC_S_NOT_LISTENING)
{
WCHAR ProcessName[MAX_PATH+1];
WCHAR WindowsDirectory[MAX_PATH+1];
//
// In the future this should work , but
// for the time being , wow64 redirects
// any CreateProcess initiated from a wow
// app and requesting an app from system32
// to syswow64. That is why I moving the exe
// out of the system32 directory.
//
GetSystemWindowsDirectory(WindowsDirectory,MAX_PATH);
wsprintf(ProcessName,L"%ws\\splwow64.exe",WindowsDirectory);
if(!CreateProcess(ProcessName,
L"splwow64",
NULL,
NULL,
FALSE,
CREATE_DEFAULT_ERROR_MODE |
CREATE_NO_WINDOW |
DETACHED_PROCESS,
NULL,
WindowsDirectory,
&StartUPInfo,
&ProcessInfo))
{
RetVal = GetLastError();
}
else
{
*hProcess = ProcessInfo.hProcess;
//
// A spinlock making sure that the process is really live and kicking.
// I also added to the spin lock a time out value in order not to enter
// in an endless loop. So, after a minute we just break.
//
for(i=0,
RpcRetCode = RpcMgmtIsServerListening(hSurrogate);
((i<60) && (RpcRetCode == RPC_S_NOT_LISTENING));
Sleep(1000),
RpcRetCode = RpcMgmtIsServerListening(hSurrogate),
i++
);
}
}
else
{
*hProcess = (HANDLE) RPCSplWOW64GetProcessHndl((DWORD)GetCurrentProcessId(),&RetVal);
}
}
ReleaseMutex(hOneProcessMutex);
CloseHandle(hOneProcessMutex);
if(!(hMonitorStartedEvent=CreateEvent(NULL,FALSE,FALSE,NULL)))
{
RetVal = GetLastError();
}
else
{
MonitorThrdData ThrdData;
ThrdData.hEvent = hMonitorStartedEvent;
ThrdData.hProcess = hProcess;
if(!(hThread = CreateThread(NULL,
INITIAL_STACK_COMMIT,
MonitorRPCServerProcess,
(PVOID)&ThrdData,
0,
&ThreadId)))
{
RetVal = GetLastError();
}
else
{
LeaveCriticalSection(&ProcessHndlCS);
{
WaitForSingleObject(hMonitorStartedEvent,INFINITE);
}
EnterCriticalSection(&ProcessHndlCS);
CloseHandle(hThread);
}
CloseHandle(hMonitorStartedEvent);
}
}
else
{
RetVal = GetLastError();
}
}
}
else
{
RetVal = GetLastError();
}
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
RetVal = RpcExceptionCode();
}
RpcEndExcept
}
else
{
//
// Refresh the life of the server
//
RpcTryExcept
{
RPCSplWOW64RefreshLifeSpan();
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
RetVal = RpcExceptionCode();
}
RpcEndExcept
}
}
LeaveCriticalSection(&ProcessHndlCS);
return(RetVal);
}
DWORD
AddHandleToList(
HWND hWnd
)
{
LPWndHndlNode NewNode = 0x00000000;
DWORD RetVal = ERROR_SUCCESS;
EnterCriticalSection(&ProcessHndlCS);
{
if(GWndHndlList)
{
if(NewNode = (LPWndHndlNode)LocalAlloc(LMEM_FIXED, sizeof(WndHndlNode)))
{
NewNode->PrevNode = 0x000000000;
NewNode->NextNode = 0x000000000;
NewNode->hWnd = hWnd;
if(!GWndHndlList->Head &&
!GWndHndlList->NumOfHndls)
{
GWndHndlList->Head = NewNode;
GWndHndlList->Tail = NewNode;
}
else
{
NewNode->PrevNode = GWndHndlList->Tail;
GWndHndlList->Tail->NextNode = NewNode;
GWndHndlList->Tail = NewNode;
}
GWndHndlList->NumOfHndls++;
}
else
{
RetVal = GetLastError();
}
}
else
{
RetVal = ERROR_INVALID_PARAMETER;
}
}
LeaveCriticalSection(&ProcessHndlCS);
return(RetVal);
}
BOOL
DelHandleFromList(
HWND hWnd
)
{
DWORD RetVal = ERROR_SUCCESS;
BOOL Found = FALSE;
EnterCriticalSection(&ProcessHndlCS);
{
LPWndHndlNode TempNode = 0x00000000;
if(GWndHndlList)
{
if(GWndHndlList->NumOfHndls)
{
//
// Is it last Element in list
//
if(GWndHndlList->Tail->hWnd == hWnd)
{
TempNode = GWndHndlList->Tail;
GWndHndlList->Tail = TempNode->PrevNode;
if(GWndHndlList->Tail)
{
GWndHndlList->Tail->NextNode = 0x00000000;
}
Found = TRUE;
}
//
// Is it first Element in list
//
else if(GWndHndlList->Head->hWnd == hWnd)
{
TempNode = GWndHndlList->Head;
GWndHndlList->Head = TempNode->NextNode;
if(GWndHndlList->Head)
GWndHndlList->Head->PrevNode = 0x00000000;
Found = TRUE;
}
//
// Is it an intermediate Element
//
else
{
TempNode = GWndHndlList->Head->NextNode;
while(TempNode &&
(TempNode->hWnd != hWnd) &&
TempNode != GWndHndlList->Tail)
{
TempNode = TempNode->NextNode;
}
if(TempNode && TempNode!=GWndHndlList->Tail)
{
Found = TRUE;
TempNode->PrevNode->NextNode = TempNode->NextNode;
TempNode->NextNode->PrevNode = TempNode->PrevNode;
}
}
if(Found)
{
if(!--GWndHndlList->NumOfHndls)
{
GWndHndlList->Head = GWndHndlList->Tail = 0x00000000;
}
LocalFree(TempNode);
}
}
}
else
{
RetVal = ERROR_INVALID_PARAMETER;
}
}
LeaveCriticalSection(&ProcessHndlCS);
return(RetVal);
}
VOID
ReleaseAndCleanupWndList(
VOID
)
{
LPWndHndlNode TempNode = (LPWndHndlNode)GWndHndlList->Head;
while(TempNode)
{
PostMessage(TempNode->hWnd,
WM_SURROGATEFAILURE,
0,
0);
GWndHndlList->Head = TempNode->NextNode;
LocalFree(TempNode);
TempNode = GWndHndlList->Head;
}
GWndHndlList->NumOfHndls = 0;
}
BOOL
JobCanceled(
IN PSJobCancelInfo pJobCancelInfo
)
{
if (!pJobCancelInfo->NumOfCmpltWrts && pJobCancelInfo->pSpool->cbFlushPending)
{
//
// Data to be flushed =
// pSpool->cbFlushPending
//
FlushPrinter(pJobCancelInfo->pSpool,
pJobCancelInfo->pSpool->pBuffer+pJobCancelInfo->cbFlushed,
pJobCancelInfo->pSpool->cbFlushPending,
pJobCancelInfo->pcbWritten,
0);
pJobCancelInfo->pSpool->Flushed = 1;
}
else
{
DWORD WrittenDataSize = *pJobCancelInfo->pcTotalWritten + pJobCancelInfo->cbFlushed;
//
// Data to be flushed =
// I/P Data + Pending Data - Total Written
//
SPLASSERT(WrittenDataSize <= pJobCancelInfo->ReqTotalDataSize);
if (pJobCancelInfo->ReqTotalDataSize - WrittenDataSize)
{
LPBYTE pFlushBuffer;
//
// Location in pFlushBuffer where data from the
// i/p buffer starts
//
DWORD InitialBuffStart = 0;
if ((pFlushBuffer = VirtualAlloc(NULL,
(pJobCancelInfo->ReqTotalDataSize - WrittenDataSize),
MEM_COMMIT, PAGE_READWRITE)))
{
//
// Since this seems to be quite a complicated functionality
// I'll try explaining it in details here
// These are the Data Buffers we are dealing with and their
// initial states
//
// pSpool->pBuffer pBuf = pInitialBuf
// ____________________ _________________________________
// | | | | |
// | | | | |
// -------------------- ---------------------------------
// <-------> <-------------------------------->
// pending ReqToWriteDataSize
// | |
// | |
// ----------------+----------------
// |
// (RequiredTotalDataSize)
//
// At this stage of the function we could have the
// following conditions
// 1. Written < Pending -----> Then we have to
// count both Buffers for Flushing
// 2. Written > Pending -----> Then we count only pBuf for
// Flushing
// Based on these conditions we need to figure out which of the
// of the 2 buffers is used for flushing the data and what pointer
// in either is the starting point of this data
// For Condition 1 FlushBuffer would be the aggregation of :
// pSpool->pBuffer pBuf = pInitialBuf
// ____________________ _________________________________
// | | | | | |
// | | | | | |
// -------------------- ---------------------------------
// <---> <------------------------------->
// Pending-Written ReqToWriteDataSize
//
// FlushBuffer
// _____________________________________
// | | |
// | | |
// -------------------------------------
// |
// |
// InitialBuffStart(where pBuf starts in FlushBuffer)
// <---><------------------------------->
// Pending-Written ReqToWriteDataSize
//
// For Condition 2 FlushBuffer would be a portion of pBuf:
// pBuf = pInitialBuf
// _________________________________
// | | |
// | | |
// ---------------------------------
// <----------------------->
// ReqTotalDataSize - Written
//
// FlushBuffer
// _______________________
// | |
// | |
// -----------------------
// |
// |
// InitialBuffStart(at the very beginning)
// <--------------------->
// ReqTotalDataSize - Written
//
if (WrittenDataSize < pJobCancelInfo->FlushPendingDataSize)
{
InitialBuffStart = pJobCancelInfo->FlushPendingDataSize - WrittenDataSize;
CopyMemory( pFlushBuffer ,
pJobCancelInfo->pSpool->pBuffer + WrittenDataSize,
InitialBuffStart);
}
CopyMemory(pFlushBuffer + InitialBuffStart ,
pJobCancelInfo->pInitialBuf +
(InitialBuffStart ? 0 : WrittenDataSize - pJobCancelInfo->FlushPendingDataSize),
pJobCancelInfo->ReqTotalDataSize - WrittenDataSize - InitialBuffStart);
FlushPrinter(pJobCancelInfo->pSpool,
pFlushBuffer,
pJobCancelInfo->ReqTotalDataSize - WrittenDataSize,
pJobCancelInfo->pcbWritten,
0);
VirtualFree(pFlushBuffer,
(pJobCancelInfo->ReqTotalDataSize - WrittenDataSize),
MEM_RELEASE);
pJobCancelInfo->ReturnValue = TRUE;
pJobCancelInfo->pSpool->Flushed = 1;
if (*pJobCancelInfo->pcbWritten == (pJobCancelInfo->ReqTotalDataSize - WrittenDataSize))
{
*pJobCancelInfo->pcTotalWritten+=pJobCancelInfo->ReqToWriteDataSize;
}
}
else
{
DBGMSG(DBG_WARNING, ("JObCanceled::VirtualAlloc Failed to allocate 4k buffer %d\n",GetLastError()));
}
}
}
if (pJobCancelInfo->pSpool->Flushed)
{
pJobCancelInfo->pSpool->cbFlushPending = 0;
pJobCancelInfo->pSpool->cbBuffer = 0;
}
return pJobCancelInfo->ReturnValue;
}