/*++ 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(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(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(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(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(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 ); }