/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: umpd.c Abstract: User-mode printer driver support Environment: Windows NT 5.0 Revision History: 06/30/97 -davidx- Created it. 09/17/97 -davidx- Clean up km-um thunking. --*/ extern "C" { #include "precomp.h" #include "winddi.h" #include "psapi.h" #include "proxyport.h" static PUMPD gCachedUMPDList = NULL; // cached list of user-mode printer drivers } #include "umpd.hxx" #define IA64_PAGE_SIZE 0x2000 #define PP_SHAREDSECTION_SIZE IA64_PAGE_SIZE PVOID MyGetPrinterDriver( HANDLE hPrinter, DWORD dwLevel ) /*++ Routine Description: Wrapper function for spooler's GetPrinterDriver API Arguments: hPrinter - Handle to the printer dwLevel - Level of DRIVER_INFO_x structure the caller is interested in Return Value: Pointer to a DRIVER_INFO_x structure, NULL if there is an error --*/ { DWORD cbNeeded; PVOID pv; INT retries = 0; // // Start with a default buffer size to avoid always // having to call GetPrinterDriver twice. // cbNeeded = 2 * MAX_PATH * sizeof(WCHAR); while (retries++ < 2) { if (! (pv = LOCALALLOC(cbNeeded))) { WARNING("Memory allocation failed.\n"); return NULL; } if (fpGetPrinterDriverW(hPrinter, NULL, dwLevel, (LPBYTE)pv, cbNeeded, &cbNeeded)) return pv; // // If GetPrinterDriver failed not for insufficient buffer, // skip the retry // if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) retries++; LOCALFREE(pv); } WARNING("GetPrinterDriver failed.\n"); return NULL; } PWSTR DuplicateString( PCWSTR pSrc ) /*++ Routine Description: Allocate memory and make a duplicate of the source string Arguments: pSrc - String to be duplicated Return Value: Pointer to the duplicated string NULL if there is an error --*/ { PWSTR pDest; INT cb; ASSERTGDI(pSrc != NULL, "Duplicate NULL string!\n"); cb = (wcslen(pSrc) + 1) * sizeof(WCHAR); if (pDest = (PWSTR) LOCALALLOC(cb)) { CopyMemory(pDest, pSrc, cb); return pDest; } else { WARNING("DuplicateString: out-of-memory.\n"); return NULL; } } PDRIVER_INFO_5W DuplicateDriverInfo5W(PDRIVER_INFO_5W inDriverInfo) { ULONG size = sizeof(DRIVER_INFO_5W); PDRIVER_INFO_5W pDriverInfo; PWSTR pStr; ULONG len; size += (inDriverInfo->pName == NULL ? 0 : (wcslen(inDriverInfo->pName) + 1) * sizeof(WCHAR)); size += (inDriverInfo->pEnvironment == NULL ? 0 : (wcslen(inDriverInfo->pEnvironment) + 1) * sizeof(WCHAR)); size += (inDriverInfo->pDriverPath == NULL ? 0 : (wcslen(inDriverInfo->pDriverPath) + 1) * sizeof(WCHAR)); size += (inDriverInfo->pDataFile == NULL ? 0 : (wcslen(inDriverInfo->pDataFile) + 1) * sizeof(WCHAR)); size += (inDriverInfo->pConfigFile == NULL ? 0 : (wcslen(inDriverInfo->pConfigFile) + 1) * sizeof(WCHAR)); if(pDriverInfo = (PDRIVER_INFO_5W) LOCALALLOC(size)) { *pDriverInfo = *inDriverInfo; pStr = (PWSTR) (pDriverInfo + 1); if(pDriverInfo->pName) { len = wcslen(pDriverInfo->pName) + 1; RtlCopyMemory(pStr, pDriverInfo->pName, len * sizeof(WCHAR)); pDriverInfo->pName = pStr; pStr += len; } if(pDriverInfo->pEnvironment) { len = wcslen(pDriverInfo->pEnvironment) + 1; RtlCopyMemory(pStr, pDriverInfo->pEnvironment, len * sizeof(WCHAR)); pDriverInfo->pEnvironment = pStr; pStr += len; } if(pDriverInfo->pDriverPath) { len = wcslen(pDriverInfo->pDriverPath) + 1; RtlCopyMemory(pStr, pDriverInfo->pDriverPath, len * sizeof(WCHAR)); pDriverInfo->pDriverPath = pStr; pStr += len; } if(pDriverInfo->pDataFile) { len = wcslen(pDriverInfo->pDataFile) + 1; RtlCopyMemory(pStr, pDriverInfo->pDataFile, len * sizeof(WCHAR)); pDriverInfo->pDataFile = pStr; pStr += len; } if(pDriverInfo->pConfigFile) { len = wcslen(pDriverInfo->pConfigFile) + 1; RtlCopyMemory(pStr, pDriverInfo->pConfigFile, len * sizeof(WCHAR)); pDriverInfo->pConfigFile = pStr; pStr += len; } } return pDriverInfo; } PUMPD FindUserModePrinterDriver( PCWSTR pDriverDllName, DWORD dwDriverVersion, BOOL bUseVersion ) /*++ Routine Description: Search the cached list of user-mode printer drivers and see if the specified driver is found Arguments: pDriverDllName - Specifies the name of the driver DLL to be found dwDriverVersion - Current version number of the driver bUseVersion - Flag for using the version check Return Value: Pointer to the UMPD structure corresponding to the specified driver NULL if the specified driver is not in the cached list Note: This function must be called inside a critical section: ENTERCRITICALSECTION(&semUMPD); ... LEAVECRITICALSECTION(&semUMPD); --*/ { PUMPD pUMPD = gCachedUMPDList; while (pUMPD != NULL && pUMPD->pDriverInfo2 != NULL && _wcsicmp(pDriverDllName, pUMPD->pDriverInfo2->pDriverPath) != 0) { pUMPD = pUMPD->pNext; } // Do the version check if neccesary if (bUseVersion && pUMPD) { if (dwDriverVersion != pUMPD->dwDriverVersion) { // We have a version mismatch. Remove artificial increments on this // driver. if (pUMPD->bArtificialIncrement) { pUMPD->bArtificialIncrement = FALSE; if (UnloadUserModePrinterDriver(pUMPD, FALSE, 0)) { pUMPD = NULL; } } } } return pUMPD; } BOOL GdiArtificialDecrementDriver( LPWSTR pDriverDllName, DWORD dwDriverAttributes ) /*++ Routine Description: Remove the artificial increment on the driver, if any. Arguments: pDriverDllName - Specifies the name of the driver DLL to be found dwDriverAttributes - User/Kernel mode printer driver Return Value: TRUE if the driver file is no longer loaded in the spooler FALSE otherwise --*/ { PUMPD pUMPD; BOOL bReturn = FALSE; if (!pDriverDllName || !*pDriverDllName) { // Nothing to unload return bReturn; } if (dwDriverAttributes & DRIVER_KERNELMODE) { // Unload kernel mode driver return NtGdiUnloadPrinterDriver(pDriverDllName, (wcslen(pDriverDllName) + 1) * sizeof(WCHAR)); } ENTERCRITICALSECTION(&semUMPD); pUMPD = gCachedUMPDList; while (pUMPD != NULL && _wcsicmp(pDriverDllName, pUMPD->pDriverInfo2->pDriverPath) != 0) { pUMPD = pUMPD->pNext; } if (pUMPD) { if (pUMPD->bArtificialIncrement) { pUMPD->bArtificialIncrement = FALSE; bReturn = UnloadUserModePrinterDriver(pUMPD, FALSE, 0); } } else { bReturn = TRUE; } LEAVECRITICALSECTION(&semUMPD); return bReturn; } BOOL LoadUserModePrinterDriverEx( PDRIVER_INFO_5W pDriverInfo5, LPWSTR pwstrPrinterName, PUMPD *ppUMPD, PRINTER_DEFAULTSW *pdefaults, HANDLE hPrinter ) { PDRIVER_INFO_2W pDriverInfo2; HINSTANCE hInst = NULL; BOOL bResult = FALSE; PUMPD pUMPD = NULL; ProxyPort * pp = NULL; KERNEL_PVOID umpdCookie = NULL; BOOL bFreeDriverInfo2 = TRUE; if ((pDriverInfo2 = (PDRIVER_INFO_2W) DuplicateDriverInfo5W(pDriverInfo5)) == NULL) return FALSE; // // Check the list of cached user-mode printer drivers // and see if the requested printer driver is already loaded // ENTERCRITICALSECTION(&semUMPD); if (*ppUMPD = FindUserModePrinterDriver(pDriverInfo5->pDriverPath, pDriverInfo5->dwDriverVersion, TRUE)) { if (gbWOW64) { pUMPD = *ppUMPD; ASSERTGDI(pUMPD->pp, "LoadUserModePrinterDriver NULL proxyport\n"); PROXYPORT proxyport(pUMPD->pp); if (proxyport.bValid()) { umpdCookie = proxyport.LoadDriver(pDriverInfo5, pwstrPrinterName, pdefaults, hPrinter); if (pUMPD->umpdCookie == umpdCookie) { // 64-bit UMPD matches the 32-bit one bResult = TRUE; } else { WARNING("LoadUserModePrinterDriveEx: umpdCookie doesn't match\n"); } } else WARNING("LoadUserModePrinterDriverEx: invalid proxyport\n"); } else { // x86 or native ia64 printing, don't need to anything bResult = TRUE; } } else { // Can't find UMPD, first time load the printer driver if (gbWOW64) { PROXYPORT proxyport(PP_SHAREDSECTION_SIZE); if (pp = proxyport.GetPort()) { umpdCookie = proxyport.LoadDriver(pDriverInfo5, pwstrPrinterName, pdefaults, hPrinter); } } else { hInst = LoadLibraryExW(pDriverInfo2->pDriverPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); } if (hInst || umpdCookie) { if (pUMPD = (PUMPD) LOCALALLOC(sizeof(UMPD))) { ZeroMemory(pUMPD, sizeof(UMPD)); pUMPD->dwSignature = UMPD_SIGNATURE; pUMPD->hInst = hInst; pUMPD->pDriverInfo2 = pDriverInfo2; pUMPD->bArtificialIncrement = TRUE; pUMPD->dwDriverVersion = pDriverInfo5->dwDriverVersion; pUMPD->iRefCount = 1; // aritficial ref count to keep the printer driver around // till the process goes away pUMPD->pp = pp; pUMPD->umpdCookie = umpdCookie; *ppUMPD = pUMPD; bResult = TRUE; bFreeDriverInfo2 = FALSE; // // Add the newly loaded driver to the list of // cached user-mode printer drivers // pUMPD->pNext = gCachedUMPDList; gCachedUMPDList = pUMPD; } } if (!bResult) { if (pp) { PROXYPORT proxyport(pp); if (umpdCookie) proxyport.UnloadDriver(umpdCookie, 0, FALSE); proxyport.Close(); } if (hInst) FreeLibrary(hInst); } } if (bResult) (*ppUMPD)->iRefCount++; LEAVECRITICALSECTION(&semUMPD); if (bFreeDriverInfo2) LOCALFREE(pDriverInfo2); if (!bResult) { WARNING("LoadUserModePrinterDriverEx failed\n"); } return bResult; } BOOL LoadUserModePrinterDriver( HANDLE hPrinter, LPWSTR pwstrPrinterName, PUMPD *ppUMPD, PRINTER_DEFAULTSW *pdefaults ) /*++ Routine Description: Load user-mode printer driver DLL Arguments: hPrinter - Handle to the current printer ppUMPD - Return a pointer to a UMPD structure Return Value: TRUE if successful, FALSE if there is an error If the printer uses a user-mode printer driver, *ppUMPD will be a pointer to a UMPD structure. If the printer uses a kernel-mode printer driver, *ppUMPD will be NULL. --*/ { PDRIVER_INFO_5W pDriverInfo5; BOOL bResult; HMODULE hModule; WCHAR moduleName[256]; *ppUMPD = NULL; // // Get printer driver information // if ((pDriverInfo5 = (PDRIVER_INFO_5W)MyGetPrinterDriver(hPrinter, 5)) == NULL) return FALSE; if (pDriverInfo5->dwDriverAttributes & DRIVER_KERNELMODE) { LOCALFREE(pDriverInfo5); return TRUE; } bResult = LoadUserModePrinterDriverEx(pDriverInfo5, pwstrPrinterName, ppUMPD, pdefaults, hPrinter); LOCALFREE(pDriverInfo5); return bResult; } BOOL UnloadUserModePrinterDriver( PUMPD pUMPD, BOOL bNotifySpooler, HANDLE hPrinter ) /*++ Routine Description: Unload user-mode printer driver module and notify the spooler if necessary Arguments: pUMPD - Pointer to user-mode printer driver information bNotifySpooler - Call into the spooler to notify driver unloading Return Value: TRUE if the driver instance was freed (i.e ref cnt == 0) FALSE otherwise --*/ { PUMPD *ppStartUMPD; ASSERTGDI(VALID_UMPD(pUMPD), "Corrupted UMPD structure.\n"); ASSERTGDI(pUMPD->iRefCount > 0, "Bad UMPD reference count.\n"); if (gbWOW64) { PROXYPORT proxyport(pUMPD->pp); if (proxyport.bValid()) { proxyport.UnloadDriver(pUMPD->umpdCookie, hPrinter, bNotifySpooler); } } ENTERCRITICALSECTION(&semUMPD); if (pUMPD->iRefCount > 0) { pUMPD->iRefCount--; } if (pUMPD->iRefCount != 0 || pUMPD->pHandleList != NULL) { LEAVECRITICALSECTION(&semUMPD); return FALSE; } // Remove the UMPD node from umpd cache list for (ppStartUMPD = &gCachedUMPDList; *ppStartUMPD; ppStartUMPD = &((*ppStartUMPD)->pNext)) { if (*ppStartUMPD == pUMPD) { *ppStartUMPD = pUMPD->pNext; break; } } LEAVECRITICALSECTION(&semUMPD); if (gbWOW64) { PROXYPORT proxyport(pUMPD->pp); if (proxyport.bValid()) proxyport.Close(); } else { PFN pfn = pUMPD->apfn[INDEX_DrvDisableDriver]; if (pfn) { pfn(); } FreeLibrary(pUMPD->hInst); } if (bNotifySpooler && pUMPD->pDriverInfo2->pDriverPath) { (*fpSplDriverUnloadComplete)(pUMPD->pDriverInfo2->pDriverPath); } LOCALFREE(pUMPD->pDriverInfo2); LOCALFREE(pUMPD); return TRUE; } PUMPD UMPDDrvEnableDriver( PWSTR pDriverDllName, ULONG iEngineVersion ) /*++ Routine Description: Client-side stub for DrvEnableDriver Arguments: iDriverDllName - Name of the user-mode printer driver DLL iEngineVersion - Same parameter as that for DrvEnableDriver Return Value: Pointer to the UMPD structure corresponding to the specified driver NULL if there is an error Note: The pointer value returned by this function will be passed back from the kernel-mode side to the user-mode side for each subsequent DDI call. --*/ { PUMPD pUMPD; DRVENABLEDATA ded; ENTERCRITICALSECTION(&semUMPD); // // Find the specified user-mode printer driver // pUMPD = FindUserModePrinterDriver(pDriverDllName, 0, FALSE); if(pUMPD == NULL) { WARNING("failed to find printer driver\n"); return NULL; } if(pUMPD->hInst == NULL) { WARNING("driver library not loaded\n"); return NULL; } ASSERTGDI(pUMPD != NULL, "Non-existent user-mode printer driver.\n"); if (! (pUMPD->dwFlags & UMPDFLAG_DRVENABLEDRIVER_CALLED)) { PFN_DrvEnableDriver pfn; // // If we haven't called DrvEnableDriver for this driver, do it now // if ((pfn = (PFN_DrvEnableDriver) GetProcAddress(pUMPD->hInst, "DrvEnableDriver")) && pfn(iEngineVersion, sizeof(ded), &ded)) { PDRVFN pdrvfn; ULONG count; // // Convert driver entrypoint function table to a more convenient format // for (pdrvfn = ded.pdrvfn, count = ded.c; count--; pdrvfn++) { if (pdrvfn->iFunc < INDEX_LAST) pUMPD->apfn[pdrvfn->iFunc] = pdrvfn->pfn; else { WARNING("Unrecognized DDI entrypoint index.\n"); } } pUMPD->dwFlags |= UMPDFLAG_DRVENABLEDRIVER_CALLED; } else { WARNING("DrvEnableDriver failed.\n"); pUMPD = NULL; } } LEAVECRITICALSECTION(&semUMPD); return pUMPD; } extern "C" int DocumentEventEx( PUMPD pUMPD, HANDLE hPrinter, HDC hdc, int iEsc, ULONG cjIn, PVOID pvIn, ULONG cjOut, PVOID pvOut ) { int iRet; if (WOW64PRINTING(pUMPD)) { PROXYPORT proxyport(pUMPD->pp); iRet = proxyport.DocumentEvent(pUMPD->umpdCookie, hPrinter, hdc, iEsc, cjIn, pvIn, cjOut, pvOut); } else { iRet = (*fpDocumentEvent)(hPrinter, hdc, iEsc, cjIn, pvIn, cjOut, pvOut); } return iRet; } extern "C" DWORD StartDocPrinterWEx( PUMPD pUMPD, HANDLE hPrinter, DWORD level, LPBYTE pDocInfo) { if (WOW64PRINTING(pUMPD) && level == 1) { PROXYPORT proxyport(pUMPD->pp); return proxyport.StartDocPrinterW(pUMPD->umpdCookie,hPrinter, level, pDocInfo); } else return (*fpStartDocPrinterW)(hPrinter, level, pDocInfo); } extern "C" BOOL EndDocPrinterEx(PUMPD pUMPD, HANDLE hPrinter) { if (WOW64PRINTING(pUMPD)) { PROXYPORT proxyport(pUMPD->pp); return proxyport.EndDocPrinter(pUMPD->umpdCookie, hPrinter); } else return (*fpEndDocPrinter)(hPrinter); } extern "C" BOOL StartPagePrinterEx(PUMPD pUMPD, HANDLE hPrinter) { if (WOW64PRINTING(pUMPD)) { PROXYPORT proxyport(pUMPD->pp); return proxyport.StartPagePrinter(pUMPD->umpdCookie, hPrinter); } else return (*fpStartPagePrinter)(hPrinter); } extern "C" BOOL EndPagePrinterEx(PUMPD pUMPD, HANDLE hPrinter) { if (WOW64PRINTING(pUMPD)) { PROXYPORT proxyport(pUMPD->pp); return proxyport.EndPagePrinter(pUMPD->umpdCookie, hPrinter); } else return (*fpEndPagePrinter)(hPrinter); } extern "C" BOOL AbortPrinterEx(PLDC pldc, BOOL bEMF) { if (!bEMF && WOW64PRINTING(pldc->pUMPD)) { PROXYPORT proxyport(pldc->pUMPD->pp); return proxyport.AbortPrinter(pldc->pUMPD->umpdCookie, pldc->hSpooler); } else return (*fpAbortPrinter)(pldc->hSpooler); } extern "C" BOOL ResetPrinterWEx(PLDC pldc, PRINTER_DEFAULTSW *pPtrDef) { BOOL bRet = TRUE; if (WOW64PRINTING(pldc->pUMPD)) { if (!(pldc->fl & LDC_META_PRINT)) { // either RAW printing or // ResetDC called before StartDoc, don't know // whether it is going RAW or EMF yet PROXYPORT proxyport(pldc->pUMPD->pp); bRet = proxyport.ResetPrinterW(pldc->pUMPD->umpdCookie, pldc->hSpooler, pPtrDef); } if (bRet && !(pldc->fl & LDC_PRINT_DIRECT)) { // either EMF printing or // ResetDC called before StartDoc, we need to // call ResetPrinter on both 32-bit and 64-bit // printer handles. bRet = (*fpResetPrinterW)(pldc->hSpooler, pPtrDef); } } else { bRet = (*fpResetPrinterW)(pldc->hSpooler, pPtrDef); } return bRet; } extern "C" BOOL QueryColorProfileEx( PLDC pldc, PDEVMODEW pDevMode, ULONG ulQueryMode, PVOID pvProfileData, ULONG * pcjProfileSize, FLONG * pflProfileFlag) { if (!(pldc->fl & LDC_META_PRINT) && WOW64PRINTING(pldc->pUMPD)) { PROXYPORT proxyport(pldc->pUMPD->pp); return proxyport.QueryColorProfile(pldc->pUMPD->umpdCookie, pldc->hSpooler, pDevMode, ulQueryMode, pvProfileData, pcjProfileSize, pflProfileFlag); } else return (*fpQueryColorProfile)(pldc->hSpooler, pDevMode, ulQueryMode, pvProfileData, pcjProfileSize, pflProfileFlag); } PPORT_MESSAGE PROXYPORT::InitMsg( PPROXYMSG Msg, SERVERPTR pvIn, ULONG cjIn, SERVERPTR pvOut, ULONG cjOut ) { Msg->h.u1.s1.DataLength = (short) (sizeof(*Msg) - sizeof(Msg->h)); Msg->h.u1.s1.TotalLength = (short) (sizeof(*Msg)); Msg->h.u2.ZeroInit = 0; if(pvOut == 0) cjOut = 0; Msg->cjIn = cjIn; Msg->pvIn = pvIn; Msg->cjOut = cjOut; Msg->pvOut = pvOut; return( (PPORT_MESSAGE)Msg ); } BOOL PROXYPORT::CheckMsg( NTSTATUS Status, PPROXYMSG Msg, SERVERPTR pvOut, ULONG cjOut ) { ULONG cbData = Msg->h.u1.s1.DataLength; if (cbData == (sizeof(*Msg) - sizeof(Msg->h))) { if(pvOut != Msg->pvOut) { return(FALSE); } if(cjOut != Msg->cjOut) { return(FALSE); } // do nothing } else { return(FALSE); } return( TRUE ); } NTSTATUS PROXYPORT::SendRequest( SERVERPTR pvIn, ULONG cjIn, SERVERPTR pvOut, ULONG cjOut ) { NTSTATUS Status; PROXYMSG Request; PROXYMSG Reply; InitMsg( &Request, pvIn, cjIn, pvOut, cjOut ); Status = NtRequestWaitReplyPort( pp->PortHandle, (PPORT_MESSAGE)&Request, (PPORT_MESSAGE)&Reply ); if (!NT_SUCCESS( Status )) { return( Status ); } if (Reply.h.u2.s2.Type == LPC_REPLY) { if (!CheckMsg( Status, &Reply, pvOut, cjOut )) { return(STATUS_UNSUCCESSFUL); } } else { return(STATUS_UNSUCCESSFUL); } return( Status ); } #define ALIGN_UMPD_BUFFER(cj) (((cj) + (sizeof(KERNEL_PVOID) -1)) & ~(sizeof(KERNEL_PVOID)-1)) SERVERPTR PROXYPORT::HeapAlloc(ULONG inSize) { KPBYTE ptr; if(pp->ClientMemoryAllocSize + ALIGN_UMPD_BUFFER(inSize) > pp->ClientMemorySize) return 0; ptr = pp->ClientMemoryBase + pp->ClientMemoryAllocSize + pp->ServerMemoryDelta; pp->ClientMemoryAllocSize += ALIGN_UMPD_BUFFER(inSize); return (SERVERPTR) ptr; } PROXYPORT::PROXYPORT(ULONGLONG inMaxSize) { NTSTATUS Status; PORT_VIEW ClientView; ULONG MaxMessageLength; LARGE_INTEGER MaximumSize; UNICODE_STRING PortName; SECURITY_QUALITY_OF_SERVICE DynamicQos; WCHAR awcPortName[MAX_PATH] = {0}; DWORD CurrSessionId; DWORD CurrProcessId = GetCurrentProcessId(); ProcessIdToSessionId(CurrProcessId,&CurrSessionId); wsprintfW(awcPortName, L"%s_%x", L"\\RPC Control\\UmpdProxy", CurrSessionId); DynamicQos.Length = 0; DynamicQos.ImpersonationLevel = SecurityImpersonation; DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; DynamicQos.EffectiveOnly = TRUE; if ((pp = (ProxyPort *) LOCALALLOC(sizeof(ProxyPort))) == NULL || !NT_SUCCESS((NTSTATUS)INITIALIZECRITICALSECTION(&pp->semPort))) { WARNING("PROXYPORT::PROXYPORT mem alloc OR critical section init failed\n"); if (pp) LOCALFREE(pp); pp = NULL; return; } pp->ClientMemoryBase = 0; pp->ClientMemorySize = 0; pp->ClientMemoryAllocSize = 0; pp->PortHandle = NULL; pp->ServerMemoryBase = 0; pp->ServerMemoryDelta = 0; Status = STATUS_SUCCESS; RtlInitUnicodeString( &PortName, awcPortName ); MaximumSize.QuadPart = inMaxSize; ZeroMemory(&ClientView.SectionHandle, sizeof(ClientView.SectionHandle)); Status = NtCreateSection( (PHANDLE)&ClientView.SectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL, &MaximumSize, PAGE_READWRITE, SEC_COMMIT, NULL ); if (NT_SUCCESS( Status )) { ClientView.Length = sizeof( ClientView ); ClientView.SectionOffset = 0; ClientView.ViewSize = (LPC_SIZE_T) inMaxSize; ClientView.ViewBase = 0; ClientView.ViewRemoteBase = 0; if (BLOADSPOOLER && (*fpLoadSplWow64)(NULL) == ERROR_SUCCESS) { Status = NtConnectPort( &pp->PortHandle, &PortName, &DynamicQos, &ClientView, NULL, (PULONG)&MaxMessageLength, NULL, 0 ); if (NT_SUCCESS( Status )) { pp->SectionHandle = (HANDLE)ClientView.SectionHandle; pp->ClientMemoryBase = (KPBYTE)ClientView.ViewBase; pp->ClientMemorySize = (SIZE_T)inMaxSize; pp->ServerMemoryBase = (KPBYTE)ClientView.ViewRemoteBase; pp->ServerMemoryDelta = pp->ServerMemoryBase - pp->ClientMemoryBase; NtRegisterThreadTerminatePort(pp->PortHandle); } else { WARNING("PROXYPORT::PROXYPORT: NtConnectPort failed\n"); } } else { Status = STATUS_UNSUCCESSFUL; WARNING("PROXYPORT::PROXYPORT failed to load spooler or splwow64\n"); } } else { WARNING("PROXYPORT::PROXYPORT: failed to create section\n"); } if(!NT_SUCCESS( Status )) { if ((HANDLE)ClientView.SectionHandle) { NtClose((HANDLE)ClientView.SectionHandle); } DELETECRITICALSECTION(&pp->semPort); LOCALFREE(pp); pp = NULL; } else { // grab port access ENTERCRITICALSECTION(&pp->semPort); } } void PROXYPORT::Close() { if (pp->SectionHandle) { if (!CloseHandle(pp->SectionHandle)) { WARNING("PROXYPORT::Close failed to close the section handle\n"); } } if (pp->PortHandle != NULL) { if (!CloseHandle( pp->PortHandle )) { WARNING("PROXYPORT::Close failed to close the port handle\n"); } } LEAVECRITICALSECTION(&pp->semPort); DELETECRITICALSECTION(&pp->semPort); LOCALFREE(pp); pp = NULL; } void vUMPDWow64Shutdown() { PUMPD pUMPD = gCachedUMPDList; while(pUMPD) { if (pUMPD->pp) { PROXYPORT proxyPort(pUMPD->pp); proxyPort.Shutdown(); } pUMPD = pUMPD->pNext; } } BOOL PROXYPORT::ThunkMemBlock( KPBYTE * ptr, ULONG size) { BOOL bRet = TRUE; if (*ptr) { SERVERPTR sp = HeapAlloc(size); CLIENTPTR cp = ServerToClientPtr(sp); if (cp) { RtlCopyMemory((PVOID)cp, (PVOID)*ptr, size); *ptr = sp; } else bRet = FALSE; } return bRet; } BOOL PROXYPORT::ThunkStr(LPWSTR * ioLpstr) { BOOL bRet = TRUE; if(*ioLpstr != NULL) { bRet = ThunkMemBlock((KPBYTE *) ioLpstr, (wcslen(*ioLpstr) + 1) * sizeof(WCHAR)); } return bRet; } KERNEL_PVOID PROXYPORT::LoadDriver( PDRIVER_INFO_5W pDriverInfo, LPWSTR pwstrPrinterName, PRINTER_DEFAULTSW* pdefaults, HANDLE hPrinter32 ) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; LOADDRIVERINPUT* pInput; KERNEL_PVOID umpdCookie = NULL; HeapInit(); if (!(spInput = HeapAlloc(sizeof(LOADDRIVERINPUT))) || !(spOutput = HeapAlloc(sizeof(KERNEL_PVOID)))) return NULL; pInput = (LOADDRIVERINPUT *) ServerToClientPtr(spInput); pInput->driverInfo.cVersion = pDriverInfo->cVersion; pInput->driverInfo.pName = (KLPWSTR)pDriverInfo->pName; pInput->driverInfo.pEnvironment = (KLPWSTR)pDriverInfo->pEnvironment; pInput->driverInfo.pDriverPath = (KLPWSTR)pDriverInfo->pDriverPath; pInput->driverInfo.pDataFile = (KLPWSTR)pDriverInfo->pDataFile; pInput->driverInfo.pConfigFile = (KLPWSTR)pDriverInfo->pConfigFile; pInput->driverInfo.dwDriverAttributes = pDriverInfo->dwDriverAttributes; pInput->driverInfo.dwConfigVersion = pDriverInfo->dwConfigVersion; pInput->driverInfo.dwDriverVersion = pDriverInfo->dwDriverVersion; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); pInput->pPrinterName = (KLPWSTR)pwstrPrinterName; pInput->defaults.pDatatype = (KLPWSTR)pdefaults->pDatatype; pInput->defaults.pDevMode = (KPBYTE)pdefaults->pDevMode; pInput->defaults.DesiredAccess = pdefaults->DesiredAccess; if (!ThunkStr((LPWSTR *)&pInput->driverInfo.pName) || !ThunkStr((LPWSTR *)&pInput->driverInfo.pEnvironment) || !ThunkStr((LPWSTR *)&pInput->driverInfo.pDriverPath) || !ThunkStr((LPWSTR *)&pInput->driverInfo.pDataFile) || !ThunkStr((LPWSTR *)&pInput->driverInfo.pConfigFile) || !ThunkStr((LPWSTR *)&pInput->pPrinterName) || !ThunkStr((LPWSTR *)&pInput->defaults.pDatatype) || !ThunkMemBlock(&pInput->defaults.pDevMode, sizeof(DEVMODEW)) ) return NULL; pInput->umpdthdr.umthdr.ulType = INDEX_LoadUMPrinterDrv; Status = SendRequest(spInput, sizeof(LOADDRIVERINPUT), spOutput, sizeof(KERNEL_PVOID)); if (NT_SUCCESS( Status )) { umpdCookie = *((KERNEL_PVOID *) ServerToClientPtr(spOutput)); } return umpdCookie; } void PROXYPORT::UnloadDriver( KERNEL_PVOID umpdCookie, HANDLE hPrinter32, BOOL bNotifySpooler ) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; UNLOADDRIVERINPUT * pInput; HeapInit(); if (spInput = HeapAlloc(sizeof(UNLOADDRIVERINPUT))) { pInput = (UNLOADDRIVERINPUT *) ServerToClientPtr(spInput); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); pInput->bNotifySpooler = bNotifySpooler; pInput->umpdthdr.umthdr.ulType = INDEX_UnloadUMPrinterDrv; Status = SendRequest(spInput, sizeof(spInput), 0, 0); } } int PROXYPORT::DocumentEvent( KERNEL_PVOID umpdCookie, HANDLE hPrinter32, HDC hdc, INT iEsc, ULONG cjIn, PVOID pvIn, ULONG cjOut, PVOID pvOut ) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; DOCUMENTEVENTINPUT* pInput; CLIENTPTR cppvIn = NULL; INT iRet = DOCUMENTEVENT_FAILURE; ULONG cjAlloc = 0; DEVMODEW *pdmCopy = NULL; HeapInit(); if (!(spInput = HeapAlloc(sizeof(DOCUMENTEVENTINPUT))) || !(pInput = (DOCUMENTEVENTINPUT *) ServerToClientPtr(spInput)) || !(spOutput = HeapAlloc(sizeof(int)))) { return DOCUMENTEVENT_FAILURE; } pInput->umpdthdr.umthdr.ulType = INDEX_DocumentEvent; pInput->umpdthdr.umthdr.cjSize = sizeof(*pInput); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); pInput->hdc = (KHDC)hdc; pInput->iEsc = iEsc; pInput->cjIn = cjIn; pInput->pvIn = NULL; pInput->cjOut = cjOut; pInput->pvOut = NULL; pInput->pdmCopy = NULL; // thunk input data if (cjIn && pvIn) { if (iEsc == DOCUMENTEVENT_CREATEDCPRE) { cjAlloc = sizeof(DOCEVENT_CREATEDCPRE_UMPD); } else if (iEsc == DOCUMENTEVENT_CREATEDCPOST || iEsc == DOCUMENTEVENT_RESETDCPRE || iEsc == DOCUMENTEVENT_RESETDCPOST || iEsc == DOCUMENTEVENT_STARTDOCPRE) { cjAlloc = sizeof(KPBYTE); } else if (iEsc == DOCUMENTEVENT_ESCAPE) { cjAlloc = sizeof(DOCEVENT_ESCAPE_UMPD); } // allocate heap if (cjAlloc) { if (!(pInput->pvIn = HeapAlloc(cjAlloc))) { return DOCUMENTEVENT_FAILURE; } pInput->cjIn = cjAlloc; cppvIn = ServerToClientPtr(pInput->pvIn); } switch (iEsc) { case DOCUMENTEVENT_CREATEDCPRE: { DOCEVENT_CREATEDCPRE_UMPD *pDocEvent = (DOCEVENT_CREATEDCPRE_UMPD*)cppvIn; DOCEVENT_CREATEDCPRE *pDocEventIn = (DOCEVENT_CREATEDCPRE*)pvIn; pDocEvent->pszDriver = (KPBYTE)0; pDocEvent->pszDevice = (KPBYTE)pDocEventIn->pszDevice; pDocEvent->pdm = (KPBYTE)pDocEventIn->pdm; pDocEvent->bIC = pDocEventIn->bIC; if (!ThunkStr((LPWSTR*)&pDocEvent->pszDevice) || !ThunkMemBlock(&pDocEvent->pdm, sizeof(DEVMODEW))) { return DOCUMENTEVENT_FAILURE; } } break; case DOCUMENTEVENT_RESETDCPRE: { *((KPBYTE*)cppvIn) = (KPBYTE)(*((DEVMODEW**)pvIn)); if (!ThunkMemBlock((KPBYTE*)cppvIn, sizeof(DEVMODEW))) return DOCUMENTEVENT_FAILURE; } break; case DOCUMENTEVENT_STARTDOCPRE: { SERVERPTR spTemp; DOCINFOW_UMPD *pDocInfo; DOCINFOW *pDocInfoIn = *((DOCINFOW**)pvIn); if (!(spTemp = HeapAlloc(sizeof(DOCINFOW_UMPD)))) return DOCUMENTEVENT_FAILURE; *((KPBYTE*)cppvIn) = spTemp; pDocInfo = (DOCINFOW_UMPD*)ServerToClientPtr(spTemp); pDocInfo->cbSize = pDocInfoIn->cbSize; pDocInfo->lpszDocName = (KLPWSTR)pDocInfoIn->lpszDocName; pDocInfo->lpszOutput = (KLPWSTR)pDocInfoIn->lpszOutput; pDocInfo->lpszDatatype = (KLPWSTR)pDocInfoIn->lpszDatatype; pDocInfo->fwType = pDocInfoIn->fwType; if (!ThunkStr((LPWSTR*)&pDocInfo->lpszDocName) || !ThunkStr((LPWSTR*)&pDocInfo->lpszOutput) || !ThunkStr((LPWSTR*)&pDocInfo->lpszDatatype)) { return DOCUMENTEVENT_FAILURE; } } break; case DOCUMENTEVENT_CREATEDCPOST: case DOCUMENTEVENT_RESETDCPOST: { if (*((PDEVMODEW *)pvIn)) { KPBYTE *ppdmDrv = (KPBYTE*)(*((PBYTE*)pvIn) + sizeof(DEVMODEW)); *((KPBYTE*)cppvIn) = *ppdmDrv; LOCALFREE(*(PBYTE*)pvIn); } else { *((KPBYTE*)cppvIn) = NULL; } } break; case DOCUMENTEVENT_STARTDOCPOST: { pInput->pvIn = (KPBYTE)pvIn; if (!ThunkMemBlock(&pInput->pvIn, sizeof(LONG))) return DOCUMENTEVENT_FAILURE; } break; case DOCUMENTEVENT_ESCAPE: { DOCEVENT_ESCAPE_UMPD *pEscape = (DOCEVENT_ESCAPE_UMPD*)cppvIn; DOCEVENT_ESCAPE *pEscapeIn = (DOCEVENT_ESCAPE*)pvIn; pEscape->iEscape = pEscapeIn->iEscape; pEscape->cjInput = pEscapeIn->cjInput; pEscape->pvInData = (KPBYTE)pEscapeIn->pvInData; if (!ThunkMemBlock(&pEscape->pvInData, (ULONG)pEscapeIn->cjInput)) return DOCUMENTEVENT_FAILURE; } break; default: return DOCUMENTEVENT_FAILURE; } } if (cjOut && pvOut) { if (iEsc == DOCUMENTEVENT_CREATEDCPRE || iEsc == DOCUMENTEVENT_RESETDCPRE) { if (!(pInput->pvOut = HeapAlloc(sizeof(KPBYTE))) || !(pInput->pdmCopy = HeapAlloc(sizeof(DEVMODEW))) || !(pdmCopy = (DEVMODEW*)LOCALALLOC(sizeof(DEVMODEW) + sizeof(KPBYTE)))) { return DOCUMENTEVENT_FAILURE; } *((KPBYTE*)ServerToClientPtr(pInput->pvOut)) = 0; pInput->cjOut = sizeof(KPBYTE); } else if (iEsc == DOCUMENTEVENT_ESCAPE) { if (!(pInput->pvOut = HeapAlloc(cjOut))) return DOCUMENTEVENT_FAILURE; } } Status = SendRequest(spInput, sizeof(DOCUMENTEVENTINPUT), spOutput, sizeof(int)); if (NT_SUCCESS( Status )) { iRet = *((int *) ServerToClientPtr(spOutput)); if (iRet != DOCUMENTEVENT_FAILURE) { if (iEsc == DOCUMENTEVENT_CREATEDCPRE || iEsc == DOCUMENTEVENT_RESETDCPRE) { PDEVMODEW *ppvOut = (PDEVMODEW*)pvOut; KPBYTE kpdm = *((KPBYTE*)ServerToClientPtr(pInput->pvOut)); ASSERTGDI(pvOut, "ProxyPort::DocumentEvent pvOut NULL\n"); *ppvOut = kpdm ? pdmCopy : NULL; if (kpdm) { RtlCopyMemory(pdmCopy, (PVOID)ServerToClientPtr(pInput->pdmCopy), sizeof(DEVMODEW)); KPBYTE *ppdmDrv = (KPBYTE*)((PBYTE)pdmCopy + sizeof(DEVMODEW)); *ppdmDrv = kpdm; } else { LOCALFREE(pdmCopy); } } else if (iEsc == DOCUMENTEVENT_ESCAPE) { if (cjOut && pvOut) RtlCopyMemory(pvOut, (PVOID)ServerToClientPtr(pInput->pvOut), cjOut); } } else { WARNING("DocumentEvent failed \n"); } } if (iRet == DOCUMENTEVENT_FAILURE && pdmCopy) { LOCALFREE(pdmCopy); } return iRet; } DWORD PROXYPORT::StartDocPrinterW( KERNEL_PVOID umpdCookie, HANDLE hPrinter32, DWORD level, LPBYTE pDocInfo ) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; STARTDOCPRINTERWINPUT* pInput; CLIENTPTR cppDocInfo; HeapInit(); if (!(spInput = HeapAlloc(sizeof(STARTDOCPRINTERWINPUT))) || !(spOutput = HeapAlloc(sizeof(DWORD)))) return 0; pInput = (STARTDOCPRINTERWINPUT *) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_StartDocPrinterW; pInput->umpdthdr.umthdr.cjSize = sizeof(*pInput); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); pInput->level = level; pInput->lastError = 0; pInput->docInfo.pDocName = (KLPWSTR)((DOC_INFO_1W*)pDocInfo)->pDocName; pInput->docInfo.pOutputFile = (KLPWSTR)((DOC_INFO_1W*)pDocInfo)->pOutputFile; pInput->docInfo.pDatatype = (KLPWSTR)((DOC_INFO_1W*)pDocInfo)->pDatatype; // GDI only uses level 1 and level 3 if (level == 3) pInput->docInfo.dwFlags = ((DOC_INFO_3W*)pDocInfo)->dwFlags; else pInput->docInfo.dwFlags = 0; if (!ThunkStr((LPWSTR *)&pInput->docInfo.pDocName) || !ThunkStr((LPWSTR *)&pInput->docInfo.pOutputFile) || !ThunkStr((LPWSTR *)&pInput->docInfo.pDatatype)) return 0; Status = SendRequest(spInput, sizeof(STARTDOCPRINTERWINPUT), spOutput, sizeof(DWORD)); if (!NT_SUCCESS( Status )) return 0; else { if (pInput->lastError) GdiSetLastError(pInput->lastError); return (*((DWORD *) ServerToClientPtr(spOutput))); } } BOOL PROXYPORT::StartPagePrinter(KERNEL_PVOID umpdCookie, HANDLE hPrinter32) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; UMPDSIMPLEINPUT* pInput; HeapInit(); if (!(spInput = HeapAlloc(sizeof(UMPDSIMPLEINPUT))) || !(spOutput = HeapAlloc(sizeof(BOOL)))) return FALSE; pInput = (UMPDSIMPLEINPUT *) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_StartPagePrinter; pInput->umpdthdr.umthdr.cjSize = sizeof(UMPDSIMPLEINPUT); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); Status = SendRequest(spInput, sizeof(UMPDSIMPLEINPUT), spOutput, sizeof(BOOL)); if (!NT_SUCCESS( Status )) return FALSE; return (*((BOOL*)ServerToClientPtr(spOutput))); } BOOL PROXYPORT::EndPagePrinter(KERNEL_PVOID umpdCookie, HANDLE hPrinter32) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; UMPDSIMPLEINPUT* pInput; HeapInit(); if (!(spInput = HeapAlloc(sizeof(UMPDSIMPLEINPUT))) || !(spOutput = HeapAlloc(sizeof(BOOL)))) return FALSE; pInput = (UMPDSIMPLEINPUT *) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_EndPagePrinter; pInput->umpdthdr.umthdr.cjSize = sizeof(UMPDSIMPLEINPUT); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); Status = SendRequest(spInput, sizeof(UMPDSIMPLEINPUT), spOutput, sizeof(BOOL)); if (!NT_SUCCESS( Status )) return FALSE; return (*((BOOL*)ServerToClientPtr(spOutput))); } BOOL PROXYPORT::EndDocPrinter(KERNEL_PVOID umpdCookie, HANDLE hPrinter32) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; UMPDSIMPLEINPUT* pInput; HeapInit(); if (!(spInput = HeapAlloc(sizeof(UMPDSIMPLEINPUT))) || !(spOutput = HeapAlloc(sizeof(BOOL)))) return FALSE; pInput = (UMPDSIMPLEINPUT *) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_EndDocPrinter; pInput->umpdthdr.umthdr.cjSize = sizeof(UMPDSIMPLEINPUT); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); Status = SendRequest(spInput, sizeof(UMPDSIMPLEINPUT), spOutput, sizeof(BOOL)); if (!NT_SUCCESS( Status )) return FALSE; return (*((BOOL*)ServerToClientPtr(spOutput))); } BOOL PROXYPORT::AbortPrinter(KERNEL_PVOID umpdCookie, HANDLE hPrinter32) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; UMPDSIMPLEINPUT* pInput; HeapInit(); if (!(spInput = HeapAlloc(sizeof(UMPDSIMPLEINPUT))) || !(spOutput = HeapAlloc(sizeof(BOOL)))) return FALSE; pInput = (UMPDSIMPLEINPUT *) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_AbortPrinter; pInput->umpdthdr.umthdr.cjSize = sizeof(UMPDSIMPLEINPUT); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); Status = SendRequest(spInput, sizeof(UMPDSIMPLEINPUT), spOutput, sizeof(BOOL)); if (!NT_SUCCESS( Status )) return FALSE; return (*((BOOL*)ServerToClientPtr(spOutput))); } BOOL PROXYPORT::ResetPrinterW(KERNEL_PVOID umpdCookie, HANDLE hPrinter32, PRINTER_DEFAULTSW *pPtrDef) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; RESETPRINTERWINPUT* pInput; HeapInit(); if (!(spInput = HeapAlloc(sizeof(RESETPRINTERWINPUT))) || !(spOutput = HeapAlloc(sizeof(BOOL)))) return FALSE; pInput = (RESETPRINTERWINPUT *) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_ResetPrinterW; pInput->umpdthdr.umthdr.cjSize = sizeof(RESETPRINTERWINPUT); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); pInput->ptrDef.pDatatype = (KLPWSTR)pPtrDef->pDatatype; pInput->ptrDef.pDevMode = (KPBYTE)pPtrDef->pDevMode; pInput->ptrDef.DesiredAccess = pPtrDef->DesiredAccess; if (!ThunkStr((LPWSTR *)&pInput->ptrDef.pDatatype) || !ThunkMemBlock(&pInput->ptrDef.pDevMode, sizeof(DEVMODEW))) return FALSE; Status = SendRequest(spInput, sizeof(RESETPRINTERWINPUT), spOutput, sizeof(BOOL)); if (!NT_SUCCESS( Status )) return FALSE; return (*((BOOL*)ServerToClientPtr(spOutput))); } BOOL PROXYPORT::QueryColorProfile( KERNEL_PVOID umpdCookie, HANDLE hPrinter32, PDEVMODEW pDevMode, ULONG ulQueryMode, PVOID pvProfileData, ULONG* pcjProfileSize, FLONG* pflProfileFlag ) { NTSTATUS Status = STATUS_SUCCESS; SERVERPTR spInput; SERVERPTR spOutput; QUERYCOLORPROFILEINPUT* pInput; HeapInit(); if (!(spInput = HeapAlloc(sizeof(QUERYCOLORPROFILEINPUT))) || !(spOutput = HeapAlloc(sizeof(BOOL)))) return -1; pInput = (QUERYCOLORPROFILEINPUT*) ServerToClientPtr(spInput); pInput->umpdthdr.umthdr.ulType = INDEX_QueryColorProfile; pInput->umpdthdr.umthdr.cjSize = sizeof(QUERYCOLORPROFILEINPUT); pInput->umpdCookie = umpdCookie; pInput->clientPid = GetCurrentProcessId(); pInput->hPrinter32 = HandleToUlong(hPrinter32); pInput->pDevMode = pDevMode; pInput->ulQueryMode = ulQueryMode; pInput->cjProfileSize = *pcjProfileSize; pInput->flProfileFlag = *pflProfileFlag; pInput->lastError = 0; if (!(pInput->pvProfileData = HeapAlloc(*pcjProfileSize)) || !ThunkMemBlock((KPBYTE*)&pInput->pDevMode, sizeof(DEVMODEW))) return -1; Status = SendRequest(spInput, sizeof(QUERYCOLORPROFILEINPUT), spOutput, sizeof(BOOL)); if (!NT_SUCCESS( Status )) return -1; else { if (pInput->lastError) GdiSetLastError(pInput->lastError); return (*((BOOL*)ServerToClientPtr(spOutput))); } }