mirror of https://github.com/lianthony/NT4.0
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.
1415 lines
29 KiB
1415 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
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
|
|
|
|
/********************************************************************
|
|
|
|
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( _hNotify,
|
|
&dwChange,
|
|
0,
|
|
NULL );
|
|
|
|
if( !bSuccess ){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "DataRefresh.vProcessNotifyWork: %x FNPCN %x failed: %d\n",
|
|
this,
|
|
_hNotify,
|
|
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.
|
|
//
|
|
_hNotify = FindFirstPrinterChangeNotification(
|
|
ExecGuard._hPrinterWait,
|
|
_fdwWatch,
|
|
0,
|
|
0 );
|
|
|
|
if( _hNotify == INVALID_HANDLE_VALUE ){
|
|
_hNotify = NULL;
|
|
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, _hNotify ));
|
|
|
|
//
|
|
// 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", _hNotify ));
|
|
//
|
|
// Unregister from TNotify.
|
|
//
|
|
gpPrintLib->pNotify()->sUnregister( this );
|
|
|
|
//
|
|
// If we have a notification event, close it.
|
|
//
|
|
if( _hNotify ){
|
|
|
|
bStatus DBGCHK = FindClosePrinterChangeNotification( _hNotify );
|
|
_hNotify = 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::
|
|
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;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
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_INFO,
|
|
( "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( !_hNotify ){
|
|
return TPrinter::kExecReopen;
|
|
}
|
|
|
|
//
|
|
// !! HACK !!
|
|
//
|
|
// Nuke extraneous refreshes by resetting _hNotify.
|
|
//
|
|
if( !ResetEvent( _hNotify )){
|
|
|
|
DBGMSG( DBG_ERROR, ( "DataRefresh.svRefresh: reset %x failed %d\n",
|
|
_hNotify, 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
|
|
)
|
|
{
|
|
//
|
|
// 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."
|
|
//
|
|
return pszDataSource &&
|
|
pszDataSource[0] == TEXT( '\\' ) &&
|
|
pszDataSource[1] == TEXT( '\\' ) &&
|
|
_tcschr( &pszDataSource[2], TEXT( '\\' ));
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
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 );
|
|
SPLASSERT( pPrinter2->pServerName );
|
|
|
|
//
|
|
// Skip the server prefix and extra backslash.
|
|
//
|
|
|
|
//
|
|
// !! LATER !!
|
|
//
|
|
// The pServerName should be cached since it doesn't change.
|
|
//
|
|
Info.pszData = pPrinter2->pPrinterName +
|
|
_tcslen( pPrinter2->pServerName ) + 1;
|
|
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;
|
|
|
|
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 _hNotify.
|
|
//
|
|
if( !ResetEvent( _hNotify )){
|
|
|
|
DBGMSG( DBG_ERROR, ( "DataRefresh.svRefresh: reset %x failed %d\n",
|
|
_hNotify, 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();
|
|
|
|
if( dwError == NERR_QNotFound ){
|
|
|
|
INFO Info;
|
|
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 );
|
|
}
|
|
|