|
|
/*++
Copyright (C) Microsoft Corporation, 1990 - 1998 All rights reserved
Module Name:
thread.cxx
Abstract:
Contains the worker thread for the Connect To browsing dialog. This is a secondary thread spun off to call the spooler enumerate-printer APIs. EnumPrinters frequently takes a long time to return, especially across the network, so calling it on a separate thread enables the window to be painted without delays.
There is a common data structure between the main thread and this worker thread, pBrowseDlgData, defined in browse.h.
Author:
Created by AndrewBe on 1 Dec 1992 Steve Kiraly (SteveKi) 1 May 1998 Lazar Ivanov (LazarI) Jun-2000 (Win64 fixes)
Environment:
User Mode -Win32
Revision History:
1 May 1998 moved from winspool.drv to printui.dll
--*/
#include "precomp.hxx"
#pragma hdrstop
#include "browse.hxx"
#include "thread.hxx"
#if DBG
//#define DBG_THREADINFO DBG_INFO
#define DBG_THREADINFO DBG_NONE
#endif
PSAVED_BUFFER_SIZE pFirstSavedBufferSize = NULL;
VOID BrowseThread( PBROWSE_DLG_DATA pBrowseDlgData ) { DWORD RequestId; LPTSTR pEnumerateName; /* These may get overwritten before we return */ PVOID pEnumerateObject; /* in the case of BROWSE_THREAD_GET_PRINTER, */ /* so make sure we take a copy. */
for( ; ; ) { RECEIVE_BROWSE_THREAD_REQUEST( pBrowseDlgData, RequestId, pEnumerateName, pEnumerateObject );
DBGMSG( DBG_THREADINFO, ( "BrowseThread: Request ID = %d\n", RequestId ) );
switch( pBrowseDlgData->RequestId ) { case BROWSE_THREAD_ENUM_OBJECTS: BrowseThreadEnumerate( pBrowseDlgData, (PCONNECTTO_OBJECT )pEnumerateObject, pEnumerateName ); break;
case BROWSE_THREAD_GET_PRINTER: BrowseThreadGetPrinter( pBrowseDlgData, pEnumerateName, (PPRINTER_INFO_2)pEnumerateObject ); break;
case BROWSE_THREAD_TERMINATE: BrowseThreadDelete( pBrowseDlgData ); BrowseThreadTerminate( pBrowseDlgData ); ExitThread( 0 ); } } }
/* BrowseThreadEnumerate
* * Called to enumerate objects on a given node. * */ VOID BrowseThreadEnumerate( PBROWSE_DLG_DATA pBrowseDlgData, PCONNECTTO_OBJECT pConnectToParent, LPTSTR pParentName ) { DWORD cEnum; DWORD Result;
Result = EnumConnectToObjects( pBrowseDlgData, pConnectToParent, pParentName );
DBGMSG( DBG_TRACE, ( "Sending WM_ENUM_OBJECTS_COMPLETE; cEnum == %d\n", pConnectToParent->cSubObjects ) );
ENTER_CRITICAL( pBrowseDlgData );
cEnum = pConnectToParent->cSubObjects;
SEND_BROWSE_THREAD_REQUEST_COMPLETE( pBrowseDlgData, WM_ENUM_OBJECTS_COMPLETE, pConnectToParent, cEnum );
LEAVE_CRITICAL( pBrowseDlgData );
DBGMSG( DBG_TRACE, ( "Sent WM_ENUM_OBJECTS_COMPLETE; cEnum == %d\n", pConnectToParent->cSubObjects ) );
}
/*
* */ VOID BrowseThreadGetPrinter( PBROWSE_DLG_DATA pBrowseDlgData, LPTSTR pPrinterName, LPPRINTER_INFO_2 pPrinterInfo ) { HANDLE hPrinter = NULL; LPPRINTER_INFO_2 pPrinter = NULL; DWORD cbPrinter = 0; DWORD cbNeeded = 0; BOOL OK = FALSE;
DBGMSG( DBG_TRACE, ( "BrowseThreadGetPrinter %s\n", pPrinterName ) );
if( OpenPrinter( pPrinterName, &hPrinter, NULL ) ) { /* We don't free up this memory until the thread terminates.
* Just leave it so we can use it the next time we're called, * increasing the size when necessary: */ if( cbPrinter = pBrowseDlgData->cbPrinterInfo ) pPrinter = (PPRINTER_INFO_2)AllocSplMem( cbPrinter );
if( pPrinter || !cbPrinter ) { DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x )\n", hPrinter, 2, pPrinter, cbPrinter ) );
OK = GetPrinter( hPrinter, 2, (LPBYTE)pPrinter, cbPrinter, &cbNeeded );
DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x ) returned %d; cbNeeded = %x\n", hPrinter, 2, pPrinter, cbPrinter, OK, cbNeeded ) );
if( !OK ) { if( GetLastError( ) == ERROR_INSUFFICIENT_BUFFER ) { DBGMSG( DBG_TRACE, ( "ReallocSplMem( %08x, %x, %x )\n", pPrinter, cbPrinter, cbNeeded ) );
if( pPrinter ) pPrinter = (PPRINTER_INFO_2)ReallocSplMem( pPrinter, cbPrinter, cbNeeded ); else pPrinter = (PPRINTER_INFO_2)AllocSplMem( cbNeeded );
if( pPrinter ) { cbPrinter = cbNeeded;
DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x )\n", hPrinter, 2, pPrinter, cbPrinter ) );
OK = GetPrinter( hPrinter, 2, (LPBYTE)pPrinter, cbPrinter, &cbNeeded );
DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x ) returned %d; cbNeeded = %x\n", hPrinter, 2, pPrinter, cbPrinter, OK, cbNeeded ) ); }
} } }
ClosePrinter(hPrinter); }
else { DBGMSG( DBG_WARNING, ( "Couldn't open "TSTR"\n", pPrinterName ) ); }
ENTER_CRITICAL( pBrowseDlgData );
if( pBrowseDlgData->pPrinterInfo ) FreeSplMem( pBrowseDlgData->pPrinterInfo ); pBrowseDlgData->pPrinterInfo = pPrinter; pBrowseDlgData->cbPrinterInfo = cbPrinter;
LEAVE_CRITICAL( pBrowseDlgData );
SEND_BROWSE_THREAD_REQUEST_COMPLETE( pBrowseDlgData, ( OK ? WM_GET_PRINTER_COMPLETE : WM_GET_PRINTER_ERROR ), pPrinterName, ( OK ? (ULONG_PTR)pPrinter : GetLastError( ) ) ); }
/* BrowseThreadDelete
* * Called to delete objects on a given node. * */ VOID BrowseThreadDelete( PBROWSE_DLG_DATA pBrowseDlgData ) { PCONNECTTO_OBJECT pConnectToParent; DWORD ObjectsDeleted;
DBGMSG( DBG_TRACE, ( "BrowseThreadDelete\n" ) );
pConnectToParent = pBrowseDlgData->pConnectToData;
if( pConnectToParent ) { ENTER_CRITICAL( pBrowseDlgData );
ObjectsDeleted = FreeConnectToObjects( &pConnectToParent->pSubObject[0], pConnectToParent->cSubObjects, pConnectToParent->cbPrinterInfo );
pConnectToParent->pSubObject = NULL; pConnectToParent->cSubObjects = 0; pConnectToParent->cbPrinterInfo = 0;
LEAVE_CRITICAL( pBrowseDlgData ); } }
/* BrowseThreadTerminate
* * Frees up the top-level connect-to object, deletes the critical section, * and closes the semaphore, then frees the dialog data. * Note, if there are remaining enumerated objects below the top-level * object, they should have been freed by BrowseThreadDelete. */ VOID BrowseThreadTerminate( PBROWSE_DLG_DATA pBrowseDlgData ) { DBGMSG( DBG_TRACE, ( "BrowseThreadTerminate\n" ) ); pBrowseDlgData->cDecRef(); }
/* EnumConnectToObjects
* * Calls GetPrinterInfo (which in turn calls EnumPrinters) on the requested * parent node. It allocates an initial buffer and sets up the subobject * fields in the supplied CONNECTTO_OBJECT structure. * * Arguments: * * pParentName - The object on which the enumeration is to be performed. * This may be a domain or server name, depending upon the proprietory * network implementation. If this value is NULL, this indicates * that the top-level objects should be enumerated. * * pConnectToParent - A pointer to a CONNECTTO_OBJECT for the parent node. * The subobject and cbPrinterInfo fields will be filled in * by this function. * * Return: * * FALSE if an error occurred, otherwise TRUE. * * Author: * * andrewbe, July 1992 * */ DWORD EnumConnectToObjects( PBROWSE_DLG_DATA pBrowseDlgData, PCONNECTTO_OBJECT pConnectToParent, LPTSTR pParentName ) { DWORD i, j, cReturned; DWORD cbNeeded; DWORD cbPrinter; LPPRINTER_INFO_1 pPrinter; LPPRINTER_INFO_1 pOrderPrinter; PCONNECTTO_OBJECT pConnectToChildren; BOOL Success = FALSE; DWORD Error = 0;
cbPrinter = GetSavedBufferSize( pParentName, NULL );
/* Allocate a buffer that will probably be big enough to hold
* all the information we'll need. * This is so that GetPrinterInfo doesn't have to call EnumPrinters twice. */ pPrinter = (PPRINTER_INFO_1)AllocSplMem( cbPrinter );
if( pPrinter ) { pPrinter = (LPPRINTER_INFO_1)GetPrinterInfo( PRINTER_ENUM_NAME | PRINTER_ENUM_REMOTE, pParentName, 1, (LPBYTE)pPrinter, &cbPrinter, &cReturned, &cbNeeded, &Error );
if( pPrinter ) { /* Allocate an array of CONNECTTO_OBJECTs, one for each object returned:
*/ if( cReturned > 0 ) { pConnectToChildren = (PCONNECTTO_OBJECT)AllocSplMem( cReturned * sizeof( CONNECTTO_OBJECT ) ); SaveBufferSize( pParentName, cbPrinter ); } else { FreeSplMem( pPrinter ); cbPrinter = 0; pConnectToChildren = EMPTY_CONTAINER; }
if( pConnectToChildren && ( pConnectToChildren != EMPTY_CONTAINER ) ) { /*
* Allocate a buffer to sort the printer info entries. Once ordered * copy the entries back to original area and free the buffer. * * */ pOrderPrinter = (PPRINTER_INFO_1)AllocSplMem( cReturned * sizeof( PRINTER_INFO_1 ) );
if( pOrderPrinter ) {
pOrderPrinter[0] = pPrinter[0]; /* Copy 1st printer info */
for( i = 1; i < cReturned; i++ ) { for ( j = 0; j< i; j++ ) { if ( _wcsicmp(pPrinter[i].pDescription, pOrderPrinter[j].pDescription) < 0 ) { memmove(&pOrderPrinter[j+1], &pOrderPrinter[j], sizeof( PRINTER_INFO_1 ) * (i-j)); break; } } pOrderPrinter[j] = pPrinter[i]; }
memmove(pPrinter, pOrderPrinter, cReturned * sizeof( PRINTER_INFO_1 ));
FreeSplMem( pOrderPrinter ); }
/*
* Build ConnectTo array */ for( i = 0; i < cReturned; i++ ) { pConnectToChildren[i].pPrinterInfo = &pPrinter[i]; pConnectToChildren[i].pSubObject = NULL; pConnectToChildren[i].cSubObjects = 0; pConnectToChildren[i].cbPrinterInfo = 0; }
ENTER_CRITICAL( pBrowseDlgData );
pConnectToParent->pSubObject = pConnectToChildren; pConnectToParent->cSubObjects = cReturned; pConnectToParent->cbPrinterInfo = cbPrinter;
LEAVE_CRITICAL( pBrowseDlgData );
Success = TRUE; } } }
SetCursor( pBrowseDlgData->hcursorWait );
return Error; }
/* GetPrinterInfo
* * Calls EnumPrinters using the supplied parameters. * If the buffer is not big enough, it is reallocated, * and a second attempt is made. * * Returns a pointer to the buffer of printer info. * * pPrinters may be NULL, in which case *pcbPrinters must equal 0. * * andrewbe, April 1992 */ #define MAX_RETRIES 5 /* How many times we retry if we get
ERROR_INSUFFICIENT_BUFFER */
LPBYTE GetPrinterInfo( IN DWORD Flags, IN LPTSTR Name, IN DWORD Level, IN LPBYTE pPrinters, OUT LPDWORD pcbPrinters, OUT LPDWORD pcReturned, OUT LPDWORD pcbNeeded OPTIONAL, OUT LPDWORD pError OPTIONAL ) { DWORD cbCurrent; BOOL rc; DWORD cbNeeded; DWORD Error = 0; DWORD Retry;
/* cbCurrent holds our current buffer size.
* This will change if we have to realloc: */ cbCurrent = *pcbPrinters;
DBGMSG( DBG_TRACE, ( "Calling EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x )\n", Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent ) );
rc = EnumPrinters( Flags, Name, Level, pPrinters, cbCurrent, &cbNeeded, pcReturned );
DBGMSG( DBG_TRACE, ( "EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x ) returned %d; cbNeeded 0x%x; cReturned 0x%x\n", Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent, rc, cbNeeded, *pcReturned ) );
Retry = 1;
while (!rc) {
Error = GetLastError( );
if ( Error != ERROR_INSUFFICIENT_BUFFER || Retry > MAX_RETRIES ) {
break; }
/* Hopefully the error will be buffer not big enough.
* If not, we'll have to bomb out: */
/* The problem here is that, the second time we call EnumPrinters,
* the size of buffer we need may have increased because new devices * have come on line, or the server that provided the list of objects * has updated itself. In this case, we should try again, * but don't keep trying indefinitely, because something might be amiss. */
DBGMSG( DBG_THREADINFO, ( "EnumPrinters failed with ERROR_INSUFFICIENT_BUFFER; buffer size = %d\nRetry #%d with buffer size = %d\n", cbCurrent, Retry, cbNeeded ) );
if( cbCurrent != 0 ) FreeSplMem(pPrinters);
pPrinters = (PBYTE)AllocSplMem( cbNeeded );
if( pPrinters ) { cbCurrent = cbNeeded;
DBGMSG( DBG_THREADINFO, ( "Calling EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x )\n", Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent ) );
rc = EnumPrinters( Flags, Name, Level, pPrinters, cbCurrent, &cbNeeded, pcReturned );
DBGMSG( DBG_THREADINFO, ( "EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x )\nreturned %d; cbNeeded 0x%x; cReturned 0x%x\n", Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent, rc, cbNeeded, *pcReturned ) ); }
Retry++; }
if( !rc ) { DBGMSG( DBG_WARNING, ( "EnumPrinters failed: Error %d\n", Error ) );
if( cbCurrent != 0 ) FreeSplMem(pPrinters);
pPrinters = NULL; cbCurrent = 0; *pcReturned = 0; Error = GetLastError( ); }
*pcbPrinters = cbCurrent;
if( pError ) *pError = Error;
return pPrinters; }
DWORD GetSavedBufferSize( LPTSTR pName, PSAVED_BUFFER_SIZE *ppSavedBufferSize OPTIONAL ) { PSAVED_BUFFER_SIZE pSavedBufferSize;
if( !pName ) pName = TEXT("");
pSavedBufferSize = pFirstSavedBufferSize;
while( pSavedBufferSize ) { if( !_tcscmp( pSavedBufferSize->pName, pName ) ) { if( ppSavedBufferSize ) *ppSavedBufferSize = pSavedBufferSize; return pSavedBufferSize->Size; }
pSavedBufferSize = pSavedBufferSize->pNext; }
if( ppSavedBufferSize ) *ppSavedBufferSize = NULL; return 0; }
VOID SaveBufferSize( LPTSTR pName, DWORD Size ) { PSAVED_BUFFER_SIZE pSavedBufferSize;
if( !pName ) pName = TEXT("");
if( GetSavedBufferSize( pName, &pSavedBufferSize ) ) { if( pSavedBufferSize->Size < Size ) { DBGMSG( DBG_TRACE, ( "Updating buffer size for "TSTR" from %d (0x%x) to %d (0x%x)\n", ( pName ? pName : TEXT("NULL") ), pSavedBufferSize->Size, pSavedBufferSize->Size, Size, Size ) );
pSavedBufferSize->Size = Size; } } else { DBGMSG( DBG_TRACE, ( "Saving buffer size %d (0x%x) for "TSTR"\n", Size, Size, ( pName ? pName : TEXT("NULL") ) ) );
if( pSavedBufferSize = (PSAVED_BUFFER_SIZE)AllocSplMem( sizeof( SAVED_BUFFER_SIZE ) ) ) { pSavedBufferSize->pName = AllocSplStr( pName ); pSavedBufferSize->Size = Size; pSavedBufferSize->pNext = pFirstSavedBufferSize; pFirstSavedBufferSize = pSavedBufferSize; } } }
|