Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

883 lines
23 KiB

// Printer.cpp: implementation of the CPrinter class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Printer.h"
#include "wininet.h"
static const DWORD BIT_IGNORED_JOB = (JOB_STATUS_PAUSED |
JOB_STATUS_PRINTED |
JOB_STATUS_DELETING |
JOB_STATUS_OFFLINE |
JOB_STATUS_SPOOLING);
static const DWORD BIT_ERROR_JOB = (JOB_STATUS_ERROR | JOB_STATUS_PAPEROUT);
static const DWORD CHAR_PER_PAGE = 4800;
static const DWORD PAGEPERJOB = 1;
static const DWORD BYTEPERJOB = 2;
#ifdef WIN9X
#error This code requires DRIVER_INFO_6 which is not availabe under Win9X
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CPrinter::CPrinter():
m_hPrinter(NULL),
m_pInfo2(NULL),
m_pInfo4(NULL),
m_pDriverInfo6(NULL),
m_bCalcJobETA(NULL),
m_pszUrlBuffer(NULL),
m_pszOemUrl (NULL),
m_pszManufacturer (NULL)
{
}
CPrinter::~CPrinter()
{
if (m_pInfo2) {
LocalFree (m_pInfo2);
}
if (m_pInfo4) {
LocalFree (m_pInfo4);
}
if (m_pDriverInfo6) {
LocalFree (m_pDriverInfo6);
}
if (m_hPrinter) {
ClosePrinter (m_hPrinter);
}
if (m_pszUrlBuffer) {
LocalFree (m_pszUrlBuffer);
}
if (m_pszOemUrl) {
LocalFree (m_pszOemUrl);
}
if (m_pszManufacturer) {
LocalFree (m_pszManufacturer);
}
}
BOOL CPrinter::Open(LPTSTR pPrinterName, LPHANDLE phPrinter)
{
PRINTER_DEFAULTS pd = {NULL, NULL, PRINTER_ACCESS_USE};
if (m_hPrinter != NULL)
return FALSE;
if (! (OpenPrinter(pPrinterName, &m_hPrinter, &pd)))
return FALSE;
if (phPrinter) {
*phPrinter = m_hPrinter;
}
return TRUE;
}
// This is a compiler safe way of checking that an unsigned integer is
// negative and returning zero if it is or the integer if it is not
// Note: Tval can be any size but must be unsigned
template<class T> inline T ZeroIfNegative(T Tval) {
if (Tval & (T(1) << (sizeof(Tval) * 8 - 1))) return 0;
else return Tval;
}
BOOL CPrinter::CalJobEta()
{
DWORD dwPrintRate = DWERROR;
PJOB_INFO_2 pJobInfo = NULL;
PJOB_INFO_2 pJob;
DWORD dwNumJobs;
DWORD dwNeeded = 0;
DWORD i;
float fFactor = 1;
float fTotal = 0;
DWORD dwNumJobsReqested;
BOOL bRet = FALSE;
const DWORD cdwLimit = 256;
if (! AllocGetPrinterInfo2())
return NULL;
if (m_pInfo2->cJobs > 0) {
if (m_pInfo2->cJobs > cdwLimit) {
fFactor = (float) m_pInfo2->cJobs / cdwLimit;
dwNumJobsReqested = cdwLimit;
}
else
dwNumJobsReqested = m_pInfo2->cJobs;
EnumJobs (m_hPrinter, 0, dwNumJobsReqested, 2,
NULL, 0, &dwNeeded, &dwNumJobs);
if ((GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(NULL == (pJobInfo = (PJOB_INFO_2)LocalAlloc(LPTR, dwNeeded))) ||
(!EnumJobs (m_hPrinter, 0, dwNumJobsReqested, 2, (LPBYTE) pJobInfo, dwNeeded,
&dwNeeded, &dwNumJobs))) {
goto Cleanup;
}
// Get the average Job size
// Find out if we can use page as the unit
for (pJob = pJobInfo, i = 0; i < dwNumJobs; i++, pJob++) {
if (pJob->Status & BIT_IGNORED_JOB)
continue;
if (pJob->Size > 0 && pJob->TotalPages == 0)
break;
}
m_dwPendingJobCount = 0;
if (i == dwNumJobs) {
// All the jobs have the page information. Use page as the unit
m_dwAvgJobSizeUnit = PAGEPERJOB;
for (pJob = pJobInfo, i = 0; i < dwNumJobs; i++, pJob++) {
if (pJob->Status & BIT_IGNORED_JOB)
continue;
m_dwPendingJobCount++;
fTotal += pJob->TotalPages;
}
}
else {
// Use byte as the unit
m_dwAvgJobSizeUnit = BYTEPERJOB;
for (pJob = pJobInfo, i = 0; i < dwNumJobs; i++, pJob++) {
if (pJob->Status & BIT_IGNORED_JOB)
continue;
m_dwPendingJobCount++;
fTotal += ZeroIfNegative(pJob->Size);
}
}
// Calculate the averate job size
if (m_dwPendingJobCount) {
m_dwAvgJobSize = DWORD ((fTotal) / (float) m_dwPendingJobCount);
dwPrintRate = GetPPM();
if (dwPrintRate != DWERROR)
m_dwJobCompletionMinute = (DWORD) (fFactor * GetWaitingMinutes (dwPrintRate, pJobInfo, dwNumJobs));
}
else {
m_dwAvgJobSize = 0;
m_dwJobCompletionMinute = 0;
}
m_dwPendingJobCount = (DWORD) (m_dwPendingJobCount * fFactor);
}
else {
m_dwPendingJobCount = 0;
m_dwAvgJobSize = 0;
m_dwJobCompletionMinute = 0;
m_dwAvgJobSizeUnit = PAGEPERJOB;
}
m_bCalcJobETA = TRUE;
// Set the last error to ERROR_INVALID_DATA if the printer rate is not available for the current printer
// or if the printer status is not suitable to display the summary information
//
if (dwPrintRate == DWERROR ||
m_pInfo2->Status & ( PRINTER_STATUS_PAUSED |
PRINTER_STATUS_ERROR |
PRINTER_STATUS_PENDING_DELETION |
PRINTER_STATUS_PAPER_JAM |
PRINTER_STATUS_PAPER_OUT |
PRINTER_STATUS_OFFLINE )) {
SetLastError (ERROR_INVALID_DATA);
m_dwJobCompletionMinute = DWERROR;
bRet = FALSE;
}
else
bRet = TRUE;
Cleanup:
if (pJobInfo)
LocalFree(pJobInfo);
return bRet;
}
DWORD CPrinter::GetWaitingMinutes(DWORD dwPPM, PJOB_INFO_2 pJobInfo, DWORD dwNumJob)
{
DWORD dwWaitingTime = 0;
DWORD dwTotalPages = 0;
if (dwNumJob == 0)
return 0;
if (dwPPM == 0)
return DWERROR;
for (DWORD i = 0; i < dwNumJob; i++, pJobInfo++) {
if (pJobInfo->Status & BIT_IGNORED_JOB)
continue;
if (pJobInfo->Status & BIT_ERROR_JOB)
return DWERROR;
if (pJobInfo->TotalPages > 0) {
dwTotalPages += pJobInfo->TotalPages;
}
else {
if (pJobInfo->Size) {
dwTotalPages += 1 + ZeroIfNegative(pJobInfo->Size) / CHAR_PER_PAGE;
}
}
}
if (dwTotalPages)
dwWaitingTime = 1 + dwTotalPages / dwPPM;
return dwWaitingTime;
}
DWORD CPrinter::GetPPM()
{
DWORD dwPrintRate;
DWORD dwPrintRateUnit;
// Get PrintRate
dwPrintRate = MyDeviceCapabilities(m_pInfo2->pPrinterName,
NULL,
DC_PRINTRATE,
NULL,
NULL);
if (dwPrintRate == DWERROR ) {
return dwPrintRate;
}
dwPrintRateUnit = MyDeviceCapabilities(m_pInfo2->pPrinterName,
NULL,
DC_PRINTRATEUNIT,
NULL,
NULL);
switch (dwPrintRateUnit) {
case PRINTRATEUNIT_PPM: // PagesPerMinute
break;
case PRINTRATEUNIT_CPS: // CharactersPerSecond
dwPrintRate = CPS2PPM (dwPrintRate);
break;
case PRINTRATEUNIT_LPM: // LinesPerMinute
dwPrintRate = LPM2PPM (dwPrintRate);
break;
case PRINTRATEUNIT_IPM: // InchesPerMinute
dwPrintRate = DWERROR;
break;
default: // Unknown
dwPrintRate = DWERROR;
break;
}
return dwPrintRate ;
}
BOOL CPrinter::AllocGetPrinterInfo2()
{
DWORD dwNeeded = 0;
PPRINTER_INFO_2 pPrinterInfo = NULL;
LPTSTR pszTmp;
if (m_hPrinter == NULL) {
SetLastError (ERROR_INVALID_HANDLE);
return FALSE;
}
// Get a PRINTER_INFO_2 structure filled up
if (GetPrinter(m_hPrinter, 2, NULL, 0, &dwNeeded) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(NULL == (pPrinterInfo = (PPRINTER_INFO_2)LocalAlloc(LPTR, dwNeeded))) ||
(!GetPrinter(m_hPrinter, 2, (byte *)pPrinterInfo, dwNeeded, &dwNeeded))) {
if (pPrinterInfo)
LocalFree(pPrinterInfo);
if (! GetLastError())
SetLastError (ERROR_INVALID_DATA);
return FALSE;
}
// Mark the offline status if the attribute says offline
if (pPrinterInfo->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE) {
pPrinterInfo->Status |= PRINTER_STATUS_OFFLINE;
}
// Extract the first port for the case of pooled printing. i.e. we don't support pooled printing.
if ( pPrinterInfo->pPortName) {
if( pszTmp = _tcschr( pPrinterInfo->pPortName, TEXT(',')))
*pszTmp = TEXT('\0');
}
if (m_pInfo2) {
LocalFree (m_pInfo2);
}
m_pInfo2 = pPrinterInfo;
return TRUE;
}
BOOL CPrinter::AllocGetPrinterInfo4()
{
DWORD dwNeeded = 0;
PPRINTER_INFO_4 pPrinterInfo = NULL;
if (m_hPrinter == NULL) {
SetLastError (ERROR_INVALID_HANDLE);
return FALSE;
}
// Get a PRINTER_INFO_4 structure filled up
if (GetPrinter(m_hPrinter, 4, NULL, 0, &dwNeeded) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(NULL == (pPrinterInfo = (PPRINTER_INFO_4)LocalAlloc(LPTR, dwNeeded))) ||
(!GetPrinter(m_hPrinter, 4, (byte *)pPrinterInfo, dwNeeded, &dwNeeded))) {
if (pPrinterInfo)
LocalFree(pPrinterInfo);
if (! (GetLastError()))
SetLastError (ERROR_INVALID_DATA);
return FALSE;
}
if (m_pInfo4) {
LocalFree (m_pInfo4);
}
m_pInfo4 = pPrinterInfo;
return TRUE;
}
BOOL CPrinter::AllocGetDriverInfo6()
{
DWORD dwNeeded = 0;
PDRIVER_INFO_6 pDriverInfo = NULL;
if (m_hPrinter == NULL) {
SetLastError (ERROR_INVALID_HANDLE);
return FALSE;
}
// Get a DRIVER_INFO_6 structure filled up
if (GetPrinterDriver(m_hPrinter, NULL, 6, NULL, 0, &dwNeeded) ||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
(NULL == (pDriverInfo = (PDRIVER_INFO_6)LocalAlloc(LPTR, dwNeeded))) ||
(!GetPrinterDriver(m_hPrinter, NULL, 6, (LPBYTE)pDriverInfo, dwNeeded, &dwNeeded))) {
if (pDriverInfo)
LocalFree(pDriverInfo);
if (! (GetLastError()))
SetLastError (ERROR_INVALID_DATA);
return FALSE;
}
if (m_pDriverInfo6) {
LocalFree (m_pDriverInfo6);
}
m_pDriverInfo6 = pDriverInfo;
return TRUE;
}
PPRINTER_INFO_2 CPrinter::GetPrinterInfo2()
{
if (m_pInfo2 == NULL) {
if (! AllocGetPrinterInfo2())
return NULL;
}
return m_pInfo2;
}
PDRIVER_INFO_6 CPrinter::GetDriverInfo6()
{
if (m_pDriverInfo6 == NULL) {
if (! AllocGetDriverInfo6()) {
return NULL;
}
}
return m_pDriverInfo6;
}
DWORD CPrinter::GetWaitingTime()
{
if (!m_bCalcJobETA) {
SetLastError (ERROR_INVALID_DATA);
return FALSE;
}
return m_dwJobCompletionMinute;
}
BOOL CPrinter::GetJobEtaData (DWORD & dwWaitingTime, DWORD &dwJobCount, DWORD &dwJobSize, DWORD &dwJob)
{
if (!m_bCalcJobETA) {
SetLastError (ERROR_INVALID_DATA);
return FALSE;
}
dwJobCount = m_dwPendingJobCount;
dwJobSize = m_dwAvgJobSize;
dwWaitingTime = m_dwJobCompletionMinute;
dwJob = m_dwAvgJobSizeUnit;
return TRUE;
}
LPTSTR CPrinter::GetPrinterWebUrl(void)
{
static const TCHAR c_szHttp[] = TEXT("http://");
static const TCHAR c_szHttps[] = TEXT("https://");
BOOL bReturn = FALSE;
DWORD dwLastError = ERROR_SUCCESS;
DWORD dwLen = 0;
TCHAR szBuffer[MAX_COMPUTERNAME_LENGTH+1];
DWORD dwBufferSize;
DWORD dwAttributes;
LPCTSTR pszServerName;
LPTSTR pszShareName;
LPTSTR pszSplServerName;
LPTSTR pszSplPrinterName;
LPTSTR pszSplShareName;
//
// Get printer info 4 to fetch the attribute.
//
bReturn = AllocGetPrinterInfo4();
if (!bReturn) {
if (GetLastError () == ERROR_INVALID_LEVEL ) {
//
// Most likely it is a remote printers folder, no support for level4, so
// we have to use level 2 instead.
//
if (! AllocGetPrinterInfo2())
goto Cleanup;
}
else {
//
// The call fails with other reasons
//
goto Cleanup;
}
}
else{
if (m_pInfo4->Attributes & PRINTER_ATTRIBUTE_LOCAL) {
// Check if the local flag is on. If so, try to get printer info2 for more information
if (! AllocGetPrinterInfo2())
goto Cleanup;
}
}
//
// Assume failure
//
bReturn = FALSE;
if (m_pInfo2) {
dwAttributes = m_pInfo2->Attributes;
pszSplServerName = m_pInfo2->pServerName;
pszSplPrinterName = m_pInfo2->pPrinterName;
pszSplShareName = m_pInfo2->pShareName;
}
else
{
dwAttributes = m_pInfo4->Attributes;
pszSplServerName = m_pInfo4->pServerName;
pszSplPrinterName = m_pInfo4->pPrinterName;
pszSplShareName = NULL;
}
//
// Check if it is a printer connected to an http port
// then the port name is the url.
//
if( m_pInfo2 )
{
if( m_pInfo2->pPortName )
{
//
// Compare the port name prefex to see if it is an HTTP port.
//
if( !_tcsnicmp( m_pInfo2->pPortName, c_szHttp, _tcslen( c_szHttp ) ) ||
!_tcsnicmp( m_pInfo2->pPortName, c_szHttps, _tcslen( c_szHttps ) ) )
{
//
// We always use portname as the URL
//
dwLen = 1 + lstrlen( m_pInfo2->pPortName );
if (! (m_pszUrlBuffer = (LPTSTR) LocalAlloc (LPTR, dwLen * sizeof (TCHAR))))
{
goto Cleanup;
}
lstrcpy( m_pszUrlBuffer, m_pInfo2->pPortName );
bReturn = TRUE;
goto Cleanup;
}
}
}
//
// If it is an unshared printer, return false
//
if ( !(dwAttributes & PRINTER_ATTRIBUTE_SHARED) )
{
goto Cleanup;
}
//
// Check if it is a connection or a local printer or a masq printer
// which is not connected over http, then build the url
// from the \\server name\share name.
//
if( !pszSplServerName )
{
dwBufferSize = COUNTOF( szBuffer );
if( !GetComputerName( szBuffer, &dwBufferSize ) )
{
goto Cleanup;
}
pszServerName = szBuffer;
}
//
// Server name was provided then set our pointer to just
// after the two leading wacks.
//
else
{
if( pszSplServerName[0] == TEXT('\\') &&
pszSplServerName[1] == TEXT('\\') )
{
pszServerName = pszSplServerName + 2;
}
else
{
goto Cleanup;
}
}
if ( !IsWebServerInstalled(pszSplServerName) ) {
dwLastError = ERROR_NO_BROWSER_SERVERS_FOUND;
goto Cleanup;
}
//
// Build the URL - http://server/printers/ipp_0004.asp?printer=ShareName
//
if (pszSplShareName)
{
pszShareName = pszSplShareName;
}
else {
//
// Parse the sharename/printername from the printer name
//
if(pszSplPrinterName) {
if (pszSplPrinterName[0] == TEXT ('\\') && pszSplPrinterName[1] == TEXT ('\\') )
{
pszShareName = _tcschr (pszSplPrinterName + 2, TEXT ('\\'));
pszShareName++;
}
else
pszShareName = pszSplPrinterName;
}
else
{
goto Cleanup;
}
}
GetWebUIUrl (pszServerName, pszShareName, NULL, &dwLen);
if (GetLastError () != ERROR_INSUFFICIENT_BUFFER )
{
goto Cleanup;
}
if (! (m_pszUrlBuffer = (LPTSTR) LocalAlloc (LPTR, dwLen * sizeof (TCHAR))))
{
goto Cleanup;
}
if (!GetWebUIUrl (pszServerName, pszShareName, m_pszUrlBuffer, &dwLen))
{
goto Cleanup;
}
//
// Indicate success.
//
bReturn = TRUE;
//
// Clean any opened or allocated resources.
//
Cleanup:
//
// If this routine failed then set the last error.
//
if( !bReturn )
{
//
// If the last error was not set then a called routine may
// have set the last error. We don't want to clear the
// last error.
//
if( dwLastError != ERROR_SUCCESS )
{
SetLastError( dwLastError );
}
}
return m_pszUrlBuffer;
}
BOOL CPrinter::GetDriverData(
DriverData dwDriverData,
LPTSTR &pszData)
{
static const ULONG_PTR ulpOffset[LastDriverData] = {
// This has the offsets into the DRIVER_INFO_6 structure.....
ULOFFSET( PDRIVER_INFO_6, pszOEMUrl ) ,
ULOFFSET( PDRIVER_INFO_6, pszHardwareID ) ,
ULOFFSET( PDRIVER_INFO_6, pszMfgName)
};
LPTSTR pszDataString = NULL;
BOOL bRet = FALSE;
DWORD dwSize;
ASSERT( (int)dwDriverData >= 0 && (int)dwDriverData < LastDriverData );
if (! GetDriverInfo6() )
goto Cleanup;
pszDataString = *(LPTSTR *)(((ULONG_PTR) m_pDriverInfo6) + ulpOffset[dwDriverData] );
if (pszDataString == NULL || *pszDataString == NULL)
goto Cleanup;
dwSize = sizeof(TCHAR) * (lstrlen( pszDataString ) + 1);
if (! (pszData = (LPTSTR)LocalAlloc(LPTR, dwSize)))
goto Cleanup;
lstrcpy( pszData, pszDataString);
bRet = TRUE;
Cleanup:
return bRet;
}
BOOL CPrinter::ParseUrlPattern(
LPTSTR pSrc,
LPTSTR pDest,
DWORD &dwDestLen)
{
const dwMaxMacroLen = 255;
enum {
NORMALTEXT, STARTMACRO
} URL_PATTERN_STATE;
BOOL bRet = FALSE;
DWORD dwLen = 0;
DWORD dwMacroLen = 0;
DWORD dwAvailbleSize;
DWORD dwState = NORMALTEXT;
int i;
TCHAR ch;
TCHAR szMacroName [dwMaxMacroLen + 1];
LPTSTR pMacroValue = NULL;
LPTSTR pszMacroList[] = {
TEXT ("MODEL"),
TEXT ("HARDWAREID"),
};
while (ch = *pSrc++) {
switch (dwState) {
case NORMALTEXT:
if (ch == TEXT ('%')) {
// Start a macro
dwState = STARTMACRO;
dwMacroLen = 0;
szMacroName[0] = 0;
}
else {
if (dwLen >= dwDestLen) {
dwLen ++;
}
else {
pDest[dwLen++] = ch;
}
}
break;
case STARTMACRO:
if (ch == TEXT ('%')) {
szMacroName[dwMacroLen] = 0;
// Replace Macro
for (int i = 0; i < sizeof (pszMacroList) / sizeof (pszMacroList[0]); i++) {
if (!lstrcmpi (szMacroName, pszMacroList[i])) {
pMacroValue = 0;
switch (i) {
case 0:
AssignString (pMacroValue, m_pInfo2->pDriverName);
break;
case 1:
GetDriverData (HardwareID , pMacroValue);
break;
default:
break;
}
if (pMacroValue) {
if (dwDestLen > dwLen)
dwAvailbleSize = dwDestLen - dwLen;
else
dwAvailbleSize = 0;
TCHAR szPlaceHolder[1];
DWORD dwBufSize = sizeof (szPlaceHolder) / sizeof (TCHAR);
if (!InternetCanonicalizeUrl (pMacroValue, szPlaceHolder, &dwBufSize, 0)) {
if (dwBufSize < dwAvailbleSize ) {
if (!InternetCanonicalizeUrl (pMacroValue, pDest + dwLen, &dwBufSize, 0)) {
LocalFree (pMacroValue);
return bRet;
}
else {
dwLen = lstrlen (pDest);
}
}
else {
dwLen += dwBufSize;
}
}
LocalFree (pMacroValue);
}
break;
}
}
dwState = NORMALTEXT;
}
else {
if (dwMacroLen < dwMaxMacroLen) {
szMacroName[dwMacroLen ++] = ch;
}
}
break;
}
}
if (dwState == STARTMACRO) {
SetLastError ( ERROR_INVALID_DATA );
}
else {
if (dwLen >= dwDestLen) {
SetLastError (ERROR_INSUFFICIENT_BUFFER);
dwDestLen = dwLen + 1;
}
else {
pDest[dwLen] = 0;
bRet = TRUE;
}
}
return bRet;
}
LPTSTR CPrinter::GetOemUrl(
LPTSTR & pszManufacturer)
{
LPTSTR pszOemUrlPattern = NULL;
DWORD dwLen = 0;
LPTSTR pszUrl = NULL;
if (!GetPrinterInfo2 () )
goto Cleanup;
if (GetDriverData (OEMUrlPattern, pszOemUrlPattern)) {
if (! ParseUrlPattern (pszOemUrlPattern, NULL, dwLen)
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
m_pszOemUrl = (LPTSTR) LocalAlloc (LPTR, sizeof (TCHAR) * dwLen);
if (m_pszOemUrl) {
if (!ParseUrlPattern (pszOemUrlPattern, m_pszOemUrl, dwLen))
goto Cleanup;
}
else
goto Cleanup;
}
}
if (GetDriverData (Manufacturer, m_pszManufacturer)) {
pszManufacturer = m_pszManufacturer;
pszUrl = m_pszOemUrl;
}
Cleanup:
if (pszOemUrlPattern) {
LocalFree ( pszOemUrlPattern);
}
return pszUrl;
}