1850 lines
44 KiB
C++
1850 lines
44 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1992-1997 Microsoft Corporation
|
|||
|
All rights reserved.
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
splsvc.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Resource DLL for Spooler
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Albert Ting (albertt) 17-Sept-1996
|
|||
|
Based on resdll\genapp
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.hxx"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#include "splsvc.hxx"
|
|||
|
#include "spooler.hxx"
|
|||
|
#include "clusinfo.hxx"
|
|||
|
#include "clusrtl.h"
|
|||
|
|
|||
|
MODULE_DEBUG_INIT( DBG_ERROR|DBG_WARN|DBG_TRACE, DBG_ERROR );
|
|||
|
|
|||
|
#define MAX_SPOOLER 60
|
|||
|
|
|||
|
#define MAX_GROUP_NAME_LENGTH 120
|
|||
|
|
|||
|
#define SPOOLER_TERMINATE // Kill the spooler on terminate if pending offline.
|
|||
|
|
|||
|
#define SplSvcLogEvent ClusResLogEvent
|
|||
|
#define SplSvcSetResourceStatus ClusResSetResourceStatus
|
|||
|
|
|||
|
#define NET_NAME_RESOURCE_NAME CLUS_RESTYPE_NAME_NETNAME
|
|||
|
|
|||
|
#define PARAM_NAME__DEFAULTSPOOLDIRECTORY CLUSREG_NAME_PRTSPOOL_DEFAULT_SPOOL_DIR
|
|||
|
#define PARAM_NAME__JOBCOMPLETIONTIMEOUT CLUSREG_NAME_PRTSPOOL_TIMEOUT
|
|||
|
|
|||
|
#define KEY_NAME__DEFAULTSPOOLDIRECTORY L"Printers"
|
|||
|
#define KEY_NAME__JOBCOMPLETIONTIMEOUT NULL
|
|||
|
|
|||
|
#define PARAM_MIN__JOBCOMPLETIONTIMEOUT 0
|
|||
|
#define PARAM_MAX__JOBCOMPLETIONTIMEOUT ((DWORD) -1)
|
|||
|
#define PARAM_DEFAULT_JOBCOMPLETIONTIMEOUT 160
|
|||
|
|
|||
|
typedef struct _SPOOLER_PARAMS {
|
|||
|
LPWSTR DefaultSpoolDirectory;
|
|||
|
DWORD JobCompletionTimeout;
|
|||
|
} SPOOLER_PARAMS, *PSPOOLER_PARAMS;
|
|||
|
|
|||
|
//
|
|||
|
// Properties
|
|||
|
//
|
|||
|
|
|||
|
RESUTIL_PROPERTY_ITEM
|
|||
|
SplSvcResourcePrivateProperties[] = {
|
|||
|
{ PARAM_NAME__DEFAULTSPOOLDIRECTORY, KEY_NAME__DEFAULTSPOOLDIRECTORY, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(SPOOLER_PARAMS,DefaultSpoolDirectory) },
|
|||
|
{ PARAM_NAME__JOBCOMPLETIONTIMEOUT, KEY_NAME__JOBCOMPLETIONTIMEOUT, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT_JOBCOMPLETIONTIMEOUT, PARAM_MIN__JOBCOMPLETIONTIMEOUT, PARAM_MAX__JOBCOMPLETIONTIMEOUT, 0, FIELD_OFFSET(SPOOLER_PARAMS,JobCompletionTimeout) },
|
|||
|
{ 0 }
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Lock to protect the ProcessInfo table
|
|||
|
//
|
|||
|
CRITICAL_SECTION gProcessLock;
|
|||
|
|
|||
|
//
|
|||
|
// Global count of spooler resource instances.
|
|||
|
//
|
|||
|
UINT gcSpoolerInfo;
|
|||
|
|
|||
|
extern CLRES_FUNCTION_TABLE SplSvcFunctionTable;
|
|||
|
|
|||
|
#define PSPOOLERINFO_FROM_RESID(resid) ((PSPOOLER_INFORMATION)resid)
|
|||
|
#define RESID_FROM_SPOOLERINFO(pSpoolerInfo) ((RESID)pSpoolerInfo)
|
|||
|
|
|||
|
//
|
|||
|
// Forward prototypes.
|
|||
|
//
|
|||
|
|
|||
|
#ifdef __cplusplus
|
|||
|
extern "C"
|
|||
|
#endif
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
WINAPI
|
|||
|
SplSvcDllEntryPoint(
|
|||
|
IN HINSTANCE DllHandle,
|
|||
|
IN DWORD Reason,
|
|||
|
IN LPVOID Reserved
|
|||
|
);
|
|||
|
|
|||
|
RESID
|
|||
|
WINAPI
|
|||
|
SplSvcOpen(
|
|||
|
IN LPCWSTR ResourceName,
|
|||
|
IN HKEY ResourceKey,
|
|||
|
IN RESOURCE_HANDLE ResourceHandle
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
WINAPI
|
|||
|
SplSvcClose(
|
|||
|
IN RESID Resid
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
SplSvcOnline(
|
|||
|
IN RESID Resid,
|
|||
|
IN OUT PHANDLE EventHandle
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
SplSvcOffline(
|
|||
|
IN RESID Resid
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
WINAPI
|
|||
|
SplSvcTerminate(
|
|||
|
IN RESID Resource
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
WINAPI
|
|||
|
SplSvcIsAlive(
|
|||
|
IN RESID Resource
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
WINAPI
|
|||
|
SplSvcLooksAlive(
|
|||
|
IN RESID Resource
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcGetPrivateResProperties(
|
|||
|
IN OUT PSPOOLER_INFORMATION ResourceEntry,
|
|||
|
OUT PVOID OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcValidatePrivateResProperties(
|
|||
|
IN OUT PSPOOLER_INFORMATION ResourceEntry,
|
|||
|
IN PVOID InBuffer,
|
|||
|
IN DWORD InBufferSize,
|
|||
|
OUT PSPOOLER_PARAMS Params
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcSetPrivateResProperties(
|
|||
|
IN OUT PSPOOLER_INFORMATION ResourceEntry,
|
|||
|
IN PVOID InBuffer,
|
|||
|
IN DWORD InBufferSize
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
PSPOOLER_INFORMATION
|
|||
|
pNewSpoolerInfo(
|
|||
|
LPCTSTR pszResource,
|
|||
|
RESOURCE_HANDLE ResourceHandle,
|
|||
|
PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus,
|
|||
|
PLOG_EVENT_ROUTINE LogEvent
|
|||
|
)
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo = NULL;
|
|||
|
LPTSTR pszResourceNew = NULL;
|
|||
|
|
|||
|
pSpoolerInfo = (PSPOOLER_INFORMATION)LocalAlloc(
|
|||
|
LPTR,
|
|||
|
sizeof( SPOOLER_INFORMATION ));
|
|||
|
|
|||
|
if( !pSpoolerInfo ){
|
|||
|
goto Fail;
|
|||
|
}
|
|||
|
|
|||
|
pszResourceNew = (LPTSTR)LocalAlloc(
|
|||
|
LMEM_FIXED,
|
|||
|
( lstrlen( pszResource ) + 1 )
|
|||
|
* sizeof( TCHAR ));
|
|||
|
|
|||
|
if( !pszResourceNew ){
|
|||
|
goto Fail;
|
|||
|
}
|
|||
|
|
|||
|
lstrcpy( pszResourceNew, pszResource );
|
|||
|
|
|||
|
pSpoolerInfo->pfnLogEvent = LogEvent;
|
|||
|
pSpoolerInfo->ResourceHandle = ResourceHandle;
|
|||
|
pSpoolerInfo->pfnSetResourceStatus = SetResourceStatus;
|
|||
|
|
|||
|
pSpoolerInfo->pszResource = pszResourceNew;
|
|||
|
pSpoolerInfo->pszName = NULL;
|
|||
|
pSpoolerInfo->pszAddress = NULL;
|
|||
|
|
|||
|
return pSpoolerInfo;
|
|||
|
|
|||
|
Fail:
|
|||
|
|
|||
|
if( pszResourceNew ){
|
|||
|
LocalFree( (HLOCAL)pszResourceNew );
|
|||
|
}
|
|||
|
|
|||
|
if( pSpoolerInfo ){
|
|||
|
LocalFree( (HLOCAL)pSpoolerInfo );
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
vDeleteSpoolerInfo(
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo
|
|||
|
)
|
|||
|
{
|
|||
|
if( !pSpoolerInfo ){
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
SPLASSERT( !pSpoolerInfo->cRef );
|
|||
|
|
|||
|
//
|
|||
|
// Shut down everything.
|
|||
|
//
|
|||
|
if( pSpoolerInfo->pszName ){
|
|||
|
LocalFree( (HLOCAL)pSpoolerInfo->pszName );
|
|||
|
}
|
|||
|
|
|||
|
if( pSpoolerInfo->pszAddress ){
|
|||
|
LocalFree( (HLOCAL)pSpoolerInfo->pszAddress );
|
|||
|
}
|
|||
|
|
|||
|
if( pSpoolerInfo->pszResource ){
|
|||
|
LocalFree( (HLOCAL)pSpoolerInfo->pszResource );
|
|||
|
}
|
|||
|
|
|||
|
LocalFree( (HLOCAL)pSpoolerInfo );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
Init(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
InitializeCriticalSection( &gProcessLock );
|
|||
|
return bSplLibInit();
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
WINAPI
|
|||
|
SplSvcDllEntryPoint(
|
|||
|
IN HINSTANCE DllHandle,
|
|||
|
IN DWORD Reason,
|
|||
|
IN LPVOID Reserved
|
|||
|
)
|
|||
|
{
|
|||
|
UNREFERENCED_PARAMETER(DllHandle);
|
|||
|
UNREFERENCED_PARAMETER(Reserved);
|
|||
|
|
|||
|
switch( Reason ) {
|
|||
|
|
|||
|
case DLL_PROCESS_ATTACH:
|
|||
|
|
|||
|
if( !Init() ){
|
|||
|
|
|||
|
DBGMSG( DBG_ERROR, ( "DllEntryPoint: failed to init\n" ));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case DLL_PROCESS_DETACH:
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
|
|||
|
Required exports and function table entries used by clustering.
|
|||
|
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
|
|||
|
Resource DLL functions.
|
|||
|
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
bSplRegCopyTree(
|
|||
|
IN HKEY hClusterKey,
|
|||
|
IN HKEY hLocalKey
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Recursives copies every key and value from under hLocalKey to hClusterKey
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hClusterKey - handle to the cluster registry (destination)
|
|||
|
hLocalKey - handle to the local registry (source)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - success
|
|||
|
FALSE - failure
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOL bStatus = FALSE;
|
|||
|
DWORD dwError, dwIndex, cbValueName, cbData, cbKeyName, dwType;
|
|||
|
DWORD cbMaxSubKeyLen, cbMaxValueNameLen, cValues, cSubKeys, cbMaxValueLen;
|
|||
|
LPBYTE lpValueName = NULL, lpData = NULL, lpKeyName = NULL;
|
|||
|
HKEY hLocalSubKey = NULL, hClusterSubKey = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Retrieve the max buffer sizes required for the copy
|
|||
|
//
|
|||
|
dwError = RegQueryInfoKey( hLocalKey, NULL, NULL, NULL, &cSubKeys,
|
|||
|
&cbMaxSubKeyLen, NULL, &cValues,
|
|||
|
&cbMaxValueNameLen, &cbMaxValueLen,
|
|||
|
NULL, NULL );
|
|||
|
if( dwError ){
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add for the terminating NULL character
|
|||
|
//
|
|||
|
++cbMaxSubKeyLen;
|
|||
|
++cbMaxValueNameLen;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the buffers
|
|||
|
//
|
|||
|
lpValueName = (LPBYTE) LocalAlloc( LMEM_FIXED, cbMaxValueNameLen * sizeof(WCHAR) );
|
|||
|
lpData = (LPBYTE) LocalAlloc( LMEM_FIXED, cbMaxValueLen );
|
|||
|
lpKeyName = (LPBYTE) LocalAlloc( LMEM_FIXED, cbMaxSubKeyLen * sizeof(WCHAR) );
|
|||
|
|
|||
|
if( !lpValueName || !lpData || !lpKeyName){
|
|||
|
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy all the values in the current key
|
|||
|
//
|
|||
|
for (dwIndex = 0; dwIndex < cValues; ++ dwIndex) {
|
|||
|
|
|||
|
cbValueName = cbMaxValueNameLen;
|
|||
|
cbData = cbMaxValueLen;
|
|||
|
|
|||
|
//
|
|||
|
// Retrieve the value name and the data
|
|||
|
//
|
|||
|
dwError = RegEnumValue( hLocalKey, dwIndex, (LPWSTR) lpValueName, &cbValueName,
|
|||
|
NULL, &dwType, lpData, &cbData );
|
|||
|
|
|||
|
if( dwError ){
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the value in the cluster registry
|
|||
|
//
|
|||
|
dwError = ClusterRegSetValue( hClusterKey, (LPWSTR) lpValueName, dwType,
|
|||
|
lpData, cbData );
|
|||
|
|
|||
|
if( dwError ){
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Recursively copies all the subkeys
|
|||
|
//
|
|||
|
for (dwIndex = 0; dwIndex < cSubKeys; ++ dwIndex) {
|
|||
|
|
|||
|
cbKeyName = cbMaxSubKeyLen;
|
|||
|
|
|||
|
//
|
|||
|
// Retrieve the key name
|
|||
|
//
|
|||
|
dwError = RegEnumKeyEx( hLocalKey, dwIndex, (LPWSTR) lpKeyName, &cbKeyName,
|
|||
|
NULL, NULL, NULL, NULL );
|
|||
|
|
|||
|
if( dwError ){
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open local subkey
|
|||
|
//
|
|||
|
if( dwError = RegOpenKeyEx( hLocalKey, (LPWSTR) lpKeyName, 0,
|
|||
|
KEY_READ, &hLocalSubKey ) ){
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create the cluster subkey
|
|||
|
//
|
|||
|
|
|||
|
if(( dwError = ClusterRegOpenKey( hClusterKey, (LPWSTR) lpKeyName,
|
|||
|
KEY_READ|KEY_WRITE,
|
|||
|
&hClusterSubKey)) == ERROR_FILE_NOT_FOUND )
|
|||
|
{
|
|||
|
if( dwError = ClusterRegCreateKey( hClusterKey, (LPWSTR) lpKeyName,
|
|||
|
0,KEY_READ|KEY_WRITE,
|
|||
|
NULL, &hClusterSubKey, NULL ) )
|
|||
|
{
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the subkey tree
|
|||
|
//
|
|||
|
if( !bSplRegCopyTree( hClusterSubKey, hLocalSubKey ) ){
|
|||
|
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Close the registry handle
|
|||
|
//
|
|||
|
RegCloseKey( hLocalSubKey );
|
|||
|
ClusterRegCloseKey( hClusterSubKey );
|
|||
|
|
|||
|
hLocalSubKey = NULL;
|
|||
|
hClusterSubKey = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
bStatus = TRUE;
|
|||
|
|
|||
|
CleanUp:
|
|||
|
|
|||
|
if( lpValueName ){
|
|||
|
LocalFree( lpValueName );
|
|||
|
}
|
|||
|
if( lpData ){
|
|||
|
LocalFree( lpData );
|
|||
|
}
|
|||
|
if( lpKeyName ){
|
|||
|
LocalFree( lpKeyName );
|
|||
|
}
|
|||
|
if( hLocalSubKey ){
|
|||
|
RegCloseKey( hLocalSubKey );
|
|||
|
}
|
|||
|
if( hClusterSubKey ){
|
|||
|
ClusterRegCloseKey( hClusterSubKey );
|
|||
|
}
|
|||
|
|
|||
|
return bStatus;
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
bUpdateRegPort(
|
|||
|
IN HKEY hClusterKey,
|
|||
|
OUT LPBOOL pbRegUpdated
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Moves Port data from the local to the cluster registry
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hClusterKey - handle to the cluster registry
|
|||
|
pbRegUpdated - flag to indicate if the registry was updated
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - success
|
|||
|
FALSE - failure
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOL bStatus = FALSE;
|
|||
|
DWORD dwError, dwType, dwValue, dwSize;
|
|||
|
HKEY hLocalLPR = NULL, hClusterLPR = NULL;
|
|||
|
|
|||
|
WCHAR szLocalLPRKey[] = L"System\\CurrentControlSet\\Control\\Print\\Monitors\\LPR Port";
|
|||
|
WCHAR szClusterLPRKey[] = L"Monitors\\LPR Port";
|
|||
|
WCHAR szSplVersion[] = L"Spooler Version";
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the bRegUpdate flag
|
|||
|
//
|
|||
|
*pbRegUpdated = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Check if ports have been migrated already
|
|||
|
//
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
if( dwError = ClusterRegQueryValue( hClusterKey, szSplVersion, &dwType,
|
|||
|
(LPBYTE) &dwValue, &dwSize ) ){
|
|||
|
|
|||
|
if( dwError != ERROR_FILE_NOT_FOUND ){
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if( dwValue == 1 ){
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the local LPR Port key, if any
|
|||
|
//
|
|||
|
if( dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szLocalLPRKey, 0,
|
|||
|
KEY_READ, &hLocalLPR ) ){
|
|||
|
|
|||
|
//
|
|||
|
// LPR Port key was not found
|
|||
|
//
|
|||
|
if( dwError == ERROR_FILE_NOT_FOUND ){
|
|||
|
|
|||
|
bStatus = TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
SetLastError( dwError );
|
|||
|
}
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create the LPR Port key on the cluster registry
|
|||
|
//
|
|||
|
|
|||
|
if(( dwError = ClusterRegOpenKey( hClusterKey, szClusterLPRKey,
|
|||
|
KEY_READ|KEY_WRITE,
|
|||
|
&hClusterLPR ) ) == ERROR_FILE_NOT_FOUND)
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
if( dwError = ClusterRegCreateKey( hClusterKey, szClusterLPRKey,
|
|||
|
0,KEY_READ|KEY_WRITE,
|
|||
|
NULL, &hClusterLPR, NULL ) )
|
|||
|
{
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( dwError )
|
|||
|
{
|
|||
|
SetLastError( dwError );
|
|||
|
goto CleanUp;
|
|||
|
}
|
|||
|
|
|||
|
bStatus = bSplRegCopyTree( hClusterLPR, hLocalLPR );
|
|||
|
|
|||
|
if( bStatus ){
|
|||
|
|
|||
|
//
|
|||
|
// Create a value in the cluster to avoid repeated copying of the port data
|
|||
|
//
|
|||
|
dwValue = 1;
|
|||
|
dwSize = sizeof(dwValue);
|
|||
|
ClusterRegSetValue( hClusterKey, szSplVersion, REG_DWORD,
|
|||
|
(LPBYTE) &dwValue, dwSize );
|
|||
|
*pbRegUpdated = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
CleanUp:
|
|||
|
|
|||
|
if( hLocalLPR ){
|
|||
|
RegCloseKey( hLocalLPR );
|
|||
|
}
|
|||
|
|
|||
|
if( hClusterLPR ){
|
|||
|
ClusterRegCloseKey( hClusterLPR );
|
|||
|
}
|
|||
|
|
|||
|
return bStatus;
|
|||
|
}
|
|||
|
|
|||
|
RESID
|
|||
|
WINAPI
|
|||
|
SplSvcOpen(
|
|||
|
IN LPCWSTR ResourceName,
|
|||
|
IN HKEY ResourceKey,
|
|||
|
IN RESOURCE_HANDLE ResourceHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Opens the spooler resource. This will start the spooler
|
|||
|
service if necessary.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ResourceName - supplies the resource name
|
|||
|
|
|||
|
ResourceKey - supplies a handle to the resource's cluster registry key
|
|||
|
|
|||
|
ResourceHandle - the resource handle to be supplied with SetResourceStatus
|
|||
|
is called.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
RESID of created resource
|
|||
|
Zero on failure
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo = NULL;
|
|||
|
BOOL bTooMany = FALSE, bRegUpdated;
|
|||
|
HKEY parametersKey = NULL;
|
|||
|
DWORD status;
|
|||
|
HCLUSTER hCluster;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(ResourceKey);
|
|||
|
|
|||
|
vEnterSem();
|
|||
|
|
|||
|
DBGMSG( DBG_WARN, ( ">>> Open: called\n" ));
|
|||
|
|
|||
|
if( gcSpoolerInfo == MAX_SPOOLER ){
|
|||
|
bTooMany = TRUE;
|
|||
|
status = ERROR_ALLOTTED_SPACE_EXCEEDED;
|
|||
|
goto FailLeave;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the Parameters key for this resource.
|
|||
|
//
|
|||
|
|
|||
|
status = ClusterRegOpenKey( ResourceKey,
|
|||
|
CLUSREG_KEYNAME_PARAMETERS,
|
|||
|
KEY_READ,
|
|||
|
¶metersKey );
|
|||
|
if ( status != ERROR_SUCCESS || parametersKey == NULL ) {
|
|||
|
(SplSvcLogEvent)(
|
|||
|
ResourceHandle,
|
|||
|
LOG_ERROR,
|
|||
|
L"Unable to open parameters key for resource '%1!ws!'. Error: %2!u!.\n",
|
|||
|
ResourceName,
|
|||
|
status );
|
|||
|
goto FailLeave;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Move the ports data from local to cluster registry
|
|||
|
//
|
|||
|
|
|||
|
if( !bUpdateRegPort( parametersKey, &bRegUpdated ) ){
|
|||
|
|
|||
|
(SplSvcLogEvent)(
|
|||
|
ResourceHandle,
|
|||
|
LOG_WARNING,
|
|||
|
L"LPR Port settings could not be moved to cluster registry.\n" );
|
|||
|
|
|||
|
DBGMSG( DBG_WARN, ( "Port settings could not be moved to cluster registry" ));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find a free index in the process info table for this new app.
|
|||
|
//
|
|||
|
|
|||
|
pSpoolerInfo = pNewSpoolerInfo( ResourceName,
|
|||
|
ResourceHandle,
|
|||
|
SplSvcSetResourceStatus,
|
|||
|
SplSvcLogEvent );
|
|||
|
|
|||
|
if( !pSpoolerInfo ){
|
|||
|
status = ERROR_ALLOTTED_SPACE_EXCEEDED;
|
|||
|
goto FailLeave;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the name of the resource.
|
|||
|
//
|
|||
|
hCluster = OpenCluster( NULL );
|
|||
|
if ( !hCluster ) {
|
|||
|
status = GetLastError();
|
|||
|
goto FailLeave;
|
|||
|
}
|
|||
|
pSpoolerInfo->hResource = OpenClusterResource( hCluster,
|
|||
|
ResourceName );
|
|||
|
status = GetLastError();
|
|||
|
CloseCluster( hCluster );
|
|||
|
if ( pSpoolerInfo->hResource == NULL ) {
|
|||
|
goto FailLeave;
|
|||
|
}
|
|||
|
|
|||
|
pSpoolerInfo->eState = kOpen;
|
|||
|
pSpoolerInfo->ParametersKey = parametersKey;
|
|||
|
|
|||
|
vAddRef( pSpoolerInfo );
|
|||
|
++gcSpoolerInfo;
|
|||
|
|
|||
|
vLeaveSem();
|
|||
|
|
|||
|
DBGMSG( DBG_WARN,
|
|||
|
( "Open: return %x\n", RESID_FROM_SPOOLERINFO( pSpoolerInfo )));
|
|||
|
|
|||
|
return RESID_FROM_SPOOLERINFO(pSpoolerInfo);
|
|||
|
|
|||
|
FailLeave:
|
|||
|
|
|||
|
vLeaveSem();
|
|||
|
|
|||
|
if( bTooMany ){
|
|||
|
|
|||
|
(SplSvcLogEvent)(
|
|||
|
ResourceHandle,
|
|||
|
LOG_ERROR,
|
|||
|
L"Too many spoolers.\n" );
|
|||
|
|
|||
|
DBGMSG( DBG_WARN, ( "SplSvcOpen: Too many spoolers.\n" ));
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
(SplSvcLogEvent)(
|
|||
|
ResourceHandle,
|
|||
|
LOG_ERROR,
|
|||
|
L"Failed to create new spooler. Error: %1!u!.\n",
|
|||
|
GetLastError() );
|
|||
|
|
|||
|
DBGMSG( DBG_ERROR, ( "SplSvcOpen: Unable to create spooler %d\n",
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
if ( parametersKey != NULL ) {
|
|||
|
ClusterRegCloseKey( parametersKey );
|
|||
|
}
|
|||
|
|
|||
|
if ( pSpoolerInfo && (pSpoolerInfo->hResource != NULL) ) {
|
|||
|
CloseClusterResource( pSpoolerInfo->hResource );
|
|||
|
}
|
|||
|
|
|||
|
vDeleteSpoolerInfo( pSpoolerInfo );
|
|||
|
|
|||
|
SetLastError( status );
|
|||
|
|
|||
|
return (RESID)0;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
WINAPI
|
|||
|
SplSvcClose(
|
|||
|
IN RESID Resid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Close down the open spooler resource ID. Note that we'll leave
|
|||
|
the spooler running, since it always should be running.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - supplies resource id to be closed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
DBGMSG( DBG_WARN, ( ">>> Close: called %x\n", Resid ));
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( Resid );
|
|||
|
|
|||
|
//
|
|||
|
// If resource is still online, terminate it.
|
|||
|
//
|
|||
|
if( pSpoolerInfo->eState == kOnline ){
|
|||
|
SplSvcTerminate( Resid );
|
|||
|
}
|
|||
|
|
|||
|
vEnterSem();
|
|||
|
|
|||
|
--gcSpoolerInfo;
|
|||
|
pSpoolerInfo->eState = kClose;
|
|||
|
|
|||
|
vLeaveSem();
|
|||
|
|
|||
|
if (pSpoolerInfo->hResource != NULL ) {
|
|||
|
CloseClusterResource( pSpoolerInfo->hResource );
|
|||
|
}
|
|||
|
|
|||
|
if (pSpoolerInfo->ParametersKey != NULL ) {
|
|||
|
ClusterRegCloseKey( pSpoolerInfo->ParametersKey );
|
|||
|
}
|
|||
|
|
|||
|
vDecRef( pSpoolerInfo );
|
|||
|
}
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
SplSvcOnline(
|
|||
|
IN RESID Resid,
|
|||
|
IN OUT PHANDLE EventHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Online the spooler resource.
|
|||
|
|
|||
|
This always completes asynchronously with ERROR_IO_PENDING.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - supplies resource id to be brought online
|
|||
|
|
|||
|
EventHandle - supplies a pointer to a handle to signal on error.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
In success case, this returns ERROR_IO_PENDING, else win32
|
|||
|
error code.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOL bStatus;
|
|||
|
DWORD Status = ERROR_IO_PENDING;
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( EventHandle );
|
|||
|
|
|||
|
DBGMSG( DBG_WARN, ( ">>> Online: called %x\n", Resid ));
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( Resid );
|
|||
|
|
|||
|
//
|
|||
|
// Rpc to the spooler to initiate Online.
|
|||
|
//
|
|||
|
bStatus = SpoolerOnline( pSpoolerInfo );
|
|||
|
|
|||
|
if( !bStatus ){
|
|||
|
|
|||
|
Status = GetLastError();
|
|||
|
SPLASSERT( Status );
|
|||
|
|
|||
|
(pSpoolerInfo->pfnLogEvent)(
|
|||
|
pSpoolerInfo->ResourceHandle,
|
|||
|
LOG_ERROR,
|
|||
|
L"Unable to online spooler resource. Error: %1!u!.\n",
|
|||
|
Status );
|
|||
|
|
|||
|
DBGMSG( DBG_ERROR, ( "SplSvcOnline: Unable to online spooler\n" ));
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
DWORD
|
|||
|
WINAPI
|
|||
|
SplSvcOffline(
|
|||
|
IN RESID Resid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Offline the spooler resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resid - supplies the resource to be taken offline
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
In success case, this returns ERROR_IO_PENDING, else win32
|
|||
|
error code.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
|
|||
|
DBGMSG( DBG_WARN, ( ">>> Offline: called %x\n", Resid ));
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( Resid );
|
|||
|
|
|||
|
vEnterSem();
|
|||
|
pSpoolerInfo->ClusterResourceState = ClusterResourceOffline;
|
|||
|
vLeaveSem();
|
|||
|
|
|||
|
return(SpoolerOffline(pSpoolerInfo));
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
WINAPI
|
|||
|
SplSvcTerminate(
|
|||
|
IN RESID Resid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Terminate and restart the spooler--no waiting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resid - supplies resource id to be terminated
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
|
|||
|
DBGMSG( DBG_WARN, ( ">>> Terminate: called %x\n", Resid ));
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( Resid );
|
|||
|
|
|||
|
vEnterSem();
|
|||
|
pSpoolerInfo->ClusterResourceState = ClusterResourceFailed;
|
|||
|
vLeaveSem();
|
|||
|
|
|||
|
SpoolerTerminate(pSpoolerInfo);
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
WINAPI
|
|||
|
SplSvcIsAlive(
|
|||
|
IN RESID Resid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
IsAlive routine for Generice Applications resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - supplies the resource id to be polled.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - Resource is alive and well
|
|||
|
|
|||
|
FALSE - Resource is toast.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( Resid );
|
|||
|
|
|||
|
return SpoolerIsAlive( pSpoolerInfo );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
WINAPI
|
|||
|
SplSvcLooksAlive(
|
|||
|
IN RESID Resid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
LooksAlive routine for Generic Applications resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Resource - supplies the resource id to be polled.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - Resource looks like it is alive and well
|
|||
|
|
|||
|
FALSE - Resource looks like it is toast.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( Resid );
|
|||
|
|
|||
|
return SpoolerLooksAlive( pSpoolerInfo );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcGetRequiredDependencies(
|
|||
|
OUT PVOID OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes the CLCTL_GET_REQUIRED_DEPENDENCIES control function
|
|||
|
for resources of type Print Spooler.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
OutBuffer - Supplies a pointer to the output buffer to be filled in.
|
|||
|
|
|||
|
OutBufferSize - Supplies the size, in bytes, of the available space
|
|||
|
pointed to by OutBuffer.
|
|||
|
|
|||
|
BytesReturned - Returns the number of bytes of OutBuffer actually
|
|||
|
filled in by the resource. If OutBuffer is too small, BytesReturned
|
|||
|
contains the total number of bytes for the operation to succeed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully.
|
|||
|
|
|||
|
ERROR_MORE_DATA - The output buffer is too small to return the data.
|
|||
|
BytesReturned contains the required size.
|
|||
|
|
|||
|
Win32 error code - The function failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
typedef struct DEP_DATA {
|
|||
|
CLUSPROP_RESOURCE_CLASS storageEntry;
|
|||
|
CLUSPROP_SZ_DECLARE( netnameEntry, sizeof(NET_NAME_RESOURCE_NAME) / sizeof(WCHAR) );
|
|||
|
CLUSPROP_SYNTAX endmark;
|
|||
|
} DEP_DATA, *PDEP_DATA;
|
|||
|
PDEP_DATA pdepdata = (PDEP_DATA)OutBuffer;
|
|||
|
DWORD status;
|
|||
|
|
|||
|
*BytesReturned = sizeof(DEP_DATA);
|
|||
|
if ( OutBufferSize < sizeof(DEP_DATA) ) {
|
|||
|
if ( OutBuffer == NULL ) {
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
status = ERROR_MORE_DATA;
|
|||
|
}
|
|||
|
} else {
|
|||
|
ZeroMemory( pdepdata, sizeof(DEP_DATA) );
|
|||
|
pdepdata->storageEntry.Syntax.dw = CLUSPROP_SYNTAX_RESCLASS;
|
|||
|
pdepdata->storageEntry.cbLength = sizeof(DWORD);
|
|||
|
pdepdata->storageEntry.rc = CLUS_RESCLASS_STORAGE;
|
|||
|
pdepdata->netnameEntry.Syntax.dw = CLUSPROP_SYNTAX_NAME;
|
|||
|
pdepdata->netnameEntry.cbLength = sizeof(NET_NAME_RESOURCE_NAME);
|
|||
|
lstrcpyW( pdepdata->netnameEntry.sz, NET_NAME_RESOURCE_NAME );
|
|||
|
pdepdata->endmark.dw = CLUSPROP_SYNTAX_ENDMARK;
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // SplSvcGetRequiredDependencies
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcResourceControl(
|
|||
|
IN RESID ResourceId,
|
|||
|
IN DWORD ControlCode,
|
|||
|
IN PVOID InBuffer,
|
|||
|
IN DWORD InBufferSize,
|
|||
|
OUT PVOID OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
ResourceControl routine for Print Spooler resources.
|
|||
|
|
|||
|
Perform the control request specified by ControlCode on the specified
|
|||
|
resource.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ResourceId - Supplies the resource id for the specific resource.
|
|||
|
|
|||
|
ControlCode - Supplies the control code that defines the action
|
|||
|
to be performed.
|
|||
|
|
|||
|
InBuffer - Supplies a pointer to a buffer containing input data.
|
|||
|
|
|||
|
InBufferSize - Supplies the size, in bytes, of the data pointed
|
|||
|
to by InBuffer.
|
|||
|
|
|||
|
OutBuffer - Supplies a pointer to the output buffer to be filled in.
|
|||
|
|
|||
|
OutBufferSize - Supplies the size, in bytes, of the available space
|
|||
|
pointed to by OutBuffer.
|
|||
|
|
|||
|
BytesReturned - Returns the number of bytes of OutBuffer actually
|
|||
|
filled in by the resource. If OutBuffer is too small, BytesReturned
|
|||
|
contains the total number of bytes for the operation to succeed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully.
|
|||
|
|
|||
|
ERROR_INVALID_FUNCTION - The requested control code is not supported.
|
|||
|
In some cases, this allows the cluster software to perform the work.
|
|||
|
|
|||
|
Win32 error code - The function failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo;
|
|||
|
DWORD required;
|
|||
|
LPWSTR pszResourceNew;
|
|||
|
|
|||
|
pSpoolerInfo = PSPOOLERINFO_FROM_RESID( ResourceId );
|
|||
|
|
|||
|
switch ( ControlCode ) {
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_UNKNOWN:
|
|||
|
*BytesReturned = 0;
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS:
|
|||
|
status = ResUtilGetPropertyFormats( SplSvcResourcePrivateProperties,
|
|||
|
OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned,
|
|||
|
&required );
|
|||
|
if ( status == ERROR_MORE_DATA ) {
|
|||
|
*BytesReturned = required;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_SET_NAME:
|
|||
|
pszResourceNew = (LPWSTR)LocalAlloc(
|
|||
|
LMEM_FIXED,
|
|||
|
( lstrlenW( (LPWSTR)InBuffer ) + 1 )
|
|||
|
* sizeof( WCHAR ));
|
|||
|
|
|||
|
if ( pszResourceNew ) {
|
|||
|
LocalFree( (HLOCAL)pSpoolerInfo->pszResource );
|
|||
|
lstrcpyW( pszResourceNew, (LPWSTR)InBuffer );
|
|||
|
pSpoolerInfo->pszResource = (LPTSTR)pszResourceNew;
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
status = GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES:
|
|||
|
status = SplSvcGetRequiredDependencies( OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned
|
|||
|
);
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES:
|
|||
|
status = ResUtilEnumProperties( SplSvcResourcePrivateProperties,
|
|||
|
(LPWSTR) OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned,
|
|||
|
&required );
|
|||
|
if ( status == ERROR_MORE_DATA ) {
|
|||
|
*BytesReturned = required;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
|
|||
|
status = SplSvcGetPrivateResProperties( pSpoolerInfo,
|
|||
|
OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned );
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
|
|||
|
status = SplSvcValidatePrivateResProperties( pSpoolerInfo,
|
|||
|
InBuffer,
|
|||
|
InBufferSize,
|
|||
|
NULL );
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
|
|||
|
status = SplSvcSetPrivateResProperties( pSpoolerInfo,
|
|||
|
InBuffer,
|
|||
|
InBufferSize );
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_CLUSTER_VERSION_CHANGED:
|
|||
|
|
|||
|
{
|
|||
|
LPWSTR pszResourceGuid = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Retreive the guid of the spooler resource
|
|||
|
//
|
|||
|
if ((status = GetIDFromName(pSpoolerInfo->hResource,
|
|||
|
&pszResourceGuid)) == ERROR_SUCCESS)
|
|||
|
{
|
|||
|
status = SpoolerWriteClusterUpgradedKey(pszResourceGuid);
|
|||
|
|
|||
|
LocalFree(pszResourceGuid);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_DELETE:
|
|||
|
|
|||
|
SpoolerCleanDriverDirectory(pSpoolerInfo->ParametersKey);
|
|||
|
|
|||
|
//
|
|||
|
// Let the res mon do the rest of the job for us
|
|||
|
//
|
|||
|
status = ERROR_INVALID_FUNCTION;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
status = ERROR_INVALID_FUNCTION;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // SplSvcResourceControl
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcResourceTypeControl(
|
|||
|
IN LPCWSTR ResourceTypeName,
|
|||
|
IN DWORD ControlCode,
|
|||
|
IN PVOID InBuffer,
|
|||
|
IN DWORD InBufferSize,
|
|||
|
OUT PVOID OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
ResourceTypeControl routine for Print Spooler resources.
|
|||
|
|
|||
|
Perform the control request specified by ControlCode.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ResourceTypeName - Supplies the name of the resource type.
|
|||
|
|
|||
|
ControlCode - Supplies the control code that defines the action
|
|||
|
to be performed.
|
|||
|
|
|||
|
InBuffer - Supplies a pointer to a buffer containing input data.
|
|||
|
|
|||
|
InBufferSize - Supplies the size, in bytes, of the data pointed
|
|||
|
to by InBuffer.
|
|||
|
|
|||
|
OutBuffer - Supplies a pointer to the output buffer to be filled in.
|
|||
|
|
|||
|
OutBufferSize - Supplies the size, in bytes, of the available space
|
|||
|
pointed to by OutBuffer.
|
|||
|
|
|||
|
BytesReturned - Returns the number of bytes of OutBuffer actually
|
|||
|
filled in by the resource. If OutBuffer is too small, BytesReturned
|
|||
|
contains the total number of bytes for the operation to succeed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully.
|
|||
|
|
|||
|
ERROR_INVALID_FUNCTION - The requested control code is not supported.
|
|||
|
In some cases, this allows the cluster software to perform the work.
|
|||
|
|
|||
|
Win32 error code - The function failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
DWORD required;
|
|||
|
|
|||
|
switch ( ControlCode ) {
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
|
|||
|
*BytesReturned = 0;
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS:
|
|||
|
status = ResUtilGetPropertyFormats( SplSvcResourcePrivateProperties,
|
|||
|
OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned,
|
|||
|
&required );
|
|||
|
if ( status == ERROR_MORE_DATA ) {
|
|||
|
*BytesReturned = required;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_TYPE_GET_REQUIRED_DEPENDENCIES:
|
|||
|
status = SplSvcGetRequiredDependencies( OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned
|
|||
|
);
|
|||
|
break;
|
|||
|
|
|||
|
case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES:
|
|||
|
status = ResUtilEnumProperties( SplSvcResourcePrivateProperties,
|
|||
|
(LPWSTR) OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned,
|
|||
|
&required );
|
|||
|
if ( status == ERROR_MORE_DATA ) {
|
|||
|
*BytesReturned = required;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
status = ERROR_INVALID_FUNCTION;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // SplSvcResourceTypeControl
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcGetPrivateResProperties(
|
|||
|
IN OUT PSPOOLER_INFORMATION ResourceEntry,
|
|||
|
OUT PVOID OutBuffer,
|
|||
|
IN DWORD OutBufferSize,
|
|||
|
OUT LPDWORD BytesReturned
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control
|
|||
|
function for resources of type Print Spooler.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ResourceEntry - Supplies the resource entry on which to operate.
|
|||
|
|
|||
|
OutBuffer - Supplies a pointer to the output buffer to be filled in.
|
|||
|
|
|||
|
OutBufferSize - Supplies the size, in bytes, of the available space
|
|||
|
pointed to by OutBuffer.
|
|||
|
|
|||
|
BytesReturned - Returns the number of bytes of OutBuffer actually
|
|||
|
filled in by the resource. If OutBuffer is too small, BytesReturned
|
|||
|
contains the total number of bytes for the operation to succeed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully.
|
|||
|
|
|||
|
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
|
|||
|
|
|||
|
Win32 error code - The function failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
DWORD required;
|
|||
|
|
|||
|
*BytesReturned = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Clear the output buffer
|
|||
|
//
|
|||
|
ZeroMemory( OutBuffer, OutBufferSize );
|
|||
|
|
|||
|
//
|
|||
|
// Get the common properties.
|
|||
|
//
|
|||
|
status = ResUtilGetAllProperties( ResourceEntry->ParametersKey,
|
|||
|
SplSvcResourcePrivateProperties,
|
|||
|
OutBuffer,
|
|||
|
OutBufferSize,
|
|||
|
BytesReturned,
|
|||
|
&required );
|
|||
|
|
|||
|
if ( *BytesReturned == 0 ) {
|
|||
|
*BytesReturned = required;
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // SplSvcGetPrivateResProperties
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcValidateUniqueProperties(
|
|||
|
IN HRESOURCE hSelf,
|
|||
|
IN HRESOURCE hResource,
|
|||
|
IN PVOID GroupName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Callback function to validate that a resources properties are unique.
|
|||
|
|
|||
|
For the SplSvc resource we must ensure that only one print spooler
|
|||
|
resource exists in a group.
|
|||
|
|
|||
|
We will never be called for the resource we are creating, only for
|
|||
|
other resources of the same type.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hSelf - A handle to the original resource.
|
|||
|
|
|||
|
hResource - A handle to a resource of the same Type. Check against this
|
|||
|
to make sure the new properties do not conflict.
|
|||
|
|
|||
|
lpszGroupName - The name of the Group the creating resource is in.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully, only one print
|
|||
|
spooler in the given group.
|
|||
|
|
|||
|
ERROR_OBJECT_ALREADY_EXISTS - The name is not unique (i.e., already have
|
|||
|
a print spooler in that group).
|
|||
|
|
|||
|
A Win32 error code on other failure.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WCHAR groupName[MAX_GROUP_NAME_LENGTH + 1];
|
|||
|
DWORD groupNameSize = MAX_GROUP_NAME_LENGTH * sizeof(WCHAR);
|
|||
|
CLUSTER_RESOURCE_STATE resourceState;
|
|||
|
LPWSTR lpszGroupName = (LPWSTR)GroupName;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(hSelf);
|
|||
|
|
|||
|
groupName[0] = L'\0';
|
|||
|
|
|||
|
resourceState = GetClusterResourceState( hResource,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
groupName,
|
|||
|
&groupNameSize );
|
|||
|
if ( !*groupName ) {
|
|||
|
return(GetLastError());
|
|||
|
}
|
|||
|
|
|||
|
if ( lstrcmpiW( lpszGroupName, groupName ) == 0 ) {
|
|||
|
return(ERROR_OBJECT_ALREADY_EXISTS);
|
|||
|
}
|
|||
|
|
|||
|
return( ERROR_SUCCESS );
|
|||
|
|
|||
|
} // SplSvcValidateUniqueProperties
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcValidatePrivateResProperties(
|
|||
|
IN OUT PSPOOLER_INFORMATION ResourceEntry,
|
|||
|
IN PVOID InBuffer,
|
|||
|
IN DWORD InBufferSize,
|
|||
|
OUT PSPOOLER_PARAMS Params
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
|
|||
|
function for resources of type Print Spooler.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ResourceEntry - Supplies the resource entry on which to operate.
|
|||
|
|
|||
|
InBuffer - Supplies a pointer to a buffer containing input data.
|
|||
|
|
|||
|
InBufferSize - Supplies the size, in bytes, of the data pointed
|
|||
|
to by InBuffer.
|
|||
|
|
|||
|
Params - Supplies the parameter block to fill in.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully.
|
|||
|
|
|||
|
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
|
|||
|
|
|||
|
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
|
|||
|
|
|||
|
Win32 error code - The function failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
SPOOLER_PARAMS currentProps;
|
|||
|
SPOOLER_PARAMS newProps;
|
|||
|
PSPOOLER_PARAMS pParams = NULL;
|
|||
|
WCHAR groupName[MAX_GROUP_NAME_LENGTH + 1];
|
|||
|
DWORD groupNameSize = MAX_GROUP_NAME_LENGTH * sizeof(WCHAR);
|
|||
|
CLUSTER_RESOURCE_STATE resourceState;
|
|||
|
LPWSTR nameOfPropInError;
|
|||
|
|
|||
|
//
|
|||
|
// Check if there is input data.
|
|||
|
//
|
|||
|
if ( (InBuffer == NULL) ||
|
|||
|
(InBufferSize < sizeof(DWORD)) ) {
|
|||
|
return(ERROR_INVALID_DATA);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Capture the current set of private properties from the registry.
|
|||
|
//
|
|||
|
ZeroMemory( ¤tProps, sizeof(currentProps) );
|
|||
|
|
|||
|
status = ResUtilGetPropertiesToParameterBlock(
|
|||
|
ResourceEntry->ParametersKey,
|
|||
|
SplSvcResourcePrivateProperties,
|
|||
|
(LPBYTE) ¤tProps,
|
|||
|
FALSE, /*CheckForRequiredProperties*/
|
|||
|
&nameOfPropInError
|
|||
|
);
|
|||
|
|
|||
|
if ( status != ERROR_SUCCESS ) {
|
|||
|
(SplSvcLogEvent)(
|
|||
|
ResourceEntry->ResourceHandle,
|
|||
|
LOG_ERROR,
|
|||
|
L"Unable to read the '%1' property. Error: %2!u!.\n",
|
|||
|
(nameOfPropInError == NULL ? L"" : nameOfPropInError),
|
|||
|
status );
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Duplicate the resource parameter block.
|
|||
|
//
|
|||
|
if ( Params == NULL ) {
|
|||
|
pParams = &newProps;
|
|||
|
} else {
|
|||
|
pParams = Params;
|
|||
|
}
|
|||
|
|
|||
|
ZeroMemory( pParams, sizeof(SPOOLER_PARAMS) );
|
|||
|
status = ResUtilDupParameterBlock( (LPBYTE) pParams,
|
|||
|
(LPBYTE) ¤tProps,
|
|||
|
SplSvcResourcePrivateProperties );
|
|||
|
if ( status != ERROR_SUCCESS ) {
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Parse and validate the properties.
|
|||
|
//
|
|||
|
status = ResUtilVerifyPropertyTable( SplSvcResourcePrivateProperties,
|
|||
|
NULL,
|
|||
|
TRUE, // Allow unknowns
|
|||
|
InBuffer,
|
|||
|
InBufferSize,
|
|||
|
(LPBYTE) pParams );
|
|||
|
|
|||
|
if ( status == ERROR_SUCCESS ) {
|
|||
|
//
|
|||
|
// Validate the Default Spool Directory
|
|||
|
//
|
|||
|
if ( pParams->DefaultSpoolDirectory &&
|
|||
|
!ResUtilIsPathValid( pParams->DefaultSpoolDirectory ) ) {
|
|||
|
status = ERROR_INVALID_PARAMETER;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure there is only one print spooler in this group.
|
|||
|
//
|
|||
|
resourceState = GetClusterResourceState( ResourceEntry->hResource,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
groupName,
|
|||
|
&groupNameSize );
|
|||
|
if ( !*groupName ) {
|
|||
|
status = GetLastError();
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
|
|||
|
status = ResUtilEnumResources(ResourceEntry->hResource,
|
|||
|
CLUS_RESTYPE_NAME_PRTSPLR,
|
|||
|
SplSvcValidateUniqueProperties,
|
|||
|
(PVOID)groupName);
|
|||
|
|
|||
|
if (status != ERROR_SUCCESS) {
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup our parameter block.
|
|||
|
//
|
|||
|
if ( ( (status != ERROR_SUCCESS)
|
|||
|
&& (pParams != NULL) )
|
|||
|
|| ( pParams == &newProps )
|
|||
|
) {
|
|||
|
ResUtilFreeParameterBlock( (LPBYTE) pParams,
|
|||
|
(LPBYTE) ¤tProps,
|
|||
|
SplSvcResourcePrivateProperties );
|
|||
|
}
|
|||
|
|
|||
|
ResUtilFreeParameterBlock(
|
|||
|
(LPBYTE) ¤tProps,
|
|||
|
NULL,
|
|||
|
SplSvcResourcePrivateProperties
|
|||
|
);
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // SplSvcValidatePrivateResProperties
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SplSvcSetPrivateResProperties(
|
|||
|
IN OUT PSPOOLER_INFORMATION ResourceEntry,
|
|||
|
IN PVOID InBuffer,
|
|||
|
IN DWORD InBufferSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function
|
|||
|
for resources of type Print Spooler.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ResourceEntry - Supplies the resource entry on which to operate.
|
|||
|
|
|||
|
InBuffer - Supplies a pointer to a buffer containing input data.
|
|||
|
|
|||
|
InBufferSize - Supplies the size, in bytes, of the data pointed
|
|||
|
to by InBuffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS - The function completed successfully.
|
|||
|
|
|||
|
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
|
|||
|
|
|||
|
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
|
|||
|
|
|||
|
Win32 error code - The function failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
// SPOOLER_PARAMS params;
|
|||
|
|
|||
|
//
|
|||
|
// Parse the properties so they can be validated together.
|
|||
|
// This routine does individual property validation.
|
|||
|
//
|
|||
|
status = SplSvcValidatePrivateResProperties( ResourceEntry,
|
|||
|
InBuffer,
|
|||
|
InBufferSize,
|
|||
|
NULL /*¶ms*/ );
|
|||
|
if ( status != ERROR_SUCCESS ) {
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the parameter values.
|
|||
|
//
|
|||
|
|
|||
|
status = ResUtilSetPropertyTable( ResourceEntry->ParametersKey,
|
|||
|
SplSvcResourcePrivateProperties,
|
|||
|
NULL,
|
|||
|
TRUE, // Allow unknowns
|
|||
|
InBuffer,
|
|||
|
InBufferSize,
|
|||
|
NULL );
|
|||
|
// status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey,
|
|||
|
// SplSvcResourcePrivateProperties,
|
|||
|
// NULL,
|
|||
|
// (LPBYTE) ¶ms,
|
|||
|
// InBuffer,
|
|||
|
// InBufferSize,
|
|||
|
// (LPBYTE) &ResourceEntry->Params );
|
|||
|
|
|||
|
// ResUtilFreeParameterBlock( (LPBYTE) ¶ms,
|
|||
|
// (LPBYTE) &ResourceEntry->Params,
|
|||
|
// SplSvcResourcePrivateProperties );
|
|||
|
|
|||
|
//
|
|||
|
// If the resource is online, return a non-success status.
|
|||
|
//
|
|||
|
if (status == ERROR_SUCCESS) {
|
|||
|
if ( (ResourceEntry->eState == kOnline) ||
|
|||
|
(ResourceEntry->eState == kOnlinePending) ) {
|
|||
|
status = ERROR_RESOURCE_PROPERTIES_STORED;
|
|||
|
} else {
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return(status);
|
|||
|
|
|||
|
} // SplSvcSetPrivateResProperties
|
|||
|
|
|||
|
|
|||
|
/********************************************************************
|
|||
|
|
|||
|
Utility functions
|
|||
|
|
|||
|
********************************************************************/
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
vEnterSem(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
EnterCriticalSection( &gProcessLock );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
vLeaveSem(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
LeaveCriticalSection( &gProcessLock );
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
vAddRef(
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo
|
|||
|
)
|
|||
|
{
|
|||
|
vEnterSem();
|
|||
|
++pSpoolerInfo->cRef;
|
|||
|
vLeaveSem();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
vDecRef(
|
|||
|
PSPOOLER_INFORMATION pSpoolerInfo
|
|||
|
)
|
|||
|
{
|
|||
|
UINT uRef;
|
|||
|
|
|||
|
SPLASSERT( pSpoolerInfo->cRef );
|
|||
|
|
|||
|
vEnterSem();
|
|||
|
uRef = --pSpoolerInfo->cRef;
|
|||
|
vLeaveSem();
|
|||
|
|
|||
|
if( !uRef ){
|
|||
|
vDeleteSpoolerInfo( pSpoolerInfo );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//***********************************************************
|
|||
|
//
|
|||
|
// Define Function Table
|
|||
|
//
|
|||
|
//***********************************************************
|
|||
|
|
|||
|
CLRES_V1_FUNCTION_TABLE( SplSvcFunctionTable,
|
|||
|
CLRES_VERSION_V1_00,
|
|||
|
SplSvc,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
SplSvcResourceControl,
|
|||
|
SplSvcResourceTypeControl );
|
|||
|
|