/*++ 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; }