1416 lines
31 KiB
C++
1416 lines
31 KiB
C++
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1994 - 1999
|
|
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"
|
|
#include "spllibex.hxx"
|
|
#include "spinterf.hxx"
|
|
|
|
#if DBG
|
|
//#define DBG_PUIINFO DBG_INFO
|
|
#define DBG_PUIINFO DBG_NONE
|
|
#endif
|
|
|
|
//
|
|
// Singleton PrintLib object.
|
|
//
|
|
TPrintLib * TPrintLib::_pPrintLib;
|
|
|
|
extern "C" {
|
|
|
|
BOOL
|
|
DllMain(
|
|
IN HINSTANCE hInst,
|
|
IN DWORD dwReason,
|
|
IN LPVOID lpRes
|
|
);
|
|
}
|
|
|
|
VOID
|
|
DllCleanUp(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
DllMain(
|
|
IN HINSTANCE hInst,
|
|
IN DWORD dwReason,
|
|
IN LPVOID lpRes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dll entry point.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bReturn = TRUE;
|
|
|
|
switch( dwReason ){
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
ghInst = hInst;
|
|
|
|
#if DBG
|
|
// init the debug library
|
|
if( FAILED(_DbgInit()) )
|
|
{
|
|
DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to initialize debug library %d\n", GetLastError()));
|
|
bReturn = FALSE;
|
|
}
|
|
#endif // DBG
|
|
|
|
if( bReturn && !SHFusionInitializeFromModule(ghInst) )
|
|
{
|
|
DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to initialize fusion %d\n", GetLastError()));
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
// init (load) winspool.drv
|
|
if( bReturn && FAILED(Winspool_Init()) )
|
|
{
|
|
DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to load winspool.drv %d\n", GetLastError()));
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
if( bReturn && !bPrintLibInit() )
|
|
{
|
|
DBGMSG(DBG_WARN, ("DllEntryPoint: Failed to init PrintLib %d\n", GetLastError()));
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
if( !bReturn )
|
|
{
|
|
DllCleanUp();
|
|
return FALSE;
|
|
}
|
|
|
|
// setup CRT memory leak detection
|
|
CRT_DEBUG_SET_FLAG(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
|
|
CRT_DEBUG_REPORT_TO_STDOUT();
|
|
|
|
InitCommonControls();
|
|
|
|
//
|
|
// Initialize the custom time edit control class.
|
|
//
|
|
INITCOMMONCONTROLSEX icc;
|
|
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
|
icc.dwICC = ICC_DATE_CLASSES;
|
|
InitCommonControlsEx(&icc);
|
|
|
|
//
|
|
// Initialize the per therad message box counter here
|
|
//
|
|
CMsgBoxCounter::Initialize();
|
|
|
|
DisableThreadLibraryCalls( hInst );
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
//
|
|
// Uninitialize the message box counter
|
|
//
|
|
CMsgBoxCounter::Uninitialize();
|
|
|
|
VDSConnection::bStaticInitShutdown( TRUE );
|
|
|
|
//
|
|
// 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 )
|
|
{
|
|
DllCleanUp();
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
DllCleanUp(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up memory and do the uninitialization.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
Each UnInitialize function should take care of the case that Initialize is
|
|
not called. If Initialize is not called, UnInitialize will do nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Unregister the class.
|
|
//
|
|
UnregisterClass( gszClassName, ghInst );
|
|
UnregisterClass( gszQueueCreateClassName, ghInst );
|
|
|
|
delete gpCritSec;
|
|
delete TFolderList::gpFolderLock;
|
|
delete gpTrayLock;
|
|
|
|
// unload winspool.drv
|
|
VERIFY(SUCCEEDED(Winspool_Done()));
|
|
|
|
SHFusionUninitialize();
|
|
#if DBG
|
|
// shutdown the debug library
|
|
VERIFY(SUCCEEDED(_DbgDone()));
|
|
#endif // DBG
|
|
|
|
return;
|
|
}
|
|
|
|
TPrintLib::
|
|
TPrintLib(
|
|
VOID
|
|
) : TExec( gpCritSec ), _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( ));
|
|
|
|
if( !VALID_BASE( TExec ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
TNotify* pNotify = new TNotify;
|
|
|
|
if( !VALID_PTR( pNotify ))
|
|
{
|
|
if( pNotify )
|
|
{
|
|
// at this point pNotify ref count should be one, but since
|
|
// the ref count class is badly designed it is zero. we can't delete
|
|
// the class directly because the destructor is private and
|
|
// there is not much of a choise left rather than to artificially
|
|
// inc/dec the ref count so the object can get properly destroyed.
|
|
|
|
// do not keep weak ref while shutting down
|
|
pNotify->vIncRef();
|
|
|
|
pNotify->vDelete();
|
|
|
|
pNotify->cDecRef();
|
|
}
|
|
return;
|
|
}
|
|
|
|
_pNotify.vAcquire( pNotify );
|
|
}
|
|
|
|
TPrintLib::
|
|
~TPrintLib(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys the printui.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
SPLASSERT( Queue_bEmpty( ));
|
|
|
|
if( _hEventInit ){
|
|
CloseHandle( _hEventInit );
|
|
}
|
|
|
|
// if pNotify().pGet() is NULL that means the destructor is called
|
|
// twice which means that somebody is deleting the object twice.
|
|
// let's see who is the culprit here!!!
|
|
RIP(pNotify().pGet());
|
|
|
|
if( pNotify().pGet() )
|
|
{
|
|
//
|
|
// We are shutting down. Tell pNotify that it can start shutting
|
|
// down too.
|
|
//
|
|
pNotify()->vDelete();
|
|
|
|
// Release the reference. should not be used beyond this point
|
|
pNotify().vRelease();
|
|
}
|
|
|
|
//
|
|
// RL_Notify will automatically shut down when the last
|
|
// reference to it has been deleted.
|
|
//
|
|
}
|
|
|
|
BOOL
|
|
TPrintLib::
|
|
bInitialize(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the print library
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SPLASSERT( gpCritSec->bInside( ));
|
|
|
|
//
|
|
// The computer name needs to be formatted as "\\computername."
|
|
//
|
|
if( !bGetMachineName( _strComputerName, FALSE ) ){
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// 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 FALSE;
|
|
}
|
|
|
|
DWORD dwThreadId;
|
|
HANDLE hThread;
|
|
|
|
//
|
|
// Start the message pump by spawning a UI thread.
|
|
//
|
|
hThread = TSafeThread::Create( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)TPrintLib::xMessagePump,
|
|
this,
|
|
0,
|
|
&dwThreadId );
|
|
|
|
if( !hThread ){
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle( hThread );
|
|
|
|
//
|
|
// Wait for thread to initialize.
|
|
//
|
|
hr = MsgInfinitelyWaitForSingleObject( _hEventInit );
|
|
|
|
CloseHandle( _hEventInit );
|
|
_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( FAILED(hr) || !_hwndQueueCreate ){
|
|
PostThreadMessage( dwThreadId,
|
|
WM_QUIT,
|
|
0,
|
|
0 );
|
|
}
|
|
|
|
return (SUCCEEDED(hr) && bValid());
|
|
}
|
|
|
|
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_PUIINFO, ( "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_WARN,
|
|
( "PrintLib.lrQueueCreateWndProc: WinUtils_NewObject called here!\n" ));
|
|
|
|
} else {
|
|
|
|
HWND hwndQueue = NULL;
|
|
TQueue* pQueue = new TQueue(this, pInfo->_szPrinter, pInfo->_hEventClose);
|
|
|
|
if( VALID_PTR( pQueue )){
|
|
|
|
//
|
|
// don't keep weak refs...
|
|
//
|
|
pQueue->vIncRef();
|
|
|
|
if( pQueue->bInitialize( pInfo->_hwndOwner,
|
|
pInfo->_nCmdShow ) ){
|
|
|
|
hwndQueue = pQueue->hwnd();
|
|
ASSERT(hwndQueue);
|
|
|
|
} else {
|
|
|
|
if( pQueue->hwnd() ) {
|
|
|
|
DestroyWindow( pQueue->hwnd() );
|
|
}
|
|
}
|
|
|
|
//
|
|
// don't need the reference from now on
|
|
//
|
|
pQueue->vDecRefDelete();
|
|
|
|
} else {
|
|
|
|
delete pQueue;
|
|
|
|
//
|
|
// !! LATER !!
|
|
//
|
|
// Put up error message.
|
|
//
|
|
}
|
|
|
|
if( hwndQueue ){
|
|
|
|
SetForegroundWindow( hwndQueue );
|
|
ShowWindow( hwndQueue, SW_RESTORE );
|
|
|
|
} else {
|
|
|
|
if( pInfo->_hEventClose ) {
|
|
|
|
// in case of failure, set the handle, otherwise the caller
|
|
// will keep waiting on it forever.
|
|
SetEvent( pInfo->_hEventClose );
|
|
}
|
|
}
|
|
}
|
|
delete pInfo;
|
|
}
|
|
|
|
// very private worker proc
|
|
static DWORD WINAPI DefPrinterChanged_WorkerProc(LPVOID lpParameter)
|
|
{
|
|
// call back into the folder cache to update the default printer by calling SHChangeNotify.
|
|
TFolder::vDefaultPrinterChanged();
|
|
return 0;
|
|
}
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
TPrintLib::
|
|
lrQueueCreateWndProc(
|
|
IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
switch( uMsg )
|
|
{
|
|
case WM_SETTINGCHANGE:
|
|
|
|
// push this work in background as it may hit back in the folder cache and
|
|
// this should not happen in the UI thread. ask shell to run this in bkgnd.
|
|
// we don't really care if this fails, since that would mean that we are out
|
|
// of resources.
|
|
SHQueueUserWorkItem(reinterpret_cast<LPTHREAD_START_ROUTINE>(DefPrinterChanged_WorkerProc),
|
|
NULL, 0, 0, NULL, "printui.dll", 0);
|
|
break;
|
|
|
|
case WM_PRINTLIB_NEW:
|
|
|
|
SPLASSERT( _pPrintLib );
|
|
_pPrintLib->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
|
|
vQueueCreateWOW64(
|
|
IN HWND hwndOwner,
|
|
IN LPCTSTR pszPrinter,
|
|
IN INT nCmdShow,
|
|
IN LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
WOW64 version. see vQueueCreate below.
|
|
|
|
Arguments:
|
|
|
|
see vQueueCreate below.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// This function potentially may load the driver UI so we call a private API
|
|
// exported by winspool.drv, which will RPC the call to a special 64 bit surrogate
|
|
// process where the 64 bit driver can be loaded.
|
|
//
|
|
CDllLoader dll(TEXT("winspool.drv"));
|
|
ptr_PrintUIQueueCreate pfnPrintUIQueueCreate =
|
|
(ptr_PrintUIQueueCreate )dll.GetProcAddress(ord_PrintUIQueueCreate);
|
|
|
|
if( pfnPrintUIQueueCreate )
|
|
{
|
|
pfnPrintUIQueueCreate( hwndOwner, pszPrinter, nCmdShow, lParam );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
vQueueCreateNative(
|
|
IN HWND hwndOwner,
|
|
IN LPCTSTR pszPrinter,
|
|
IN INT nCmdShow,
|
|
IN LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Native version. see vQueueCreate below.
|
|
|
|
Arguments:
|
|
|
|
see vQueueCreate below.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
if( lstrlen( pszPrinter ) >= kPrinterBufMax ){
|
|
|
|
DBGMSG( DBG_WARN, ( "vQueueCreate: printer name too long "TSTR"\n", pszPrinter ));
|
|
|
|
iMessage( hwndOwner,
|
|
IDS_DESC,
|
|
IDS_ERR_BAD_PRINTER_NAME,
|
|
MB_OK|MB_ICONSTOP,
|
|
kMsgNone,
|
|
NULL );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If this print queue is using the fax driver,
|
|
// then launch the fax queue view.
|
|
//
|
|
if( bIsPrinterFaxDevice( pszPrinter ) )
|
|
{
|
|
vFaxQueueCreate( hwndOwner, pszPrinter, nCmdShow, lParam );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Bring up the win32 queue view.
|
|
//
|
|
vQueueCreateInternal( hwndOwner, pszPrinter, nCmdShow, lParam );
|
|
}
|
|
|
|
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( IsRunningWOW64() )
|
|
{
|
|
vQueueCreateWOW64( hwndOwner, pszPrinter, nCmdShow, lParam );
|
|
}
|
|
else
|
|
{
|
|
vQueueCreateNative( hwndOwner, pszPrinter, nCmdShow, lParam );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
vQueueCreateInternal(
|
|
IN HWND hwndOwner,
|
|
IN LPCTSTR pszPrinter,
|
|
IN INT nCmdShow,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
TStatusB bSuccess;
|
|
bSuccess DBGNOCHK = TRUE;
|
|
HANDLE hEventClose = NULL;
|
|
|
|
//
|
|
// Attempt to find a duplicate queue window.
|
|
//
|
|
HWND prevhwnd;
|
|
if( TQueue::bIsDuplicateWindow( hwndOwner, pszPrinter, &prevhwnd ) ){
|
|
SetForegroundWindow( prevhwnd );
|
|
ShowWindow( prevhwnd, SW_RESTORE );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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 ){
|
|
// create a manual reset event
|
|
hEventClose = CreateEvent( NULL, TRUE, FALSE, NULL );
|
|
if( !hEventClose ){
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
StringCchCopy( pInfo->_szPrinter, ARRAYSIZE(pInfo->_szPrinter), pszPrinter );
|
|
pInfo->_nCmdShow = nCmdShow;
|
|
pInfo->_hwndOwner = hwndOwner;
|
|
pInfo->_hEventClose = hEventClose;
|
|
|
|
//
|
|
// Need to grab the critical section, since this call should
|
|
// be reentrant.
|
|
//
|
|
{
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
TRefLock<TPrintLib> pPrintLib;
|
|
bSuccess DBGCHK = TPrintLib::bGetSingleton(pPrintLib);
|
|
|
|
//
|
|
// 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( pPrintLib.pGet() );
|
|
|
|
//
|
|
// Send a message to the UI thread to create a new window.
|
|
// We want all queues to use the same UI thread.
|
|
//
|
|
|
|
DBGMSG( DBG_PUIINFO, ( "vQueueCreate: posted pInfo %x\n", pInfo ));
|
|
bSuccess DBGCHK = PostMessage( pPrintLib->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 ){
|
|
VERIFY( SUCCEEDED( MsgInfinitelyWaitForSingleObject( hEventClose ) ) );
|
|
}
|
|
} 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
|
|
bIsPrinterFaxDevice(
|
|
IN LPCTSTR pszPrinter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if this printer is a fax device.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
TStatusB bStatus;
|
|
BOOL bReturn = FALSE;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
HANDLE hPrinter = NULL;
|
|
|
|
bStatus DBGCHK = OpenPrinter( (LPTSTR)pszPrinter, &hPrinter, NULL );
|
|
|
|
if( bStatus )
|
|
{
|
|
PPRINTER_INFO_2 pInfo2 = NULL;
|
|
DWORD cbInfo2 = 0;
|
|
|
|
//
|
|
// Get the current printer info 2.
|
|
//
|
|
bStatus DBGCHK = VDataRefresh::bGetPrinter( hPrinter, 2, (PVOID*)&pInfo2, &cbInfo2 );
|
|
|
|
if( bStatus )
|
|
{
|
|
if( !_tcscmp( pInfo2->pDriverName, FAX_DRIVER_NAME ) )
|
|
{
|
|
bReturn = TRUE;
|
|
}
|
|
|
|
//
|
|
// Release the printer info 2 structure.
|
|
//
|
|
FreeMem( pInfo2 );
|
|
}
|
|
|
|
//
|
|
// Close the printer handle.
|
|
//
|
|
ClosePrinter( hPrinter );
|
|
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
VOID
|
|
vFaxQueueCreate(
|
|
IN HWND hwndOwner,
|
|
IN LPCTSTR pszPrinter,
|
|
IN INT nCmdShow,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a printer fax queue view.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
TStatusB bStatus;
|
|
TString strParam;
|
|
|
|
//
|
|
// Build the command string.
|
|
//
|
|
bStatus DBGCHK = strParam.bCat(FAX_CLIENT_CONSOLE_IMAGE_NAME);
|
|
if (bStatus)
|
|
{
|
|
//
|
|
// Use our parents startup info.
|
|
//
|
|
STARTUPINFO StartupInfo;
|
|
GetStartupInfo( &StartupInfo );
|
|
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
|
|
//
|
|
// Create the rundll32 process.
|
|
//
|
|
if (CreateProcess(NULL, (LPTSTR)(LPCTSTR)strParam, NULL, NULL, FALSE,
|
|
DETACHED_PROCESS, NULL, NULL, &StartupInfo, &ProcessInformation))
|
|
{
|
|
CloseHandle(ProcessInformation.hThread);
|
|
CloseHandle(ProcessInformation.hProcess);
|
|
}
|
|
|
|
//
|
|
// If we cannot create the process then the fax queue exe may not be on
|
|
// this machine or in the path, if this happens we will fall back
|
|
// and birng up the Dumb win32 queue view.
|
|
//
|
|
else
|
|
{
|
|
vQueueCreateInternal(hwndOwner, pszPrinter, nCmdShow, lParam);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
bPrintLibInit(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the print library.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
if( !VDSConnection::bStaticInitShutdown( ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CAutoPtr<CCSLock> spFolderLock = new CCSLock;
|
|
CAutoPtr<CCSLock> spCritSec = new CCSLock;
|
|
CAutoPtr<CCSLock> spTrayLock = new CCSLock;
|
|
CAutoHandleAccel shAccel = LoadAccelerators(ghInst, (LPCTSTR)MAKEINTRESOURCE(ACCEL_PRINTQUEUE));
|
|
|
|
if( !shAccel || !spCritSec || !spFolderLock || !spTrayLock )
|
|
{
|
|
DBGMSG( DBG_WARN, ( "bPrintLibInit: globals creation failed %d\n", GetLastError( )));
|
|
return FALSE;
|
|
}
|
|
|
|
WNDCLASS WndClass;
|
|
|
|
WndClass.lpszClassName = gszClassName;
|
|
WndClass.style = 0L;
|
|
WndClass.lpfnWndProc = 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_WARN, ( "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_WARN, ( "bInitPrintLib: RegisterClass 2 failed %d\n", GetLastError( )));
|
|
return FALSE;
|
|
}
|
|
|
|
// everything seems to be OK here, initialize globals & detach the local objects
|
|
TFolderList::gpFolderLock = spFolderLock.Detach();
|
|
gpCritSec = spCritSec.Detach();
|
|
gpTrayLock = spTrayLock.Detach();
|
|
ghAccel = shAccel.Detach();
|
|
|
|
gcxSmIcon = GetSystemMetrics( SM_CXSMICON );
|
|
gcySmIcon = GetSystemMetrics( SM_CXSMICON );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Private helper routines.
|
|
|
|
********************************************************************/
|
|
|
|
|
|
BOOL
|
|
TPrintLib::
|
|
bGetSingleton(
|
|
TRefLock<TPrintLib> &RefLock
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
Revision History:
|
|
|
|
Lazar Ivanov Jul-2000 (rewrite)
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
// must be in the global CS.
|
|
CCSLock::Locker lock(*gpCritSec);
|
|
if( lock )
|
|
{
|
|
if( !_pPrintLib )
|
|
{
|
|
//
|
|
// Initialize _pPrintLib
|
|
//
|
|
TPrintLib *pPrintLib = new TPrintLib();
|
|
|
|
if( pPrintLib )
|
|
{
|
|
//
|
|
// We don't want to hold weak reference while initializing.
|
|
//
|
|
pPrintLib->vIncRef();
|
|
|
|
//
|
|
// Perform the real initialization - create the UI thread, etc...
|
|
//
|
|
if( pPrintLib->bInitialize() && VALID_PTR(pPrintLib) )
|
|
{
|
|
//
|
|
// Everything seems to be OK at this point.
|
|
//
|
|
_pPrintLib = pPrintLib;
|
|
RefLock.vAcquire(_pPrintLib);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Something has failed, nobody (except us) should be holding ref to
|
|
// pPrintLib at this point, so it will automatically go away when we dec
|
|
// ref it.
|
|
//
|
|
DBGMSG(DBG_WARN, ("PrintLib.bCreateSingleton: Abnormal termination %d %d\n", pPrintLib, GetLastError()));
|
|
}
|
|
|
|
//
|
|
// Release our own ref lock
|
|
//
|
|
pPrintLib->vDecRefDelete();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just acquire a ref to the object
|
|
//
|
|
RefLock.vAcquire(_pPrintLib);
|
|
}
|
|
|
|
// this would our measure for success
|
|
bRet = (NULL != _pPrintLib);
|
|
}
|
|
else
|
|
{
|
|
// unable to enter the global CS -- this could be only
|
|
// in the case where there is a contention and
|
|
// the kernel is unable to allocate the semaphore
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
STATUS
|
|
TPrintLib::
|
|
xMessagePump(
|
|
IN TPrintLib *pPrintLib
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
//
|
|
pPrintLib->_hwndQueueCreate = CreateWindowEx(
|
|
0,
|
|
gszQueueCreateClassName,
|
|
gszQueueCreateClassName,
|
|
WS_OVERLAPPED,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
ghInst,
|
|
NULL );
|
|
|
|
if( !pPrintLib->_hwndQueueCreate ){
|
|
bSuccess DBGCHK = FALSE;
|
|
}
|
|
|
|
SetEvent( pPrintLib->_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:
|
|
|
|
--*/
|
|
|
|
{
|
|
TPrintLib *pToDelete = NULL;
|
|
{
|
|
// must be in the global CS
|
|
CCSLock::Locker lock(*gpCritSec);
|
|
if (lock)
|
|
{
|
|
//
|
|
// we need to make this additional check here since
|
|
// somebody might have acquired a reference to us while
|
|
// waiting in the critical section in which case we
|
|
// shouldn't go away.
|
|
//
|
|
if (0 == _cRef)
|
|
{
|
|
//
|
|
// mark ourselves for destruction and zero out
|
|
// the global pointer.
|
|
//
|
|
pToDelete = this;
|
|
_pPrintLib = NULL;
|
|
SINGLETHREADRESET(UIThread);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// unable to enter the global CS -- this could be only
|
|
// in the case where there is a contention and
|
|
// the kernel is unable to allocate the semaphore.
|
|
//
|
|
// not much really we can do in this case except
|
|
// laving the object alive even if its RefCount
|
|
// is zero (i.e. leaking the object)
|
|
//
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if (pToDelete)
|
|
{
|
|
//
|
|
// initiate shutdown. this may take a while since
|
|
// it will require shutting down the background threads,
|
|
// clear all pending work items, etc...
|
|
//
|
|
pToDelete->hDestroy();
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TPrintLib::
|
|
hDestroy(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
call when the object is marked for destruction
|
|
no further access beyond this point.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Standard COM error handling
|
|
|
|
--*/
|
|
{
|
|
if (bValid())
|
|
{
|
|
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 the object will automatically go away.
|
|
//
|
|
TThreadM::vDelete();
|
|
|
|
// the object shouldn't be accessed beyond this point because
|
|
// Unlock is decrementing the internal refcount and the object
|
|
// goes away. it may not go away immediately and wait some pending
|
|
// work items to finish, but in general from now on we should
|
|
// consider ourselves dead.
|
|
TThreadM::Unlock();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Safe Thread class
|
|
|
|
********************************************************************/
|
|
|
|
HANDLE
|
|
TSafeThread::
|
|
Create(
|
|
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes
|
|
DWORD dwStackSize, // initial thread stack size, in bytes
|
|
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
|
|
LPVOID lpParameter, // argument for new thread
|
|
DWORD dwCreationFlags, // creation flags
|
|
LPDWORD lpThreadId // pointer to returned thread identifier
|
|
)
|
|
{
|
|
HANDLE hThread = NULL;
|
|
|
|
//
|
|
// Construct the thread info class.
|
|
//
|
|
CAutoPtr<TSafeThreadInfo> spThreadInfo = new TSafeThreadInfo;
|
|
CAutoHandleNT shEventReady = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if( spThreadInfo && shEventReady )
|
|
{
|
|
|
|
//
|
|
// Store the thread information.
|
|
//
|
|
spThreadInfo->lpStartAddress = lpStartAddress;
|
|
spThreadInfo->lpParameter = lpParameter;
|
|
spThreadInfo->hInstance = ghInst;
|
|
spThreadInfo->hEventReady = shEventReady;
|
|
|
|
//
|
|
// Start the thread to our routine.
|
|
//
|
|
DWORD dwThreadId;
|
|
hThread = CreateThread( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)TSafeThread::Start,
|
|
static_cast<TSafeThreadInfo*>(spThreadInfo),
|
|
0,
|
|
&dwThreadId );
|
|
|
|
if( hThread )
|
|
{
|
|
//
|
|
// If success wait for the thread to start properly
|
|
//
|
|
WaitForSingleObject( shEventReady, INFINITE );
|
|
|
|
//
|
|
// The thread will adopt this object, so we release the ownership.
|
|
//
|
|
spThreadInfo.Detach();
|
|
}
|
|
}
|
|
|
|
return hThread;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
TSafeThread::
|
|
Start(
|
|
PVOID pVoid
|
|
)
|
|
{
|
|
TSafeThreadInfo *pThreadInfo = (TSafeThreadInfo*)pVoid;
|
|
HINSTANCE hLibrary = NULL;
|
|
|
|
//
|
|
// If valid instance passed then load the library again.
|
|
//
|
|
if( pThreadInfo->hInstance ){
|
|
|
|
//
|
|
// Get the DLL name to do the load.
|
|
//
|
|
TCHAR szDllName[MAX_PATH+1];
|
|
if( SUCCEEDED( SafeGetModuleFileName( pThreadInfo->hInstance, szDllName, MAX_PATH ) ) ) {
|
|
DBGMSG( DBG_THREADM, ("Loading library " TSTR "\n", szDllName ) );
|
|
hLibrary = LoadLibrary( szDllName );
|
|
}
|
|
}
|
|
|
|
DBGMSG( DBG_THREADM, ("Thread Starting %x\n", pThreadInfo->lpStartAddress ) );
|
|
|
|
if( pThreadInfo->hEventReady )
|
|
{
|
|
SetEvent( pThreadInfo->hEventReady );
|
|
}
|
|
|
|
pThreadInfo->lpStartAddress( pThreadInfo->lpParameter );
|
|
|
|
DBGMSG( DBG_THREADM, ("Thread ending %x\n", pThreadInfo->lpStartAddress ) );
|
|
|
|
//
|
|
// Ensure we release the thread info.
|
|
//
|
|
delete pThreadInfo;
|
|
|
|
//
|
|
// If library was loaded then unload and exit the thread.
|
|
//
|
|
if( hLibrary ){
|
|
DBGMSG( DBG_THREADM, ("Releasing library \n") );
|
|
FreeLibraryAndExitThread( hLibrary, 0 );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|