Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

904 lines
24 KiB

/*++
Copyright (c) 1990-1994 Microsoft Corporation
All rights reserved
Module Name:
threads.c
Abstract:
Create a whole lot of threads that don't do much.
Author:
Environment:
User Mode -Win32
Revision History:
--*/
#include "printman.h"
HANDLE ThreadMessageRead;
HANDLE ThreadMessageWritten;
MSG ThreadMessage;
WCHAR szPrintersConnections[] = L"Printers\\Connections";
BOOL APIENTRY
NetworkPasswordDialog(
HWND hWnd,
UINT usMsg,
WPARAM wParam,
LONG lParam);
#if DBG
VOID DbgProtected( DWORD OwningThread )
{
DWORD CurrentThreadId;
CurrentThreadId = GetCurrentThreadId( );
if( OwningThread != CurrentThreadId )
{
DBGMSG( DBG_ERROR, ( "ERROR: Not in mutex: Current thread: %d; owning thread: %d\n",
CurrentThreadId, OwningThread ) );
}
}
VOID DbgNotProtected( DWORD OwningThread )
{
DWORD CurrentThreadId;
CurrentThreadId = GetCurrentThreadId( );
if( OwningThread == CurrentThreadId )
{
DBGMSG( DBG_ERROR, ( "ERROR: In mutex\n" ) );
}
}
#endif /* DBG */
/* Returns the time in milliseconds to the nearest hour.
*/
DWORD GetTime( )
{
SYSTEMTIME SystemTime;
GetSystemTime( &SystemTime );
DBGMSG( DBG_TRACE, ( "Time: %02d:%02d:%02d\n",
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond ) );
return( ( 1000
* ( ( 60 * SystemTime.wMinute )
+ SystemTime.wSecond ) )
+ SystemTime.wMilliseconds );
}
/* NOTE:
*
* This thread should not call any User APIs, since this causes a second thread
* to be spun off.
*/
VOID RefreshThread( PMDIWIN_INFO pInfo )
{
DWORD WaitFlags;
DWORD Changes;
BOOL RefreshOK;
DWORD ThisTime = 0; /* Time in milliseconds */
DWORD LastTime = 0;
DWORD Error;
HANDLE hPrinterTmp;
BOOL bPolling = FALSE;
BOOL bQuit;
DBGMSG( DBG_TRACE, ( "Thread %d created\n", GetCurrentThreadId( ) ) );
//
// Init the thread if necessary
//
if (pInfo->pfnInitThread)
(*pInfo->pfnInitThread)( pInfo->pContext );
#ifdef SEP_WAITHANDLE
//
// Necessary since provider may not allow non-serialized
// RPC handle calls.
//
// We can remove this when FindFirstPrinterChange comes online.
// -- there may be a backward compatiblity problem though, with
// 528 printman on a daytona machine.
//
ReopenPrinter(pInfo->pContext,
pInfo->WindowType,
TRUE);
#endif
DBG_OUT_PROTECTED_DATA( pInfo );
ENTER_PROTECTED_DATA( pInfo );
//
// We have tried loading once, so we are no longer loading.
//
pInfo->Status = 0;
//
// Caller passed in flags specifying what changes to wait for.
//
WaitFlags = pInfo->WaitFlags;
Changes = WaitFlags;
while( pInfo->Alive )
{
DBGMSG( DBG_THREADS, ( "pInfo->Alive == %d\n", pInfo->Alive ) );
ThisTime = GetTime( );
/* Don't update more than a couple of times per second,
* so as not to overload the network unnecessarily:
*/
if( ( ThisTime - LastTime ) < 500 )
{
DBGMSG( DBG_TRACE, ( "Sleeping %d ms\n", ( ThisTime - LastTime ) ) );
Sleep( ThisTime - LastTime );
}
LastTime = ThisTime;
/* Enumerate a buffer big enough to page up once ...
*/
*pInfo->pFirstEnumObj = (DWORD)max( 0, (int)pInfo->TopIndex - (int)pInfo->cNumLines );
RefreshOK = FALSE;
Error = NO_ERROR;
DBGMSG( DBG_TRACE | DBG_THREADS,
( "Calling refresh (1): *phWaitObject == %x\n",
*pInfo->phWaitObject ) );
if( pInfo->pfnRefresh )
RefreshOK = (*pInfo->pfnRefresh)( pInfo->pContext, &Changes );
DBGMSG( DBG_TRACE | DBG_THREADS,
( "Refresh returned: *phWaitObject == %x\n",
*pInfo->phWaitObject ) );
if( !RefreshOK )
{
//
// The refresh failed, either because the handle
// is invalid, or the refresh failed.
// Go to polling mode by default.
//
// !! POLICY !!
//
// We may wish to change this, since we keep threads around
// for "dead" printers.
//
bPolling = TRUE;
Error = GetLastError();
}
else
{
//
// The refresh may succeed, even though the handle is invalid.
// In this case, we enter polling mode.
//
if (!*pInfo->phWaitObject)
{
Error = ERROR_INVALID_PARAMETER;
bPolling = TRUE;
}
else
{
bPolling = FALSE;
}
}
//
// Check if printer was deleted.
//
bQuit = (pInfo->pfnCheckQuit) ?
(*pInfo->pfnCheckQuit)( pInfo->pContext ) :
FALSE;
LEAVE_PROTECTED_DATA(pInfo);
DBG_OUT_PROTECTED_DATA( pInfo);
if (bQuit) {
SEND_THREAD_MESSAGE(hwndFrame,
WM_DELETE_PRINTER,
0,
pInfo->hwnd);
}
WaitForSingleObject( pInfo->RefreshSignal, INFINITE );
pInfo->Changes = Changes;
DBGMSG( DBG_THREADS, ( "Sending thread message\n" ) );
SEND_THREAD_MESSAGE( pInfo->hwnd, WM_UPDATE_LIST, pInfo, 0 );
if( !bPolling )
{
hPrinterTmp = *pInfo->phWaitObject;
if (hPrinterTmp) {
Changes = WaitForPrinterChange( hPrinterTmp, WaitFlags );
DBGMSG( DBG_THREADS, ( "WaitForPrinterChange returned %08x\n", Changes ) );
if( Changes == 0 )
Error = GetLastError( );
} else {
Error = ERROR_INVALID_HANDLE;
}
}
if( pInfo->Alive && Error )
{
if( Changes == 0 )
{
DBGMSG( DBG_WARNING, ( "WaitForPrinterChange failed: Error %d\n", Error ) );
}
else if( !bPolling )
{
DBGMSG( DBG_WARNING, ( "Refresh failed: Error %d\n", Error ) );
}
/* If we're polling, wait a minute, then try to reopen the printer:
*/
DBGMSG( DBG_TRACE, ( "Polling\n" ) );
Sleep( 60000 );
//
// !! LATER !!
//
// If the handles get refreshed (by F5), we still close and
// reopen them. Not too crucial now, since the server shouldn't
// go down often, but something to look at later.
//
Changes = WaitFlags;
/* If access was denied, there's not much we can do:
*/
if( Error != ERROR_ACCESS_DENIED )
{
ENTER_PROTECTED_DATA( pInfo );
/* Try to reopen the printer or server.
* The point is that the server might have been rebooted,
* in which case our handles will be invalid.
*/
if( *pInfo->phWaitObject )
{
/* Call refresh. This is more or less bound to fail,
* but will deallocate any information we have:
*/
DBGMSG( DBG_TRACE, ( "Calling refresh (2): *phWaitObject == %x\n",
*pInfo->phWaitObject ) );
RefreshOK = (*pInfo->pfnRefresh)( pInfo->pContext, &WaitFlags );
DBGMSG( DBG_TRACE, ( "Calling Close Printer: *phWaitObject == %x\n",
*pInfo->phWaitObject ) );
ClosePrinter(*pInfo->phWaitObject);
*pInfo->phWaitObject = NULL;
}
//
// Only do this if we are still alive. If we are not,
// then the window is closing and we might as well
// forget the open.
//
if (pInfo->Alive) {
#ifdef SEP_WAITHANDLE
//
// Must reopen up if not same handle.
//
if (*pInfo->phMain) {
ClosePrinter(*pInfo->phMain);
*pInfo->phMain = NULL;
}
LEAVE_PROTECTED_DATA( pInfo );
ReopenPrinter(pInfo->pContext,
pInfo->WindowType,
FALSE);
if (!*pInfo->phWaitObject) {
ReopenPrinter(pInfo->pContext,
pInfo->WindowType,
TRUE);
}
#else
LEAVE_PROTECTED_DATA( pInfo );
ReopenPrinter(pInfo->pContext,
pInfo->WindowType,
FALSE);
#endif
} else {
LEAVE_PROTECTED_DATA( pInfo );
}
}
DBGMSG( DBG_WARNING, ( "Refresh error: Printer thread entering/continuing polling mode\n" ) );
bPolling = TRUE;
}
DBG_OUT_PROTECTED_DATA( pInfo );
ENTER_PROTECTED_DATA( pInfo );
}
#ifdef SEP_WAITHANDLE
if (*pInfo->phWaitObject) {
ClosePrinter(*pInfo->phWaitObject);
*pInfo->phWaitObject = NULL;
}
#endif
LEAVE_PROTECTED_DATA( pInfo );
DBG_OUT_PROTECTED_DATA( pInfo );
DestroyMDIWinInfo( pInfo );
DBGMSG( DBG_TRACE, ( "Thread %d exiting\n", GetCurrentThreadId( ) ) );
ExitThread( 0 );
return;
}
//
// Must be callable from either worker or main thread
// (Caller must synchronize, however)
//
DWORD
OpenThreadObject(
LPWSTR pszName,
PHANDLE phPrinter,
PDWORD pdwAccessGranted,
DWORD WindowType)
{
DWORD dwReturn = ERROR_SUCCESS;
switch(WindowType)
{
case MDIWIN_SERVER:
{
PRINTER_DEFAULTS PrinterDefaults = { NULL, NULL,
SERVER_ALL_ACCESS };
DBGMSG( DBG_TRACE, ( "Attempting to open %s\n",
pszName));
if( OpenPrinter( pszName, phPrinter, &PrinterDefaults ) )
{
*pdwAccessGranted = SERVER_ALL_ACCESS;
DBGMSG( DBG_TRACE, ( "Open was successful\n" ) );
}
else
{
DBGMSG( DBG_TRACE, ( "Open failed: Error %d\n", GetLastError( ) ) );
*phPrinter = NULL;
dwReturn = GetLastError();
}
break;
}
default:
{
DBGMSG( DBG_TRACE, ( "Attempting to open %s\n",
pszName ) );
if( OpenPrinterForSpecifiedAccess( pszName,
phPrinter,
PRINTER_ACCESS_HIGHEST_PERMITTED,
pdwAccessGranted))
{
DBGMSG( DBG_TRACE, ( "Open was successful\n" ) );
}
else
{
DBGMSG( DBG_TRACE, ( "Open failed: Error %d\n", GetLastError( ) ) );
*phPrinter = NULL;
dwReturn = GetLastError();
}
break;
}
}
return dwReturn;
}
VOID
InitQueueThread(
PVOID pContext)
{
PQUEUE pQueue = (PQUEUE)pContext;
HWND hwndActive;
ReopenPrinter(pQueue,
MDIWIN_PRINTER,
FALSE);
if ( pQueue->Error )
{
hwndActive = (HWND)SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0L);
if (!hwndActive)
hwndActive = hwndFrame;
if ( pQueue->Error == ERROR_INVALID_PASSWORD )
{
HANDLE hPrinter;
//
// !! LATER !!
//
// Move out all these USER calls to prevent extra threads
// and keep good syncing.
//
hPrinter = (HANDLE)DialogBoxParam( hInst,
MAKEINTRESOURCE( DLG_NETWORK_PASSWORD ),
hwndActive,
(DLGPROC)NetworkPasswordDialog,
(LPARAM)pQueue->pPrinterName );
//
// If we have a valid printer handle, a network connection must have
// been added. Make a note of this, so that we can delete it
// if something fails later on.
//
if( hPrinter && ( hPrinter != (HANDLE)-1 ) )
{
ClosePrinter( hPrinter );
ReopenPrinter(pQueue,
MDIWIN_PRINTER,
FALSE );
}
}
}
if ( pQueue->Error )
{
SEND_THREAD_MESSAGE( hwndFrame,
WM_THREAD_ERROR,
pQueue->Error,
pQueue->pMDIWinInfo );
}
}
BOOL
CheckQuitQueue(
PVOID pContext)
{
PQUEUE pQueue = (PQUEUE)pContext;
if (pQueue->pPrinter &&
(pQueue->pPrinter->Status & PRINTER_STATUS_PENDING_DELETION) &&
!pQueue->cJobs) {
return TRUE;
}
return FALSE;
}
/* InitServerWindowThread
*
* The routine called by EnumRegistryKeys with the name of each key found.
* This is called only when Print Manager starts up.
*
* Parameters:
*
* hwnd - The handle of the MDI parent window.
*
* pName - The server name for which the window is to be created.
*
*
* Returns:
*
* VOID
*
* Notes:
*
* ->hServer == NULL -> haven't tried opening it yet.
* == (HANDLE)-1 -> tried but got ERROR_ACCESS_DENIED
* else -> valid, use it.
*
*/
VOID
InitServerWindowThread(
PVOID pContext)
{
PRINTER_DEFAULTS PrinterDefaults = { NULL, NULL,
SERVER_ALL_ACCESS };
PSERVER_CONTEXT pServerContext = (PSERVER_CONTEXT)pContext;
HANDLE hServer = NULL;
LPPRINTER_INFO_2 pPrinters = NULL;
DWORD cbPrinters = 0;
DWORD cReturned;
DWORD Error = 0;
BOOL OK;
DWORD dwFirstError = 0;
//
// Check if the server was opened successfully in the server viewer
// dialog. If not, we need to do it now.
//
if (!pServerContext->hServer)
{
//
// Haven't tried to open it yet, do so now.
//
if (!OpenPrinter( pServerContext->pServerName,
&hServer,
&PrinterDefaults))
{
dwFirstError = GetLastError( );
}
else
{
pServerContext->AccessGranted = SERVER_ALL_ACCESS;
pServerContext->hServer = hServer;
}
}
else if (pServerContext->hServer == (HANDLE)-1)
{
//
// Tried to open it earlier, but failed with ERROR_ACCESS_DENIED.
//
dwFirstError = ERROR_ACCESS_DENIED;
pServerContext->hServer = NULL;
}
else
{
//
// Tried and succeeded!
//
pServerContext->AccessGranted = SERVER_ALL_ACCESS;
}
OK = ENUM_PRINTERS( PRINTER_ENUM_NAME,
pServerContext->pServerName,
2,
pPrinters,
cbPrinters,
&cbPrinters,
&cReturned );
if( OK )
{
#if 0
//
// This is disabled because it is annoying.
//
if (dwFirstError)
{
//
// We can use the Customer bit of the win32 error code
// to indicate to ourselves that we weren't able to open
// a handle to the server, but we were able to enumerate.
//
// The error will be something like:
//
// "Error opening server for auto-refresh mode."
//
// Remember to clear this bit!
//
dwFirstError |= (1 << 29);
SEND_THREAD_MESSAGE( hwndFrame,
WM_THREAD_ERROR,
dwFirstError,
pServerContext->pMDIWinInfo );
}
#endif
pServerContext->pPrinters = pPrinters;
pServerContext->cPrinters = cReturned;
pServerContext->cbPrinters = cbPrinters;
pServerContext->pSelPrinter = &pPrinters[0]; /* Select the first printer */
}
else
{
//
// Only use this new error code if the first call failed.
//
pServerContext->Error = dwFirstError ?
dwFirstError :
GetLastError( );
SEND_THREAD_MESSAGE( hwndFrame,
WM_THREAD_ERROR,
pServerContext->Error,
pServerContext->pMDIWinInfo );
if( pPrinters )
FreeSplMem( pPrinters );
}
return;
}
/* This guy just hangs around in case a printer gets created on the local
* machine. If this happens, it posts a message to the Print Manager
* frame-window procedure.
* This is because the printer might have been created remotely, in which
* case we won't know about it.
*
*/
void
LocalServerThread(
HANDLE hServer)
{
DWORD Changes;
DWORD dwWait;
HKEY hKeyCreate;
DWORD Error;
HKEY hKey = NULL;
HANDLE ahEvent[2] = { NULL, NULL };
ahEvent[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
ahEvent[1] = FindFirstPrinterChangeNotification(hServer,
PRINTER_CHANGE_ADD_PRINTER,
0,
NULL);
if (!ahEvent[0] || !ahEvent[1])
goto Fail;
//
// Since no one was smart enough to put this into the default hives,
// we need to create the key kere. We can't put it in the default
// hives now because we'll break people with floating profiles.
//
if (!RegCreateKey(HKEY_CURRENT_USER,
L"Printers",
&hKeyCreate)) {
RegCloseKey(hKeyCreate);
if (!RegCreateKey(HKEY_CURRENT_USER,
szPrintersConnections,
&hKeyCreate)) {
RegCloseKey(hKeyCreate);
}
}
if (RegOpenKey(HKEY_CURRENT_USER,
szPrintersConnections,
&hKey)) {
goto Fail;
}
if (RegNotifyChangeKeyValue(hKey,
TRUE,
REG_NOTIFY_CHANGE_NAME,
ahEvent[0],
TRUE))
goto Fail;
while (TRUE) {
dwWait = WaitForMultipleObjects(2, ahEvent, FALSE, INFINITE);
if (dwWait == WAIT_FAILED) {
DBGMSG(DBG_WARNING, ("Printman ServerThread: Wait failed: %d %d\n",
dwWait, GetLastError()));
goto Fail;
}
//
// Sleep an arbitrary value: this allows multiple notifications
// to collapse into one.
//
Sleep(1500);
switch (dwWait) {
case WAIT_OBJECT_0:
Error = RegNotifyChangeKeyValue(hKey,
TRUE,
REG_NOTIFY_CHANGE_NAME,
ahEvent[0],
TRUE);
if (Error) {
DBGMSG(DBG_ERROR, ("Printman ServerThread: RegNotifyChangeKeyValue failed: %d\n",
Error));
goto Fail;
}
break;
case WAIT_OBJECT_0+1:
if (!FindNextPrinterChangeNotification(ahEvent[1],
&Changes,
0,
NULL)) {
DBGMSG(DBG_WARNING, ("Printman ServerThread: FNPCN failed: %d\n",
GetLastError()));
goto Fail;
}
break;
default:
DBGMSG(DBG_ERROR, ("Printman ServerThread: Wait failed: %d %d\n",
dwWait, GetLastError()));
goto Fail;
}
DBGMSG( DBG_THREADS, ( "Notification on server returned %08x\n", Changes ) );
SEND_THREAD_MESSAGE(hwndFrame,
WM_PRINTER_ADDED,
dwWait-WAIT_OBJECT_0,
0);
}
Fail:
if (ahEvent[0])
CloseHandle(ahEvent[0]);
if (ahEvent[1])
FindClosePrinterChangeNotification(ahEvent[1]);
if (hKey)
RegCloseKey(hKey);
DBGMSG( DBG_TRACE, ( "Local server thread %d exiting\n", GetCurrentThreadId( ) ) );
ExitThread( 0 );
return;
}
VOID
ReopenPrinter(
PVOID pContext,
DWORD WindowType,
BOOL bWait)
{
HANDLE hPrinterNew;
DWORD dwAccessGrantedNew;
DWORD dwErrorNew;
WCHAR szPrinterName[MAX_PATH];
PHANDLE phPrinterTarg;
#define pQueue ((PQUEUE)pContext)
#define pServerContext ((PSERVER_CONTEXT)pContext)
if (WindowType != MDIWIN_SERVER) {
ENTER_PROTECTED_DATA( pQueue->pMDIWinInfo );
wcscpy(szPrinterName, pQueue->pPrinterName);
LEAVE_PROTECTED_DATA( pQueue->pMDIWinInfo);
dwErrorNew = OpenThreadObject(szPrinterName,
&hPrinterNew,
&dwAccessGrantedNew,
WindowType);
ENTER_PROTECTED_DATA( pQueue->pMDIWinInfo );
#ifdef SEP_WAITHANDLE
phPrinterTarg = bWait ?
&pQueue->hPrinterWait :
&pQueue->hPrinter;
#else
phPrinterTarg = &pQueue->hPrinter;
#endif
if ( !*phPrinterTarg ) {
*phPrinterTarg = hPrinterNew;
if (!bWait) {
pQueue->AccessGranted = dwAccessGrantedNew;
pQueue->Error = dwErrorNew;
}
LEAVE_PROTECTED_DATA( pQueue->pMDIWinInfo);
} else {
LEAVE_PROTECTED_DATA( pQueue->pMDIWinInfo);
//
// Close the handle if necessary.
//
if (hPrinterNew)
ClosePrinter(hPrinterNew);
}
} else {
ENTER_PROTECTED_DATA( pServerContext->pMDIWinInfo );
wcscpy(szPrinterName, pServerContext->pServerName);
LEAVE_PROTECTED_DATA( pServerContext->pMDIWinInfo);
dwErrorNew = OpenThreadObject(szPrinterName,
&hPrinterNew,
&dwAccessGrantedNew,
WindowType);
ENTER_PROTECTED_DATA( pServerContext->pMDIWinInfo );
#ifdef SEP_WAITHANDLE
phPrinterTarg = bWait ?
&pServerContext->hServerWait :
&pServerContext->hServer;
#else
phPrinterTarg = &pServerContext->hServer;
#endif
if ( !*phPrinterTarg ) {
*phPrinterTarg = hPrinterNew;
if (!bWait) {
pServerContext->AccessGranted = dwAccessGrantedNew;
pServerContext->Error = dwErrorNew;
}
LEAVE_PROTECTED_DATA( pServerContext->pMDIWinInfo);
} else {
LEAVE_PROTECTED_DATA( pServerContext->pMDIWinInfo);
//
// Close the handle if necessary.
//
if (hPrinterNew)
ClosePrinter(hPrinterNew);
}
}
#undef pQueue
#undef pServerContext
}