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.
928 lines
27 KiB
928 lines
27 KiB
/*++
|
|
|
|
Copyright (c) 1990-1994 Microsoft Corporation
|
|
All rights reserved
|
|
|
|
Module Name:
|
|
|
|
init.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Revision History:
|
|
|
|
4-Jan-1999 Khaleds
|
|
Added Code for optimiziting the load time of the spooler by decoupling
|
|
the startup dependency between spoolsv and spoolss
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "local.h"
|
|
#include <wmi.h>
|
|
#include "ncmgr.hxx"
|
|
#pragma hdrstop
|
|
|
|
WCHAR szDefaultPrinterNotifyInfoDataSize[] = L"DefaultPrinterNotifyInfoDataSize";
|
|
WCHAR szFailAllocs[] = L"FailAllocs";
|
|
|
|
WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 3];
|
|
|
|
#define DEFAULT_PRINTER_NOTIFY_DATA 0x80
|
|
DWORD cDefaultPrinterNotifyInfoData = DEFAULT_PRINTER_NOTIFY_DATA;
|
|
WCHAR szRouterCacheSize[] = L"RouterCacheSize";
|
|
|
|
WCHAR szUpgradeInProgKey[] = L"UpgradeInProgress";
|
|
WCHAR szMiniSetupInProgKey[] = L"MiniSetupInProgress";
|
|
|
|
fnWinSpoolDrv fnClientSide;
|
|
BOOL bWinspoolInitialized = FALSE;
|
|
|
|
HANDLE hEventInit = NULL;
|
|
BOOL Initialized = FALSE;
|
|
DWORD dwUpgradeFlag = 0;
|
|
SERVICE_STATUS_HANDLE ghSplHandle = NULL;
|
|
|
|
extern CRITICAL_SECTION RouterCriticalSection;
|
|
extern CRITICAL_SECTION DeviceArrivalCS;
|
|
extern PROUTERCACHE RouterCacheTable;
|
|
extern DWORD RouterCacheSize;
|
|
extern LPWSTR *ppszOtherNames;
|
|
|
|
BOOL
|
|
SpoolerInitAll();
|
|
|
|
VOID
|
|
RegisterForPnPEvents(
|
|
VOID
|
|
);
|
|
|
|
LPPROVIDOR
|
|
InitializeProvidor(
|
|
LPWSTR pProvidorName,
|
|
LPWSTR pFullName)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HANDLE hModule = NULL;
|
|
LPPROVIDOR pProvidor = NULL;
|
|
HANDLE hToken = NULL;
|
|
UINT ErrorMode;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if (!hToken)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// WARNING-WARNING-WARNING, we null set the print providor
|
|
// structure. older version of the print providor have different print
|
|
// providor sizes so they will set only some function pointers and not
|
|
// all of them
|
|
//
|
|
|
|
if ( !(pProvidor = (LPPROVIDOR)AllocSplMem(sizeof(PROVIDOR))) ||
|
|
!(pProvidor->lpName = AllocSplStr(pProvidorName)) ) {
|
|
|
|
DBGMSG(DBG_ERROR,
|
|
("InitializeProvidor can't allocate memory for %ws\n",
|
|
pProvidorName));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Make sure we don't get any dialogs popping up while this goes on.
|
|
//
|
|
ErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
|
|
|
|
hModule = pProvidor->hModule = LoadLibraryEx( pProvidorName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
|
|
|
|
SetErrorMode( ErrorMode );
|
|
|
|
if ( !hModule ) {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("InitializeProvider failed LoadLibrary( %ws ) error %d\n",
|
|
pProvidorName, GetLastError() ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
pProvidor->fpInitialize = GetProcAddress(hModule, "InitializePrintProvidor");
|
|
|
|
if ( !pProvidor->fpInitialize )
|
|
goto Cleanup;
|
|
|
|
bRet = (BOOL)pProvidor->fpInitialize(&pProvidor->PrintProvidor,
|
|
sizeof(PRINTPROVIDOR),
|
|
pFullName);
|
|
|
|
if ( !bRet ) {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("InitializePrintProvider failed for providor %ws error %d\n",
|
|
pProvidorName, GetLastError()));
|
|
}
|
|
|
|
//
|
|
// It is not a critical error if ImpersonatePrinterClient fails.
|
|
// If fpInitialize succeeds and ImpersonatePrinterClient fails,
|
|
// then if we set bRet to FALSE we forcefully unload the initialized
|
|
// provider DLL and can cause resource leaks.
|
|
//
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
Cleanup:
|
|
|
|
if ( bRet ) {
|
|
|
|
//
|
|
// Fixup any NULL entrypoints.
|
|
//
|
|
FixupOldProvidor( &pProvidor->PrintProvidor );
|
|
|
|
return pProvidor;
|
|
} else {
|
|
|
|
if ( hModule )
|
|
FreeLibrary(hModule);
|
|
|
|
if ( pProvidor ) {
|
|
|
|
FreeSplStr(pProvidor->lpName);
|
|
FreeSplMem(pProvidor);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
DllMain(
|
|
HINSTANCE hInstDLL,
|
|
DWORD fdwReason,
|
|
LPVOID lpvReserved)
|
|
{
|
|
BOOL Failed = FALSE;
|
|
BOOL ThreadInitted = FALSE,
|
|
WPCInitted = FALSE,
|
|
RouterCritSecInit = TRUE,
|
|
DevArrCritSecInit = TRUE;
|
|
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
|
|
if( !bSplLibInit(NULL)){
|
|
Failed = TRUE;
|
|
goto Done;
|
|
}
|
|
|
|
DisableThreadLibraryCalls(hInstDLL);
|
|
|
|
if(!InitializeCriticalSectionAndSpinCount(&RouterCriticalSection, 0x80000000))
|
|
{
|
|
Failed = TRUE;
|
|
RouterCritSecInit = FALSE;
|
|
goto Done;
|
|
}
|
|
|
|
if(!InitializeCriticalSectionAndSpinCount(&DeviceArrivalCS, 0x80000000))
|
|
{
|
|
Failed = TRUE;
|
|
DevArrCritSecInit = FALSE;
|
|
goto Done;
|
|
}
|
|
|
|
if (!WPCInit()) {
|
|
Failed = TRUE;
|
|
goto Done;
|
|
} else {
|
|
WPCInitted = TRUE;
|
|
}
|
|
|
|
if (!ThreadInit()) {
|
|
Failed = TRUE;
|
|
goto Done;
|
|
} else {
|
|
ThreadInitted = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create our global init event (manual reset)
|
|
// This will be set when we are initialized.
|
|
//
|
|
hEventInit = CreateEvent(NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (!hEventInit) {
|
|
|
|
Failed = TRUE;
|
|
goto Done;
|
|
}
|
|
|
|
Done:
|
|
if (Failed)
|
|
{
|
|
if (RouterCritSecInit) {
|
|
DeleteCriticalSection(&RouterCriticalSection);
|
|
}
|
|
|
|
if (DevArrCritSecInit)
|
|
{
|
|
DeleteCriticalSection(&DeviceArrivalCS);
|
|
}
|
|
|
|
if (hEventInit) {
|
|
CloseHandle(hEventInit);
|
|
}
|
|
|
|
if (WPCInitted) {
|
|
WPCDestroy();
|
|
}
|
|
|
|
if (ThreadInitted) {
|
|
ThreadDestroy();
|
|
}
|
|
|
|
WmiTerminateTrace(); // Unregisters spoolss from WMI.
|
|
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
ThreadDestroy();
|
|
WPCDestroy();
|
|
|
|
CloseHandle(hEventInit);
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
InitializeRouter(
|
|
IN RouterInitializationParams *pRouterParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will Initialize the Routing layer for the Print Providors.
|
|
This will involve scanning the win.ini file, loading Print Providors, and
|
|
creating instance data for each.
|
|
|
|
Arguments:
|
|
|
|
pRouterParams - Parameters passed in to the router.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful.
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPPROVIDOR pProvidor;
|
|
DWORD cbDll;
|
|
WCHAR ProvidorName[MAX_PATH], Dll[MAX_PATH], szFullName[MAX_PATH];
|
|
HKEY hKey, hKey1;
|
|
LONG Status;
|
|
|
|
LPWSTR lpMem = NULL;
|
|
LPWSTR psz = NULL;
|
|
DWORD dwRequired = 0;
|
|
BOOL bRet = FALSE;
|
|
|
|
DWORD SpoolerPriorityClass = 0;
|
|
NT_PRODUCT_TYPE NtProductType;
|
|
DWORD dwCacheSize = 0;
|
|
|
|
DWORD dwType;
|
|
DWORD cbData;
|
|
|
|
DWORD i;
|
|
extern DWORD cOtherNames;
|
|
WCHAR szSetupKey[] = L"System\\Setup";
|
|
|
|
//
|
|
// First, assign the server side exports to our global variable to keep
|
|
// track of them.
|
|
//
|
|
gpServerExports = (PrintSpoolerServerExports*)(pRouterParams->pExports);
|
|
|
|
//
|
|
// WMI Trace Events. Registers spoolss with WMI.
|
|
//
|
|
WmiInitializeTrace();
|
|
|
|
//
|
|
// Initliaize the name cache
|
|
//
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = CacheInitNameCache();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InitializePnPIPAddressChangeListener(CacheRefresh);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DBGMSG(DBG_ERROR, ("Failed initializing the cache hr %x\n", hr));
|
|
|
|
ExitProcess(0);
|
|
}
|
|
}
|
|
|
|
ghSplHandle = pRouterParams->SpoolerStatusHandle;
|
|
|
|
//
|
|
// We are now assume that the other services and drivers have
|
|
// initialized. The loader of this dll must do this syncing.
|
|
//
|
|
// spoolss\server does this by using the GroupOrderList
|
|
// SCM will try load load parallel and serial before starting
|
|
// the spooler service.
|
|
//
|
|
|
|
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
szPrintKey,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey)) {
|
|
|
|
cbData = sizeof(SpoolerPriorityClass);
|
|
|
|
//
|
|
// SpoolerPriority
|
|
//
|
|
Status = RegQueryValueEx(hKey,
|
|
L"SpoolerPriority",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&SpoolerPriorityClass,
|
|
&cbData);
|
|
|
|
|
|
if (Status == ERROR_SUCCESS &&
|
|
(SpoolerPriorityClass == IDLE_PRIORITY_CLASS ||
|
|
SpoolerPriorityClass == NORMAL_PRIORITY_CLASS ||
|
|
SpoolerPriorityClass == HIGH_PRIORITY_CLASS)) {
|
|
|
|
Status = SetPriorityClass(GetCurrentProcess(), SpoolerPriorityClass);
|
|
}
|
|
|
|
|
|
cbData = sizeof(cDefaultPrinterNotifyInfoData);
|
|
|
|
//
|
|
// Ignore failure case since we can use the default
|
|
//
|
|
RegQueryValueEx(hKey,
|
|
szDefaultPrinterNotifyInfoDataSize,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&cDefaultPrinterNotifyInfoData,
|
|
&cbData);
|
|
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
|
|
// Is it an upgrade?
|
|
|
|
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
szSetupKey,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey)) {
|
|
|
|
/*++
|
|
You can tell if you are inside gui setup by looking for
|
|
HKLM\System\Setup\SystemSetupInProgress -- non zero means gui-setup is running.
|
|
|
|
|
|
The following description is outdated.
|
|
|
|
Description: the query update flag is set up by TedM. We will read this flag
|
|
if the flag has been set, we will set a boolean variable saying that we're in
|
|
the upgrade mode. All upgrade activities will be carried out based on this flag.
|
|
For subsequents startups of the spooler, this flag will be unvailable so we
|
|
won't run the spooler in upgrade mode.
|
|
|
|
--*/
|
|
|
|
dwUpgradeFlag = 0;
|
|
|
|
cbData = sizeof(dwUpgradeFlag);
|
|
|
|
Status = RegQueryValueEx(hKey,
|
|
L"SystemSetupInProgress",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwUpgradeFlag,
|
|
&cbData);
|
|
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
dwUpgradeFlag = 0;
|
|
}
|
|
|
|
|
|
DBGMSG(DBG_TRACE, ("The Spooler Upgrade flag is %d\n", dwUpgradeFlag));
|
|
|
|
//
|
|
// In case of OutOfBoxExperience(OOBE) the SystemSetupInProgress key is set as well.
|
|
// However, we want to run spooler in normal mode in this case. So, if we find that
|
|
// OOBE is running we are going to reset dwUpgradeFlag to zero.
|
|
//
|
|
if (dwUpgradeFlag)
|
|
{
|
|
DWORD dwUpgradeInProgFlag = 0;
|
|
DWORD dwMiniSetupInProgFlag = 0;
|
|
|
|
cbData = sizeof(dwUpgradeInProgFlag);
|
|
|
|
Status = RegQueryValueEx(hKey,
|
|
szUpgradeInProgKey,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwUpgradeInProgFlag,
|
|
&cbData);
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
cbData = sizeof(dwMiniSetupInProgFlag);
|
|
|
|
Status = RegQueryValueEx(hKey,
|
|
szMiniSetupInProgKey,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&dwMiniSetupInProgFlag,
|
|
&cbData);
|
|
}
|
|
//
|
|
// If we successfully read all the registry keys, and the minisetup flag is
|
|
// not set and the upgrade flag is set. It means that OOBE is running, so we
|
|
// want to run spooler in normal mode.
|
|
//
|
|
if (Status == ERROR_SUCCESS && !dwMiniSetupInProgFlag && dwUpgradeInProgFlag)
|
|
{
|
|
dwUpgradeFlag = 0;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
|
|
|
|
// Setup machine names
|
|
szMachineName[0] = szMachineName[1] = L'\\';
|
|
|
|
i = MAX_COMPUTERNAME_LENGTH + 1;
|
|
|
|
if (!GetComputerName(szMachineName+2, &i)) {
|
|
DBGMSG(DBG_ERROR, ("Failed to get computer name %d\n", GetLastError()));
|
|
ExitProcess(0);
|
|
}
|
|
|
|
if (!BuildOtherNamesFromMachineName(&ppszOtherNames, &cOtherNames)) {
|
|
DBGMSG(DBG_TRACE, ("Failed to determine other machine names %d\n", GetLastError()));
|
|
}
|
|
|
|
|
|
if (!(pLocalProvidor = InitializeProvidor(szLocalSplDll, NULL))) {
|
|
|
|
DBGMSG(DBG_WARN, ("Failed to initialize local print provider, error %d\n", GetLastError() ));
|
|
|
|
ExitProcess(0);
|
|
}
|
|
|
|
pProvidor = pLocalProvidor;
|
|
|
|
Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegistryProvidors, 0,
|
|
KEY_READ, &hKey);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Now query szCacheSize for the RouterCacheSize value
|
|
// if there is no RouterCacheSize replace it with the
|
|
// default value.
|
|
//
|
|
RouterCacheSize = ROUTERCACHE_DEFAULT_MAX;
|
|
|
|
cbData = sizeof(dwCacheSize);
|
|
|
|
Status = RegQueryValueEx(hKey,
|
|
szRouterCacheSize,
|
|
NULL, NULL,
|
|
(LPBYTE)&dwCacheSize,
|
|
&cbData);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_TRACE, ("RouterCacheSize = %d\n", dwCacheSize));
|
|
|
|
if (dwCacheSize > 0) {
|
|
RouterCacheSize = dwCacheSize;
|
|
}
|
|
}
|
|
|
|
if ((RouterCacheTable = AllocSplMem(RouterCacheSize *
|
|
sizeof(ROUTERCACHE))) == NULL) {
|
|
|
|
DBGMSG(DBG_ERROR, ("Error: Cannot create RouterCache Table\n"));
|
|
RouterCacheSize = 0;
|
|
}
|
|
|
|
//
|
|
// Now query szRegistryProvidors for the Order value
|
|
// if there is no Order value for szRegistryProvidors
|
|
// RegQueryValueEx will return ERROR_FILE_NOT_FOUND
|
|
// if that's the case, then quit, because we have
|
|
// no providors to initialize.
|
|
//
|
|
Status = RegQueryValueEx(hKey, szOrder, NULL, NULL,
|
|
(LPBYTE)NULL, &dwRequired);
|
|
|
|
//
|
|
// If RegQueryValueEx returned ERROR_SUCCESS, then
|
|
// call it again to determine how many bytes were
|
|
// allocated. Note, if Order does exist, but it has
|
|
// no data then dwReturned will be zero, in which
|
|
// don't allocate any memory for it, and don't
|
|
// bother to call RegQueryValueEx a second time.
|
|
//
|
|
if (Status == ERROR_SUCCESS) {
|
|
if (dwRequired != 0) {
|
|
lpMem = (LPWSTR) AllocSplMem(dwRequired);
|
|
if (lpMem == NULL) {
|
|
|
|
Status = GetLastError();
|
|
|
|
} else {
|
|
Status = RegQueryValueEx(hKey,
|
|
szOrder,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)lpMem,
|
|
&dwRequired);
|
|
}
|
|
}
|
|
}
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
cbDll = sizeof(Dll);
|
|
|
|
pProvidor = pLocalProvidor;
|
|
|
|
// Now parse the string retrieved from \Providors{Order = "....."}
|
|
// Remember each string is separated by a null terminator char ('\0')
|
|
// and the entire array is terminated by two null terminator chars
|
|
|
|
// Also remember, that if there was no data in Order, then
|
|
// psz = lpMem = NULL, and we have nothing to parse, so
|
|
// break out of the while loop, if psz is NULL as well
|
|
|
|
psz = lpMem;
|
|
|
|
while (psz && *psz) {
|
|
|
|
//
|
|
// Truncate the provider name if it does not fit in
|
|
// the stack allocated buffer.
|
|
//
|
|
lstrcpyn(ProvidorName, psz, COUNTOF(ProvidorName));
|
|
|
|
psz = psz + lstrlen(psz) + 1; // skip (length) + 1
|
|
// lstrlen returns length sans '\0'
|
|
|
|
if (RegOpenKeyEx(hKey, ProvidorName, 0, KEY_READ, &hKey1)
|
|
== ERROR_SUCCESS) {
|
|
|
|
cbDll = sizeof(Dll);
|
|
|
|
if (RegQueryValueEx(hKey1,
|
|
L"Name",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)Dll,
|
|
&cbDll) == ERROR_SUCCESS)
|
|
{
|
|
if((StrNCatBuff(szFullName,
|
|
COUNTOF(szFullName),
|
|
szRegistryProvidors,
|
|
L"\\",
|
|
ProvidorName,
|
|
NULL)==ERROR_SUCCESS))
|
|
{
|
|
if (pProvidor->pNext = InitializeProvidor(Dll, szFullName))
|
|
{
|
|
|
|
pProvidor = pProvidor->pNext;
|
|
}
|
|
}
|
|
} //close RegQueryValueEx
|
|
|
|
RegCloseKey(hKey1);
|
|
|
|
} // closes RegOpenKeyEx on ERROR_SUCCESS
|
|
|
|
} // end of while loop parsing REG_MULTI_SZ
|
|
|
|
// Now free the buffer allocated for RegQuery
|
|
// (that is if you have allocated - if dwReturned was
|
|
// zero, then no memory was allocated (since none was
|
|
// required (Order was empty)))
|
|
|
|
if (lpMem) {
|
|
FreeSplMem(lpMem);
|
|
}
|
|
|
|
} // closes RegQueryValueEx on ERROR_SUCCESS
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
//
|
|
// We are now initialized!
|
|
//
|
|
SetEvent(hEventInit);
|
|
Initialized=TRUE;
|
|
|
|
//
|
|
// Register for PnP events we care about
|
|
//
|
|
RegisterForPnPEvents();
|
|
|
|
bRet = SpoolerInitAll();
|
|
|
|
//
|
|
// Free the passed in router parameters.
|
|
//
|
|
FreeSplMem(pRouterParams);
|
|
|
|
// When we return this thread goes away
|
|
|
|
//
|
|
// NOTE-NOTE-NOTE-NOTE-NOTE KrishnaG 12/22/93
|
|
// This thread should go away, however the HP Monitor relies on this
|
|
// thread. HPMon calls the initialization function on this thread which
|
|
// calls an asynchronous receive for data. While the data itself is
|
|
// picked up by hmon!_ReadThread, if the thread which initiated the
|
|
// receive goes away, we will not be able to receive the data.
|
|
//
|
|
|
|
//
|
|
// Instead of sleeping infinite, let's use it to for providors that
|
|
// just want FFPCNs to poll. This call never returns.
|
|
//
|
|
|
|
HandlePollNotifications();
|
|
return bRet;
|
|
}
|
|
|
|
|
|
VOID
|
|
WaitForSpoolerInitialization(
|
|
VOID)
|
|
{
|
|
HANDLE hPhase2Init;
|
|
HANDLE hImpersonationToken = NULL;
|
|
|
|
if (!Initialized)
|
|
{
|
|
//
|
|
// Impersonate the spooler service token
|
|
//
|
|
hImpersonationToken = RevertToPrinterSelf();
|
|
|
|
//
|
|
// Start phase 2 initialization. hPhase2Init may set multiple times, but that
|
|
// is OK since there is only 1 thread waiting once on this event.
|
|
//
|
|
hPhase2Init = OpenEvent(EVENT_ALL_ACCESS,FALSE,L"RouterPreInitEvent");
|
|
|
|
if (hPhase2Init == NULL)
|
|
{
|
|
//
|
|
// Fail if the event is not created
|
|
//
|
|
DBGMSG(DBG_ERROR, ("Failed to create Phase2Init Event in WaitForSpoolerInitialization, error %d\n", GetLastError()));
|
|
ExitProcess(0);
|
|
}
|
|
SetEvent(hPhase2Init);
|
|
CloseHandle(hPhase2Init);
|
|
|
|
//
|
|
// Revert back to the client token
|
|
//
|
|
if (hImpersonationToken)
|
|
{
|
|
if (!ImpersonatePrinterClient(hImpersonationToken))
|
|
{
|
|
DBGMSG(DBG_ERROR, ("Failed to impersonate the client, error %d\n", GetLastError()));
|
|
ExitProcess(0);
|
|
}
|
|
}
|
|
|
|
WaitForSingleObject(hEventInit, INFINITE);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ShutDownProvidor(
|
|
LPPROVIDOR pProvidor)
|
|
{
|
|
if (pProvidor->PrintProvidor.fpShutDown) {
|
|
|
|
(*pProvidor->PrintProvidor.fpShutDown)(NULL);
|
|
}
|
|
|
|
FreeSplStr(pProvidor->lpName);
|
|
FreeLibrary(pProvidor->hModule);
|
|
FreeSplMem(pProvidor);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SplShutDownRouter(
|
|
VOID
|
|
)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("SplShutDownRouter:\n"));
|
|
|
|
//
|
|
// WMI Trace Events. Unregisters spoolss from WMI.
|
|
//
|
|
WmiTerminateTrace();
|
|
}
|
|
|
|
BOOL
|
|
SplInitializeWinSpoolDrv(
|
|
pfnWinSpoolDrv pfnList)
|
|
{
|
|
HANDLE hWinSpoolDrv;
|
|
|
|
// Check if the client side handles are available in fnClientSide
|
|
if (!bWinspoolInitialized) {
|
|
|
|
if (!(hWinSpoolDrv = LoadLibrary(TEXT("winspool.drv")))) {
|
|
// Could not load the client side of the spooler
|
|
return FALSE;
|
|
}
|
|
|
|
fnClientSide.pfnOpenPrinter = (BOOL (*)(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS))
|
|
GetProcAddress( hWinSpoolDrv,"OpenPrinterW" );
|
|
|
|
fnClientSide.pfnClosePrinter = (BOOL (*)(HANDLE))
|
|
GetProcAddress( hWinSpoolDrv,"ClosePrinter" );
|
|
|
|
fnClientSide.pfnDocumentProperties = (LONG (*)(HWND, HANDLE, LPWSTR, PDEVMODE,
|
|
PDEVMODE, DWORD))
|
|
GetProcAddress( hWinSpoolDrv,"DocumentPropertiesW" );
|
|
|
|
fnClientSide.pfnDevQueryPrint = (BOOL (*)(HANDLE, LPDEVMODE, DWORD *, LPWSTR, DWORD))
|
|
GetProcAddress( hWinSpoolDrv,"SpoolerDevQueryPrintW" );
|
|
|
|
fnClientSide.pfnPrinterEvent = (BOOL (*)(LPWSTR, INT, DWORD, LPARAM, DWORD *))
|
|
GetProcAddress( hWinSpoolDrv,"SpoolerPrinterEvent" );
|
|
|
|
fnClientSide.pfnLoadPrinterDriver = (HANDLE (*)(HANDLE))
|
|
GetProcAddress( hWinSpoolDrv,
|
|
(LPCSTR)MAKELPARAM( 212, 0 ));
|
|
|
|
fnClientSide.pfnRefCntLoadDriver = (HANDLE (*)(LPWSTR, DWORD, DWORD, BOOL))
|
|
GetProcAddress( hWinSpoolDrv,
|
|
(LPCSTR)MAKELPARAM( 213, 0 ));
|
|
|
|
fnClientSide.pfnRefCntUnloadDriver = (BOOL (*)(HANDLE, BOOL))
|
|
GetProcAddress( hWinSpoolDrv,
|
|
(LPCSTR)MAKELPARAM( 214, 0 ));
|
|
|
|
fnClientSide.pfnForceUnloadDriver = (BOOL (*)(LPWSTR))
|
|
GetProcAddress( hWinSpoolDrv,
|
|
(LPCSTR)MAKELPARAM( 215, 0 ));
|
|
|
|
if ( fnClientSide.pfnOpenPrinter == NULL ||
|
|
fnClientSide.pfnClosePrinter == NULL ||
|
|
fnClientSide.pfnDocumentProperties == NULL ||
|
|
fnClientSide.pfnPrinterEvent == NULL ||
|
|
fnClientSide.pfnDevQueryPrint == NULL ||
|
|
fnClientSide.pfnLoadPrinterDriver == NULL ||
|
|
fnClientSide.pfnRefCntLoadDriver == NULL ||
|
|
fnClientSide.pfnRefCntUnloadDriver == NULL ||
|
|
fnClientSide.pfnForceUnloadDriver == NULL ) {
|
|
|
|
FreeLibrary(hWinSpoolDrv);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Use these pointers for future calls to SplInitializeWinspoolDrv
|
|
bWinspoolInitialized = TRUE;
|
|
}
|
|
|
|
pfnList->pfnOpenPrinter = fnClientSide.pfnOpenPrinter;
|
|
pfnList->pfnClosePrinter = fnClientSide.pfnClosePrinter;
|
|
pfnList->pfnDocumentProperties = fnClientSide.pfnDocumentProperties;
|
|
pfnList->pfnDevQueryPrint = fnClientSide.pfnDevQueryPrint;
|
|
pfnList->pfnPrinterEvent = fnClientSide.pfnPrinterEvent;
|
|
pfnList->pfnLoadPrinterDriver = fnClientSide.pfnLoadPrinterDriver;
|
|
pfnList->pfnRefCntLoadDriver = fnClientSide.pfnRefCntLoadDriver;
|
|
pfnList->pfnRefCntUnloadDriver = fnClientSide.pfnRefCntUnloadDriver;
|
|
pfnList->pfnForceUnloadDriver = fnClientSide.pfnForceUnloadDriver;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SpoolerHasInitialized(
|
|
VOID
|
|
)
|
|
{
|
|
return Initialized;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
SplPowerEvent
|
|
|
|
Routine Description:
|
|
|
|
Checks if the spooler is ready for power management events like hibernation/stand by.
|
|
|
|
Arguments:
|
|
|
|
Event - power management event
|
|
|
|
Return Value:
|
|
|
|
TRUE - the spooler allowd the system to be powered down
|
|
FALSE - the spooler denies the request for powering down
|
|
|
|
--*/
|
|
BOOL
|
|
SplPowerEvent(
|
|
DWORD Event
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
//
|
|
// We need the router to be completely initialized and having loaded
|
|
// all print providers in order to check if we can allow powering down
|
|
// the system
|
|
//
|
|
if (SpoolerHasInitialized())
|
|
{
|
|
HMODULE hLib = NULL;
|
|
typedef BOOL (*PACPIFUNC)(DWORD);
|
|
PACPIFUNC pfn;
|
|
|
|
if ((hLib = LoadLibrary(L"localspl.dll")) &&
|
|
(pfn = (PACPIFUNC)GetProcAddress(hLib, "SplPowerEvent")))
|
|
{
|
|
bRet = (*pfn)(Event);
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if (hLib)
|
|
{
|
|
FreeLibrary(hLib);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|