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