windows-nt/Source/XPSP1/NT/printscan/ui/printui/datar.cxx
2020-09-26 16:20:57 +08:00

1935 lines
40 KiB
C++

/*++
Copyright (C) Microsoft Corporation, 1995 - 1998
All rights reserved.
Module Name:
datar.cxx
Abstract:
VDataRefresh routines. Handles talking to downlevel clients
(NT 3.5, wfw, lm, win95) which do not support full notifications.
Note that the spooler simulates all single DWORD notifications
for down-down level clients (wfw, lm, win95) so we don't have
to handle polling.
Author:
Albert Ting (AlbertT) 07-12-95
Revision History:
--*/
#include "precomp.hxx"
#pragma hdrstop
#if DBG
//#define DBG_DATARINFO DBG_INFO
#define DBG_DATARINFO DBG_NONE
#endif
/********************************************************************
VDataRefresh handling: downlevel case.
********************************************************************/
VDataRefresh::
VDataRefresh(
IN MDataClient* pDataClient,
IN PFIELD_TABLE pFieldTable,
IN DWORD fdwWatch
) : VData( pDataClient, pFieldTable ), _fdwWatch( fdwWatch )
{
ExecGuard._hPrinterWait = NULL;
}
VDataRefresh::
~VDataRefresh(
VOID
)
{
SPLASSERT( !ExecGuard._hPrinterWait );
}
VOID
VDataRefresh::
vProcessNotifyWork(
IN TNotify* pNotify
)
{
UNREFERENCED_PARAMETER( pNotify );
DWORD dwChange;
//
// Notification caught. We must signal that caught it before
// issuing the refresh, since there may be notifications between
// the refresh and the FNPCN, which would be lost.
//
// Just issue a refresh since we don't support hot notifications.
//
BOOL bSuccess = FindNextPrinterChangeNotification( m_shNotify,
&dwChange,
0,
NULL );
if( !bSuccess ){
DBGMSG( DBG_WARN,
( "DataRefresh.vProcessNotifyWork: %x FNPCN %x failed: %d\n",
this,
static_cast<HANDLE>(m_shNotify),
GetLastError( )));
INFO Info;
Info.dwData = TPrinter::kExecReopen | TPrinter::kExecDelay;
_pDataClient->vContainerChanged( kContainerStateVar, Info );
return;
}
//
// Filter out any extranous watch flags that were set. This
// may happen when the printer goes down and all flags are set.
//
if( dwChange & ~_fdwWatch ){
DBGMSG( DBG_WARN,
( "VDataRefresh:vProcessNotifyWork: extra flags %x: %x %x\n",
this, dwChange, _fdwWatch ));
}
dwChange &= _fdwWatch;
CONTAINER_CHANGE ContainerChange = kContainerNull;
INFO Info;
Info.dwData = TPrinter::kExecRefresh;
if( dwChange & PRINTER_CHANGE_PRINTER ){
Info.dwData |= TPrinter::kExecRefreshContainer;
}
if( dwChange & PRINTER_CHANGE_JOB ){
Info.dwData |= TPrinter::kExecRefreshItem;
}
_pDataClient->vContainerChanged( kContainerStateVar, Info );
}
/********************************************************************
Worker thread functions for Refresh.
********************************************************************/
STATEVAR
VDataRefresh::
svNotifyStart(
IN STATEVAR StateVar
)
/*++
Routine Description:
Begin downlevel notifications.
HACK: To support downlevel providers that don't support
F*PCN calls, we must open a separate handle.
Arguments:
Return Value:
--*/
{
TStatus Status;
TStatusB bStatus;
ExecGuard._hPrinterWait = _pDataClient->hPrinterNew();
if( !ExecGuard._hPrinterWait ){
goto Fail;
}
//
// Get the notification handle.
//
m_shNotify = FindFirstPrinterChangeNotification(
ExecGuard._hPrinterWait,
_fdwWatch,
0,
0 );
if( !m_shNotify )
{
DBGMSG( DBG_WARN, ( "DataRefresh.svNotifyStart: FFPCN failed %d\n", GetLastError( )));
goto Fail;
}
DBGMSG( DBG_NOTIFY,
( "DataRefresh.svNotifyStart: %x FFPCN success returns 0x%x\n",
_pDataClient, static_cast<HANDLE>(m_shNotify) ));
//
// Successfully opened, request that it be registered and then
// refresh.
//
return (StateVar & ~TPrinter::kExecNotifyStart) |
TPrinter::kExecRegister | TPrinter::kExecRefreshAll;
Fail:
//
// Force a reopen. Everything gets reset (handles closed, etc.) when
// the reopen occurs.
//
return StateVar | TPrinter::kExecDelay | TPrinter::kExecReopen;
}
STATEVAR
VDataRefresh::
svNotifyEnd(
IN STATEVAR StateVar
)
/*++
Routine Description:
Stop downlevel notifications.
Arguments:
Return Value:
--*/
{
TStatusB bStatus;
DBGMSG( DBG_NOTIFY, ( "DataRefresh.svNotifyEnd: handle %x\n", static_cast<HANDLE>(m_shNotify) ));
//
// Unregister from TNotify.
//
_pPrintLib->pNotify()->sUnregister( this );
//
// If we have a notification event, close it.
//
m_shNotify = NULL;
//
// Close our separate printer.
//
if( ExecGuard._hPrinterWait ){
bStatus DBGCHK = ClosePrinter( ExecGuard._hPrinterWait );
ExecGuard._hPrinterWait = NULL;
}
return StateVar & ~TPrinter::kExecNotifyEnd;
}
/********************************************************************
Static services.
********************************************************************/
BOOL
VDataRefresh::
bGetPrinter(
IN HANDLE hPrinter,
IN DWORD dwLevel,
IN OUT PVOID* ppvBuffer, CHANGE
IN OUT PDWORD pcbBuffer
)
/*++
Routine Description:
Gets printer information, reallocing as necessary.
Arguments:
hPrinter - Printer to query.
dwLevel - PRINTER_INFO_x level to retrieve.
ppvBuffer - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbBuffer - Initial buffer size. On exit, actual.
Return Value:
TRUE = success, FALSE = fail.
--*/
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPrinter if it's not set.
//
if( !*pcbBuffer ){
*pcbBuffer = kMaxPrinterInfo2;
}
Retry:
SPLASSERT( *pcbBuffer < 0x100000 );
if( !( *ppvBuffer )){
*ppvBuffer = (PVOID)AllocMem( *pcbBuffer );
if( !*ppvBuffer ){
*pcbBuffer = 0;
return FALSE;
}
}
if( !GetPrinter( hPrinter,
dwLevel,
(PBYTE)*ppvBuffer,
*pcbBuffer,
&cbNeeded )){
FreeMem( *ppvBuffer );
*ppvBuffer = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbBuffer = 0;
return FALSE;
}
*pcbBuffer = cbNeeded + kExtraPrinterBufferBytes;
SPLASSERT( *pcbBuffer < 0x100000 );
goto Retry;
}
return TRUE;
}
BOOL
VDataRefresh::
bGetJob(
IN HANDLE hPrinter,
IN DWORD dwJobId,
IN DWORD dwLevel,
IN OUT PVOID* ppvBuffer, CHANGE
IN OUT PDWORD pcbBuffer
)
/*++
Routine Description:
Gets printer job information, reallocing as necessary.
Arguments:
hPrinter - Printer to query.
dwLevel - JOB_INFO_x level to retrieve.
ppvBuffer - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbBuffer - Initial buffer size. On exit, actual.
Return Value:
TRUE = success, FALSE = fail.
--*/
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPrinter if it's not set.
//
if( !*pcbBuffer ){
*pcbBuffer = kInitialJobHint;
}
Retry:
SPLASSERT( *pcbBuffer < 0x100000 );
if( !( *ppvBuffer )){
*ppvBuffer = (PVOID)AllocMem( *pcbBuffer );
if( !*ppvBuffer ){
*pcbBuffer = 0;
return FALSE;
}
}
if( !GetJob( hPrinter,
dwJobId,
dwLevel,
(PBYTE)*ppvBuffer,
*pcbBuffer,
&cbNeeded )){
FreeMem( *ppvBuffer );
*ppvBuffer = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbBuffer = 0;
return FALSE;
}
*pcbBuffer = cbNeeded + kExtraPrinterBufferBytes;
SPLASSERT( *pcbBuffer < 0x100000 );
goto Retry;
}
return TRUE;
}
BOOL
VDataRefresh::
bGetPrinterDriver(
IN HANDLE hPrinter,
IN LPCTSTR pszEnvironment,
IN DWORD dwLevel,
IN OUT PVOID* ppvBuffer, CHANGE
IN OUT PDWORD pcbBuffer
)
/*++
Routine Description:
Gets printer driver information, reallocing as necessary.
Arguments:
hPrinter - Printer to query.
pszEnvironment - Environment.
dwLevel - DRIVER_INFO_x level to retrieve.
ppvBuffer - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbBuffer - Initial buffer size. On exit, actual.
Return Value:
TRUE = success, FALSE = fail.
--*/
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPrinter if it's not set.
//
if( !*pcbBuffer ){
*pcbBuffer = kInitialDriverInfo3Hint;
}
Retry:
if( !( *ppvBuffer )){
*ppvBuffer = (PVOID)AllocMem( *pcbBuffer );
if( !*ppvBuffer ){
*pcbBuffer = 0;
return FALSE;
}
}
if( !GetPrinterDriver( hPrinter,
(LPTSTR)pszEnvironment,
dwLevel,
(PBYTE)*ppvBuffer,
*pcbBuffer,
&cbNeeded )){
FreeMem( *ppvBuffer );
*ppvBuffer = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbBuffer = 0;
return FALSE;
}
*pcbBuffer = cbNeeded;
goto Retry;
}
return TRUE;
}
BOOL
VDataRefresh::
bEnumJobs(
IN HANDLE hPrinter,
IN DWORD dwLevel,
IN OUT PVOID* ppvBuffer, CHANGE
IN OUT PDWORD pcbBuffer,
OUT PDWORD pcJobs
)
/*++
Routine Description:
Enumerates job information, reallocing as necessary.
Arguments:
hPrinter - Printer to query.
dwLevel - JOB_INFO_x level to retrieve.
ppvBuffer - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbBuffer - Initial buffer size. On exit, actual.
pcJobs - Number of jobs returned.
Return Value:
TRUE = success, FALSE = fail.
--*/
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPrinter if it's not set.
//
if( !*pcbBuffer ){
*pcbBuffer = kInitialJobHint;
}
Retry:
if( !( *ppvBuffer )){
*ppvBuffer = (PVOID)AllocMem( *pcbBuffer );
if( !*ppvBuffer ){
*pcbBuffer = 0;
*pcJobs = 0;
return FALSE;
}
}
if( !EnumJobs( hPrinter,
0,
(DWORD)-1,
dwLevel,
(PBYTE)*ppvBuffer,
*pcbBuffer,
&cbNeeded,
pcJobs )){
FreeMem( *ppvBuffer );
*ppvBuffer = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbBuffer = 0;
*pcJobs = 0;
return FALSE;
}
*pcbBuffer = cbNeeded + kExtraJobBufferBytes;
goto Retry;
}
return TRUE;
}
BOOL
VDataRefresh::
bEnumPrinters(
IN DWORD dwFlags,
IN LPCTSTR pszServer,
IN DWORD dwLevel,
IN OUT PVOID* ppvBuffer, CHANGE
IN OUT PDWORD pcbBuffer,
OUT PDWORD pcPrinters
)
/*++
Routine Description:
Enumerates printer information, reallocing as necessary.
Arguments:
dwFlags - Scope of query.
pszServer - Server to query.
dwLevel - PRINTER_INFO_x level to retrieve.
ppvBuffer - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbBuffer - Initial buffer size. On exit, actual.
pcPrinters - Number of printers returned.
Return Value:
TRUE = success, FALSE = fail.
--*/
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPrinter if it's not set.
//
if( !*pcbBuffer ){
*pcbBuffer = kInitialPrinterHint;
}
Retry:
if( !( *ppvBuffer )){
*ppvBuffer = (PVOID)AllocMem( *pcbBuffer );
if( !*ppvBuffer ){
*pcbBuffer = 0;
*pcPrinters = 0;
return FALSE;
}
}
if( !EnumPrinters( dwFlags,
(LPTSTR)pszServer,
dwLevel,
(PBYTE)*ppvBuffer,
*pcbBuffer,
&cbNeeded,
pcPrinters )){
FreeMem( *ppvBuffer );
*ppvBuffer = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbBuffer = 0;
*pcPrinters = 0;
return FALSE;
}
*pcbBuffer = cbNeeded;
goto Retry;
}
return TRUE;
}
BOOL
VDataRefresh::
bEnumDrivers(
IN LPCTSTR pszServer,
IN LPCTSTR pszEnvironment,
IN DWORD dwLevel,
IN OUT PVOID *ppvBuffer, CHANGE
IN OUT PDWORD pcbBuffer,
OUT PDWORD pcDrivers
)
/*++
Routine Description:
Enumerates driver information, reallocing as necessary.
Arguments:
dwFlags - Scope of query.
pszServer - Server to query.
dwLevel - DRIVER_INFO_x level to retrieve.
ppvBuffer - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbBuffer - Initial buffer size. On exit, actual.
pcPrinters - Number of printers returned.
Return Value:
TRUE = success, FALSE = fail.
--*/
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPrinter if it's not set.
//
if( !*pcbBuffer ){
*pcbBuffer = kInitialDriverHint;
}
Retry:
if( !( *ppvBuffer )){
*ppvBuffer = (PVOID)AllocMem( *pcbBuffer );
if( !*ppvBuffer ){
*pcbBuffer = 0;
*pcDrivers = 0;
return FALSE;
}
}
if( !EnumPrinterDrivers( (LPTSTR)pszServer,
(LPTSTR)pszEnvironment,
dwLevel,
(PBYTE)*ppvBuffer,
*pcbBuffer,
&cbNeeded,
pcDrivers )){
FreeMem( *ppvBuffer );
*ppvBuffer = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbBuffer = 0;
*pcDrivers = 0;
return FALSE;
}
*pcbBuffer = cbNeeded;
goto Retry;
}
return TRUE;
}
/*++
Routine Name:
bGetDefaultDevMode
Routine Description:
Allocates the buffer needed to hold the dev mode.
Arguments:
hPrinter - Opened printer handle
pszPrinterName - Pointer to printer name
ppDevMode - Pointer where to return devmode
bFillWithDefault - Flag indicates to fill dev mode with default information.
TRUE fill allocated dev mode, FALSE do not fill
Return Value:
TRUE success, FALSE error occurred.
--*/
BOOL
VDataRefresh::
bGetDefaultDevMode(
IN HANDLE hPrinter,
IN LPCTSTR pszPrinterName,
OUT PDEVMODE *ppDevMode,
IN BOOL bFillWithDefault
)
{
LONG lResult = 0;
PDEVMODE pDevMode = NULL;
//
// Call document properties to get the size of the dev mode.
//
lResult = DocumentProperties( NULL,
hPrinter,
(LPTSTR)pszPrinterName,
NULL,
NULL,
0 );
//
// If the size of the dev mode was returned.
//
if( lResult > 0 )
{
pDevMode = (PDEVMODE)AllocMem( lResult );
}
//
// If allocated then copy back the pointer.
//
if( pDevMode )
{
if( bFillWithDefault )
{
//
// Call document properties to get the default dev mode.
//
lResult = DocumentProperties( NULL,
hPrinter,
(LPTSTR)pszPrinterName,
pDevMode,
NULL,
DM_OUT_BUFFER );
}
if( lResult >= 0 )
{
*ppDevMode = pDevMode;
}
else
{
FreeMem( pDevMode );
}
}
return *ppDevMode != NULL;
}
/*++
Routine Name:
bEnumPorts
Routine Description:
Enumerates the ports on the specified machine.
Arguments:
pszServer - Server to query.
dwLevel - PORT_INFOX level to retrieve.
ppvPorts - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbPorts - Initial buffer size. On exit, actual.
pcPorts - Number of ports returned.
Return Value:
TRUE success, FALSE error occurred.
--*/
BOOL
VDataRefresh::
bEnumPorts(
IN LPCTSTR pszServer,
IN DWORD dwLevel,
IN OUT PVOID *ppvPorts, CHANGE
IN OUT PDWORD pcbPorts,
OUT PDWORD pcPorts
)
{
DWORD cbNeeded;
//
// Pre-initialize *pcbPorts if it's not set.
//
if( !*pcbPorts ){
*pcbPorts = kEnumPortsHint;
}
Retry:
if( !(*ppvPorts)){
*ppvPorts = (PVOID)AllocMem( *pcbPorts );
if( !*ppvPorts ){
*pcbPorts = 0;
*pcPorts = 0;
DBGMSG( DBG_WARN,( "VDataRefresh::bEnumPorts can't alloc %d %d\n", *pcbPorts, GetLastError( )));
return FALSE;
}
}
if( !EnumPorts( (LPTSTR)pszServer,
dwLevel,
(PBYTE)*ppvPorts,
*pcbPorts,
&cbNeeded,
pcPorts ) ){
FreeMem( *ppvPorts );
*ppvPorts = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbPorts = 0;
*pcPorts = 0;
return FALSE;
}
*pcbPorts = cbNeeded;
goto Retry;
}
return TRUE;
}
/*++
Routine Name:
bEnumPortsMaxLevel
Routine Description:
Enumerates the ports on the specified machine. With maximum level
specified.
Arguments:
pszServer - Server to query.
dwLevel - Max PORT_INFOX level to retrieve. We try this level and
decrement until dwLevel is zero.
ppvPorts - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbPorts - Initial buffer size. On exit, actual.
pcPorts - Number of ports returned.
Return Value:
TRUE success, FALSE error occurred.
--*/
BOOL
VDataRefresh::
bEnumPortsMaxLevel(
IN LPCTSTR pszServer,
IN PDWORD pdwLevel,
IN OUT PVOID *ppvPorts, CHANGE
IN OUT PDWORD pcbPorts,
OUT PDWORD pcPorts
)
{
BOOL bStatus = FALSE;
//
// Try all levels to level zero.
//
for( ; *pdwLevel; (*pdwLevel)-- )
{
bStatus = bEnumPorts( pszServer,
*pdwLevel,
ppvPorts,
pcbPorts,
pcPorts );
//
// If the call succeeded then we are done.
//
if( bStatus )
{
break;
}
else
{
//
// The call failed and it is not invalid level then
// exit with error.
//
if( GetLastError() != ERROR_INVALID_LEVEL )
{
break;
}
}
}
return bStatus;
}
/*++
Routine Name:
bEnumPorts
Routine Description:
Enumerates the ports on the specified machine.
Arguments:
pszServer - Server to query.
dwLevel - PORT_INFOX level to retrieve.
ppvPorts - Buffer to store information. If *ppvBuffer is
NULL, then it is allocated. On failure, this buffer is
freed and NULLed
pcbPorts - Initial buffer size. On exit, actual.
pcPorts - Number of ports returned.
Return Value:
TRUE success, FALSE error occurred.
--*/
BOOL
VDataRefresh::
bEnumMonitors(
IN LPCTSTR pszServer,
IN DWORD dwLevel,
IN OUT PVOID *ppvMonitors, CHANGE
IN OUT PDWORD pcbMonitors,
OUT PDWORD pcMonitors
)
{
DWORD cbNeeded;
//
// Pre-initialize *pcbMonitors if it's not set.
//
if( !*pcbMonitors ){
*pcbMonitors = kEnumMonitorsHint;
}
Retry:
if( !(*ppvMonitors)){
*ppvMonitors = (PVOID)AllocMem( *pcbMonitors );
if( !*ppvMonitors ){
*pcbMonitors = 0;
*pcMonitors = 0;
DBGMSG( DBG_WARN,( "VDataRefresh::bEnumMonitors can't alloc %d %d\n", *pcbMonitors, GetLastError( )));
return FALSE;
}
}
if( !EnumMonitors( (LPTSTR)pszServer,
dwLevel,
(PBYTE)*ppvMonitors,
*pcbMonitors,
&cbNeeded,
pcMonitors ) ){
FreeMem( *ppvMonitors );
*ppvMonitors = NULL;
if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ){
*pcbMonitors = 0;
*pcMonitors = 0;
return FALSE;
}
*pcbMonitors = cbNeeded;
goto Retry;
}
return TRUE;
}
/********************************************************************
TDataRJob
********************************************************************/
TDataRJob::
TDataRJob(
IN MDataClient* pDataClient
) : VDataRefresh( pDataClient, &TDataNJob::gFieldTable, kfdwWatch )
{
ExecGuard._cbJobHint = kInitialJobHint;
UIGuard._pJobs = NULL;
}
TDataRJob::
~TDataRJob(
VOID
)
{
FreeMem( UIGuard._pJobs );
}
/********************************************************************
Data interface for Refresh.
********************************************************************/
HITEM
TDataRJob::
GetItem(
IN NATURAL_INDEX NaturalIndex
) const
{
SPLASSERT( UIGuard._pJobs );
SPLASSERT( NaturalIndex < VData::UIGuard._cItems );
return (HITEM)&UIGuard._pJobs[NaturalIndex];
}
HITEM
TDataRJob::
GetNextItem(
IN HITEM hItem
) const
{
SPLASSERT( UIGuard._pJobs );
//
// NULL passed in, return first item.
//
if( !hItem ){
return &UIGuard._pJobs[0];
}
PJOB_INFO_2 pJob2 = (PJOB_INFO_2)hItem;
return (HITEM)( pJob2 + 1 );
}
INFO
TDataRJob::
GetInfo(
IN HITEM hItem,
IN DATA_INDEX DataIndex
) const
{
SPLASSERT( hItem );
SPLASSERT( DataIndex < _pFieldTable->cFields );
INFO Info = kInfoNull;
PJOB_INFO_2 pJob2 = (PJOB_INFO_2)hItem;
BOOL bString = FALSE;
//
// Translate the index into the appropriate field.
//
FIELD Field = _pFieldTable->pFields[DataIndex];
switch( Field ){
case JOB_NOTIFY_FIELD_DOCUMENT:
Info.pszData = pJob2->pDocument;
bString = TRUE;
break;
case JOB_NOTIFY_FIELD_STATUS:
Info.dwData = pJob2->Status;
break;
case JOB_NOTIFY_FIELD_STATUS_STRING:
Info.pszData = pJob2->pStatus;
bString = TRUE;
break;
case JOB_NOTIFY_FIELD_USER_NAME:
Info.pszData = pJob2->pUserName;
bString = TRUE;
break;
case JOB_NOTIFY_FIELD_TOTAL_PAGES:
Info.dwData = pJob2->TotalPages;
break;
case JOB_NOTIFY_FIELD_PAGES_PRINTED:
//
// In chicago, PagesPrinted is overloaded so that if
// TotalPages is zero, PagesPrinted is actually BytesPrinted.
//
Info.dwData = pJob2->TotalPages ?
pJob2->PagesPrinted :
0;
break;
case JOB_NOTIFY_FIELD_TOTAL_BYTES:
Info.dwData = pJob2->Size;
break;
case JOB_NOTIFY_FIELD_BYTES_PRINTED:
//
// In chicago, PagesPrinted is overloaded so that if
// TotalPages is zero, PagesPrinted is actually BytesPrinted.
//
Info.dwData = !pJob2->TotalPages ?
pJob2->PagesPrinted :
0;
break;
case JOB_NOTIFY_FIELD_SUBMITTED:
Info.pSystemTime = &pJob2->Submitted;
break;
case JOB_NOTIFY_FIELD_PORT_NAME:
Info.pszData = gszNULL;
break;
default:
DBGMSG( DBG_ERROR,
( "DataRJob.GetInfo: Unimplemented field %d\n", Field ));
break;
}
if( bString && !Info.pszData ){
Info.pszData = gszNULL;
}
return Info;
}
IDENT
TDataRJob::
GetId(
IN HITEM hItem
) const
{
SPLASSERT( hItem );
PJOB_INFO_2 pJob2 = (PJOB_INFO_2)hItem;
return pJob2->JobId;
}
NATURAL_INDEX
TDataRJob::
GetNaturalIndex(
IN IDENT Id,
OUT PHITEM phItem OPTIONAL
) const
{
COUNT cItems = VData::UIGuard._cItems;
PJOB_INFO_2 pJob2 = UIGuard._pJobs;
if( phItem ){
*phItem = NULL;
}
//
// If no _pJobs, return 0. This may happen if during a refresh,
// the selected item is deleted.
//
if( pJob2 ){
//
// Look for a JobId that matches ours.
//
COUNT i;
for( i = 0; i < cItems; ++i ){
if( pJob2[i].JobId == Id ){
if( phItem ){
*phItem = (HITEM)&pJob2[i];
}
return i;
}
}
}
DBGMSG( DBG_DATARINFO,
( "DataRefresh.GetNaturalIndex: Item %d not found (cItems = %d, pJob2 = %x) %x\n",
Id, cItems, pJob2, this ));
return kInvalidNaturalIndexValue;
}
STATEVAR
TDataRJob::
svRefresh(
IN STATEVAR StateVar
)
/*++
Routine Description:
Refresh the printer data object.
Arguments:
Return Value:
--*/
{
if( !m_shNotify )
{
return TPrinter::kExecReopen;
}
//
// !! HACK !!
//
// Nuke extraneous refreshes by resetting m_shNotify.
//
if( !ResetEvent( m_shNotify ))
{
DBGMSG( DBG_ERROR, ( "DataRefresh.svRefresh: reset %x failed %d\n",
static_cast<HANDLE>(m_shNotify), GetLastError( )));
}
//
// Check if jobs need to be refreshed.
//
if( StateVar & TPrinter::kExecRefreshItem ){
//
// Attempt an enum of approximately the same size as last time.
//
DWORD cJobs = 0;
DWORD cbJobs = ExecGuard._cbJobHint;
PJOB_INFO_2 pJobs = (PJOB_INFO_2)AllocMem( cbJobs );
if( !pJobs ){
goto Fail;
}
TStatusB bStatus( DBG_WARN,
RPC_S_SERVER_UNAVAILABLE,
RPC_S_SERVER_TOO_BUSY,
RPC_S_CALL_FAILED_DNE );
bStatus DBGCHK = bEnumJobs( _pDataClient->hPrinter(),
2,
(PVOID*)&pJobs,
&cbJobs,
&cJobs );
if( bStatus ){
ExecGuard._cbJobHint = cbJobs;
//
// Inform the printer that we have new data.
// vRequestBlockProcess adopts pJobs, so we don't free it here.
//
vBlockAdd( cJobs, 0, (HBLOCK)pJobs );
pJobs = NULL;
}
FreeMem( pJobs );
if( !bStatus ){
//
// Failed; delay then reopen.
//
goto Fail;
}
}
//
// Check if the printer needs to be refreshed.
//
if( StateVar & TPrinter::kExecRefreshContainer ){
//
// Update printer name.
//
PPRINTER_INFO_2 pPrinter2 = NULL;
DWORD cbPrinter2 = kMaxPrinterInfo2;
TStatusB bStatus( DBG_WARN );
bStatus DBGCHK = TDataRJob::bGetPrinter( _pDataClient->hPrinter(),
2,
(PVOID*)&pPrinter2,
&cbPrinter2 );
if( bStatus ){
//
// Inform the printer that we have new data.
// vRequestBlockProcess adopts pPrinter2, so we don't free it here.
//
vBlockAdd( kInvalidCountValue, 0, (HBLOCK)pPrinter2 );
pPrinter2 = NULL;
}
FreeMem( pPrinter2 );
if( !bStatus ){
goto Fail;
}
}
return StateVar & ~TPrinter::kExecRefreshAll;
Fail:
//
// If we get NERR_QNotFound, then this is a masq case where
// the printer is no longer shared. Don't bother retrying.
//
DWORD dwError = GetLastError();
if( dwError == NERR_QNotFound ){
INFO Info;
Info.dwData = kConnectStatusInvalidPrinterName;
_pDataClient->vContainerChanged( kContainerConnectStatus, Info );
return TPrinter::kExecError;
}
//
// !! LATER !!
//
// Put error in status bar.
//
SPLASSERT( dwError );
return StateVar | TPrinter::kExecReopen | TPrinter::kExecDelay;
}
/********************************************************************
UI Thread interaction routines.
********************************************************************/
VOID
TDataRJob::
vBlockProcessImp(
IN DWORD dwParam1,
IN DWORD dwParam2,
IN HBLOCK hBlock ADOPT
)
/*++
Routine Description:
Take a job block and update the internal data structure. This
function will call back into _pPrinter to refresh the screen.
Arguments:
dwParam1 - job count
hBlock - PJOB_INFO_2 block
Return Value:
--*/
{
UNREFERENCED_PARAMETER( dwParam2 );
//
// If dwParam is an invalid count value, then we have printer
// information. Else it is job information.
//
if( dwParam1 == kInvalidCountValue ){
PPRINTER_INFO_2 pPrinter2 = (PPRINTER_INFO_2)hBlock;
INFO Info;
//
// Update all printer information.
//
//
// Note: We do not update strServer, because the printer
// name in PRINTER_INFO_2 already includes the server name!
//
Info.pszData = pPrinter2->pPrinterName;
_pDataClient->vContainerChanged( kContainerName, Info );
Info.dwData = pPrinter2->Status;
_pDataClient->vContainerChanged( kContainerStatus, Info );
Info.dwData = pPrinter2->Attributes;
_pDataClient->vContainerChanged( kContainerAttributes, Info );
FreeMem( pPrinter2 );
} else {
//
// Must save the selections since we are deleting and
// re-adding them.
//
_pDataClient->vSaveSelections();
//
// No need to grab any critical sections since we are in
// the UI thread.
//
FreeMem( UIGuard._pJobs );
UIGuard._pJobs = (PJOB_INFO_2)hBlock;
VData::UIGuard._cItems = dwParam1;
//
// Job count is stored in dwParm; pass to vContainerChanged.
//
INFO Info;
Info.dwData = dwParam1;
_pDataClient->vContainerChanged( kContainerReloadItems, Info );
_pDataClient->vContainerChanged( kContainerRefreshComplete, kInfoNull );
_pDataClient->vRestoreSelections();
}
}
VOID
TDataRJob::
vBlockDelete(
IN HBLOCK hBlock
)
/*++
Routine Description:
Free a Block. Called when the PostMessage fails and the
job block needs to be destroyed.
Arguments:
hBlock - Job block to delete.
Return Value:
--*/
{
FreeMem( hBlock );
}
/********************************************************************
TDataRPrinter
********************************************************************/
TDataRPrinter::
TDataRPrinter(
IN MDataClient* pDataClient
) : VDataRefresh( pDataClient, &TDataNPrinter::gFieldTable, kfdwWatch )
{
ExecGuard._cbPrinterHint = kInitialPrinterHint;
UIGuard._pPrinters = NULL;
//
// Determine whether this is a printer or a server.
//
TCHAR szDataSource[kPrinterBufMax];
LPTSTR pszDataSource = _pDataClient->pszPrinterName( szDataSource );
_bSinglePrinter = TDataRPrinter::bSinglePrinter( pszDataSource );
}
TDataRPrinter::
~TDataRPrinter(
VOID
)
{
FreeMem( UIGuard._pPrinters );
}
BOOL
TDataRPrinter::
bSinglePrinter(
LPCTSTR pszDataSource
)
{
BOOL bReturn = FALSE;
//
// Check if it's a single printer vs. a server. The only
// way I can think of doing this is to look for the format
// "\\server." This works fine for Win9x downlevel printers
// however fails for internet connected printers.
//
// So adding to the hack we will do a little more checking
// and look if the printer name has a prfix string of
// http:// or https://. So what we are saying is that printer
// names of this form are considered a masq printers.
//
if( pszDataSource )
{
bReturn = pszDataSource[0] == TEXT( '\\' ) &&
pszDataSource[1] == TEXT( '\\' ) &&
_tcschr( &pszDataSource[2], TEXT( '\\' ));
if( !bReturn )
{
bReturn = !_tcsnicmp( pszDataSource, gszHttpPrefix0, _tcslen(gszHttpPrefix0) ) ||
!_tcsnicmp( pszDataSource, gszHttpPrefix1, _tcslen(gszHttpPrefix1) );
}
}
return bReturn;
}
/********************************************************************
Data interface for Refresh.
********************************************************************/
HITEM
TDataRPrinter::
GetItem(
IN NATURAL_INDEX NaturalIndex
) const
{
SPLASSERT( UIGuard._pPrinters );
SPLASSERT( NaturalIndex < VData::UIGuard._cItems );
return (HITEM)&UIGuard._pPrinters[NaturalIndex];
}
HITEM
TDataRPrinter::
GetNextItem(
IN HITEM hItem
) const
{
SPLASSERT( UIGuard._pPrinters );
//
// Requesting first item.
//
if( !hItem ){
SPLASSERT( VData::UIGuard._cItems > 0 );
return (HITEM)&UIGuard._pPrinters[0];
}
PPRINTER_INFO_2 pPrinter2 = (PPRINTER_INFO_2)hItem;
return (HITEM)( pPrinter2 + 1 );
}
INFO
TDataRPrinter::
GetInfo(
IN HITEM hItem,
IN DATA_INDEX DataIndex
) const
{
SPLASSERT( hItem );
SPLASSERT( DataIndex < _pFieldTable->cFields );
INFO Info = kInfoNull;
PPRINTER_INFO_2 pPrinter2 = (PPRINTER_INFO_2)hItem;
//
// Translate the index into the appropriate field.
//
FIELD Field = _pFieldTable->pFields[DataIndex];
BOOL bString = FALSE;
switch( Field ){
case PRINTER_NOTIFY_FIELD_PRINTER_NAME:
SPLASSERT( pPrinter2->pPrinterName );
//
// Skip the server prefix and extra backslash.
//
Info.pszData = pPrinter2->pPrinterName +
(pPrinter2->pServerName ? _tcslen( pPrinter2->pServerName ) + 1 : 0);
break;
case PRINTER_NOTIFY_FIELD_CJOBS:
Info.dwData = pPrinter2->cJobs;
break;
case PRINTER_NOTIFY_FIELD_ATTRIBUTES:
Info.dwData = pPrinter2->Attributes;
break;
case PRINTER_NOTIFY_FIELD_STATUS:
Info.dwData = pPrinter2->Status;
break;
case PRINTER_NOTIFY_FIELD_COMMENT:
Info.pszData = pPrinter2->pComment;
bString = TRUE;
break;
case PRINTER_NOTIFY_FIELD_LOCATION:
Info.pszData = pPrinter2->pLocation;
bString = TRUE;
break;
case PRINTER_NOTIFY_FIELD_DRIVER_NAME:
Info.pszData = pPrinter2->pDriverName;
bString = TRUE;
break;
case PRINTER_NOTIFY_FIELD_PORT_NAME:
Info.pszData = pPrinter2->pPortName;
bString = TRUE;
break;
default:
DBGMSG( DBG_ERROR,
( "DataRPrinter.GetInfo: Unimplemented field %d\n", Field ));
break;
}
if( bString && !Info.pszData ){
Info.pszData = gszNULL;
}
return Info;
}
IDENT
TDataRPrinter::
GetId(
IN HITEM hItem
) const
{
SPLASSERT( hItem );
return kInvalidIdentValue;
}
NATURAL_INDEX
TDataRPrinter::
GetNaturalIndex(
IN IDENT Id,
OUT PHITEM phItem OPTIONAL
) const
{
UNREFERENCED_PARAMETER( Id );
UNREFERENCED_PARAMETER( phItem );
return kInvalidNaturalIndexValue;
}
STATEVAR
TDataRPrinter::
svRefresh(
IN STATEVAR StateVar
)
/*++
Routine Description:
Refresh the printer data object.
Arguments:
Return Value:
--*/
{
//
// !! HACK !!
//
// Nuke extraneous refreshes by resetting m_shNotify.
//
if( !ResetEvent( m_shNotify ))
{
DBGMSG( DBG_ERROR, ( "DataRefresh.svRefresh: reset %x failed %d\n",
static_cast<HANDLE>(m_shNotify), GetLastError( )));
}
//
// Attempt an enum of approximately the same size as last time.
//
DWORD cPrinters = 0;
DWORD cbPrinters = ExecGuard._cbPrinterHint;
PPRINTER_INFO_2 pPrinters = (PPRINTER_INFO_2)AllocMem( cbPrinters );
if( pPrinters ){
TStatusB bStatus;
if( _bSinglePrinter ){
cPrinters = 1;
bStatus DBGCHK = TDataRJob::bGetPrinter( _pDataClient->hPrinter(),
2,
(PVOID*)&pPrinters,
&cbPrinters );
} else {
//
// It's a server, but it's stored in the printer name string
// since that's where we put the datasource.
//
TCHAR szServerBuffer[kPrinterBufMax];
LPTSTR pszServer = _pDataClient->pszPrinterName( szServerBuffer );
bStatus DBGCHK = bEnumPrinters( PRINTER_ENUM_NAME,
pszServer,
2,
(PVOID*)&pPrinters,
&cbPrinters,
&cPrinters );
}
if( bStatus ){
ExecGuard._cbPrinterHint = cbPrinters;
//
// Inform the printer that we have new data.
// vRequestBlockProcess adopts pJobs, so we don't free it here.
//
vBlockAdd( cPrinters, 0, (HBLOCK)pPrinters );
return StateVar & ~TPrinter::kExecRefreshAll;
} else {
//
// If we get NERR_QNotFound, then this is a masq case where
// the printer is no longer shared. Don't bother retrying.
//
DWORD dwError = GetLastError();
INFO Info;
if( dwError == ERROR_ACCESS_DENIED ){
Info.dwData = kConnectStatusAccessDenied;
} else {
Info.dwData = kConnectStatusInvalidPrinterName;
}
_pDataClient->vContainerChanged( kContainerConnectStatus, Info );
return TPrinter::kExecError;
}
}
//
// !! LATER !!
//
// Put error in status bar.
//
SPLASSERT( GetLastError( ));
return StateVar | TPrinter::kExecReopen | TPrinter::kExecDelay;
}
/********************************************************************
UI Thread interaction routines.
********************************************************************/
VOID
TDataRPrinter::
vBlockProcessImp(
IN DWORD dwParam1,
IN DWORD dwParam2,
IN HBLOCK hBlock ADOPT
)
/*++
Routine Description:
Take a item block and update the internal data structure. This
function will call back into _pPrinter to refresh the screen.
Arguments:
dwParam1 - Item count
hBlock - PJOB_INFO_2 block
Return Value:
--*/
{
UNREFERENCED_PARAMETER( dwParam2 );
//
// No need to grab any critical sections since we are in
// the UI thread.
//
FreeMem( UIGuard._pPrinters );
UIGuard._pPrinters = (PPRINTER_INFO_2)hBlock;
VData::UIGuard._cItems = dwParam1;
//
// Item count is stored in dwParm; pass to vContainerChanged.
//
INFO Info;
Info.dwData = dwParam1;
_pDataClient->vContainerChanged( kContainerReloadItems, Info );
_pDataClient->vContainerChanged( kContainerRefreshComplete, kInfoNull );
}
VOID
TDataRPrinter::
vBlockDelete(
IN HBLOCK hBlock
)
/*++
Routine Description:
Free a Block. Called when the PostMessage fails and the
Item block needs to be destroyed.
Arguments:
hBlock - Item block to delete.
Return Value:
--*/
{
FreeMem( hBlock );
}