|
|
/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name:
w32draut
Abstract:
This module defines a special subclass of the Win32 client-side RDP printer redirection "device" class. The subclass, W32DrAutoPrn manages a queue that is automatically discovered by the client via enumerating client-side printer queues.
Author:
Tad Brockway 3/23/99
Revision History:
--*/
#include <precom.h>
#define TRC_FILE "W32DrAut"
#include "regapi.h"
#include "w32draut.h"
#include "proc.h"
#include "drdbg.h"
#include "w32utl.h"
#include "utl.h"
///////////////////////////////////////////////////////////////
//
// Defines
//
#define COM_PORT_NAME _T("COM")
#define COM_PORT_NAMELEN 3
#define LPT_PORT_NAME _T("LPT")
#define LPT_PORT_NAMELEN 3
#define USB_PORT_NAME _T("USB")
#define USB_PORT_NAMELEN 3
#define RDP_PORT_NAME _T("TS")
#define RDP_PORT_NAMELEN 2
///////////////////////////////////////////////////////////////
//
// W32DrAutoPrn Members
//
W32DrAutoPrn::W32DrAutoPrn( IN ProcObj *processObject, IN const DRSTRING printerName, IN const DRSTRING driverName, IN const DRSTRING portName, IN BOOL isDefault, IN ULONG deviceID, IN const TCHAR *devicePath ) : W32DrPRN(processObject, printerName, driverName, portName, NULL, isDefault, deviceID, devicePath) /*++
Routine Description:
Constructor
Arguments:
processObject - Associated Process Object. printerName - Name of automatic printer queue. driverName - Print Driver Name portName - Client Port Name isDefault - Is this the default printer? deviceID - Unique Device Identifier devicePath - Path to pass to OpenPrinter when opening the device.
Return Value:
NA
--*/ { DC_BEGIN_FN("W32DrAutoPrn"); _jobID = 0; _printerHandle = INVALID_HANDLE_VALUE; memset(_szLocalPrintingDocName, 0, sizeof(_szLocalPrintingDocName));
ASSERT(processObject); PRDPDR_DATA prdpdrData = processObject->GetVCMgr().GetInitData(); ASSERT(prdpdrData);
LPTSTR szDocName = prdpdrData->szLocalPrintingDocName; ASSERT(szDocName); ASSERT(szDocName[0] != 0); _tcsncpy(_szLocalPrintingDocName, szDocName, sizeof(_szLocalPrintingDocName)/sizeof(TCHAR));
OSVERSIONINFO osVersion; osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); _bRunningOn9x = TRUE; if (GetVersionEx(&osVersion)) { if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) { _bRunningOn9x = FALSE; } } else { TRC_ERR((TB, _T("GetVersionEx: %08X"), GetLastError())); } DC_END_FN(); }
W32DrAutoPrn::~W32DrAutoPrn() /*++
Routine Description:
Destructor
Arguments:
NA
Return Value:
NA
--*/ { //
// Make sure all docs are finished and the printer handle closed.
//
ClosePrinter(); }
BOOL W32DrAutoPrn::ShouldAddThisPrinter( DWORD queueFilter, DWORD userSessionID, PPRINTERINFO pPrinterInfo, DWORD printerSessionID ) /*++
Routine Description:
Detemine if we should redirect this printer.
Arguments:
queueFilter - redirect printer filter type. userSessionID - current user session ID. pPrinterInfo - printer info printerSessionID - Printer session ID or INVALID_SESSIONID if printers is not a TS printer.
Return Value:
TRUE to add printer, FALSE otherwise
--*/ { BOOL fAddThisPrinter = FALSE; DWORD sessionID;
DC_BEGIN_FN("W32DrAutoPrn::AddThisPrinter");
//
// Check filters.
//
if (queueFilter == FILTER_ALL_QUEUES ) { fAddThisPrinter = TRUE; } else if ((queueFilter & FILTER_NET_QUEUES) && (pPrinterInfo->Attributes & PRINTER_ATTRIBUTE_NETWORK)) { fAddThisPrinter = TRUE; }
//
// If it's a non-network printer then get the port name.
//
else if((queueFilter & FILTER_LPT_QUEUES) && (_tcsnicmp( pPrinterInfo->pPortName, LPT_PORT_NAME, LPT_PORT_NAMELEN) == 0) ) { fAddThisPrinter = TRUE; } else if ((queueFilter & FILTER_COM_QUEUES) && (_tcsnicmp( pPrinterInfo->pPortName, COM_PORT_NAME, COM_PORT_NAMELEN) == 0) ) { fAddThisPrinter = TRUE; } else if ((queueFilter & FILTER_USB_QUEUES) && (_tcsnicmp( pPrinterInfo->pPortName, USB_PORT_NAME, USB_PORT_NAMELEN) == 0) ) { fAddThisPrinter = TRUE; } else if ((queueFilter & FILTER_RDP_QUEUES) && (_tcsnicmp( pPrinterInfo->pPortName, RDP_PORT_NAME, RDP_PORT_NAMELEN) == 0) ) { fAddThisPrinter = TRUE; }
if ((TRUE == fAddThisPrinter) && (userSessionID != INVALID_SESSIONID) && (printerSessionID != INVALID_SESSIONID)) {
//
// Compare this with our session ID
//
if( printerSessionID != userSessionID ) { // this printer is from different session,
// don't redirect it.
fAddThisPrinter = FALSE; } }
DC_END_FN(); return fAddThisPrinter; }
LPTSTR W32DrAutoPrn::GetRDPDefaultPrinter() /*++
Routine Description:
Get the printer name of the default printer.
This function allocates memory and returns a pointer to the allocated string, if successful. Otherwise, it returns NULL.
Arguments:
NA
Return Value:
The default printer name. The caller has to free the memory.
--*/ { TCHAR* szIniEntry = NULL; LPTSTR pPrinterName = NULL;
DC_BEGIN_FN("DrPRN::GetRDPDefaultPrinter");
szIniEntry = new TCHAR[ MAX_DEF_PRINTER_ENTRY ];
if( NULL == szIniEntry ) { TRC_ERR((TB, _T("Memory allocation failed:%ld."), GetLastError())); goto Cleanup; }
szIniEntry[0] = _T('\0');
//
// Get the default printer key from the win.ini file.
//
DWORD dwResult = GetProfileString(_T("windows"), _T("device"), _T(""), szIniEntry, MAX_DEF_PRINTER_ENTRY); if (dwResult && szIniEntry[0]) { //
// Get the printer name. The device value is of the form
// <printer name>,<driver name>,<port>
//
TCHAR *pComma = _tcschr( szIniEntry, _T(',')); if( pComma ) {
*pComma = _T('\0'); UINT cchLen = _tcslen( szIniEntry ) + 1;
pPrinterName = new TCHAR [cchLen]; if (pPrinterName) { StringCchCopy(pPrinterName, cchLen, szIniEntry); TRC_NRM((TB, _T("Def. is: %s"), pPrinterName)); } else { TRC_ERR((TB, _T("Memory allocation failed:%ld."), GetLastError())); } } else { TRC_ERR((TB, _T("Invalid device entry in win.ini."))); } } else { TRC_NRM((TB, _T("Device entry not found in win.ini."))); } Cleanup: DC_END_FN(); delete[] szIniEntry; return pPrinterName; }
DWORD W32DrAutoPrn::GetPrinterInfoAndSessionID( IN ProcObj *procObj, IN LPTSTR printerName, IN DWORD printerAttribs, IN OUT BYTE **pPrinterInfoBuf, IN OUT DWORD *pPrinterInfoBufSize, OUT DWORD *sessionID, OUT PPRINTERINFO printerInfo ) /*++
Routine Description:
Get printer info for a printer and its corresponding TS session ID, if it exists.
Arguments:
procObj - Active Proc Obj printerName - Name of printer. printerAttribs - Printer Attribs pPrinterInfoBuf - This function may resize this buffer. pPrinterInfoBufsSize - Current size of the pPrinterInfo2 buf. sessionID - TS session ID, if applicable. Otherwise INVALID_SESSIONID. printerInfo - Printer information is returned here. We avoid pulling in level 2 info if possible. Fields in this data structure should not be free'd.
Return Value:
ERROR_SUCCESS on success. Otherwise, an error status is returned.
--*/ { HANDLE hPrinter = NULL; DWORD status = ERROR_SUCCESS; DWORD bytesRequired; DWORD type; DWORD cbNeeded; DWORD ret;
DC_BEGIN_FN("W32DrAutoPrn::GetPrinterInfoAndSessionID");
//
// No unicode wrappers for GetPrinter on 9x.
//
ASSERT(!procObj->Is9x());
//
// Get a printer handle
//
if (!OpenPrinter(printerName, &hPrinter, NULL)) { status = GetLastError(); TRC_ALT((TB, L"OpenPrinter: %ld", status)); goto CLEANUPANDEXIT; }
//
// Check the proc obj shutdown state since we just left an RPC call.
//
if (procObj->IsShuttingDown()) { status = ERROR_SHUTDOWN_IN_PROGRESS; TRC_ALT((TB, _T("Bailing out of printer enumeration because of shutdown."))); goto CLEANUPANDEXIT; }
//
// If the printer is a network printer then we try to avoid hitting the
// network for additional info. For a non-network printer, we need info level 2
// for info about the port.
//
if (printerAttribs & PRINTER_ATTRIBUTE_NETWORK) {
//
// Just need the driver name.
//
if (!GetPrinterDriver(hPrinter, NULL, 1, *pPrinterInfoBuf, *pPrinterInfoBufSize, &bytesRequired)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { *pPrinterInfoBuf = (PBYTE)new BYTE[bytesRequired]; if (*pPrinterInfoBuf == NULL) { TRC_ERR((TB, L"Failed to allocate printer driver info")); *pPrinterInfoBufSize = 0; status = ERROR_INSUFFICIENT_BUFFER; goto CLEANUPANDEXIT; } else { *pPrinterInfoBufSize = bytesRequired; } }
if (!GetPrinterDriver(hPrinter, NULL, 1, *pPrinterInfoBuf, *pPrinterInfoBufSize, &bytesRequired)) { status = GetLastError(); TRC_ERR((TB, _T("GetPrinter: %08X"), status)); goto CLEANUPANDEXIT; } } PDRIVER_INFO_1 p1 = (PDRIVER_INFO_1)*pPrinterInfoBuf; printerInfo->pPrinterName = printerName; printerInfo->pPortName = NULL; printerInfo->pDriverName = p1->pName; printerInfo->Attributes = printerAttribs; } else {
if (!GetPrinter(hPrinter, 2, *pPrinterInfoBuf, *pPrinterInfoBufSize, &bytesRequired)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { *pPrinterInfoBuf = (PBYTE)new BYTE[bytesRequired]; if (*pPrinterInfoBuf == NULL) { TRC_ERR((TB, L"Failed to allocate printer info 2")); *pPrinterInfoBufSize = 0; status = ERROR_INSUFFICIENT_BUFFER; goto CLEANUPANDEXIT; } else { *pPrinterInfoBufSize = bytesRequired; }
if (!GetPrinter(hPrinter, 2, *pPrinterInfoBuf, *pPrinterInfoBufSize, &bytesRequired)) { status = GetLastError(); TRC_ERR((TB, _T("GetPrinter: %08X"), status)); goto CLEANUPANDEXIT; } } else { status = GetLastError(); TRC_ERR((TB, _T("GetPrinter: %08X"), status)); goto CLEANUPANDEXIT; } } PPRINTER_INFO_2 p2 = (PPRINTER_INFO_2)*pPrinterInfoBuf; printerInfo->pPrinterName = p2->pPrinterName; printerInfo->pPortName = p2->pPortName; printerInfo->pDriverName = p2->pDriverName; printerInfo->Attributes = p2->Attributes; }
//
// Check the proc obj shutdown state since we just left an RPC call.
//
if (procObj->IsShuttingDown()) { status = ERROR_SHUTDOWN_IN_PROGRESS; TRC_ALT((TB, _T("Bailing out of printer enumeration because of shutdown."))); goto CLEANUPANDEXIT; }
//
// Get the session ID, if it exists.
//
ret = GetPrinterData( hPrinter, DEVICERDR_SESSIONID, &type, (PBYTE)sessionID, sizeof(DWORD), &cbNeeded ); if (ret != ERROR_SUCCESS || type != REG_DWORD) { *sessionID = INVALID_SESSIONID; }
CLEANUPANDEXIT:
if (hPrinter != NULL) { ::ClosePrinter(hPrinter); }
DC_END_FN(); return status; }
DWORD W32DrAutoPrn::Enumerate( IN ProcObj *procObj, IN DrDeviceMgr *deviceMgr ) { ULONG ulBufSizeNeeded; ULONG ulNumStructs; ULONG i; LPTSTR szDefaultPrinter = NULL; PRINTER_INFO_4 *pPrinterInfo4Buf = NULL; DWORD pPrinterInfo4BufSize = 0; PBYTE pPrinterInfoBuf = NULL; DWORD pPrinterInfoBufSize = 0; W32DrPRN *prnDevice = NULL; DWORD result = ERROR_SUCCESS; DWORD queueFilter; LPTSTR friendlyName = NULL; LPTSTR pName; DWORD userSessionID; DWORD printerSessionID; BOOL ret; PRINTERINFO currentPrinter; HRESULT hr;
DC_BEGIN_FN("W32DrAutoPrn::Enumerate");
if(!procObj->GetVCMgr().GetInitData()->fEnableRedirectPrinters) { TRC_DBG((TB,_T("Printer redirection disabled, bailing out"))); return ERROR_SUCCESS; }
queueFilter = GetPrinterFilterMask(procObj);
//
// Get the size of the printer buffer required to enumerate.
//
if (!procObj->Is9x()) { //
// Level 2 can hang on NT if a network print server is down.
//
ret = EnumPrinters( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, NULL, 0, &ulBufSizeNeeded, &ulNumStructs ); } else { //
// Level 4 is not supported on 9x and level 2 doesn't hang anyway.
//
// !!!!Note!!!!
// For 9x the Unicode wrapper function, EnumPrintersWrapW takes over.
// Also, note that level 2 is partially supported.
//
ret = EnumPrinters( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &ulBufSizeNeeded, &ulNumStructs ); } if (!ret && (result = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) {
TRC_ERR((TB, _T("EnumPrinter failed: %ld."), result)); goto Cleanup; }
//
// Check the proc obj shutdown state since we just left an RPC call.
//
if (procObj->IsShuttingDown()) { TRC_ALT((TB, _T("Bailing out of printer enumeration because of shutdown."))); goto Cleanup; }
//
// Allocate the printer enumeration buffer.
//
if (!procObj->Is9x()) { pPrinterInfo4Buf = (PRINTER_INFO_4 *)(new BYTE[ulBufSizeNeeded]); if (pPrinterInfo4Buf == NULL) { TRC_ERR((TB, _T("Alloc failed."))); result = ERROR_INSUFFICIENT_BUFFER; goto Cleanup; } else { pPrinterInfo4BufSize = ulBufSizeNeeded; } } else { pPrinterInfoBuf = (PBYTE)(new BYTE[ulBufSizeNeeded]); if (pPrinterInfoBuf == NULL) { TRC_ERR((TB, _T("Alloc failed."))); result = ERROR_INSUFFICIENT_BUFFER; goto Cleanup; } else { pPrinterInfoBufSize = ulBufSizeNeeded; } }
//
// Get the printers.
//
if (!procObj->Is9x()) { ret = EnumPrinters( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, (PBYTE)pPrinterInfo4Buf, pPrinterInfo4BufSize, &ulBufSizeNeeded, &ulNumStructs); } else { //
// !!!!Note!!!!
// For 9x the Unicode wrapper function, EnumPrintersWrapW takes over.
// Also, note that level 2 is only partially supported.
//
ret = EnumPrinters( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, (PBYTE)pPrinterInfoBuf, pPrinterInfoBufSize, &ulBufSizeNeeded, &ulNumStructs); } if (!ret) { result = GetLastError(); TRC_ALT((TB, _T("EnumPrinter failed, %ld."), result)); goto Cleanup; }
//
// Check the proc obj shutdown state since we just left an RPC call.
//
if (procObj->IsShuttingDown()) { TRC_ALT((TB, _T("Bailing out of printer enumeration because of shutdown."))); goto Cleanup; }
//
// Trace the results of EnumPrinters.
//
TRC_NRM((TB, _T("Number of Printers found, %ld."), ulNumStructs));
//
// Get the name of the current default printer.
//
szDefaultPrinter = GetRDPDefaultPrinter();
//
// Get User Session ID
//
userSessionID = GetUserSessionID();
//
// Iterate through the results of EnumPrinters and add each printer to the
// device manager that passes the printer adding filter.
//
for (i = 0; i < ulNumStructs; i++) {
if (friendlyName != NULL) { delete friendlyName; friendlyName = NULL; }
//
// Get info for the printer and its corresponding TS session ID,
// if it exists.
//
if (!procObj->Is9x()) {
if (GetPrinterInfoAndSessionID( procObj, pPrinterInfo4Buf[i].pPrinterName, pPrinterInfo4Buf[i].Attributes, &pPrinterInfoBuf, &pPrinterInfoBufSize, &printerSessionID, ¤tPrinter ) != ERROR_SUCCESS) { continue; } } else { PPRINTER_INFO_2 p2; p2 = (PPRINTER_INFO_2)pPrinterInfoBuf;
if (p2 != NULL) { currentPrinter.pPrinterName = p2[i].pPrinterName; currentPrinter.pPortName = p2[i].pPortName; currentPrinter.pDriverName = p2[i].pDriverName; currentPrinter.Attributes = p2[i].Attributes; }
printerSessionID = INVALID_SESSIONID; }
//
// Check the proc obj shutdown state since we just left an RPC call.
//
if (procObj->IsShuttingDown()) { TRC_ALT((TB, _T("Bailing out of printer enumeration because of shutdown."))); goto Cleanup; }
if( FALSE == ShouldAddThisPrinter( queueFilter, userSessionID, ¤tPrinter, printerSessionID) ) { continue; }
TCHAR UniquePortName[MAX_PATH]; ULONG DeviceId;
//
// Is this one the default queue.
//
BOOL fDefault = ((szDefaultPrinter) && (currentPrinter.pPrinterName) && (_tcsicmp(szDefaultPrinter, currentPrinter.pPrinterName) == 0));
//
// Generate a "friendly" name if this is a network
// queue.
//
BOOL fNetwork = FALSE, fTSqueue = FALSE; RDPDR_VERSION serverVer;
serverVer = procObj->serverVersion();
// 4 is minor version of post win2000
if (COMPARE_VERSION(serverVer.Minor, serverVer.Major, 4, 1) < 0) { // the server is Win2000 or lower
if (currentPrinter.Attributes & PRINTER_ATTRIBUTE_NETWORK) { friendlyName = CreateFriendlyNameFromNetworkName( currentPrinter.pPrinterName, TRUE ); // We don't set the fNetwork flag for Win2K because it can't
// do anything with it anyway.
} } else { // the server is higher than Win2000
// is it a network printer?
if (currentPrinter.Attributes & PRINTER_ATTRIBUTE_NETWORK) { fNetwork = TRUE; }
// is it a TS queue?
if ((currentPrinter.pPortName != NULL) && _tcsnicmp(currentPrinter.pPortName, RDP_PORT_NAME, RDP_PORT_NAMELEN) == 0) { fTSqueue = TRUE; friendlyName = CreateNestedName(currentPrinter.pPrinterName, &fNetwork); // should we set fNetwork and fTSqueue only in case of success?
} else if (fNetwork) {
friendlyName = CreateFriendlyNameFromNetworkName( currentPrinter.pPrinterName, FALSE ); } }
//
// Create a new printer device object.
//
pName = (friendlyName != NULL) ? friendlyName : currentPrinter.pPrinterName; DeviceId = deviceMgr->GetUniqueObjectID();
//
// The unique port name is going to be passed to the server
// as preferred dos name (max 7 characters long). As we want to
// keep a unique dos name for each printer device, we need
// to fake our own port name. e.g.
// For network printer, they share same port name if it's
// same printer with different network printer names.
// We use the format of PRN# as our unique port name
hr = StringCchPrintf(UniquePortName, SIZE_TCHARS(UniquePortName), TEXT("PRN%ld"), DeviceId); if (FAILED(hr)) { TRC_ERR((TB,_T("Error copying portname :0x%x"),hr)); result = ERROR_INSUFFICIENT_BUFFER; goto Cleanup; } UniquePortName[7] = TEXT('\0');
prnDevice = new W32DrAutoPrn( procObj, pName, currentPrinter.pDriverName, UniquePortName, fDefault, DeviceId, currentPrinter.pPrinterName );
//
// Add to the device manager if we got a valid object.
//
if (prnDevice != NULL) {
prnDevice->SetNetwork(fNetwork); prnDevice->SetTSqueue(fTSqueue); prnDevice->Initialize();
if (!(prnDevice->IsValid() && (deviceMgr->AddObject(prnDevice) == STATUS_SUCCESS))) { delete prnDevice; } } else { TRC_ERR((TB, _T("Failed to allocate W32DrPRN."))); result = ERROR_INSUFFICIENT_BUFFER; goto Cleanup; } }
Cleanup:
//
// Release the "friendly" printer name.
//
if (friendlyName != NULL) { delete friendlyName; } //
// Release the default printer buffer.
//
if (szDefaultPrinter) { delete[] szDefaultPrinter; }
//
// Release the level 4 printer enumeration buffer.
//
if (pPrinterInfo4Buf != NULL) { delete pPrinterInfo4Buf; }
//
// Release the printer enumeration buffer.
//
if (pPrinterInfoBuf != NULL) { delete pPrinterInfoBuf; }
DC_END_FN();
return result; }
LPTSTR W32DrAutoPrn::CreateNestedName(LPTSTR printerName, BOOL* pfNetwork) /*++
Routine Description:
Create a printer name from the names stored in the registry.
Arguments:
printerName - Name returned by EnumPrinters
Return Value:
Nested name on success, that should be released by a call to delete. NULL is returned on error.
--*/ { DWORD printerNameLen; LPTSTR name = NULL; HANDLE hPrinter = NULL; DWORD i, cbNeeded, dwError; BOOL fFail = TRUE;
DC_BEGIN_FN("W32DrAutoPrn::CreateNestedName");
if (OpenPrinter(printerName, &hPrinter, NULL)) {
// In all cases the name will begin with "__"
printerNameLen = 2;
// try the Server name
// WARNING: it returns ERROR_SUCCESS under Win9X if nSize = 0
dwError = GetPrinterData(hPrinter, DEVICERDR_PRINT_SERVER_NAME, NULL, NULL, 0, &cbNeeded); if( (dwError == ERROR_MORE_DATA) || (dwError == ERROR_SUCCESS)) { printerNameLen += cbNeeded / sizeof(TCHAR); *pfNetwork = TRUE; } else { *pfNetwork = FALSE; }
// try the Client name
dwError = GetPrinterData(hPrinter, DEVICERDR_CLIENT_NAME, NULL, NULL, 0, &cbNeeded); if( (dwError == ERROR_MORE_DATA) || (dwError == ERROR_SUCCESS)) { printerNameLen += cbNeeded / sizeof(TCHAR);
if (*pfNetwork) { // if there's already a print server name, add a '\'
printerNameLen += 1; } } else if(!*pfNetwork) { // no print server, no client name, things are going bad...
DC_QUIT; }
// try the Printer name
dwError = GetPrinterData(hPrinter, DEVICERDR_PRINTER_NAME, NULL, NULL, 0, &cbNeeded); if( (dwError == ERROR_MORE_DATA) || (dwError == ERROR_SUCCESS)) { // add also a '\'
printerNameLen += 1 + cbNeeded / sizeof(TCHAR); } else { // no printer name
DC_QUIT; }
//
// Allocate space for the nested name.
//
name = new TCHAR[printerNameLen + 1]; if (name == NULL) {
TRC_ERR((TB, _T("Can't allocate %ld bytes for printer name."), printerNameLen));
} else {
name[0] = _T('!'); name[1] = _T('!'); i = 2;
// try the Server name
if (*pfNetwork) { if (ERROR_SUCCESS == GetPrinterData(hPrinter, DEVICERDR_PRINT_SERVER_NAME, NULL, (LPBYTE)(name + i), (printerNameLen - i) * sizeof(TCHAR), &cbNeeded)) { i = _tcslen(name); name[i++] = _T('!');
} else { // weird...
DC_QUIT; } }
// try the Client name
if (ERROR_SUCCESS == GetPrinterData(hPrinter, DEVICERDR_CLIENT_NAME, NULL, (LPBYTE)(name + i), (printerNameLen - i) * sizeof(TCHAR), &cbNeeded)) { i = _tcslen(name); name[i++] = _T('!');
} else {
if(!*pfNetwork) { // no print server, no client name, things are going bad...
DC_QUIT; } }
// try the Printer name
if (ERROR_SUCCESS == GetPrinterData(hPrinter, DEVICERDR_PRINTER_NAME, NULL, (LPBYTE)(name + i), (printerNameLen - i) * sizeof(TCHAR), &cbNeeded)) { fFail = FALSE; } else { DC_QUIT; } }
}
DC_EXIT_POINT:
if (hPrinter) { ::ClosePrinter(hPrinter); }
if (fFail && name) { delete[] name; name = NULL; }
DC_END_FN();
return name; }
LPTSTR W32DrAutoPrn::CreateFriendlyNameFromNetworkName(LPTSTR printerName, BOOL serverIsWin2K) /*++
Routine Description:
Create a "friendly" printer name from the printer name of a network printer.
Arguments:
printerName - Name returned by EnumPrinters serverIsWin2K - For Win2K servers, we format the name as it will appear on the server. Whistler servers and beyond do the formatting for us.
Return Value:
Friendly name on success, that should be released by a call to delete. NULL is returned on error.
--*/ { DWORD printerNameLen; LPTSTR name; DWORD i; WCHAR replaceChar;
DC_BEGIN_FN("W32DrAutoPrn::CreateFriendlyNameFromNetworkName");
//
// The \ placeholder is '_' for Win2K because Win2K doesn't reformat
// printer names. The '_' will actually be visible.
//
replaceChar = serverIsWin2K ? TEXT('_') : TEXT('!');
//
// Get the length of the printer name.
//
printerNameLen = _tcslen(printerName);
//
// Allocate space for ther "friendly" name.
//
name = new TCHAR[printerNameLen + 1]; if (name == NULL) { TRC_ERR((TB, _T("Can't allocate %ld bytes for printer name."), printerNameLen)); }
//
// Copy and convert the name.
//
if (name != NULL) { for (i=0; i<printerNameLen; i++) { if (printerName[i] != TEXT('\\')) { name[i] = printerName[i]; } else { name[i] = replaceChar; } } name[i] = TEXT('\0'); }
DC_END_FN();
return name; }
LPTSTR W32DrAutoPrn::GetLocalPrintingDocName() /*++
Routine Description:
Read Local printer Doc Name from the passed in struct
Arguments:
NA
Return Value:
Local Printer Document Name
--*/ { DC_BEGIN_FN("W32DrAutoPrn::GetLocalPrintingDocName");
DC_END_FN(); return _szLocalPrintingDocName; }
VOID W32DrAutoPrn::MsgIrpCreate( IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket, IN UINT32 packetLen ) /*++
Routine Description:
Handle a Create IRP from the server.
Arguments:
params - Context for the IO request.
Return Value:
NA
--*/ { W32DRDEV_ASYNCIO_PARAMS *params = NULL;
DWORD result = ERROR_SUCCESS;
DC_BEGIN_FN("W32DrAutoPrn::MsgIrpCreate");
params = new W32DRDEV_ASYNCIO_PARAMS(this, pIoRequestPacket); if (params != NULL) { params->thrPoolReq = _threadPool->SubmitRequest( _AsyncMsgIrpCreateFunc, params, NULL ); if (params->thrPoolReq == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; goto CLEANUPANDEXIT; } } else { TRC_ERR((TB, L"Can't allocate parms.")); result = ERROR_NOT_ENOUGH_MEMORY; goto CLEANUPANDEXIT; }
CLEANUPANDEXIT:
if (result != ERROR_SUCCESS) {
//
// Return the failed result back to the server and clean up.
//
DefaultIORequestMsgHandle(pIoRequestPacket, result); if (params != NULL) { params->pIoRequestPacket = NULL; delete params; }
}
DC_END_FN(); }
DWORD W32DrAutoPrn::AsyncMsgIrpCreateFunc( IN W32DRDEV_ASYNCIO_PARAMS *params ) /*++
Routine Description:
Handle a "Close" IO request from the server, in a background thread.
Arguments:
params - Context for the IO request.
Return Value:
NA
--*/ { PRDPDR_DEVICE_IOREQUEST pIoRequest; ULONG ulRetCode; DWORD result; DOC_INFO_1 sDocInfo1;
DC_BEGIN_FN("W32DrAutoPrn::AsyncMsgIrpCreateFunc");
//
// This version does not work without a printer name.
//
ASSERT(_tcslen(GetPrinterName()));
//
// Get IO request pointer.
//
pIoRequest = ¶ms->pIoRequestPacket->IoRequest; //
// Finish/Cancel any Current Jobs and Close, if Open.
//
ClosePrinter();
//
// Open the printer.
//
if (!W32DrOpenPrinter(_devicePath, &_printerHandle)) { ulRetCode = GetLastError(); TRC_ERR((TB, _T("OpenPrinter %ld."), ulRetCode)); goto Cleanup; }
//
// Start Doc.
//
sDocInfo1.pDocName = GetLocalPrintingDocName(); sDocInfo1.pOutputFile = NULL; sDocInfo1.pDatatype = _T("RAW"); _jobID = StartDocPrinter(_printerHandle, 1, (PBYTE)&sDocInfo1); if (_jobID == 0) { ulRetCode = GetLastError(); TRC_ERR((TB, _T("StartDocPrinter %ld."), ulRetCode)); ClosePrinter(); goto Cleanup; }
//
// Attempt to disable annoying printer pop up if we have sufficient
// privilege. Not a big deal, if we fail.
//
DisablePrinterPopup(_printerHandle, _jobID);
//
// Start the first page.
//
if (!StartPagePrinter(_printerHandle)) { ulRetCode = GetLastError(); TRC_ERR((TB, _T("StartPagePrinter %ld."), ulRetCode)); ClosePrinter(); goto Cleanup; }
//
// We are done successfully, say so.
//
ulRetCode = ERROR_SUCCESS;
Cleanup:
//
// Send the result to the server.
//
result = (ulRetCode == ERROR_SUCCESS) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; DefaultIORequestMsgHandle(params->pIoRequestPacket, result);
//
// Clean up the IO request parameters. DefaultIORequestMsgHandle cleans up
// the request packet.
//
if (params->thrPoolReq != INVALID_THREADPOOLREQUEST) { _threadPool->CloseRequest(params->thrPoolReq); params->thrPoolReq = INVALID_THREADPOOLREQUEST; } params->pIoRequestPacket = NULL; delete params;
DC_END_FN(); return result; }
VOID W32DrAutoPrn::ClosePrinter() /*++
Routine Description:
End any jobs in progress and close the printer.
Arguments:
NA
Return Value:
NA
--*/ { DC_BEGIN_FN("W32DrAutoPrn::ClosePrinter");
if (_printerHandle != INVALID_HANDLE_VALUE) {
//
// Finish the current page for the current print job.
//
if (!EndPagePrinter(_printerHandle)) { TRC_ERR((TB, _T("EndPagePrinter %ld."), GetLastError())); }
//
// End the current print job.
//
if (!EndDocPrinter(_printerHandle)) { TRC_ERR((TB, _T("EndDocPrinter %ld."), GetLastError())); }
//
// Close the printer.
//
if (!::ClosePrinter(_printerHandle)) { TRC_ERR((TB, _T("ClosePrinter %ld."), GetLastError())); }
//
// Negate the handle and the pending job ID.
//
_printerHandle = INVALID_HANDLE_VALUE; _jobID = 0;
}
DC_END_FN(); }
VOID W32DrAutoPrn::MsgIrpClose( IN PRDPDR_IOREQUEST_PACKET pIoRequestPacket, IN UINT32 packetLen ) /*++
Routine Description:
Handle a "Close" IO request from the server by dispatching the request to the thread pool. TODO: move this async dispatch to the parent class in a future release. All closes should be handled outside the main thread.-TadB
Arguments:
pIoRequestPacket - Server IO request packet.
Return Value:
NA
--*/ { W32DRDEV_ASYNCIO_PARAMS *params = NULL;
DWORD result = ERROR_SUCCESS;
DC_BEGIN_FN("W32DrAutoPrn::MsgIrpClose");
params = new W32DRDEV_ASYNCIO_PARAMS(this, pIoRequestPacket); if (params != NULL) { params->thrPoolReq = _threadPool->SubmitRequest( _AsyncMsgIrpCloseFunc, params, NULL ); if (params->thrPoolReq == NULL) { result = ERROR_NOT_ENOUGH_MEMORY; goto CLEANUPANDEXIT; } } else { TRC_ERR((TB, L"Can't allocate parms.")); result = ERROR_NOT_ENOUGH_MEMORY; goto CLEANUPANDEXIT; }
CLEANUPANDEXIT:
if (result != ERROR_SUCCESS) {
//
// Return the failed result back to the server and clean up.
//
DefaultIORequestMsgHandle(pIoRequestPacket, result); if (params != NULL) { params->pIoRequestPacket = NULL; delete params; }
}
DC_END_FN(); }
DWORD W32DrAutoPrn::AsyncMsgIrpCloseFunc( IN W32DRDEV_ASYNCIO_PARAMS *params ) /*++
Routine Description:
Handle a "Close" IO request from the server, in a background thread.
Arguments:
params - Context for the IO request.
Return Value:
NA
--*/ { PRDPDR_DEVICE_IOREQUEST pIoRequest; DWORD returnValue = STATUS_SUCCESS;
DC_BEGIN_FN("W32DrAutoPrn::MsgIrpClose");
//
// Close the printer.
//
ClosePrinter();
//
// Send the result to the server.
//
DefaultIORequestMsgHandle(params->pIoRequestPacket, returnValue);
//
// Clean up the IO request parameters. DefaultIORequestMsgHandle cleans up
// the request packet.
//
if (params->thrPoolReq != INVALID_THREADPOOLREQUEST) { _threadPool->CloseRequest(params->thrPoolReq); params->thrPoolReq = INVALID_THREADPOOLREQUEST; } params->pIoRequestPacket = NULL; delete params;
DC_END_FN();
return returnValue; }
DWORD W32DrAutoPrn::GetPrinterFilterMask( IN ProcObj *procObj ) /*++
Routine Description:
Returns the configurable print redirection filter mask.
Arguments:
procObj - The relevant process object.
Return Value:
Configurable Filter Mask
--*/ { DWORD filter;
//
// Read FilterQueueType parameters so we know which devices
// to redirect.
//
if (procObj->GetDWordParameter( REG_RDPDR_FILTER_QUEUE_TYPE, &filter) != ERROR_SUCCESS) { //
// Default.
//
filter = FILTER_ALL_QUEUES; } return filter; }
BOOL W32DrAutoPrn::W32DrOpenPrinter( IN LPTSTR pPrinterName, IN LPHANDLE phPrinter )
/*++
Routine Description:
Open a printer with highest access possible.
Arguments:
pPrinterName - Pointer to printer or server name. phPrinter - Pointer to printer or server handle.
Return Value:
TRUE on success. FALSE, otherwise.
--*/ { PRINTER_DEFAULTS sPrinter; BOOL result;
DC_BEGIN_FN("W32DrAutoPrn::W32DrOpenPrinter");
//
// Open printer.
//
sPrinter.pDatatype = NULL; sPrinter.pDevMode = NULL; sPrinter.DesiredAccess = PRINTER_ACCESS_USE; result = OpenPrinter(pPrinterName, phPrinter, &sPrinter); if (!result) { TRC_ALT((TB, _T("Full-Access OpenPrinter %ld."), GetLastError()));
//
// Try with default access.
//
result = OpenPrinter(pPrinterName, phPrinter, NULL); if (!result) { TRC_ERR((TB, _T("OpenPrinter %ld."), GetLastError())); } }
DC_END_FN();
return result; }
VOID W32DrAutoPrn::DisablePrinterPopup( HANDLE hPrinterHandle, ULONG ulJobID ) /*++
Routine Description:
Disable annoying printer pop up for the specified printer and print job.
Arguments:
hPrinterHandle - handle to a printer device.
ulJobID - ID of the job.
Return Value:
NA
--*/ { JOB_INFO_2* pJobInfo2 = NULL; ULONG ulJobBufSize;
DC_BEGIN_FN("W32DrAutoPrn::DisablePrinterPopup");
ulJobBufSize = 2 * 1024; pJobInfo2 = (JOB_INFO_2 *)new BYTE[ulJobBufSize];
//
// Note we call the ANSI version of the API
// because we don't have a UNICODE wrapper for Get/SetJob.
// Main reason is we don't actually use any returned string
// data directly.
//
if(!_bRunningOn9x) { //Call unicode API's
if( pJobInfo2 != NULL ) { //
// Get job info.
//
if( GetJob( hPrinterHandle, ulJobID, 2, (PBYTE)pJobInfo2, ulJobBufSize, &ulJobBufSize )) {
//
// Disable popup notification by setting pNotifyName
// NULL.
//
pJobInfo2->pNotifyName = NULL; pJobInfo2->Position = JOB_POSITION_UNSPECIFIED; if( !SetJob( hPrinterHandle, ulJobID, 2, (PBYTE)pJobInfo2, 0 )) {
TRC_ERR((TB, _T("SetJob %ld."), GetLastError())); } } else { TRC_ERR((TB, _T("GetJob %ld."), GetLastError())); } delete (PBYTE)pJobInfo2; } else { TRC_ERR((TB, _T("Memory Allocation failed."))); } } else { //Call ANSI API's
if( pJobInfo2 != NULL ) { //
// Get job info.
//
if( GetJobA( hPrinterHandle, ulJobID, 2, (PBYTE)pJobInfo2, ulJobBufSize, &ulJobBufSize )) {
//
// Disable popup notification by setting pNotifyName
// NULL.
//
pJobInfo2->pNotifyName = NULL; if( !SetJobA( hPrinterHandle, ulJobID, 2, (PBYTE)pJobInfo2, 0 )) {
TRC_ERR((TB, _T("SetJob %ld."), GetLastError())); } } else { TRC_ERR((TB, _T("GetJob %ld."), GetLastError())); } delete (PBYTE)pJobInfo2; } else { TRC_ERR((TB, _T("Memory Allocation failed."))); } } DC_END_FN(); }
DWORD W32DrAutoPrn::AsyncWriteIOFunc( IN W32DRDEV_ASYNCIO_PARAMS *params ) /*++
Routine Description:
Asynchronous Write Operation
Arguments:
params - Context for the IO request.
Return Value:
Returns 0 on success. Otherwise, a Windows error code is returned.
--*/ { PBYTE pDataBuffer; PRDPDR_IOCOMPLETION_PACKET pReplyPacket; PRDPDR_DEVICE_IOREQUEST pIoRequest; ULONG ulReplyPacketSize = 0; DWORD status;
DC_BEGIN_FN("W32DrAutoPrn::AsyncWriteIOFunc");
// Assert the integrity of the IO context
ASSERT(params->magicNo == GOODMEMMAGICNUMBER);
//
// Get the IO request and reply..
//
pIoRequest = ¶ms->pIoRequestPacket->IoRequest; pReplyPacket = params->pIoReplyPacket;
//
// Get the data buffer pointer.
//
pDataBuffer = (PBYTE)(pIoRequest + 1);
//
// Write the data to the print queue with the help of the spooler.
//
if (!WritePrinter( _printerHandle, pDataBuffer, pIoRequest->Parameters.Write.Length, &(pReplyPacket->IoCompletion.Parameters.Write.Length)) ) {
status = GetLastError(); TRC_ERR((TB, _T("WritePrinter %ld."), status)); } else { TRC_NRM((TB, _T("WritePrinter completed."))); status = ERROR_SUCCESS; }
DC_END_FN(); return status; }
|