1860 lines
41 KiB
C++
1860 lines
41 KiB
C++
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
datan.cxx
|
|
|
|
Abstract:
|
|
|
|
vDataNotify class, handles communication with uplevel (3.51+)
|
|
clients. Data is returned with each notification, so it
|
|
is unnecessary to call refresh.
|
|
|
|
Author:
|
|
|
|
Albert Ting (AlbertT) 07-11-95
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#if DBG
|
|
//#define DBG_DATANINFO DBG_INFO
|
|
#define DBG_DATANINFO DBG_NONE
|
|
#endif
|
|
|
|
#define DEFINE( field, attrib, table, y, z )\
|
|
table,
|
|
|
|
TABLE aTableJob[] = {
|
|
#include "ntfyjob.h"
|
|
0 };
|
|
|
|
TABLE aTablePrinter[] = {
|
|
#include "ntfyprn.h"
|
|
0 };
|
|
|
|
#undef DEFINE
|
|
|
|
TABLE* aaTable[2] = {
|
|
aTablePrinter,
|
|
aTableJob
|
|
};
|
|
|
|
/********************************************************************
|
|
|
|
Hard code the notification structure for uplevel clients.
|
|
Later this can be changed for dynamic creation in case the
|
|
user wants to change the columns.
|
|
|
|
********************************************************************/
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Jobs.
|
|
|
|
********************************************************************/
|
|
|
|
//
|
|
// Index -> Field translation. Used by TData*, and also
|
|
// in the PRINTER_NOTIFY_OPTIONS_TYPE structure for FFPCN.
|
|
//
|
|
FIELD_TABLE
|
|
TDataNJob::gFieldTable = {
|
|
TDataNJob::kFieldTableSize,
|
|
TDataNJob::gaFields
|
|
};
|
|
|
|
FIELD
|
|
TDataNJob::gaFields[TDataNJob::kFieldTableSize+1] = {
|
|
JOB_COLUMN_FIELDS,
|
|
JOB_INDEX_EXTRA_FIELDS,
|
|
kInvalidFieldValue
|
|
};
|
|
|
|
|
|
FIELD
|
|
TDataNJob::gaFieldOther[TDataNJob::kFieldOtherSize] = {
|
|
PRINTER_NOTIFY_FIELD_STATUS,
|
|
PRINTER_NOTIFY_FIELD_SERVER_NAME,
|
|
PRINTER_NOTIFY_FIELD_PRINTER_NAME,
|
|
PRINTER_NOTIFY_FIELD_ATTRIBUTES
|
|
};
|
|
|
|
PRINTER_NOTIFY_OPTIONS_TYPE
|
|
TDataNJob::gaNotifyOptionsType[TDataNJob::kTypeSize] = {
|
|
{
|
|
JOB_NOTIFY_TYPE,
|
|
0,
|
|
0,
|
|
0,
|
|
TDataNJob::kFieldTableSize,
|
|
TDataNJob::gaFields
|
|
},
|
|
{
|
|
PRINTER_NOTIFY_TYPE,
|
|
0,
|
|
0,
|
|
0,
|
|
COUNTOF( TDataNJob::gaFieldOther ),
|
|
TDataNJob::gaFieldOther
|
|
}
|
|
};
|
|
|
|
PRINTER_NOTIFY_OPTIONS
|
|
TDataNJob::gNotifyOptions = {
|
|
2,
|
|
0,
|
|
COUNTOF( TDataNJob::gaNotifyOptionsType ),
|
|
TDataNJob::gaNotifyOptionsType
|
|
};
|
|
|
|
/********************************************************************
|
|
|
|
Printers.
|
|
|
|
********************************************************************/
|
|
|
|
//
|
|
// Index -> Field translation. Used by TData*, and also
|
|
// in the PRINTER_NOTIFY_OPTIONS_TYPE structure for FFPCN.
|
|
//
|
|
FIELD_TABLE
|
|
TDataNPrinter::gFieldTable = {
|
|
TDataNPrinter::kFieldTableSize,
|
|
TDataNPrinter::gaFields
|
|
};
|
|
|
|
FIELD
|
|
TDataNPrinter::gaFields[TDataNPrinter::kFieldTableSize+1] = {
|
|
PRINTER_COLUMN_FIELDS,
|
|
PRINTER_INDEX_EXTRA_FIELDS,
|
|
kInvalidFieldValue
|
|
};
|
|
|
|
PRINTER_NOTIFY_OPTIONS_TYPE
|
|
TDataNPrinter::gaNotifyOptionsType[TDataNPrinter::kTypeSize] = {
|
|
{
|
|
PRINTER_NOTIFY_TYPE,
|
|
0,
|
|
0,
|
|
0,
|
|
TDataNPrinter::kFieldTableSize,
|
|
TDataNPrinter::gaFields
|
|
}
|
|
};
|
|
|
|
PRINTER_NOTIFY_OPTIONS
|
|
TDataNPrinter::gNotifyOptions = {
|
|
2,
|
|
0,
|
|
COUNTOF( TDataNPrinter::gaNotifyOptionsType ),
|
|
TDataNPrinter::gaNotifyOptionsType
|
|
};
|
|
|
|
/********************************************************************
|
|
|
|
VDataNotify
|
|
|
|
Used in uplevel (NT 3.51+) connections
|
|
|
|
********************************************************************/
|
|
|
|
VDataNotify::
|
|
VDataNotify(
|
|
IN MDataClient* pDataClient,
|
|
IN PFIELD_TABLE pFieldTable,
|
|
IN DWORD TypeItem
|
|
) : VData( pDataClient, pFieldTable ), _TypeItem( TypeItem )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the DataNotify Object.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
}
|
|
|
|
VDataNotify::
|
|
~VDataNotify(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the DataNotifyObject.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
vDeleteAllItemData();
|
|
SPLASSERT( !m_shNotify );
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
VDataNotify::
|
|
vProcessNotifyWork(
|
|
IN TNotify* pNotify
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Our event was signaled, so we need to pickup any notifications.
|
|
|
|
This must execute quickly, since it runs in the notification
|
|
thread.
|
|
|
|
Arguments:
|
|
|
|
pNotify - Notification manager object.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER( pNotify );
|
|
|
|
DWORD dwChange;
|
|
PPRINTER_NOTIFY_INFO pInfo = NULL;
|
|
|
|
//
|
|
// Notification caught. We must signal that caught it before
|
|
// issuing the Notify, since there may be notifications between
|
|
// the Notify and the FNPCN, which would be lost.
|
|
//
|
|
BOOL bSuccess = FindNextPrinterChangeNotification( m_shNotify,
|
|
&dwChange,
|
|
0,
|
|
(PVOID*)&pInfo );
|
|
|
|
if( !bSuccess || dwChange & PRINTER_CHANGE_FAILED_CONNECTION_PRINTER ){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "DataNotify.vProcessNotifyWork: %x FNPCN %x dwChange %d failed: %d\n",
|
|
this,
|
|
static_cast<HANDLE>(m_shNotify),
|
|
dwChange,
|
|
GetLastError( )));
|
|
}
|
|
|
|
//
|
|
// Check for a discard.
|
|
//
|
|
if( !pInfo || (pInfo->Flags & PRINTER_NOTIFY_INFO_DISCARDED )){
|
|
|
|
DBGMSG( DBG_NOTIFY,
|
|
( "DataNotify.vProcessNotifyWork: %x discard %x pInfo %x %x\n",
|
|
this,
|
|
static_cast<HANDLE>(m_shNotify),
|
|
pInfo,
|
|
pInfo ?
|
|
pInfo->Flags :
|
|
0 ));
|
|
|
|
INFO Info;
|
|
Info.dwData = TPrinter::kExecRefresh;
|
|
|
|
_pDataClient->vContainerChanged( kContainerStateVar, Info );
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
vDbgOutputInfo( pInfo );
|
|
#endif
|
|
|
|
//
|
|
// Post the work to the message thread. If pInfo is NULL, then
|
|
// a reopen will be requested.
|
|
//
|
|
vBlockAdd( kProcessIncremental, dwChange, (HBLOCK)pInfo );
|
|
|
|
//
|
|
// vBlockAdd has adopted pInfo; set it to NULL
|
|
// so we don't free it.
|
|
//
|
|
pInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Since we adopted pInfo, free it now if we haven't passed it to
|
|
// some other function.
|
|
//
|
|
if( pInfo ){
|
|
FreePrinterNotifyInfo( pInfo );
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Data interface for Notify.
|
|
|
|
********************************************************************/
|
|
|
|
HITEM
|
|
VDataNotify::
|
|
GetItem(
|
|
IN NATURAL_INDEX NaturalIndex
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve a handle to the item based on the NaturalIndex.
|
|
|
|
The data is stored as a linked list, so walk the list
|
|
and return a pointer to TItemData*.
|
|
|
|
Arguments:
|
|
|
|
NaturalIndex - Index of printing order for index to retrieve.
|
|
|
|
Return Value:
|
|
|
|
HITEM, of NULL if ItemData not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
SPLASSERT( NaturalIndex < VData::UIGuard._cItems );
|
|
|
|
TIter Iter;
|
|
|
|
for( UIGuard.ItemData_vIterInit( Iter ), Iter.vNext();
|
|
Iter.bValid();
|
|
Iter.vNext(), --NaturalIndex ){
|
|
|
|
if( !NaturalIndex ){
|
|
return (HITEM)UIGuard.ItemData_pConvert( Iter );
|
|
}
|
|
}
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "DataNotify.GetItem: NI %d not found %x\n", this ));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HITEM
|
|
VDataNotify::
|
|
GetNextItem(
|
|
IN HITEM hItem
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the next item based on the one passed in.
|
|
|
|
There is no runtime checking to ensure that client doesn't walk
|
|
off the end of the list.
|
|
|
|
Arguments:
|
|
|
|
hItem - Handle to item immediately before the one returned.
|
|
If NULL, returns first item.
|
|
|
|
Return Value:
|
|
|
|
Next item after hItem.
|
|
|
|
--*/
|
|
|
|
{
|
|
TItemData* pItemData = (TItemData*)hItem;
|
|
|
|
//
|
|
// NULL passed in, return first item.
|
|
//
|
|
if( !hItem ){
|
|
SPLASSERT( VData::UIGuard._cItems > 0 );
|
|
return UIGuard.ItemData_pHead();
|
|
}
|
|
|
|
//
|
|
// Ensure we are not walking off the end of the list.
|
|
//
|
|
SPLASSERT( UIGuard.ItemData_bValid( pItemData->ItemData_pdlNext( )));
|
|
|
|
return (HITEM)( pItemData->ItemData_pNext( ));
|
|
}
|
|
|
|
INFO
|
|
VDataNotify::
|
|
GetInfo(
|
|
IN HITEM hItem,
|
|
IN DATA_INDEX DataIndex
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns information about a item based on the index.
|
|
|
|
Arguments:
|
|
|
|
hItem - Item to get information about.
|
|
|
|
DataIndex - Index into pFieldTable->pFields, indicating type of
|
|
information requested.
|
|
|
|
Return Value:
|
|
|
|
INFO - Information about item.
|
|
|
|
--*/
|
|
|
|
{
|
|
TItemData* pItemData = (TItemData*)hItem;
|
|
|
|
//
|
|
// We don't need to do a translation since they are stored in
|
|
// index format anyway.
|
|
//
|
|
SPLASSERT( DataIndex < _pFieldTable->cFields );
|
|
|
|
return pItemData->_aInfo[DataIndex];
|
|
}
|
|
|
|
IDENT
|
|
VDataNotify::
|
|
GetId(
|
|
IN HANDLE hItem
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves JobId from hItem.
|
|
|
|
Arguments:
|
|
|
|
hItem - Item to retrieve Id from.
|
|
|
|
Return Value:
|
|
|
|
ID.
|
|
|
|
--*/
|
|
|
|
{
|
|
TItemData* pItemData = (TItemData*)hItem;
|
|
return pItemData->_Id;
|
|
}
|
|
|
|
NATURAL_INDEX
|
|
VDataNotify::
|
|
GetNaturalIndex(
|
|
IN IDENT Id,
|
|
OUT HITEM* phItem OPTIONAL
|
|
) const
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves NaturalIndex, and optionally hItem based on Id.
|
|
|
|
Arguments:
|
|
|
|
Id - Job Id to search for.
|
|
|
|
phItem - Optional, receives handle to Job matching Job Id.
|
|
|
|
Return Value:
|
|
|
|
NATURAL_INDEX.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Scan through linked list looking for the right ID.
|
|
//
|
|
TIter Iter;
|
|
|
|
NATURAL_INDEX NaturalIndex;
|
|
TItemData* pItemData;
|
|
|
|
if( phItem ){
|
|
*phItem = NULL;
|
|
}
|
|
|
|
for( NaturalIndex = 0, UIGuard.ItemData_vIterInit( Iter ), Iter.vNext();
|
|
Iter.bValid();
|
|
++NaturalIndex, Iter.vNext( )){
|
|
|
|
pItemData = UIGuard.ItemData_pConvert( Iter );
|
|
|
|
if( Id == pItemData->Id( )){
|
|
if( phItem ){
|
|
*phItem = (PHITEM)pItemData;
|
|
}
|
|
return NaturalIndex;
|
|
}
|
|
}
|
|
|
|
DBGMSG( DBG_DATANINFO,
|
|
( "DataNotify.GetNaturalIndex: ItemData id %x not found %x\n", Id, this ));
|
|
|
|
return kInvalidNaturalIndexValue;
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
VDataNotify::TItemData
|
|
|
|
********************************************************************/
|
|
|
|
VDataNotify::TItemData*
|
|
VDataNotify::TItemData::
|
|
pNew(
|
|
IN VDataNotify* pDataNotify,
|
|
IN IDENT Id
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a new TItemData* structure. Note that this is not a first-
|
|
class C++ object; it is a variable size structure. Consequently,
|
|
it must be allocated and freed using pNew and vDelete.
|
|
|
|
Note: if the pData->pPrinter->pFieldsFromIndex changes, the
|
|
TItemData* members must be deleted first, since we need to
|
|
delete strings.
|
|
|
|
We could implement deletion of data members using an
|
|
abc, but this would require storing a vtbl with each field.
|
|
A better way would be to implement singleton classes where the
|
|
data is passed in explicitly.
|
|
|
|
Arguments:
|
|
|
|
pDataNotify - Owning pDataNotify. We need this to determine
|
|
how bit the aInfo array should be.
|
|
|
|
Id - Job Id.
|
|
|
|
Return Value:
|
|
|
|
TItemData* or NULL on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT uSize = sizeof( TItemData ) +
|
|
( pDataNotify->_pFieldTable->cFields - 1 ) * sizeof( INFO );
|
|
TItemData* pItemData = (TItemData*)AllocMem( uSize );
|
|
|
|
if( !pItemData ){
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory( pItemData, uSize );
|
|
pItemData->_pDataNotify = pDataNotify;
|
|
pItemData->_Id = Id;
|
|
|
|
return pItemData;
|
|
}
|
|
|
|
VOID
|
|
VDataNotify::TItemData::
|
|
vDelete(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the TItemData. This routine must be called instead of using
|
|
the delete operator.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PFIELD pFields = _pDataNotify->_pFieldTable->pFields;
|
|
COUNT cItemData = _pDataNotify->_pFieldTable->cFields;
|
|
PINFO pInfo;
|
|
UINT i;
|
|
|
|
for( i= 0, pInfo = _aInfo;
|
|
i < cItemData;
|
|
++i, ++pInfo, ++pFields ){
|
|
|
|
if( aaTable[_pDataNotify->_TypeItem][*pFields] != TABLE_DWORD ){
|
|
FreeMem( pInfo->pvData );
|
|
}
|
|
}
|
|
|
|
FreeMem( this );
|
|
}
|
|
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Worker thread functions for Notify.
|
|
|
|
********************************************************************/
|
|
|
|
STATEVAR
|
|
VDataNotify::
|
|
svNotifyStart(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Begin uplevel notifcations.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
TStatus Status;
|
|
|
|
//
|
|
// Get the notification handle.
|
|
//
|
|
m_shNotify = FindFirstPrinterChangeNotification(
|
|
_pDataClient->hPrinter(),
|
|
0,
|
|
0,
|
|
pNotifyOptions( ));
|
|
|
|
if( !m_shNotify )
|
|
{
|
|
DBGMSG( DBG_WARN,
|
|
( "DataNotify.svNotifyStart: FFPCN failed %d\n",
|
|
GetLastError( )));
|
|
goto Fail;
|
|
}
|
|
|
|
DBGMSG( DBG_NOTIFY,
|
|
( "DataNotify.svNotifyStart: %x FFPCN success returns 0x%x\n",
|
|
_pDataClient->hPrinter(),
|
|
static_cast<HANDLE>(m_shNotify) ));
|
|
|
|
//
|
|
// Successfully opened, request that it be registered and then
|
|
// refresh.
|
|
//
|
|
return (StateVar & ~TPrinter::kExecNotifyStart) |
|
|
TPrinter::kExecRegister | TPrinter::kExecRefresh;
|
|
|
|
Fail:
|
|
|
|
//
|
|
// Force a reopen. Everything gets reset (handles closed, etc.) when
|
|
// the reopen occurs.
|
|
//
|
|
return StateVar | TPrinter::kExecDelay | TPrinter::kExecReopen;
|
|
}
|
|
|
|
STATEVAR
|
|
VDataNotify::
|
|
svNotifyEnd(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stop downlevel notifications.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
DBGMSG( DBG_NOTIFY, ( "DataNotify.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;
|
|
|
|
return StateVar & ~TPrinter::kExecNotifyEnd;
|
|
}
|
|
|
|
STATEVAR
|
|
VDataNotify::
|
|
svRefresh(
|
|
IN STATEVAR StateVar
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Refresh the printer data object.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PRINTER_NOTIFY_OPTIONS Options;
|
|
PPRINTER_NOTIFY_INFO pInfo;
|
|
BOOL bReturn;
|
|
DWORD dwChange;
|
|
|
|
//
|
|
// We must unregister ourselves to guarantee that there
|
|
// are no synchronization problems:
|
|
//
|
|
// There are two threads: this one is requesting a refresh,
|
|
// while the other one is waiting for notifications about a
|
|
// printer. When the notify thread gets a change, it puts
|
|
// it on a linked list.
|
|
//
|
|
// If you are still registered, then there is a window where
|
|
// you can get a notification, and put it on your linked list
|
|
// before you call FNPCN to do the refresh. When you call
|
|
// FNPCN, the spooler will clear out any stale data in its
|
|
// datastructure, but not our internal linked list.
|
|
//
|
|
// The fix is to remove ourselves from the notification list
|
|
// (via sUnregister). handle the refresh, then add ourselves back
|
|
// to the list (sRegister), thus preventing any stale data
|
|
// from coming from the notification thread.
|
|
//
|
|
|
|
_pPrintLib->pNotify()->sUnregister( this );
|
|
|
|
Options.Version = 2;
|
|
Options.Flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
|
|
Options.Count = 0;
|
|
Options.pTypes = NULL;
|
|
|
|
bReturn = FindNextPrinterChangeNotification(
|
|
m_shNotify,
|
|
&dwChange,
|
|
&Options,
|
|
(PVOID*)&pInfo);
|
|
|
|
if( !bReturn || !pInfo ){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "DataNotify.svRefresh Failed %d\n", GetLastError( )));
|
|
|
|
return StateVar | TPrinter::kExecReopen | TPrinter::kExecDelay;
|
|
}
|
|
|
|
vBlockAdd( kProcessRefresh, 0, (HBLOCK)pInfo );
|
|
|
|
TStatus Status;
|
|
Status DBGCHK = _pPrintLib->pNotify()->sRegister( this );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
|
|
//
|
|
// Failed to register; delay then reopen printer. We could
|
|
// just try and re-register later, but this should be a very
|
|
// rare event, so do the least amount of work.
|
|
//
|
|
DBGMSG( DBG_WARN,
|
|
( "DataNotify.svRefresh: sRegister %x failed %d\n",
|
|
this, Status ));
|
|
|
|
StateVar |= TPrinter::kExecDelay | TPrinter::kExecReopen;
|
|
}
|
|
|
|
return StateVar & ~TPrinter::kExecRefreshAll;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
UI Thread interaction routines.
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
VDataNotify::
|
|
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 _pDataClient to Notify the screen.
|
|
|
|
Arguments:
|
|
|
|
dwParam1 - job count
|
|
|
|
dwParam2 - Change flags.
|
|
|
|
hBlock - PJOB_INFO_2 block
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER( dwParam2 );
|
|
SPLASSERT( hBlock );
|
|
|
|
BOOL bRestoreSelections = FALSE;
|
|
|
|
//
|
|
// If this is a complete refresh, then clear everything.
|
|
//
|
|
if( dwParam1 == kProcessRefresh ){
|
|
|
|
//
|
|
// Save the selections since we are refreshing jobs.
|
|
//
|
|
_pDataClient->vSaveSelections();
|
|
bRestoreSelections = TRUE;
|
|
|
|
//
|
|
// Clear out all jobs. Below in pInfo processing,
|
|
// we will add them back in.
|
|
//
|
|
_pDataClient->vContainerChanged( kContainerReloadItems, kInfoNull );
|
|
|
|
//
|
|
// Delete our data structure.
|
|
//
|
|
vDeleteAllItemData();
|
|
|
|
DBGMSG( DBG_DATANINFO,
|
|
( "DataNotify.vBlockProcessImp: DataN %x P:%x %x Refresh\n",
|
|
this, _pDataClient, hBlock ));
|
|
}
|
|
|
|
//
|
|
// No need to grab any critical sections since we are in
|
|
// the UI thread.
|
|
//
|
|
PPRINTER_NOTIFY_INFO_DATA pData;
|
|
PPRINTER_NOTIFY_INFO pInfo = (PPRINTER_NOTIFY_INFO)hBlock;
|
|
COUNT i;
|
|
|
|
CACHE Cache;
|
|
|
|
Cache.pItemData = NULL;
|
|
Cache.Id = kInvalidIdentValue;
|
|
Cache.NaturalIndex = kInvalidNaturalIndexValue;
|
|
Cache.bNew = FALSE;
|
|
|
|
#if DBG
|
|
vDbgOutputInfo( pInfo );
|
|
#endif
|
|
|
|
for( pData = pInfo->aData, i = pInfo->Count; i; --i, ++pData ){
|
|
|
|
//
|
|
// Switch based on type.
|
|
//
|
|
if( pData->Type != _TypeItem ){
|
|
|
|
//
|
|
// Looks for changes in status, attributes, or name.
|
|
//
|
|
vContainerProcess( pData );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Process changes in jobs. We cache the latest Id->
|
|
// NaturalIndex mapping since they tend to come in clumps.
|
|
//
|
|
if( !bItemProcess( pData, Cache )){
|
|
|
|
INFO Info;
|
|
Info.dwData = TPrinter::kExecRefresh | TPrinter::kExecDelay;
|
|
|
|
_pDataClient->vContainerChanged( kContainerStateVar, Info );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Notify the client that the refresh block has been processed.
|
|
// When processing a refresh, the folder does not want to send
|
|
// notification for each item. Instead, it will refresh everything
|
|
// once the refresh is complete.
|
|
//
|
|
if( dwParam1 == kProcessRefresh ){
|
|
_pDataClient->vContainerChanged( kContainerRefreshComplete, kInfoNull );
|
|
}
|
|
|
|
FreePrinterNotifyInfo( (PPRINTER_NOTIFY_INFO)hBlock );
|
|
if( bRestoreSelections ){
|
|
_pDataClient->vRestoreSelections();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VDataNotify::
|
|
vBlockDelete(
|
|
IN OUT HBLOCK hBlock ADOPT
|
|
)
|
|
|
|
/*++
|
|
|
|
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:
|
|
|
|
--*/
|
|
|
|
{
|
|
FreePrinterNotifyInfo( (PPRINTER_NOTIFY_INFO)hBlock );
|
|
}
|
|
|
|
|
|
VOID
|
|
VDataNotify::
|
|
vContainerProcess(
|
|
IN const PPRINTER_NOTIFY_INFO_DATA pData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process printer specific change information.
|
|
|
|
Arguments:
|
|
|
|
pData - Data about the printer change.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
INFO Info;
|
|
CONTAINER_CHANGE ContainerChange = kContainerNull;
|
|
|
|
//
|
|
// Processes changes in printer.
|
|
//
|
|
switch( pData->Field ){
|
|
case PRINTER_NOTIFY_FIELD_STATUS:
|
|
|
|
ContainerChange = kContainerStatus;
|
|
Info.dwData = pData->NotifyData.adwData[0];
|
|
break;
|
|
|
|
case PRINTER_NOTIFY_FIELD_ATTRIBUTES:
|
|
|
|
ContainerChange = kContainerAttributes;
|
|
Info.dwData = pData->NotifyData.adwData[0];
|
|
break;
|
|
|
|
case PRINTER_NOTIFY_FIELD_SERVER_NAME:
|
|
|
|
ContainerChange = kContainerServerName;
|
|
Info.pszData = (LPTSTR)pData->NotifyData.Data.pBuf;
|
|
break;
|
|
|
|
case PRINTER_NOTIFY_FIELD_PRINTER_NAME:
|
|
|
|
ContainerChange = kContainerName;
|
|
Info.pszData = (LPTSTR)pData->NotifyData.Data.pBuf;
|
|
break;
|
|
|
|
default:
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "DataNotify.vPrinterProcess: Unknown field %d\n",
|
|
pData->Field ));
|
|
return;
|
|
}
|
|
|
|
_pDataClient->vContainerChanged( ContainerChange, Info );
|
|
}
|
|
|
|
|
|
BOOL
|
|
VDataNotify::
|
|
bItemProcess(
|
|
IN const PPRINTER_NOTIFY_INFO_DATA pData,
|
|
IN CACHE& Cache CHANGE
|
|
)
|
|
{
|
|
//
|
|
// Process a single Item change.
|
|
//
|
|
|
|
TItemData* pItemData;
|
|
NATURAL_INDEX NaturalIndex = kInvalidNaturalIndexValue;
|
|
FIELD Field = pData->Field;
|
|
|
|
//
|
|
// Try and match Id with TItemData.
|
|
//
|
|
if( pData->Id != Cache.Id ){
|
|
|
|
//
|
|
// ItemData has not been cached.
|
|
//
|
|
NaturalIndex = GetNaturalIndex( pData->Id, (PHITEM)&pItemData );
|
|
|
|
//
|
|
// Now cache it.
|
|
//
|
|
Cache.Id = pData->Id;
|
|
Cache.NaturalIndex = NaturalIndex;
|
|
Cache.pItemData = pItemData;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Retrieve data from cache.
|
|
//
|
|
NaturalIndex = Cache.NaturalIndex;
|
|
pItemData = Cache.pItemData;
|
|
|
|
SPLASSERT( pItemData );
|
|
}
|
|
|
|
//
|
|
// Check if the ItemData is new and should be created.
|
|
//
|
|
if( !pItemData ){
|
|
|
|
//
|
|
// Create Item now, and append to end of linked list.
|
|
//
|
|
pItemData = pNewItemData( this, pData->Id );
|
|
|
|
if( !pItemData ){
|
|
Cache.Id = kInvalidIdentValue;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update the cache. The Item is always initally added
|
|
// to the end of the list view, so NaturalIndex = current
|
|
// Item count.
|
|
//
|
|
NaturalIndex =
|
|
Cache.NaturalIndex = VData::UIGuard._cItems;
|
|
|
|
Cache.pItemData = pItemData;
|
|
Cache.bNew = TRUE;
|
|
++VData::UIGuard._cItems;
|
|
|
|
UIGuard.ItemData_vAppend( pItemData );
|
|
|
|
SPLASSERT( pItemData == (TItemData*)GetItem( NaturalIndex ));
|
|
}
|
|
|
|
//
|
|
// Match the pData->Field with our field column.
|
|
//
|
|
PFIELD pField;
|
|
DATA_INDEX DataIndex;
|
|
|
|
for( pField = _pFieldTable->pFields, DataIndex = 0;
|
|
*pField != kInvalidFieldValue;
|
|
++pField, ++DataIndex ){
|
|
|
|
if( Field == *pField ){
|
|
|
|
//
|
|
// Found field -- update it. If the return value is TRUE,
|
|
// then the item must be deleted.
|
|
//
|
|
if( bUpdateInfo( pData, DataIndex, Cache )){
|
|
|
|
DBGMSG( DBG_TRACE,
|
|
( "DataNotify.bItemProcess: delete %x Id %x NI %d cItems %d\n",
|
|
pItemData, Cache.Id, Cache.NaturalIndex,
|
|
VData::UIGuard._cItems - 1 ));
|
|
|
|
pItemData->ItemData_vDelinkSelf();
|
|
pItemData->vDelete();
|
|
|
|
SPLASSERT( VData::UIGuard._cItems );
|
|
--VData::UIGuard._cItems;
|
|
|
|
//
|
|
// The cache is now invalid.
|
|
//
|
|
Cache.Id = kInvalidIdentValue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
SPLASSERT( *pField != kInvalidFieldValue );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
VDataNotify::
|
|
vUpdateInfoData(
|
|
IN const PPRINTER_NOTIFY_INFO_DATA pData,
|
|
IN TABLE Table,
|
|
IN PINFO pInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the VDataNotify internal data structure, and issue
|
|
a vItemChanged to the queue UI to keep it in sync.
|
|
|
|
Arguments:
|
|
|
|
pData - Data item that changed.
|
|
|
|
Table - Type of data stored in pData.
|
|
|
|
pInfo - Pointer to INFO receiving new data. Old data freed if
|
|
necessary.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
switch( Table ){
|
|
case TABLE_DWORD:
|
|
|
|
pInfo->dwData = pData->NotifyData.adwData[0];
|
|
break;
|
|
|
|
default:
|
|
|
|
SPLASSERT( pData->NotifyData.Data.cbBuf );
|
|
|
|
FreeMem( pInfo->pvData );
|
|
|
|
pInfo->pvData = AllocMem( pData->NotifyData.Data.cbBuf );
|
|
|
|
if( pInfo->pvData ){
|
|
|
|
CopyMemory( pInfo->pvData,
|
|
pData->NotifyData.Data.pBuf,
|
|
pData->NotifyData.Data.cbBuf );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
VDataNotify::
|
|
vDeleteAllItemData(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete all TItemData* jobs. Normally there should be an assert
|
|
to check whether this is called from the UI thread, but
|
|
the final delete case calls this from a worker thread.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Optimization: instead of delinking each individually, just
|
|
// free them then reset the whole list.
|
|
//
|
|
TIter Iter;
|
|
TItemData* pItemData;
|
|
|
|
for( UIGuard.ItemData_vIterInit( Iter ), Iter.vNext();
|
|
Iter.bValid(); ){
|
|
|
|
//
|
|
// Get a pointer to the next object, then increment the iter.
|
|
// We must increment before deleting, since once the object
|
|
// has been deleted, the next pointer is trash.
|
|
//
|
|
pItemData = UIGuard.ItemData_pConvert( Iter );
|
|
Iter.vNext();
|
|
|
|
pItemData->vDelete();
|
|
}
|
|
|
|
//
|
|
// Now that all ItemData have been deleted, reset the head link.
|
|
//
|
|
UIGuard.ItemData_vReset();
|
|
VData::UIGuard._cItems = 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
TDataNJob
|
|
|
|
********************************************************************/
|
|
|
|
TDataNJob::
|
|
TDataNJob(
|
|
IN MDataClient* pDataClient
|
|
) : VDataNotify( pDataClient,
|
|
&TDataNJob::gFieldTable,
|
|
JOB_NOTIFY_TYPE )
|
|
{
|
|
}
|
|
|
|
TDataNJob::
|
|
~TDataNJob(
|
|
VOID
|
|
)
|
|
{
|
|
}
|
|
|
|
BOOL
|
|
TDataNJob::
|
|
bUpdateInfo(
|
|
IN const PPRINTER_NOTIFY_INFO_DATA pData,
|
|
IN DATA_INDEX DataIndex,
|
|
IN CACHE& Cache CHANGE
|
|
)
|
|
{
|
|
SPLASSERT( DataIndex < _pFieldTable->cFields );
|
|
SPLASSERT( Cache.NaturalIndex < VData::UIGuard._cItems );
|
|
|
|
TItemData* pItemData = Cache.pItemData;
|
|
FIELD Field = pData->Field;
|
|
INFO Info;
|
|
INFO InfoNew;
|
|
ITEM_CHANGE ItemChange = kItemInfo;
|
|
|
|
Info.NaturalIndex = Cache.NaturalIndex;
|
|
InfoNew.NaturalIndex = kInvalidNaturalIndexValue;
|
|
|
|
TABLE Table = pData->Reserved & 0xffff;
|
|
PINFO pInfo = &pItemData->_aInfo[DataIndex];
|
|
|
|
if( Cache.bNew ){
|
|
|
|
//
|
|
// This is a new item; inform the client to create a new item.
|
|
//
|
|
_pDataClient->vItemChanged( kItemCreate,
|
|
(HITEM)pItemData,
|
|
Info,
|
|
Info );
|
|
Cache.bNew = FALSE;
|
|
}
|
|
|
|
switch( Field ){
|
|
case JOB_NOTIFY_FIELD_POSITION:
|
|
|
|
//
|
|
// Special case MOVED.
|
|
//
|
|
// Move the ItemData to the correct place in the linked list.
|
|
//
|
|
|
|
ItemChange = kItemPosition;
|
|
|
|
//
|
|
// Valid range is from 1 -> VData::UIGuard._cItems.
|
|
//
|
|
SPLASSERT( pData->NotifyData.adwData[0] );
|
|
|
|
//
|
|
// Start with base instead of head, since position 2 =
|
|
// second element, not third.
|
|
//
|
|
InfoNew.NaturalIndex = pData->NotifyData.adwData[0] - 1;
|
|
|
|
DBGMSG( DBG_DATANINFO,
|
|
( "DataNotify.vUpdateInfo: Position changed id %d ni %d->%d\n",
|
|
pData->Id,
|
|
Info.NaturalIndex,
|
|
InfoNew.NaturalIndex ));
|
|
|
|
if( InfoNew.NaturalIndex >= VData::UIGuard._cItems ){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "DataNotify.vUpdateInfo: %x %d %d, moving past end Item.\n",
|
|
this,
|
|
InfoNew.NaturalIndex,
|
|
VData::UIGuard._cItems ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remove ourselves and traverse until we get to the
|
|
// right ItemData. Then add ourselves back in.
|
|
//
|
|
|
|
pItemData->ItemData_vDelinkSelf();
|
|
|
|
NATURAL_INDEX i;
|
|
TIter Iter;
|
|
|
|
for( i = InfoNew.NaturalIndex, UIGuard.ItemData_vIterInit( Iter ),
|
|
Iter.vNext();
|
|
i;
|
|
--i, Iter.vNext( )){
|
|
SPLASSERT( UIGuard.ItemData_bValid( Iter ));
|
|
}
|
|
|
|
//
|
|
// Insert before appropriate element.
|
|
//
|
|
UIGuard.ItemData_vInsertBefore( Iter, pItemData );
|
|
|
|
if( InfoNew.NaturalIndex != Info.NaturalIndex ){
|
|
|
|
DBGMSG( DBG_DATANINFO,
|
|
( "DataNotify.vUpdateInfo: ItemData %x (%d) moved from %d to %d\n",
|
|
pItemData, pItemData->_Id, Info.NaturalIndex,
|
|
InfoNew.NaturalIndex ));
|
|
} else {
|
|
|
|
//
|
|
// If the Item didn't move, don't do anything. This occurs the
|
|
// first time a Item comes in since the refresh returns the
|
|
// position, even though it really didn't move.
|
|
//
|
|
DBGMSG( DBG_DATANINFO,
|
|
( "DataNotify.vUpdateInfo: ItemData %x (%d) did not move %d\n",
|
|
pItemData, pItemData->_Id, Info.NaturalIndex ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update cache.
|
|
//
|
|
Cache.NaturalIndex = InfoNew.NaturalIndex;
|
|
break;
|
|
|
|
case JOB_NOTIFY_FIELD_STATUS:
|
|
|
|
if (pData->NotifyData.adwData[0] & JOB_STATUS_DELETED ){
|
|
|
|
//
|
|
// Special case deletion.
|
|
//
|
|
|
|
//
|
|
// Ensure Item we are deleting matches NaturalIndex.
|
|
//
|
|
SPLASSERT( GetItem( Info.NaturalIndex ) == (HITEM)pItemData );
|
|
|
|
//
|
|
// Now inform the UI that something changed.
|
|
//
|
|
_pDataClient->vItemChanged( kItemDelete,
|
|
(HITEM)pItemData,
|
|
Info,
|
|
InfoNew );
|
|
|
|
return TRUE;
|
|
}
|
|
goto DefaultAction;
|
|
|
|
case JOB_NOTIFY_FIELD_DOCUMENT:
|
|
|
|
//
|
|
// Item disposition is slightly different: it still
|
|
// is an INFO, but we need to change the width of
|
|
// the first field (document name).
|
|
//
|
|
ItemChange = kItemName;
|
|
|
|
//
|
|
// The name really didn't change (job ids never change) but
|
|
// the function specifies that the old and new names are
|
|
// placed in Info and InfoNew.
|
|
//
|
|
InfoNew = Info;
|
|
|
|
//
|
|
// Fall through to default:
|
|
//
|
|
|
|
default:
|
|
|
|
DefaultAction:
|
|
|
|
//
|
|
// Update the standard data item.
|
|
//
|
|
vUpdateInfoData( pData, Table, pInfo );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now inform the UI that something changed.
|
|
//
|
|
_pDataClient->vItemChanged( ItemChange,
|
|
(HITEM)pItemData,
|
|
Info,
|
|
InfoNew );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
TDataNPrinter
|
|
|
|
********************************************************************/
|
|
|
|
TDataNPrinter::
|
|
TDataNPrinter(
|
|
IN MDataClient* pDataClient
|
|
) : VDataNotify( pDataClient,
|
|
&TDataNPrinter::gFieldTable,
|
|
PRINTER_NOTIFY_TYPE )
|
|
{
|
|
}
|
|
|
|
TDataNPrinter::
|
|
~TDataNPrinter(
|
|
VOID
|
|
)
|
|
{
|
|
}
|
|
|
|
VDataNotify::TItemData*
|
|
TDataNPrinter::
|
|
pNewItemData(
|
|
VDataNotify* pDataNotify,
|
|
IDENT Id
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Override the standard definition of allocating a new TItemData.
|
|
|
|
Normally this is a simple zero-initialized structure, but in
|
|
the printer case, we need to initialized the CJobs field to be
|
|
non-zero.
|
|
|
|
This fixes the case where a printer is pending deletion and has
|
|
>0 jobs. Otherwise, when the status notification comes in,
|
|
we see that the printer is pending deletion and has zero jobs
|
|
(since the CJobs field hasn't been parsed yet) so we delete the
|
|
printer. Then the other notifications about this printer come in,
|
|
causing us to recreate it. However, now the name is lost (since
|
|
it came in first) and when someone tries to enumerate, the will
|
|
AV because there's no string.
|
|
|
|
Arguments:
|
|
|
|
pDataNotify - Owning pDataNotify.
|
|
|
|
Id - Id of new ItemData.
|
|
|
|
Return Value:
|
|
|
|
TItemData*; NULL in case of failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
TItemData* pItemData = TItemData::pNew( pDataNotify, Id );
|
|
|
|
if( pItemData ){
|
|
pItemData->_aInfo[kIndexCJobs].dwData = (DWORD)-1;
|
|
}
|
|
return pItemData;
|
|
}
|
|
|
|
BOOL
|
|
TDataNPrinter::
|
|
bUpdateInfo(
|
|
IN const PPRINTER_NOTIFY_INFO_DATA pData,
|
|
IN DATA_INDEX DataIndex,
|
|
IN CACHE& Cache CHANGE
|
|
)
|
|
{
|
|
ITEM_CHANGE ItemChange;
|
|
|
|
TABLE Table = pData->Reserved & 0xffff;
|
|
TItemData* pItemData = Cache.pItemData;
|
|
PINFO pInfo = &pItemData->_aInfo[DataIndex];
|
|
INFO Info;
|
|
INFO InfoNew;
|
|
|
|
switch( pData->Field ){
|
|
case PRINTER_NOTIFY_FIELD_PRINTER_NAME: {
|
|
|
|
//
|
|
// The printer name is changing. Pass in the old name to
|
|
// the client, along with the old.
|
|
//
|
|
Info = pItemData->_aInfo[kIndexPrinterName];
|
|
InfoNew.pvData = pData->NotifyData.Data.pBuf;
|
|
|
|
//
|
|
// The folder code has an interesting problem: it always
|
|
// identifies items by string names instead of IDs, so when
|
|
// a new item is created, we must have a printer name.
|
|
// We solve this problem by using the sending an kItemCreate
|
|
// only when we have the printer name. Note this relies
|
|
// on the fact that the name is always sent as the first
|
|
// change field, a hack.
|
|
//
|
|
if( Cache.bNew ){
|
|
|
|
//
|
|
// This is a creation event since bNew is set. The old
|
|
// name is NULL (since it's new), but the SHChangeNotify
|
|
// requires the name, so set the "old" name to be the
|
|
// new one. Then mark that the item is no longer new.
|
|
//
|
|
ItemChange = kItemCreate;
|
|
Info = InfoNew;
|
|
Cache.bNew = FALSE;
|
|
|
|
} else {
|
|
ItemChange = kItemName;
|
|
}
|
|
|
|
//
|
|
// Now inform the UI that the name has changed. We can safely
|
|
// send the notification before updating our internal data
|
|
// (by calling vUpdateInfoData) since the client guarantees
|
|
// thread synchronization.
|
|
//
|
|
// We called vItemChanged before vUpdateInfoData since we
|
|
// need both the old and new name.
|
|
//
|
|
_pDataClient->vItemChanged( ItemChange,
|
|
(HITEM)pItemData,
|
|
Info,
|
|
InfoNew );
|
|
|
|
vUpdateInfoData( pData, Table, pInfo );
|
|
return FALSE;
|
|
}
|
|
|
|
default:
|
|
|
|
//
|
|
// Update the standard data item.
|
|
//
|
|
vUpdateInfoData( pData, Table, pInfo );
|
|
|
|
//
|
|
// Problem: we don't know when the printer should be removed,
|
|
// since there is no "PRINTER_STATUS_DELETED" message that
|
|
// indicates there will be no more printer notifications.
|
|
//
|
|
// Normally we'd just look for a printer that is pending
|
|
// deletion and has zero jobs, but this won't work since
|
|
// we get printer status _before_ CJobs. Therefore, we
|
|
// must pre-initialize CJobs to be some large value like -1.
|
|
//
|
|
// Determine whether an item should be deleted from the UI.
|
|
// If it's pending deletion and has no jobs, it should be removed.
|
|
//
|
|
if( pItemData->_aInfo[kIndexStatus].dwData &
|
|
PRINTER_STATUS_PENDING_DELETION &&
|
|
pItemData->_aInfo[kIndexCJobs].dwData == 0 ){
|
|
|
|
ItemChange = kItemDelete;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Distinguish between an info change (affects icon/list view)
|
|
// vs. an attribute change (affects report view, but not icon/
|
|
// list view).
|
|
//
|
|
ItemChange = ( pData->Field == PRINTER_NOTIFY_FIELD_ATTRIBUTES ) ?
|
|
kItemInfo :
|
|
kItemAttributes;
|
|
|
|
//
|
|
// Make the port name change an info change similar to the
|
|
// sharing attribute change. ( affects icon/list view )
|
|
//
|
|
if( pData->Field == PRINTER_NOTIFY_FIELD_PORT_NAME )
|
|
ItemChange = kItemInfo;
|
|
|
|
//
|
|
// If this is a security notification then map to the correct
|
|
// kItemSecurity id.
|
|
//
|
|
if( pData->Field == PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR )
|
|
ItemChange = kItemSecurity;
|
|
}
|
|
|
|
//
|
|
// Now inform the UI that something changed.
|
|
//
|
|
Info.NaturalIndex = Cache.NaturalIndex;
|
|
_pDataClient->vItemChanged( ItemChange,
|
|
(HITEM)pItemData,
|
|
Info,
|
|
Info );
|
|
break;
|
|
}
|
|
return ItemChange == kItemDelete;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Debug support
|
|
|
|
********************************************************************/
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Define debug tables.
|
|
//
|
|
UINT aFieldMax[2] = {
|
|
I_PRINTER_END,
|
|
I_JOB_END
|
|
};
|
|
|
|
//
|
|
// Define debug strings for fields.
|
|
//
|
|
#define DEFINE( field, attrib, table, y, z )\
|
|
#field,
|
|
|
|
LPSTR aszFieldJob[] = {
|
|
#include "ntfyjob.h"
|
|
NULL
|
|
};
|
|
|
|
LPSTR aszFieldPrinter[] = {
|
|
#include "ntfyprn.h"
|
|
NULL
|
|
};
|
|
|
|
#undef DEFINE
|
|
|
|
LPSTR* aaszField[2] = {
|
|
aszFieldPrinter,
|
|
aszFieldJob
|
|
};
|
|
|
|
PCSTR acszType[2] = {
|
|
"Printer: ",
|
|
"Job : "
|
|
};
|
|
|
|
VOID
|
|
VDataNotify::
|
|
vDbgOutputInfo(
|
|
IN const PPRINTER_NOTIFY_INFO pInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump out the pInfo data to the debugger.
|
|
|
|
Arguments:
|
|
|
|
pInfo - Info to dump.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PPRINTER_NOTIFY_INFO_DATA pData;
|
|
DWORD i, j;
|
|
|
|
DWORD Type;
|
|
DWORD Field;
|
|
|
|
BOOL bFound = FALSE;
|
|
|
|
for( i = 0, pData = pInfo->aData; i < pInfo->Count; i++, pData++ ){
|
|
|
|
Type = pData->Type;
|
|
Field = pData->Field;
|
|
|
|
//
|
|
// Match field to I_ index.
|
|
//
|
|
for( j=0; j< aFieldMax[Type]; j++ ){
|
|
|
|
if( j == Field ) {
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bFound ){
|
|
|
|
DBGMSG( DBG_MIN, ( "[?Field 0x%x not found (Type=%d)\n", Field, Type ));
|
|
continue;
|
|
}
|
|
|
|
switch( aaTable[Type][j] ){
|
|
case TABLE_STRING:
|
|
|
|
DBGMSG( DBG_MIN,
|
|
( "> %hs Id = 0x%x | %hs = "TSTR"\n",
|
|
acszType[Type], pData->Id, aaszField[Type][j],
|
|
pData->NotifyData.Data.pBuf ));
|
|
|
|
if( !((LPTSTR)pData->NotifyData.Data.pBuf)[0] ){
|
|
DBGMSG( DBG_MIN|DBG_NOHEAD, ( "\n" ));
|
|
}
|
|
|
|
break;
|
|
|
|
case TABLE_DWORD:
|
|
|
|
DBGMSG( DBG_MIN,
|
|
( "> %hs Id = 0x%x | %hs = 0x%x = %d\n",
|
|
acszType[Type], pData->Id, aaszField[Type][j],
|
|
pData->NotifyData.adwData[0],
|
|
pData->NotifyData.adwData[0] ));
|
|
break;
|
|
|
|
case TABLE_TIME:
|
|
{
|
|
SYSTEMTIME LocalTime;
|
|
TCHAR szTime[80];
|
|
TCHAR szDate[80];
|
|
LCID lcid = GetUserDefaultLCID();
|
|
|
|
if ( !SystemTimeToTzSpecificLocalTime(
|
|
NULL,
|
|
(PSYSTEMTIME)pData->NotifyData.Data.pBuf,
|
|
&LocalTime )) {
|
|
|
|
DBGMSG( DBG_MIN,
|
|
( "[SysTimeToTzSpecLocalTime failed %d]\n",
|
|
GetLastError( )));
|
|
break;
|
|
}
|
|
|
|
if( !GetTimeFormat( lcid,
|
|
0,
|
|
&LocalTime,
|
|
NULL,
|
|
szTime,
|
|
COUNTOF( szTime ))){
|
|
|
|
DBGMSG( DBG_MIN,
|
|
( "[No Time %d], ", GetLastError( )));
|
|
break;
|
|
}
|
|
|
|
if( !GetDateFormat( lcid,
|
|
0,
|
|
&LocalTime,
|
|
NULL,
|
|
szDate,
|
|
COUNTOF( szDate ))) {
|
|
|
|
DBGMSG( DBG_MIN, ( "[No Date %d]\n", GetLastError( )));
|
|
break;
|
|
}
|
|
|
|
DBGMSG( DBG_MIN,
|
|
( "> %hs Id = 0x%x | %hs = "TSTR" "TSTR"\n",
|
|
acszType[Type], pData->Id, aaszField[Type][j],
|
|
szTime,
|
|
szDate ));
|
|
break;
|
|
}
|
|
default:
|
|
DBGMSG( DBG_MIN,
|
|
( "[?tab %d: t_0x%x f_0x%x %x, %x]\n",
|
|
aaTable[Type][j],
|
|
Type,
|
|
Field,
|
|
pData->NotifyData.adwData[0],
|
|
pData->NotifyData.adwData[1] ));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|