1101 lines
23 KiB
C++
1101 lines
23 KiB
C++
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
printui.c
|
|
|
|
Abstract:
|
|
|
|
Singleton class that exists when printer queues are open.
|
|
|
|
Author:
|
|
|
|
Albert Ting (AlbertT) 22-Jun-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#define _GLOBALS
|
|
#include "globals.hxx"
|
|
|
|
#include "time.hxx"
|
|
#include "shlobj.h"
|
|
#include "folder.hxx"
|
|
|
|
//
|
|
// Singleton PrintLib object. This should really be
|
|
// a static of TPrintLib, but that would require qualification
|
|
// for every usage.
|
|
//
|
|
TPrintLib* gpPrintLib;
|
|
|
|
|
|
extern "C" {
|
|
|
|
MODULE_DEBUG_INIT( DBG_ERROR|DBG_WARN, DBG_ERROR );
|
|
|
|
BOOL
|
|
DllMain(
|
|
IN HINSTANCE hInst,
|
|
IN DWORD dwReason,
|
|
IN LPVOID lpRes
|
|
);
|
|
}
|
|
|
|
BOOL
|
|
DllMain(
|
|
IN HINSTANCE hInst,
|
|
IN DWORD dwReason,
|
|
IN LPVOID lpRes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dll entry point.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
switch( dwReason ){
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
ghInst = hInst;
|
|
|
|
if( !bSplLibInit( )){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "DllEntryPoint: Failed to init SplLib %d\n", GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( !bPrintLibInit( )){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "DllEntryPoint: Failed to init PrintLib %d\n", GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
InitCommonControls();
|
|
DisableThreadLibraryCalls( hInst );
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
//
|
|
// lpRes is NULL if it's a FreeLibrary, non-NULL if it's
|
|
// process termination. Don't do cleanup if it's process
|
|
// termination.
|
|
//
|
|
if( !lpRes ){
|
|
|
|
//
|
|
// Unregister the class.
|
|
//
|
|
UnregisterClass( gszClassName, hInst );
|
|
UnregisterClass( gszQueueCreateClassName, hInst );
|
|
|
|
delete gpCritSec;
|
|
|
|
vSplLibFree();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
TPrintLib::
|
|
TPrintLib(
|
|
VOID
|
|
) : TExec( gpCritSec ), _cRef( 0 ), _hEventInit( NULL )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes application object. This is a singleton object.
|
|
|
|
This will create the message pump and also the hwndQueueCreate
|
|
window which listens for requests.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
_strComputerName is the valid variable.
|
|
|
|
--*/
|
|
|
|
{
|
|
SPLASSERT( gpCritSec->bInside( ));
|
|
|
|
//
|
|
// Initialize the singleton global pointer.
|
|
//
|
|
gpPrintLib = this;
|
|
|
|
//
|
|
// Create init event. This event is used to synchronize
|
|
// the message pump initialization and this thread.
|
|
//
|
|
_hEventInit = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
|
|
if( !_hEventInit ){
|
|
return;
|
|
}
|
|
|
|
TNotify* pNotify = new TNotify;
|
|
|
|
if( !VALID_BASE( TExec ) || !VALID_PTR( pNotify )){
|
|
return;
|
|
}
|
|
|
|
Notify_vAcquire( pNotify );
|
|
|
|
//
|
|
// The computer name needs to be formatted as "\\computername."
|
|
//
|
|
|
|
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1 + 2];
|
|
DWORD cchComputerName = COUNTOF( szComputerName ) - 2;
|
|
|
|
if( !GetComputerName( &szComputerName[2], &cchComputerName )){
|
|
return;
|
|
}
|
|
|
|
szComputerName[0] =
|
|
szComputerName[1] = TEXT( '\\' );
|
|
|
|
if( !_strComputerName.bUpdate( szComputerName ) ){
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD dwThreadId;
|
|
HANDLE hThread;
|
|
|
|
//
|
|
// Start the message pump by spawning a UI thread.
|
|
//
|
|
hThread = CreateThread( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)TPrintLib::xMessagePump,
|
|
NULL,
|
|
0,
|
|
&dwThreadId );
|
|
|
|
if( !hThread ){
|
|
return;
|
|
}
|
|
CloseHandle( hThread );
|
|
|
|
//
|
|
// Wait for thread to initialize.
|
|
//
|
|
WaitForSingleObject( gpPrintLib->_hEventInit, INFINITE );
|
|
|
|
CloseHandle( gpPrintLib->_hEventInit );
|
|
gpPrintLib->_hEventInit = NULL;
|
|
|
|
//
|
|
// _hwndQueueCreate is our valid check. If it failed, cleanup.
|
|
// This is set by the worker thread (xMessagePump). We can access
|
|
// it only after _hEventInit has been triggered.
|
|
//
|
|
if( !_hwndQueueCreate ){
|
|
PostThreadMessage( dwThreadId,
|
|
WM_QUIT,
|
|
0,
|
|
0 );
|
|
}
|
|
}
|
|
|
|
TPrintLib::
|
|
~TPrintLib(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the printui.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
SPLASSERT( Queue_bEmpty( ));
|
|
|
|
if( _hEventInit ){
|
|
CloseHandle( _hEventInit );
|
|
}
|
|
|
|
//
|
|
// We are shutting down. Tell pNotify that it can start shutting
|
|
// down too.
|
|
//
|
|
pNotify()->vDelete();
|
|
|
|
//
|
|
// RL_Notify will automatically shut down when the last
|
|
// reference to it has been deleted.
|
|
//
|
|
}
|
|
|
|
|
|
HWND
|
|
TPrintLib::
|
|
hwndQueueFind(
|
|
IN LPCTSTR pszQueue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether a queue window is already open by this process.
|
|
|
|
!! LATER !!
|
|
|
|
This should be replaced by shell code so that it is system rather
|
|
than process wide.
|
|
|
|
Arguments:
|
|
|
|
psQueue - Name of the queue to check.
|
|
|
|
Return Value:
|
|
|
|
HWND - Window of the queue, if it exists.
|
|
NULL - Queue window does not exist.
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLETHREAD( UIThread );
|
|
|
|
TIter Iter;
|
|
TQueue* pQueue;
|
|
TCHAR szPrinter[kPrinterBufMax];
|
|
LPTSTR pszPrinter;
|
|
|
|
for( Queue_vIterInit( Iter ), Iter.vNext();
|
|
Iter.bValid();
|
|
Iter.vNext( )){
|
|
|
|
pQueue = Queue_pConvert( Iter );
|
|
|
|
pszPrinter = pQueue->pszPrinterName( szPrinter );
|
|
|
|
if( !lstrcmpi( szPrinter, pszQueue )){
|
|
return pQueue->hwnd();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
TPrintLib::
|
|
vHandleCreateQueue(
|
|
IN TInfo* pInfo ADOPT
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the creation of a new Queue window.
|
|
|
|
Arguments:
|
|
|
|
pInfo - Which queue should be created and extra parms.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
DBGMSG( DBG_INFO, ( "PrintLib.vHandleQueueCreate: received pInfo %x\n", pInfo ));
|
|
|
|
//
|
|
// The Add Printer wizard is invoked by double clicking
|
|
// a printer called "WinUtils_NewObject." I hope no one
|
|
// ever tries to create a printer under this name...
|
|
//
|
|
if( !lstrcmp( gszNewObject, pInfo->_szPrinter )){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "PrintLib.lrQueueCreateWndProc: WinUtils_NewObject called here!\n" ));
|
|
|
|
} else {
|
|
|
|
HWND hwndQueue = NULL;
|
|
|
|
//
|
|
// Check if printer is already open.
|
|
//
|
|
hwndQueue = gpPrintLib->hwndQueueFind( pInfo->_szPrinter );
|
|
|
|
if( !hwndQueue ){
|
|
|
|
TQueue* pQueue = new TQueue( pInfo->_hwndOwner,
|
|
pInfo->_szPrinter,
|
|
pInfo->_nCmdShow,
|
|
pInfo->_hEventClose );
|
|
|
|
if( VALID_PTR( pQueue )){
|
|
|
|
hwndQueue = pQueue->hwnd();
|
|
|
|
} else {
|
|
|
|
delete pQueue;
|
|
|
|
//
|
|
// !! LATER !!
|
|
//
|
|
// Put up error message.
|
|
//
|
|
}
|
|
}
|
|
|
|
if( hwndQueue ){
|
|
SetForegroundWindow( hwndQueue );
|
|
ShowWindow( hwndQueue, SW_RESTORE );
|
|
}
|
|
}
|
|
delete pInfo;
|
|
}
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
TPrintLib::
|
|
lrQueueCreateWndProc(
|
|
IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
switch( uMsg ){
|
|
|
|
case WM_WININICHANGE:
|
|
|
|
//
|
|
// !! HACK !!
|
|
//
|
|
// Hack to make the default printer icon change correctly.
|
|
// The NT spooler doesn't issue default change notifications,
|
|
// so create one here.
|
|
//
|
|
TFolder::vDefaultPrinterChanged();
|
|
break;
|
|
|
|
case WM_PRINTLIB_NEW:
|
|
|
|
SPLASSERT( gpPrintLib );
|
|
gpPrintLib->vHandleCreateQueue( (TInfo*)lParam );
|
|
break;
|
|
|
|
case WM_DESTROY_REQUEST:
|
|
|
|
DestroyWindow( hwnd );
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
|
|
SINGLETHREADRESET( UIThread );
|
|
PostQuitMessage( 0 );
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc( hwnd, uMsg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Public interface functions
|
|
|
|
********************************************************************/
|
|
|
|
VOID
|
|
vQueueCreate(
|
|
IN HWND hwndOwner,
|
|
IN LPCTSTR pszPrinter,
|
|
IN INT nCmdShow,
|
|
IN LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a printer queue.
|
|
|
|
Arguments:
|
|
|
|
lParam - TRUE => fModal.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
if( lstrlen( pszPrinter ) >= kPrinterBufMax ){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "vQueueCreate: printer name too long "TSTR"\n",
|
|
pszPrinter ));
|
|
|
|
iMessage( hwndOwner,
|
|
IDS_DESC,
|
|
IDS_ERR_BAD_PRINTER_NAME,
|
|
MB_OK|MB_ICONSTOP,
|
|
kMsgNone,
|
|
NULL );
|
|
|
|
return;
|
|
}
|
|
|
|
TStatusB bSuccess;
|
|
bSuccess DBGNOCHK = TRUE;
|
|
|
|
HANDLE hEventClose = NULL;
|
|
|
|
//
|
|
// Initialize TInfo and acquire the gpPrintLib.
|
|
//
|
|
TPrintLib::TInfo* pInfo = new TPrintLib::TInfo;
|
|
|
|
if( !VALID_PTR( pInfo )){
|
|
goto Fail;
|
|
}
|
|
|
|
//
|
|
// If modal, send in an event to trigger when the window is closed.
|
|
//
|
|
if( lParam ){
|
|
hEventClose = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
if( !hEventClose ){
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
lstrcpy( pInfo->_szPrinter, pszPrinter );
|
|
pInfo->_nCmdShow = nCmdShow;
|
|
pInfo->_hwndOwner = hwndOwner;
|
|
pInfo->_hEventClose = hEventClose;
|
|
|
|
//
|
|
// Need to grab the critical section, since this call should
|
|
// be reentrant.
|
|
//
|
|
{
|
|
TCritSecLock CSL( *gpCritSec );
|
|
|
|
bSuccess DBGCHK = TPrintLib::bGetSingleton();
|
|
|
|
//
|
|
// Queue creation the first time just passes the TInfo to
|
|
// a worker function that serves as the message pump. If
|
|
// gpPrintLib has been instantiated, then we'll post a message
|
|
// and return immediately (freeing this thread).
|
|
//
|
|
// If gpPrintLib has not been instantiated, then we create the
|
|
// singleton here.
|
|
//
|
|
|
|
if( bSuccess ){
|
|
|
|
//
|
|
// Acquire a reference to gpPrintLib so that
|
|
// the pInfo can be posted without worrying about losing
|
|
// gpPrintLib. This reference will automatically be
|
|
// release when pInfo is destroyed.
|
|
//
|
|
pInfo->PrintLib_vAcquire( gpPrintLib );
|
|
|
|
//
|
|
// Send a message to the UI thread to create a new window.
|
|
// We want all queues to use the same UI thread.
|
|
//
|
|
|
|
DBGMSG( DBG_INFO, ( "vQueueCreate: posted pInfo %x\n", pInfo ));
|
|
bSuccess DBGCHK = PostMessage( gpPrintLib->hwndQueueCreate(),
|
|
WM_PRINTLIB_NEW,
|
|
0,
|
|
(LPARAM)pInfo );
|
|
|
|
}
|
|
}
|
|
|
|
Fail:
|
|
|
|
if( bSuccess ){
|
|
|
|
//
|
|
// Success - check if needs to be modal. If so, wait until
|
|
// window is closed.
|
|
//
|
|
if( lParam ){
|
|
WaitForSingleObject( hEventClose, INFINITE );
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Destroy the pInfo data. This will automatically decrement
|
|
// the reference count on gpPrintLib (if necessary).
|
|
//
|
|
delete pInfo;
|
|
vShowResourceError( hwndOwner );
|
|
}
|
|
|
|
if( hEventClose ){
|
|
CloseHandle( hEventClose );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
bPrintLibInit(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the print library.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
gpCritSec = new MCritSec;
|
|
|
|
if( !VALID_PTR( gpCritSec )){
|
|
|
|
delete gpCritSec;
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "bPrintLibInit: gpCritSec creation failed %d\n",
|
|
GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( !TTime::bInitClass( )){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "bPrintLib: TTime::bInitClass failed %d\n",
|
|
GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ghAccel = LoadAccelerators( ghInst,
|
|
(LPCTSTR)MAKEINTRESOURCE( ACCEL_PRINTQUEUE ));
|
|
|
|
if( !ghAccel ){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "bInitPrintLib: LoadAccelerators failed %d\n",
|
|
GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
WNDCLASS WndClass;
|
|
|
|
WndClass.lpszClassName = gszClassName;
|
|
WndClass.style = 0L;
|
|
WndClass.lpfnWndProc = (WNDPROC)&MGenericWin::SetupWndProc;
|
|
WndClass.cbClsExtra = 0;
|
|
WndClass.cbWndExtra = sizeof( TPrintLib* );
|
|
WndClass.hInstance = ghInst;
|
|
WndClass.hIcon = NULL;
|
|
WndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); // NULL
|
|
WndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
|
|
WndClass.lpszMenuName = MAKEINTRESOURCE( MENU_PRINTQUEUE );
|
|
|
|
if( !RegisterClass( &WndClass )){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "bInitPrintLib: RegisterClass failed %d\n",
|
|
GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory( &WndClass, sizeof( WndClass ));
|
|
|
|
WndClass.lpszClassName = gszQueueCreateClassName;
|
|
WndClass.lpfnWndProc = TPrintLib::lrQueueCreateWndProc;
|
|
WndClass.hInstance = ghInst;
|
|
|
|
if( !RegisterClass( &WndClass )){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "bInitPrintLib: RegisterClass 2 failed %d\n",
|
|
GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gcxSmIcon = GetSystemMetrics( SM_CXICON )/2;
|
|
gcySmIcon = GetSystemMetrics( SM_CYICON )/2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Private helper routines.
|
|
|
|
********************************************************************/
|
|
|
|
|
|
BOOL
|
|
TPrintLib::
|
|
bGetSingleton(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the singleton object, and stores it in the global.
|
|
If the singleton has not been created, then it is instantiated
|
|
here.
|
|
|
|
Must be called in the critical section.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE = success (singleton exists), FALSE = fail.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Must be in CS.
|
|
//
|
|
SPLASSERT( gpCritSec->bInside( ));
|
|
|
|
if( !gpPrintLib ){
|
|
|
|
new TPrintLib();
|
|
|
|
if( !VALID_PTR( gpPrintLib )){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "PrintLib.bCreateSingleton: Abnormal termination %d %d\n",
|
|
gpPrintLib, GetLastError( )));
|
|
|
|
//
|
|
// Creation failed, delete the object, null the pointer,
|
|
// and prepare to fail.
|
|
//
|
|
// Normally this is a request to delete the object, which may
|
|
// not occur for a while, but since we haven't tried using
|
|
// it yet, it immediately deletes the object.
|
|
//
|
|
// There is no need to delete gpPrintLib since vDelete will
|
|
// virtually destruct it.
|
|
//
|
|
if( gpPrintLib ){
|
|
gpPrintLib->TThreadM::vDelete();
|
|
}
|
|
gpPrintLib = NULL;
|
|
SINGLETHREADRESET( UIThread );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
STATUS
|
|
TPrintLib::
|
|
xMessagePump(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main message pump. The printer windows are architected slightly
|
|
differently than regular explorer windows: there is a single UI
|
|
thread, and multiple worker threads. This has two main advantages:
|
|
|
|
1. Fewer threads even when many printer queues are open (assuming
|
|
NT->NT connections).
|
|
|
|
2. The UI virtually never hangs.
|
|
|
|
It has this slight disadvantage that sometimes the main UI thread
|
|
is busy (e.g., inserting 2,000 print jobs may take a few seconds)
|
|
in which all print UI windows are hung.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Win32 status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
MSG msg;
|
|
TStatusB bSuccess;
|
|
bSuccess DBGNOCHK = TRUE;
|
|
|
|
//
|
|
// Ensure that functions that must execute in the UI thread don't
|
|
// execute elsewhere.
|
|
//
|
|
SINGLETHREAD( UIThread );
|
|
|
|
//
|
|
// Create our window to listen for Queue Creates.
|
|
//
|
|
|
|
gpPrintLib->_hwndQueueCreate = CreateWindowEx(
|
|
0,
|
|
gszQueueCreateClassName,
|
|
gszQueueCreateClassName,
|
|
WS_OVERLAPPED,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
ghInst,
|
|
NULL );
|
|
|
|
if( !gpPrintLib->_hwndQueueCreate ){
|
|
bSuccess DBGCHK = FALSE;
|
|
}
|
|
|
|
SetEvent( gpPrintLib->_hEventInit );
|
|
|
|
if( !bSuccess ){
|
|
return 0;
|
|
}
|
|
|
|
while( GetMessage( &msg, NULL, 0, 0 )){
|
|
|
|
if( !TranslateAccelerator( ghwndActive,
|
|
ghAccel,
|
|
&msg )){
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
}
|
|
|
|
return (STATUS)msg.wParam;
|
|
}
|
|
|
|
VOID
|
|
TPrintLib::
|
|
vRefZeroed(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Virtual definition from class MRefCom.
|
|
|
|
The reference count has reached zero; we should cleanup the
|
|
object. Immediately zero out gpPrintLib then post a message
|
|
instructing it to destory itself.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
if( bValid( )){
|
|
|
|
TCritSecLock CSL( *gpCritSec );
|
|
|
|
//
|
|
// Close a potential race condition: gpPrintLib is still
|
|
// non-null, and could have been acquired right before we
|
|
// grabbed gpCritSec. If so, the refcount is now non-zero
|
|
// and we shouldn't delete the object. (This can happen
|
|
// if the last print queue window is closed, then a new one is
|
|
// opened quickly.)
|
|
//
|
|
|
|
if( !_cRef ){
|
|
PostMessage( _hwndQueueCreate, WM_DESTROY_REQUEST, 0, 0 );
|
|
|
|
//
|
|
// We can't immediately delete ourselves because we may have
|
|
// pending work items. Notify TThreadM that it should start
|
|
// shutdown, then zero the global out so that no one else will
|
|
// try to use us.
|
|
//
|
|
// When TThreadM has shut down, it will call
|
|
// vThreadMDeleteComplete then we can delete ourselves.
|
|
//
|
|
TThreadM::vDelete();
|
|
gpPrintLib = NULL;
|
|
SINGLETHREADRESET( UIThread );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
TPrintLib::
|
|
vThreadMDeleteComplete(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When the base class TExec has completed shutting down, then this
|
|
virtual function is called which completes deletion of PrintLib.
|
|
At this stage, the gpPrintLib has already been NULL'ed.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Default printer code. Should be moved into spoolss\client
|
|
so that SetPrinter w/ attribute_default works.
|
|
|
|
********************************************************************/
|
|
|
|
DEFAULT_PRINTER
|
|
CheckDefaultPrinter(
|
|
IN LPCTSTR pszPrinter OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the default printer status.
|
|
|
|
Arguments:
|
|
|
|
pszPrinter - Check if this printer is the default (optional).
|
|
|
|
Return Value:
|
|
|
|
kNoDefault - No default printer exists.
|
|
|
|
kDefault - pszPrinter is the default printer.
|
|
|
|
kOtherDefault - Default printer exists, but it's not pszPrinter
|
|
(or pszPrinter was not passed in).
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR szPrinterDefault[kPrinterDefaultStringBufMax];
|
|
szPrinterDefault[0] = 0;
|
|
|
|
if( !GetProfileString( gszWindows,
|
|
gszDevice,
|
|
szPrinterDefault,
|
|
szPrinterDefault,
|
|
COUNTOF( szPrinterDefault ))){
|
|
|
|
return kNoDefault;
|
|
}
|
|
|
|
//
|
|
// If pszPrinter passed in, see if it's the default.
|
|
//
|
|
if( pszPrinter ){
|
|
|
|
LPTSTR psz = lstrchr( szPrinterDefault, TEXT( ',' ));
|
|
|
|
//
|
|
// We should find a comma, but let's be safe and check.
|
|
// Convert "superp,winspool,Ne00:" to "superp."
|
|
//
|
|
if( psz ){
|
|
*psz = 0;
|
|
|
|
if( !lstrcmpi( szPrinterDefault, pszPrinter )){
|
|
return kDefault;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kOtherDefault;
|
|
}
|
|
|
|
|
|
BOOL
|
|
bSetDefaultPrinter(
|
|
IN LPCTSTR pszPrinter OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the default printer to pszPrinter.
|
|
|
|
Note: assumes spooler has correctly set up szDevices section.
|
|
|
|
Arguments:
|
|
|
|
pszPrinter - Printer to set as the default. If not present,
|
|
a printer from [devices] is set as the default.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE = success, FALSE = fail.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR szDefaultPrinterString[kPrinterDefaultStringBufMax];
|
|
TCHAR szBuffer[kPrinterDefaultStringBufMax];
|
|
TCHAR szAnyPrinter[kPrinterBufMax];
|
|
|
|
//
|
|
// If pszPrinter is NULL, look for any printer in the [devices]
|
|
// section.
|
|
//
|
|
if( !pszPrinter ){
|
|
|
|
if( !GetProfileString( gszDevices,
|
|
NULL,
|
|
gszNULL,
|
|
szAnyPrinter,
|
|
COUNTOF( szAnyPrinter ))){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "bSetDefaultPrinter: any printer, but none available.\n" ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Pick the first one to be the default.
|
|
//
|
|
pszPrinter = szAnyPrinter;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Avoid broadcasts as much as possible. Se if the printer
|
|
// is already the default, and don't do anything if it is.
|
|
//
|
|
if( CheckDefaultPrinter( pszPrinter ) == kDefault ){
|
|
DBGMSG( DBG_WARN,
|
|
( "bSetDefaultPrinter: "TSTR" already the default printer.\n",
|
|
pszPrinter ));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not the default, set it.
|
|
//
|
|
if( !GetProfileString( gszDevices,
|
|
pszPrinter,
|
|
gszNULL,
|
|
szBuffer,
|
|
COUNTOF( szBuffer ))){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "bSetDefaultPrinter: "TSTR" not found in szDevices\n", pszPrinter ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
lstrcpy( szDefaultPrinterString, pszPrinter );
|
|
lstrcat( szDefaultPrinterString, gszComma );
|
|
lstrcat( szDefaultPrinterString, szBuffer );
|
|
|
|
SPLASSERT( lstrlen( szDefaultPrinterString ) <
|
|
COUNTOF( szDefaultPrinterString ));
|
|
|
|
if( !WriteProfileString( gszWindows,
|
|
gszDevice,
|
|
szDefaultPrinterString )){
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "bSetDefaultPrinter: unable to set default string "TSTR", %d\n",
|
|
szDefaultPrinterString, GetLastError( )));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Tell the world and make everyone flash.
|
|
//
|
|
SendNotifyMessage( HWND_BROADCAST,
|
|
WM_WININICHANGE,
|
|
0,
|
|
(LPARAM)gszWindows );
|
|
|
|
return TRUE;
|
|
}
|