|
|
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1996-1999 Microsoft Corporation
//
// Module Name:
// clusocm.cpp
//
// Abstract:
// CLUSOCM.DLL is an Optional Components Manager DLL for installation of
// Microsoft Cluster Server. This file is the class implementation file
// for the CClusocmApp class which implements the component setup procedure.
//
// Author:
// C. Brent Thomas (a-brentt)
//
// Revision History:
// 1/29/98 original
//
// Notes:
//
// If this DLL is dynamically linked against the MFC
// DLLs, any functions exported from this DLL which
// call into MFC must have the AFX_MANAGE_STATE macro
// added at the very beginning of the function.
//
// For example:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // normal function body here
// }
//
// It is very important that this macro appear in each
// function, prior to any calls into MFC. This means that
// it must appear as the first statement within the
// function, even before any object variable declarations
// as their constructors may generate calls into the MFC
// DLL.
//
// Please see MFC Technical Notes 33 and 58 for additional
// details.
//
//
/////////////////////////////////////////////////////////////////////////////
//
#include "stdafx.h"
#include "clusocm.h"
#include <regcomobj.h>
#include <StopService.h>
#include <RemoveNetworkProvider.h>
#include <IsClusterServiceRegistered.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
/////////////////////////////////////////////////////////////////////////////
// Global Data
/////////////////////////////////////////////////////////////////////////////
// The one and only CClusocmApp object
CClusocmApp theApp;
/////////////////////////////////////////////////////////////////////////////
// Global Functions
// This is the function that OC Manager will call.
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClusOcmSetupProc
//
// Routine Description:
// This is the exported function that OC Manager calls. It merely passes
// its' parameters to the CClusocmApp object and returns the results to
// OC Manager.
//
// Arguments:
// pvComponentId - points to a string that uniquely identifies the component
// to be set up to OC Manager.
// pvSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// uxFunction - A numeric value indicating which function is to be perfomed.
// See ocmanage.h for the macro definitions.
// uxParam1 - supplies a function specific parameter.
// pvParam2 - points to a function specific parameter (which may be an
// output).
//
// Return Value:
// A function specific value is returned to OC Manager.
//
//--
/////////////////////////////////////////////////////////////////////////////
extern "C" DWORD WINAPI ClusOcmSetupProc( IN LPCVOID pvComponentId, IN LPCVOID pvSubComponentId, IN UINT uxFunction, IN UINT uxParam1, IN OUT PVOID pvParam2 ) { return theApp.ClusOcmSetupProc( pvComponentId, pvSubComponentId, uxFunction, uxParam1, pvParam2 );
} //*** exported ClusOcmSetupProc()
/////////////////////////////////////////////////////////////////////////////
// CClusocmApp
BEGIN_MESSAGE_MAP(CClusocmApp, CWinApp) //{{AFX_MSG_MAP(CClusocmApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClusocmApp construction
// Default constructor
CClusocmApp::CClusocmApp() { // TODO: add construction code here,
// Place all significant initialization in InitInstance
// Initialize the values of the HINF members in the SETUP_INIT_COMPONENT
// structure data member.
m_SetupInitComponent.OCInfHandle = (HINF) INVALID_HANDLE_VALUE; m_SetupInitComponent.ComponentInfHandle = (HINF) INVALID_HANDLE_VALUE;
m_dwStepCount = 0L; }
/////////////////////////////////////////////////////////////////////////////
//++
//
// InitInstance
//
// Routine Description:
//
//
// Arguments:
//
//
//
// Return Value:
//
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::InitInstance( void ) { BOOL fReturnValue = (BOOL) TRUE;
m_hInstance = AfxGetInstanceHandle();
// Initialize OLE libraries
if (!AfxOleInit()) { if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) == (DWORDLONG) 0L ) { AfxMessageBox( IDS_OLE_INIT_FAILED ); }
ClRtlLogPrint( "OLE initialization failed. Make sure that the OLE libraries are the correct version.\n" );
fReturnValue = (BOOL) FALSE; }
return ( fReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CClusocmApp::ClusOcmSetupProc
//
// Routine Description:
// This function implements the Optional Components Manager component
// setup procedure. This function is called by the exported function
// of the same name, which in turn is called by OC Manager.
//
// Arguments:
// pvComponentId - points to a string that uniquely identifies the component
// to be set up to OC Manager.
// pvSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// uxFunction - A numeric value indicating which function is to be perfomed.
// See ocmanage.h for the macro definitions.
// uxParam1 - supplies a function specific parameter.
// pvParam2 - points to a function specific parameter (which may be an
// output).
//
//
// Return Value:
// A function specific value is returned to OC Manager.
//
//--
/////////////////////////////////////////////////////////////////////////////
#pragma warning ( disable : 4100 ) // disable "unreferenced formal parameter" warning
// At this time (2/26/98) pvComponentId is unused.
DWORD CClusocmApp::ClusOcmSetupProc( IN LPCVOID pvComponentId, IN LPCVOID pvSubComponentId, IN UINT uxFunction, IN UINT uxParam1, IN OUT PVOID pvParam2 ) { AFX_MANAGE_STATE( AfxGetStaticModuleState() ); // As per the note in file header
DWORD dwReturnValue;
switch ( uxFunction ) { case OC_PREINITIALIZE:
// This is the first interface function that OC Manager calls.
// OnOcPreinitialize enables the log file for clusocm.dll, verifies
// that the OS version and Product Suite are correct, etc.
dwReturnValue = OnOcPreinitialize();
ClRtlLogPrint( "OnOcPreinitialize returned 0x%1!x!\n\n", dwReturnValue );
break;
case OC_INIT_COMPONENT:
// This function is called soon after the component's setup dll is
// loaded. This function provides initialization information to the
// dll, instructs the dll to initialize itself, and provides a
// mechanism for the dll to return information to OC Manager.
// It may be desirable to verify that Microsoft Cluster Server is being
// installed on NT Enterprise and to authenticate the user at this point.
dwReturnValue = OnOcInitComponent((PSETUP_INIT_COMPONENT) pvParam2 );
ClRtlLogPrint( "OnOcInitComponent returned 0x%1!x!\n\n", dwReturnValue );
break;
case OC_REQUEST_PAGES:
// OC Manager is requesting a set of wizard pages from the dll.
// As of 2/16/98 there are no wizard pages to supply.
dwReturnValue = (DWORD) 0L;
ClRtlLogPrint( "ClusOcmSetupProc is returning 0x%1!x! in response to OC_REQUEST_PAGES.\n\n", dwReturnValue );
break;
case OC_QUERY_STATE:
// Oc Manager is requesting the selection state of the component.
// It does that at least three times.
dwReturnValue = OnOcQueryState( (LPCTSTR) pvSubComponentId, uxParam1 );
ClRtlLogPrint( "OnQueryState returned 0x%1!x!\n\n", dwReturnValue );
break;
case OC_SET_LANGUAGE:
// OC Manager documentation states:
//
// "If a component does not support multiple languages, or if it does
// not support the requested language, then it may ignore OC_SET_LANGUAGE."
// I have replicated the functionality in Pat Styles' generic sample, ocgen.cpp.
dwReturnValue = OnOcSetLanguage();
ClRtlLogPrint( "OnOcSetLanguage returned 0x%1!x!.\n\n", dwReturnValue );
break;
case OC_CALC_DISK_SPACE:
dwReturnValue = OnOcCalcDiskSpace( (LPCTSTR) pvSubComponentId, uxParam1, (HDSKSPC) pvParam2 );
ClRtlLogPrint( "OnOcCalcDiskSpace returned 0x%1!x!.\n\n", dwReturnValue );
break;
case OC_QUEUE_FILE_OPS:
if ( (pvSubComponentId != (PVOID) NULL) && (*((LPCTSTR) pvSubComponentId) != _T('\0')) ) { dwReturnValue = OnOcQueueFileOps( (LPCTSTR) pvSubComponentId, (HSPFILEQ) pvParam2 );
ClRtlLogPrint( "OnOcQueueFileOps returned 0x%1!x!.\n\n", dwReturnValue ); } else { dwReturnValue = (DWORD) NO_ERROR; }
break;
case OC_QUERY_CHANGE_SEL_STATE:
dwReturnValue = OnOcQueryChangeSelState( (LPCTSTR) pvSubComponentId, (UINT) uxParam1, (DWORD) ((DWORD_PTR)pvParam2) );
ClRtlLogPrint( "OnOnQueryChangeSelState returned 0x%1!x!.\n\n", dwReturnValue );
break;
case OC_COMPLETE_INSTALLATION:
if ( (pvSubComponentId != (PVOID) NULL) && (*((LPCTSTR) pvSubComponentId) != _T('\0')) ) { dwReturnValue = OnOcCompleteInstallation( (LPCTSTR) pvSubComponentId );
ClRtlLogPrint( "OnOcCompleteInstallation returned 0x%1!x!.\n\n", dwReturnValue ); } else { dwReturnValue = (DWORD) NO_ERROR; }
break;
case OC_WIZARD_CREATED:
// This interface function code is not documented. I have replicated the
// functionality of Pat Styles' generic sample, ocgen.cpp.
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "returning 0x%1!x! for OC_WIZARD_CREATED.\n\n", dwReturnValue );
break;
case OC_NEED_MEDIA:
// This interface function code is not documented. I have replicated the
// functionality of Pat Styles' generic sample, ocgen.cpp.
dwReturnValue = (DWORD) FALSE;
ClRtlLogPrint( "returning 0x%1!x! for OC_NEED_MEDIA.\n\n" );
break;
case OC_QUERY_SKIP_PAGE:
// This interface function code is not documented. I have replicated the
// functionality of Pat Styles' generic sample, ocgen.cpp.
dwReturnValue = (DWORD) FALSE;
ClRtlLogPrint( "returning 0x%1!x! for OC_QUERY_SKIP_PAGE.\n\n", dwReturnValue );
break;
case OC_QUERY_STEP_COUNT:
// OC Manager uses this interface function code to request the number
// of "steps", other than file operations, that must be performed to
// install the component.
if ( (pvSubComponentId != (PVOID) NULL) && (*((LPCTSTR) pvSubComponentId) != _T('\0')) ) { dwReturnValue = CalculateStepCount( (LPCTSTR) pvSubComponentId );
if ( dwReturnValue != 0L ) { // Update the data member.
SetStepCount( dwReturnValue ); } } else { dwReturnValue = (DWORD) NO_ERROR; }
ClRtlLogPrint( "returning 0x%1!x! for OC_QUERY_STEP_COUNT.\n\n", dwReturnValue );
break;
case OC_CLEANUP:
// This interface function code is the last to be processed.
// OC Manager documentation states that the return value from processing
// this interface function code is ignored.
dwReturnValue = OnOcCleanup();
break;
case OC_ABOUT_TO_COMMIT_QUEUE:
if ( (pvSubComponentId != (PVOID) NULL) && (*((LPCTSTR) pvSubComponentId) != _T('\0')) ) { dwReturnValue = OnOcAboutToCommitQueue( (LPCTSTR) pvSubComponentId );
ClRtlLogPrint( "OnOcAboutToCommitQueue returned 0x%1!x!.\n\n", dwReturnValue ); } else { dwReturnValue = (DWORD) NO_ERROR; }
break;
case OC_EXTRA_ROUTINES:
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "returning 0x%1!x! for OC_EXTRA_ROUTINES.\n\n", dwReturnValue );
break;
default:
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "ClusOcmSetupProc received an unknown function code, 0x%1!x!.\n", uxFunction ); ClRtlLogPrint( "returning 0x%1!x!.\n", dwReturnValue );
break; } // end of switch( uxFunction )
return ( (DWORD) dwReturnValue ); }
#pragma warning ( default : 4100 ) // default behavior "unreferenced formal parameter" warning
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcPreinitialize
//
// Routine Description:
// This funcion processes OC_PREINITIALIZE "messages" from Optional
// Components Manager. This is the first function in the component
// setup dll that OC Manager calls.
//
// This function prevents clusocm.dll from running on down level operating
// systems and unuthorized platforms.
//
// Since Microsoft Cluster Server can only be installed on NT this function
// selects the UNICODE character set.
//
// Arguments:
// None
//
// Return Value:
// (DWORD) OCFLAG_UNICODE - indicates success
// (DWORD) 0 - indicates that the component cannot be initialized.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcPreinitialize( void ) { DWORD dwReturnValue;
// The ClRtlInitialize function that opens a log file reads the ClusterLog
// environment variable. This code segment reads that environment variable
// and saves it so that it can be restored on exit.
char * pszOriginalLogFileName;
pszOriginalLogFileName = getenv( "ClusterLog" );
if ( pszOriginalLogFileName != (char *) NULL ) { strcpy( m_szOriginalLogFileName, pszOriginalLogFileName ); } else { *m_szOriginalLogFileName = (char) '\0'; }
// Build the string used to set the ClusterLog environment variable to point
// to the log for clusocm.dll.
char szClusterLogEnvString[MAX_PATH];
// Initialize the string to be empty.
*szClusterLogEnvString = '\0';
// The log file for clusocm.dll will be located in the windows directory.
// Note that if the path to the Windows directory cannot be obtained the
// ClusterLog environment variable will be set empty. Then function ClRtlInitialize
// will use the default log file name and location.
TCHAR tszWindowsDirectory[MAX_PATH];
UINT uxReturnValue;
uxReturnValue = GetWindowsDirectory( tszWindowsDirectory, (UINT) MAX_PATH );
if ( uxReturnValue > 0 ) { // The first part of the string to set the ClusterLog environment variable is
// "ClusterLog=".
strcpy( szClusterLogEnvString, "ClusterLog=" );
// Convert the path to the Windows directory to MBCS.
char szWindowsDirectory[MAX_PATH];
wcstombs( szWindowsDirectory, tszWindowsDirectory, MAX_PATH );
// Append the path to the Windows directory.
strcat( szClusterLogEnvString, szWindowsDirectory );
// Append the log file name.
strcat( szClusterLogEnvString, "\\clusocm.log" ); } // Was the path to the Windows directory obtained?
// Set the ClusterLog environment variable. Note that _putenv returns 0
// on success.
if ( _putenv( szClusterLogEnvString ) != 0 ) { dwReturnValue = GetLastError(); // just here for debugging
}
// Initialize the cluster runtime library. FALSE indicates that debug
// output should not be redirected to a console window. If ClRtlInitialize
// fails the calls to ClRtlLogPrint will be benign.
ClRtlInitialize( FALSE );
// Note: The date in the following statement is hardcoded on purpose. It is to
// indicate when the last time the logging statements were revised.
ClRtlLogPrint( "\n\nCLUSOCM.LOG version 04/09/99 13:20.\n" );
// Microsoft Cluster Server can only be installed on Windows NT Server Enterprise 5.0+.
// This code segment checks the operating system version.
// Since Microsoft Cluster Server can only be installed on NT indicate
// to OC Manager that the UNICODE character set should be used.
dwReturnValue = (DWORD) OCFLAG_UNICODE;
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcInitComponent
//
// Routine Description:
// This funcion processes OC_INIT_COMPONENT "messages" from Optional
// Components Manager.
//
// This function is called soon after the component's setup dll is
// loaded. This function provides initialization information to the
// dll, instructs the dll to initialize itself, and provides a
// mechanism for the dll to return information to OC Manager.
//
// Arguments:
// pSetupInitComponent - points to a SETUP_INIT_COMPONENT structure
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
// Note:
// The SETUP_INIT_COMPONENT structure pointed to by pSetupInitComponent
// DOES NOT PERSIST. It is ncessary to save a copy in the CClusocmApp object.
// This function initializes the SETUP_INIT_COMPONENT data member,
// m_SetupInitComponent.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcInitComponent( IN OUT PSETUP_INIT_COMPONENT pSetupInitComponent ) { DWORD dwReturnValue;
if ( pSetupInitComponent != (PSETUP_INIT_COMPONENT) NULL ) { // Save the pointer to the SETUP_INIT_COMPONENT structure.
m_SetupInitComponent = *pSetupInitComponent;
// This code segment determines whether the version of OC Manager is
// correct.
if ( OCMANAGER_VERSION <= m_SetupInitComponent.OCManagerVersion ) { // Indicate to OC Manager which version of OC Manager this dll expects.
pSetupInitComponent->ComponentVersion = OCMANAGER_VERSION;
// Update CClusocmApp's copy of the SETUP_INIT_COMPONENT structure.
m_SetupInitComponent.ComponentVersion = OCMANAGER_VERSION;
// Is the handle to the component INF valid ?
if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { // The following call to SetupOpenAppendInfFile ensures that layout.inf
// gets appended to clusocm.inf. This is required for several reasons
// dictated by the Setup API. In theory OC Manager should do this, but
// as per Andrew Ritz, 8/24/98, OC manager neglects to do it and it is
// harmless to do it here after OC Manager is revised.
// Note that passing NULL as the first parameter causes SetupOpenAppendInfFile
// to append the file(s) listed on the LayoutFile entry in clusocm.inf.
UINT uxStatus;
SetupOpenAppendInfFile( NULL, m_SetupInitComponent.ComponentInfHandle, &uxStatus );
if ( ClRtlIsOSValid() == TRUE ) { // Since Microsoft Cluster Server can only be installed on NT indicate
// to OC Manager that the UNICODE character set should be used.
dwReturnValue = (DWORD) NO_ERROR; } else { // The operating system version is not correct.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) == (DWORDLONG) 0L ) { CString csMessage;
csMessage.LoadString( IDS_ERR_INCORRECT_OS_VERSION );
AfxMessageBox( csMessage ); }
DWORD dwErrorCode;
dwErrorCode = GetLastError();
ClRtlLogPrint( "ClRtlIsOSValid failed with error code %1!x!.\n", dwErrorCode );
dwReturnValue = (DWORD) ERROR_CALL_NOT_IMPLEMENTED; } } else { // Indicate failure.
ClRtlLogPrint( "In OnOcInitComponent the ComponentInfHandle is bad.\n" );
dwReturnValue = (DWORD) ERROR_CALL_NOT_IMPLEMENTED; } } else { // Indicate failure.
ClRtlLogPrint( "An OC Manager version mismatch. Version %1!d! is required, version %2!d! was reported.\n", OCMANAGER_VERSION, m_SetupInitComponent.OCManagerVersion );
dwReturnValue = (DWORD) ERROR_CALL_NOT_IMPLEMENTED; } } else { ClRtlLogPrint( "In OnOcInitComponent the pointer to the SETUP_INIT_COMPONENT structure is NULL.\n" );
dwReturnValue = (DWORD) ERROR_CALL_NOT_IMPLEMENTED; }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcQueryState
//
// Routine Description:
// This funcion sets the original, current, and final selection states of the
// Cluster service optional component.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// uxSelStateQueryType - indicates whether OC Manager is requesting the
// original, current, or final selection state of the
// component
//
// Return Value:
// SubcompOn - indicates that the checkbox should be set
// SubcompOff - indicates that the checkbox should be clear
// SubcompUseOCManagerDefault - OC Manager should set the state of the checkbox
// according to state information that is maintained
// internally by OC Manager itself.
//
// Note:
// By the time this function gets called OnOcInitComponent has already determined
// that Terminal Services is not installed. It is only necessary to determine
// whether Terminal Services is selected for installation.
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcQueryState( IN LPCTSTR ptszSubComponentId, IN UINT uxSelStateQueryType ) { DWORD dwReturnValue; eClusterInstallState eState;
// For unattended setup the selection state of the Cluster service optional
// component is specified in the answer file. Pat Styles recommends returning
// SubcompUseOcManagerDefault in every case.
// The following "if" statement forces SubcompUseOcManagerDefault to be returned
// if the subcomponent name is invalid.
if ( ptszSubComponentId != (LPCTSTR) NULL ) { // The subcomponent ID is valid. For what purpose was this function called?
switch ( uxSelStateQueryType ) { case OCSELSTATETYPE_ORIGINAL:
// OC Manager is requesting the intitial (original) selection state
// of the component.
// Is this a fresh install running under GUI mode setup ?
if ( ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) == (DWORDLONG) 0L ) && ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) == (DWORDLONG) 0L ) ) { // SETUPOP_STANDALONE flag clear means running under GUI mode setup.
// SETUPOP_NTUPGRADE flag clear means not performing an upgrade.
// Both flags clear means a fresh install in GUI MODE setup.
// Return SubcompUseOcManagerDefault. As per AndrewR, for unattended
// clean install return SubcompUseOcManagerDefault. For attended
// clean install let OC Manager manage the selection state. Returning
// SubcompUseOcManagerDefault will allow OC Manager to honor the
// "modes" line in clusocm.inf if one is present.
dwReturnValue = (DWORD) SubcompUseOcManagerDefault;
ClRtlLogPrint( "OnOcQueryState is returning SubcompUseOcManagerDefault for ORIGINAL on a clean install.\n" ); } // clean install?
else { // This is standalone or upgrade. Is this an unattended clean install?
if ( ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) && ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) == (DWORDLONG) 0L ) ) { // This is UNATTENDED INSTALL with OCM running STANDALONE. Since
// this is STANDALONE, the MUST be an answer file.
// As per AndrewR for unattended clean install return SubcompUseOcManagerDefault.
dwReturnValue = (DWORD) SubcompUseOcManagerDefault;
ClRtlLogPrint( "ORIGINAL selection state: SubcompUseOcManagerDefault - UNATTENDED CLEAN install.\n" ); } else { // Either an upgrade is in progress or OC Manager is running standalone.
// The state of the checkbox depends on whether Cluster service has
// previously been installed.
// GetClusterInstallationState reports on the state of the registry value
// that records the state of the Cluster service installation on NT 5 machines.
// IsClusterServiceRegistered indicates whether the Cluster service is registered on
// BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for
// upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines.
ClRtlGetClusterInstallState( NULL, &eState );
if ( ( eState != eClusterInstallStateUnknown ) || ( IsClusterServiceRegistered() == (BOOL) TRUE ) ) { // No error was detected.
dwReturnValue = (DWORD) SubcompOn;
ClRtlLogPrint( "ORIGINAL selection state: SubcompOn - UPGRADE or STANDALONE - Cluster service previously installed.\n" ); } else { // Some error occured.
dwReturnValue = (DWORD) SubcompOff;
ClRtlLogPrint( "ORIGINAL selection state: SubcompOff - UPGRADE or STANDALONE - Cluster service NOT previously installed.\n" ); } // Were the Cluster service files installed?
} // unattended?
} // Is this a fresh install?
break;
case OCSELSTATETYPE_CURRENT:
// OC Manager is requesting the current selection state of the component.
// For the cases of a "clean" install or when OC manager is running
// standalone it is safe to let OC Manager use the default selection state
// to determine the cuttent selection state. The UPGRADE case requires
// specific handling. Is this an UPGRADE?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { // This is an upgrade. The state of the checkbox depends on whether
// Cluster service has previously been installed.
// GetClusterInstallationState reports on the state of the registry value
// that records the state of the Cluster service installation on NT 5 machines.
// IsClusterServiceRegistered indicates whether the Cluster service is registered on
// BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for
// upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines.
ClRtlGetClusterInstallState( NULL, &eState );
if ( ( eState != eClusterInstallStateUnknown ) || ( IsClusterServiceRegistered() == (BOOL) TRUE ) ) { // No error was detected.
dwReturnValue = (DWORD) SubcompOn;
ClRtlLogPrint( "CURRENT selection state: SubcompOn - UPGRADE - Cluster service previously installed.\n" ); } else { // Some error occured.
dwReturnValue = (DWORD) SubcompOff;
ClRtlLogPrint( "CURRENT selection state: SubcompOff - UPGRADE - Cluster service NOT previously installed.\n" ); } // Were the Cluster service files installed?
} else { // This is either a "fresh" install or OC Manager is running standalone.
dwReturnValue = (DWORD) SubcompUseOcManagerDefault;
ClRtlLogPrint( "CURRENT selection state: SubcompUseOcManagerDefault - CLEAN install or STANDALONE.\n" ); } // Is this an UPGRADE?
break;
case OCSELSTATETYPE_FINAL:
// OC Manager is requesting the final selection state of the component.
// As per Pat Styles, this call to OnOcQueryState will occur AFTER
// OnOcCompleteInstallation has been called. The registry operation that
// creates the registry key that GetClusterInstallationState checks as
// its' indicator of success is queued by OnOcCompleteInstallation.
// Does this OC Manager Setup DLL believe it has completed without error?
ClRtlGetClusterInstallState( NULL, &eState ); if ( eState != eClusterInstallStateUnknown ) { // No error was detected.
dwReturnValue = (DWORD) SubcompOn;
ClRtlLogPrint( "FINAL selection state: SubcompOn.\n" ); } else { // Some error occured.
dwReturnValue = (DWORD) SubcompOff;
ClRtlLogPrint( "FINAL selection state: SubcompOff.\n" ); } // Were the Cluster service files installed?
break;
default:
// This is an error condition.
dwReturnValue = (DWORD) SubcompUseOcManagerDefault;
ClRtlLogPrint( "Bad Param1 passed to OnOcQueryState.\n" );
break; } // end of switch ( uxSelStateQueryType )
} else { // The subcomponent ID is invalid.
dwReturnValue = (DWORD) SubcompUseOcManagerDefault;
ClRtlLogPrint( "In OnOcQueryState the subcomponent ID is NULL.\n" ); } // Is the subcomponent ID legal?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcSetLanguage
//
// Routine Description:
// This funcion indicates to OC Manager that clusocm.dll cannot handle
// alternate langiages.
//
// Arguments:
// None
//
// Return Value:
// (DWORD) FALSE
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcSetLanguage( void ) { // Returning (DWORD) FALSE will indicate to OC Manager that the component
// setup dll does not support the requested language.
return (DWORD) FALSE; }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcCalcDiskSpace
//
// Routine Description:
// This function processes the OC_CALC_DISK_SPACE "messages" from the
// Optional Components Manager. It either adds or removes disk space
// requirements from the Disk Space List maintained by OC Manager.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// uxAddOrRemoveFlag - Non-zero means that the component setup dll is being
// asked to add space requirements for a subcomponent to
// the disk space list. ZERO means that the component
// setup dll is being asked to remove space requirements
// for the subcomponent from the disk space list.
// hDiskSpaceList - a HDSPSPC (typedefed in setupapi.h to PVOID)
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcCalcDiskSpace( IN LPCTSTR ptszSubComponentId, IN UINT uxAddOrRemoveFlag, IN OUT HDSKSPC hDiskSpaceList ) { DWORD dwReturnValue = NO_ERROR;
// Is the subcomponent ID meaningfull?
if ( ptszSubComponentId != (LPCTSTR) NULL ) { // Is the handle to the component INF file meaningfull?
if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { BOOL fReturnValue;
// The INF file is open. Now we can use it.
// Associate the user defined Directory IDs in the [DestinationDirs] section
// of clusocm.inf with actual directories.
BOOL fClusterServiceRegistered;
fClusterServiceRegistered = IsClusterServiceRegistered();
if ( SetDirectoryIds( fClusterServiceRegistered ) == (BOOL) TRUE ) { if ( uxAddOrRemoveFlag != (UINT) 0 ) { // Add disk space requirements to the file disk space list.
fReturnValue = SetupAddInstallSectionToDiskSpaceList( hDiskSpaceList, m_SetupInitComponent.ComponentInfHandle, NULL, ptszSubComponentId, 0, 0 );
if ( fReturnValue == (BOOL) FALSE ) { dwReturnValue = GetLastError(); }
ClRtlLogPrint( "SetupAddInstallSectionToDiskSpaceList returned 0x%1!x! to OnOcCalcDiskSpace.\n", fReturnValue ); } else { // Remove disk space requirements from the disk space list.
fReturnValue = SetupRemoveInstallSectionFromDiskSpaceList( hDiskSpaceList, m_SetupInitComponent.ComponentInfHandle, NULL, ptszSubComponentId, 0, 0 );
if ( fReturnValue == (BOOL) FALSE ) { dwReturnValue = GetLastError(); }
ClRtlLogPrint( "SetupRemoveInstallSectionFromDiskSpaceList returned 0x%1!x! to OnOcCalcDiskSpace.\n", fReturnValue ); } } else { dwReturnValue = GetLastError();
ClRtlLogPrint( "In OnCalcDiskSpace the call to SetDirectoryIds failed.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwReturnValue ); } // Were the directory Id associations made successfully?
} else { ClRtlLogPrint( "In OnOcCalcDiskSpace ComponentInfHandle is bad.\n" );
dwReturnValue = ERROR_FILE_NOT_FOUND; } }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcCompleteInstallation
//
// Routine Description:
// This function processes the OC_COMPLETE_INSTALLATION "messages" from the
// Optional Components Manager. It queues registry operations.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcCompleteInstallation( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue = NO_ERROR;
if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { // The INF file handle is valid.
BOOL fOriginalSelectionState; BOOL fCurrentSelectionState;
// Is the subcomponent currently selected ?
fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT );
if ( fCurrentSelectionState == (BOOL) TRUE ) { ClRtlLogPrint( "In OnOcCompleteInstallation the current selection state is TRUE.\n" );
// The subcomponent is currently selected. Is this a fresh install ?
if ( ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) == (DWORDLONG) 0L ) && ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) == (DWORDLONG) 0L ) ) { ClRtlLogPrint( "In OnOcCompleteInstallation this is a GUI mode clean install.\n" );
// SETUPOP_STANDALONE flag clear means running under GUI mode setup.
// SETUPOP_NTUPGRADE flag clear means not performing an upgrade.
// Both flags clear means a fresh install. Do not check for a selection
// state transition.
dwReturnValue = CompleteInstallingClusteringService( (LPCTSTR) ptszSubComponentId );
ClRtlLogPrint( "CompleteInstallingClusteringService returned 0x%1!x! to OnOcCompleteInstallation.\n", dwReturnValue ); } else { // This is not a GUI mode fresh install. Is it an UPGRADE ?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcCompleteInstallation - This is an UPGRADE.\n" );
// Complete the process of upgrading Cluster service.
dwReturnValue = CompleteUpgradingClusteringService( (LPCTSTR) ptszSubComponentId );
ClRtlLogPrint( "CompleteUpgradingClusteringService returned 0x%1!x! to OnOnCompleteInstallation.\n", dwReturnValue ); } else { ClRtlLogPrint( "In OnOcCompleteinstallation - this is STANDALONE.\n" );
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcCompleteInstallation this is UNATTENDED STANDALONE and Cluster service is selected.\n" ); }
// This is not an upgrade. That means Add/Remove Programs must be
// running.
// Check for a selection state transition.
fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
if ( fCurrentSelectionState != fOriginalSelectionState ) { ClRtlLogPrint( "In OnOcCompleteInstallation a selection state transition has been detected.\n" );
dwReturnValue = CompleteStandAloneInstallationOfClusteringService( (LPCTSTR) ptszSubComponentId );
ClRtlLogPrint( "CompleteStandAloneInstallationOfClusteringService returned 0x%1!x! to OnOcCompleteInstallation.\n", dwReturnValue ); } else { ClRtlLogPrint( "In OnOcCompleteInstallation since no selection state transition was detected there is nothing to do.\n" );
// The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR; } // was there a selection state transition?
} // is this an UPGRADE?
} // is this a fresh install ?
} else { ClRtlLogPrint( "In OnOcCompleteInstallation the current selection state is FALSE.\n" );
// The subcomponent is not selected. Is OC Manager running stand-alone ?
// If not, i.e. if GUI mode setup is running, there is nothing to do.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcCompleteInstallation this is STANDALONE.\n" );
// SETUPOP_STANDALONE set implies GUI mode setup is not running. If
// there was a selection state change (to unselected) then delete
// registry entries.
fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
// Has there been a selection state transition ?
if ( fCurrentSelectionState != fOriginalSelectionState ) { ClRtlLogPrint( "In OnOcCompleteInstallation a selection state transition has been detected.\n" );
// Complete the process of uninstalling Cluster service.
dwReturnValue = CompleteUninstallingClusteringService( (LPCTSTR) ptszSubComponentId );
ClRtlLogPrint( "CompleteUninstallingClusteringService returned 0x%1!x! to OnOcCompleteInstallation.\n", dwReturnValue ); } else { // The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "In OnOcCompleteInstallation a selection state transition was not detected\n" ); ClRtlLogPrint( "so there is nothing to do.\n" ); } // Was there a selection state transition ?
} else { // GUI mode setup is running. Perform no action.
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "In OnOcCompleteInstallation GUI mode setup is running and the\n" ); ClRtlLogPrint( "component is not selected so there is nothing to do.\n" ); } // Is GUI mode setup running ?
} // Is the subcomponent currently selected ?
} else { dwReturnValue = ERROR_FILE_NOT_FOUND;
ClRtlLogPrint( "In OnOcCompleteInstallation the handle to the component INF file is bad.\n" ); } // Is the handle to the component INF file valid ?
ClRtlLogPrint( "OnOcCompleteInstallation is preparing to return 0x%1!x!.\n", dwReturnValue );
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcQueryChangeSelState
//
// Routine Description:
// This function processes the OC_QUERY_CHANGE_SEL_STATE "messages" from the
// Optional Components Manager.
//
// Arguments:
// pvComponentId - points to a string that uniquely identifies the component
// to be set up to OC Manager.
// pvSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// uxFunction - OC_QUERY_CHANGE_SEL_STATE - See ocmanage.h for the macro definitions.
// uxParam1 - proposed state of the subcomponent
// zero means unselected
// non-zero means selected
// pvParam2 - Flags
//
// Return Value:
// (DWORD) TRUE - the proposed selection state should be accepted
// (DWORD) FALSE - the proposed selection state should be rejected
//
// Note:
// As currently implemented on 2/25/1998, this function will disallow all
// changes to the selection state of Cluster service during an upgrade to a
// machine on which Cluster service has previously been installed.
//
// I have assumed that if the SETUPOP_NTUPGRADE flag is set that GUI mode
// setup is running because there is no way to perform and upgrade other
// than running NT setup.
//
// Cluster service and Terminal Services are mutually exclusive. By the
// time that this function gets called, OnOcInitComponent has already determined
// that Terminal Services is not installed. Therefore it is only necessary to
// determine whether Terminal Services is selected for installation.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcQueryChangeSelState( IN LPCTSTR ptszSubComponentId, IN UINT uxProposedSelectionState, IN DWORD dwFlags ) { DWORD dwReturnValue = (DWORD) TRUE;
eClusterInstallState eState;
// Is an upgrade in progress ?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOnQueryChangeSelState this is an UPGRADE. Selection state transitions are not allowed.\n" );
// Since this is an UPGRADE the selection state cannot be changed.
// Return FALSE to disallow selection state changes.
dwReturnValue = (DWORD) FALSE; } else { if ( dwReturnValue == (DWORD) TRUE ) { // Either Cluster service is being deselected, which implies that the
// selection state of Terminal Services is inconsequential, or Terminal
// Services is not selected for installation.
// Is GUI mode setup running?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) == (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcQueryChangeSelState this is NOT STANDALONE.\n" );
// SETUPOP_STANDALONE clear means GUI mode setup is running.
// In conjunction with SETUPOP_NTUPGRADE clear it means that
// a fresh NT installation is in progress.
// It is possible for the user to request that a fresh install target
// the directory for an existing NT installation. In that event, as per
// David P., if Cluster Server has been installed, the user should
// not be allowed to deselect the Cluster Server component.
// GetClusterInstallationState reports on the state of the registry value
// that records the state of the Cluster service installation on NT 5 machines.
// IsClusterServiceRegistered indicates whether the Cluster service is registered on
// BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for
// upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines.
ClRtlGetClusterInstallState( NULL, &eState ); if ( ( eState != eClusterInstallStateUnknown ) || ( IsClusterServiceRegistered() == (BOOL) TRUE ) ) { // Disallow deselection of the cluster component(s).
dwReturnValue = (DWORD) FALSE;
ClRtlLogPrint( "In OnOcQueryChangeSelState since Clustering Server has previously\n" ); ClRtlLogPrint( "been installed selection state transitions are not allowed.\n" ); } else { // Allow the selection state to be changed.
dwReturnValue = (DWORD) TRUE;
ClRtlLogPrint( "In OnOcQueryChangeSelState since Clustering Server has never\n" ); ClRtlLogPrint( "been installed selection state transitions are allowed.\n" ); } } // GUI mode setup running?
else { // This is a standalone operation. Allow the selection state to be changed.
dwReturnValue = (DWORD) TRUE;
ClRtlLogPrint( "In OnOcQueryChangeSelState this is STANDALONE so selection state transitions are allowed.\n" ); } // GUI mode setup running?
} }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// PerformRegistryOperations
//
// Routine Description:
// This function performs the registry operations, both the AddReg and DelReg
// in the section indicated by ptszSectionName are processed.
// registry entries.
//
// Arguments:
// hInfHandle - a handle to the component INF file.
// ptszSectionName - points to a string containing the name of a section in
// the INF file.
//
// Return Value:
// (DWORD) NO_ERROR - indicated success
// Any other retuen value is a Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::PerformRegistryOperations( HINF hInfHandle, LPCTSTR ptszSectionName ) { DWORD dwReturnValue;
// Install ... perform the registry operations.
dwReturnValue = SetupInstallFromInfSection( NULL, // hwndOwner
m_SetupInitComponent.ComponentInfHandle, // inf handle
ptszSectionName, // name of section
SPINST_REGISTRY, // operation flags
NULL, // relative key root
NULL, // source root path -
// irrelevant for registry operations
0, // copy flags
NULL, // callback routine
NULL, // callback routine context
NULL, // device info set
NULL ); // device info struct
// Were the registry operations performed successfully?
if ( dwReturnValue == (DWORD) TRUE ) { dwReturnValue = (DWORD) NO_ERROR; } else { dwReturnValue = GetLastError(); } // Were the registry operations performed successfully?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// UninstallRegistryOperations
//
// Routine Description:
// This function queues the registry operations that delete cluster related
// registry entries on UNINSTALL.
//
// Arguments:
// hInfHandle - a handle to the component INF file.
// ptszSubComponentId - points to a string containing the name of the subcomponent.
//
//
// Return Value:
// (DWORD) NO_ERROR - indicated success
// Any other retuen value is a Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::UninstallRegistryOperations( IN HINF hInfHandle, IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue;
BOOL fReturnValue;
INFCONTEXT InfContext;
// There is an entry called "Uninstall" in the [Cluster] section of
// cluster.inf. That entry provides the name of an "install" section
// that should be substituted for the [Cluster] section when uninstalling.
// The following function locates that line so it can be read.
CString csSectionName;
csSectionName = UNINSTALL_INF_KEY;
fReturnValue = SetupFindFirstLine( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, csSectionName, &InfContext );
if ( fReturnValue == (BOOL) TRUE ) { // Read the name of the replacement section.
TCHAR tszReplacementSection[256]; // Receives the section name
fReturnValue = SetupGetStringField( &InfContext, 1, // there should be a single field
tszReplacementSection, sizeof( tszReplacementSection ) / sizeof( TCHAR ), NULL );
if ( fReturnValue == (BOOL) TRUE ) { // Remove the registry keys.
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, tszReplacementSection );
ClRtlLogPrint( "PerformRegistryOperations returned 0x%1!x! to UninstallRegistryOperations.\n", dwReturnValue ); } else { ClRtlLogPrint( "UninstallDelRegistryOperations could not read the INF file.\n" );
dwReturnValue = (DWORD) ERROR_FILE_NOT_FOUND; } } else { ClRtlLogPrint( "UninstallDelRegistryOperations could not read the INF file.\n" );
dwReturnValue = (DWORD) ERROR_FILE_NOT_FOUND; }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// QueueInstallFileOperations
//
// Routine Description:
// This function queues the file operations that install cluster related files.
//
// Arguments:
// hInfHandle - a handle to the component INF file.
// ptszSubComponentId - points to a string containing the name of the subcomponent.
//
//
// Return Value:
// (DWORD) NO_ERROR - indicated success
// Any other retuen value is a Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::QueueInstallFileOperations( IN HINF hInfHandle, IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue; CString csSectionName;
// Dummy do-while loop to avoid gotos.
do { // As per Pat Styles on 7/16/98 pass NULL in the SourcePath parameter.
dwReturnValue = SetupInstallFilesFromInfSection( hInfHandle, // handle to the INF file
NULL, // optional, layout INF handle
hSetupFileQueue, // handle to the file queue
ptszSubComponentId, // name of the Install section
NULL, // optional, root path to source files
SP_COPY_NEWER // optional, specifies copy behavior
);
ClRtlLogPrint( "The first call to SetupInstallFilesFromInfSection returned 0x%1!x! to QueueInstallFileOperations.\n", dwReturnValue );
// Was the operation successful ?
if ( dwReturnValue == (DWORD) FALSE ) { dwReturnValue = GetLastError(); break; }
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) == (DWORDLONG) 0L ) { dwReturnValue = (DWORD) NO_ERROR; // This is not an UPGRADE.
break; }
// If this is an UPGRADE (from NT 4.0) then queue the file operations
// specified in the [Upgrade] section.
ClRtlLogPrint( "In QueueInstallFileOperations this is an UPGRADE.\n" );
// Copy the replace-only files. Some files like IISCLUS3.DLL need to be copied
// on an upgrade only if they already existed before the upgrade.
// This is what I learnt from Brent (a-brentt) on 7/15/1999.
csSectionName = UPGRADE_REPLACEONLY_INF_KEY;
// As per Pat Styles on 7/16/98 pass NULL in the SourcePath parameter.
dwReturnValue = SetupInstallFilesFromInfSection( hInfHandle, // handle to the INF file
NULL, // optional, layout INF handle
hSetupFileQueue, // handle to the file queue
csSectionName, // name of the Install section
NULL, // optional, root path to source files
SP_COPY_REPLACEONLY // optional, specifies copy behavior
);
ClRtlLogPrint( "The first call to SetupInstallFilesFromInfSection returned 0x%1!x! to QueueInstallFileOperations.\n", dwReturnValue );
if ( dwReturnValue == (DWORD) FALSE ) { dwReturnValue = GetLastError(); break; }
// The replace only copy was successful. Now do the normal CopyFiles and DelFiles
// subsections.
csSectionName = UPGRADE_INF_KEY;
// This function processes the CopyFiles subsection under the UPGRADE_INF_KEY section.
dwReturnValue = SetupInstallFilesFromInfSection( hInfHandle, // handle to the INF file
NULL, // optional, layout INF handle
hSetupFileQueue, // handle to the file queue
csSectionName, // name of the Install section
NULL, // optional, root path to source files
SP_COPY_NEWER // optional, specifies copy behavior
);
ClRtlLogPrint( "The second call to SetupInstallFilesFromInfSection returned 0x%1!x! to QueueInstallFileOperations.\n", dwReturnValue );
if ( dwReturnValue == (DWORD) FALSE ) { dwReturnValue = GetLastError(); break; }
// This function processes the DelFiles subsection under the UPGRADE_INF_KEY section.
dwReturnValue = SetupQueueDeleteSection( hSetupFileQueue, // handle to the file queue
hInfHandle, // handle to the INF file containing the [DestinationDirs] section
hInfHandle, // handle to the INF file
csSectionName // INF section that lists the files to delete
);
ClRtlLogPrint( "The call to SetupQueueDeleteSection returned 0x%1!x! to QueueInstallFileOperations.\n", dwReturnValue );
if ( dwReturnValue == (DWORD) FALSE ) { dwReturnValue = GetLastError(); break; }
dwReturnValue = (DWORD) NO_ERROR; } while ( FALSE ); // dummy do-while loop to avoid gotos.
if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "QueueInstallFileOperations compeleted successfully.\n" ); } else { ClRtlLogPrint( "Error in QueueInstallFileOperations.\n" ); }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// QueueRemoveFileOperations
//
// Routine Description:
// This function queues the file operations that delete cluster related files.
//
// Arguments:
// hInfHandle - a handle to the component INF file.
// ptszSubComponentId - points to a string containing the name of the subcomponent.
//
//
// Return Value:
// (DWORD) NO_ERROR - indicated success
// Any other return value is a Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::QueueRemoveFileOperations( IN HINF hInfHandle, IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue;
BOOL fReturnValue;
INFCONTEXT InfContext;
// There is an entry called "Uninstall" in the [Cluster] section of
// cluster.inf. That entry provides the name of an "install" section
// that should be substituted for the [Cluster] section when uninstalling.
// The following function locates that line so it can be read.
CString csSectionName;
csSectionName = UNINSTALL_INF_KEY;
fReturnValue = SetupFindFirstLine( hInfHandle, ptszSubComponentId, csSectionName, &InfContext );
if ( fReturnValue == (BOOL) TRUE ) { // Read the name of the replacement section.
TCHAR tszReplacementSection[256]; // Receives the section name
fReturnValue = SetupGetStringField( &InfContext, 1, // there should be a single field
tszReplacementSection, sizeof( tszReplacementSection ) / sizeof( TCHAR ), NULL );
if ( fReturnValue == (BOOL) TRUE ) { // Remove the files.
dwReturnValue = SetupInstallFilesFromInfSection( hInfHandle, (HINF) NULL, // No layout file
hSetupFileQueue, (LPCTSTR) tszReplacementSection, (LPCTSTR) NULL, // SourceRootPath is irrelevant
(UINT) 0 );
ClRtlLogPrint( "SetupInstallFilesFromInfSection returned 0x%1!x! to QueueRemoveFilesOperations.\n", dwReturnValue );
// Was the operation successfull ?
if ( dwReturnValue == (DWORD) TRUE ) { dwReturnValue = (DWORD) NO_ERROR; } else { dwReturnValue = GetLastError(); } } else { dwReturnValue = (DWORD) ERROR_FILE_NOT_FOUND;
ClRtlLogPrint( "QueueRemoveFileOperations was unable to read the [Uninstall] section in the INF file.\n" ); } } else { dwReturnValue = (DWORD) ERROR_FILE_NOT_FOUND;
ClRtlLogPrint( "QueueRemoveFileOperations was unable to locate the [Uninstall] section in the INF file.\n" ); }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcAboutToCommitQueue
//
// Routine Description:
// If clusocm.dll is performing an uninstall, this function unregisters the
// cluster services.
//
// if clusocm.dll is performing an install or an upgrade this function does
// nothing.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcAboutToCommitQueue( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue;
if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { // The INF file handle is valid.
BOOL fOriginalSelectionState; BOOL fCurrentSelectionState;
// Is the subcomponent currently selected ?
fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT );
if ( fCurrentSelectionState != (BOOL) TRUE ) { // The subcomponent is not selected. Is OC Manager running stand-alone ?
// If not, i.e. if GUI mode setup is running, there is nothing to do.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcAboutToCommitQueue this is STANDALONE and the component is not selected.\n" ); ClRtlLogPrint( "So, this is an uninstall operation.\n" );
// SETUPOP_STANDALONE set implies GUI mode setup is not running. If
// there was a selection state change (to unselected) then delete
// registry entries.
fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
// Has there been a selection state transition ?
if ( fCurrentSelectionState != fOriginalSelectionState ) { CString csTemp;
ClRtlLogPrint( "In OnOcAboutToCommitQueue a selection state transition has been detected.\n" );
// At this point in cluscfg.exe file utils.cpp function DoUninstall
// called function IsOtherSoftwareInstalled. That function apparently
// checked for the presence of custom cluster resources and warned the
// user to handle them before proceeding. (at least that is what David
// told me). So, that logic needs to be replicated here.
// NOTE: According to Andrew Ritz (AndrewR), there is no way to abort the
// installation at this point. So the user is just shown the list of
// custom resource types and is prompted to remove them after the cluster
// service has been uninstalled. The user is not given an option to abort
// the installation.
// (Vvasu 14-DEC-1999)
if ( ( m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH ) == (DWORDLONG) 0L ) { // If this is not an unattended operation.
BOOL fReturnValue = CheckForCustomResourceTypes();
ClRtlLogPrint( "CheckForCustomResourceTypes returned 0x%1!x! to OnOcAboutToCommitQueue.\n", fReturnValue ); } else { ClRtlLogPrint( "CheckForCustomResourceTypes not called in OnOcAboutToCommitQueue as this is an unattended operation.\n" ); }
// Stop ClusSvc. Note that if ClusDisk is someday revised so that
// it will unload, it will be appropriate to stop ClusDisk here
// as well.
csTemp = CLUSTER_SERVICE_NAME;
// I'm going to assume that, since UNICODE is defined, the casts
// in the calls to function StopService are OK, even though there
// is probably a better approach.
StopService( (LPCWSTR) (LPCTSTR) csTemp );
csTemp = TIME_SERVICE_NAME;
StopService( (LPCWSTR) (LPCTSTR) csTemp );
ClRtlLogPrint( "OnOcAboutToCommitQueue has attempted to stop ClusSvc and TimeServ.\n" );
//
// Unregister the COM objects that may previously have been registered.
//
// Note that since msclus.dll is registered as part of NT setup
// it is not unregistered here.
//
csTemp = CLUSTER_DIRECTORY;
TCHAR tszPathToClusterDir[_MAX_PATH];
if ( ExpandEnvironmentStrings( (LPCTSTR) csTemp, tszPathToClusterDir, _MAX_PATH ) > 0L ) { BOOL bUnregisterResult = UnRegisterClusterCOMObjects( m_hInstance, tszPathToClusterDir );
ClRtlLogPrint( "OnOcAboutToCommitQueue has unregistered the COM objects. The return value is %1!d!\n", bUnregisterResult ); } else { // Couldn't expand the environment string.
ClRtlLogPrint( "OnOcAboutToCommitQueue could not unregister the COM objects\n" ); ClRtlLogPrint( "because it could not locate the cluster directory.\n" ); }
// Unload the Cluster hive so that the Cluster hive file can be removed.
UnloadClusDB();
ClRtlLogPrint( "OnOcAbouToCommitQueue has unloaded the Cluster hive.\n" );
dwReturnValue = (DWORD) NO_ERROR; } else { // The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint("In OnOcAboutToCommitQueue no selection state transition was detected.\n" ); } // Was there a selection state transition ?
} else { // OC Manager is NOT running stand-alone. This CANNOT be an uninstall
// operation. There is nothing for this function to do.
dwReturnValue = (DWORD) NO_ERROR; } // Is GUI mode setup running ?
} // Is the subcomponent cuttently selected ?
else { // Since the component is selected this CANNOT be an uninstall operation.
// There is nothing for this function to do.
dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "In OnOcAboutToCommitQueue the component is selected so there is nothing to do.\n" ); } } else { dwReturnValue = ERROR_FILE_NOT_FOUND;
ClRtlLogPrint( "In OnOcAboutToCommitQueue the handle to the INF file is bad.\n" ); } // Is the handle to the component INF file valid ?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// LocateClusterHiveFile
//
// Routine Description:
// This function attempts to locate the Cluster hive file and supply the
// path to the calling function.
//
// Arguments:
// rcsClusterHiveFilePath - a reference to a the CString to receive the
// path to the Cluster hive file.
//
// Return Value:
// TRUE - incicates that the Cluster hive file was located and that
// rcsClusterHiveFilePath is meaningfull.
// FALSE - indicates that the Cluster hive file was not located and
// rcsClusterHiveFilePath is empty.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::LocateClusterHiveFile( CString & rcsClusterHiveFilePath ) { BOOL fReturnValue;
// The path to the Cluster hive file may be deduced by reading the
// [DestinationDirs] section of the INF file. The ClusterFiles entry
// will specify the file's location.
if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { // The handle to the INF file is valid.
TCHAR tszPathToClusterDirectory[MAX_PATH];
// First, get the path to the cluster directory.
DWORD dwRequiredSize;
CString csSectionName;
csSectionName = CLUSTER_FILES_INF_KEY;
fReturnValue = SetupGetTargetPath( m_SetupInitComponent.ComponentInfHandle, (PINFCONTEXT) NULL, (PCTSTR) csSectionName, tszPathToClusterDirectory, (DWORD) MAX_PATH, (PDWORD) &dwRequiredSize );
if ( fReturnValue == (BOOL) TRUE ) { rcsClusterHiveFilePath = (CString) tszPathToClusterDirectory;
// Append the name of the Cluster hive file to the path.
CString csClusterDatabaseName;
csClusterDatabaseName = CLUSTER_DATABASE_NAME;
rcsClusterHiveFilePath += (CString) _T("\\") + csClusterDatabaseName;
ClRtlLogPrint( "LocateClusterHiveFile has deduced that the cluster hive is in %1!s!.\n", rcsClusterHiveFilePath );
// The path to the Cluster hive file has been built. Now, make
// sure that the file is present.
HANDLE hSearchHandle;
WIN32_FIND_DATA FindData;
hSearchHandle = FindFirstFile( rcsClusterHiveFilePath, &FindData);
if( hSearchHandle != (HANDLE) INVALID_HANDLE_VALUE ) { FindClose( hSearchHandle );
// A file with the correct name was located. Is it the cluster hive
// file? If it is a directory it is not.
if( (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != (DWORD) 0L ) { // The file is a directory. That means that the Cluster
// hive file could not be located.
ClRtlLogPrint( "LocateClusterHiveFile could not find file %1!s!.\n", rcsClusterHiveFilePath );
rcsClusterHiveFilePath.Empty();
fReturnValue = (BOOL) FALSE; } else { // The Cluster hive file was found.
ClRtlLogPrint( "LocateClusterHiveFile found the Cluster hive at %1!s!.\n", rcsClusterHiveFilePath );
fReturnValue = (BOOL) TRUE; } } else { // The cluster hive file was not located.
ClRtlLogPrint( "LocateClusterHiveFile could not find file %1!s!.\n", rcsClusterHiveFilePath );
rcsClusterHiveFilePath.Empty();
fReturnValue = (BOOL) FALSE; } } else { rcsClusterHiveFilePath.Empty();
ClRtlLogPrint( "SetupGetTargetPath failed in LocateClusterHiveFile.\n" ); } } else { // The handle to the INF file is not valid. That means that the Cluster
// hive file could not be located.
ClRtlLogPrint( "LocateClusterHiveFile could not locate the cluster hive because the\n" ); ClRtlLogPrint( "handle to the INF file is bad.\n" );
rcsClusterHiveFilePath.Empty();
fReturnValue = (BOOL) FALSE; }
return ( fReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CheckForCustomResourceTypes
//
// Routine Description:
// This function examines the ResourceTypes subkey of the Cluster key to
// determine whether custom resource types have been installed.
//
// Arguments:
// None
//
//
// Return Value:
// TRUE - indicates that either no custom resource types have been detected
// or the user chooses to continue uninstalling Cluster Server without
// first uninstalling the software packages associated with the custom
// resource types.
// FALSE - indicates that custom resource types were detected and the user
// wishes to terminate uninstallation of Cluster Server.
//
// Note:
// This function was originally called IsOtherSoftwareInstalled in the old
// cluster\newsetup project in the file called utils.cpp.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::CheckForCustomResourceTypes( void ) { BOOL fReturnValue = (BOOL) TRUE; BOOL fEnumerateResourceTypesKeys = (BOOL) TRUE; BOOL fClusterHiveLoadedByThisFunction = (BOOL) FALSE;
HKEY hClusterKey;
LONG lReturnValue;
// Attempt to open the Cluster Registry key.
CString csClusterRegKey;
csClusterRegKey = CLUSREG_KEYNAME_CLUSTER;
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterRegKey, 0, KEY_READ, &hClusterKey);
if ( lReturnValue != ERROR_SUCCESS ) { ClRtlLogPrint( "In CheckForCustomResourceTypes the first attempt to open the Cluster key failed.\n" );
// The Cluster hive is not currently loaded. This condition means that
// the cluster service has not been started. Attempt to load the Cluster
// hive so that it can be read.
// First, locate the Cluster hive file. It should be in the location
// specified for the ClusterFiles entry in the [DestinationDirs] section
// of clusocm.inf.
CString csClusterHiveFilePath;
fReturnValue = LocateClusterHiveFile( (CString &) csClusterHiveFilePath );
ClRtlLogPrint( "LocateClusterHiveFile returned 0x%1!x! to CheckForCustomResourceTypes.\n", fReturnValue );
// Was the Cluster hive file located?
if ( fReturnValue == (BOOL) TRUE ) { // The Cluster hive file was located. Custom resource types may exist.
// Attempt to load the cluster hive.
BOOLEAN OriginalState;
// I'm not sure what the following function does, but the prototype is
// in sdk\inc\ntrtl.h. Look in stdafx.h for the inclusion of ntrtl.h. I
// replicated the logic that was used in newsetup.h to make it work.
// RtlAdjustPrivilege returns NTSTATUS.
NTSTATUS Status;
Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OriginalState );
if ( NT_SUCCESS( Status ) ) { // Attempt to Load the Cluster Hive.
lReturnValue = RegLoadKey( HKEY_LOCAL_MACHINE, csClusterRegKey, csClusterHiveFilePath );
if ( lReturnValue == ERROR_SUCCESS ) { fClusterHiveLoadedByThisFunction = (BOOL) TRUE;
// Now that the Cluster hive has been loaded, attempt to open the
// Cluster registry key.
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterRegKey, 0, KEY_READ, &hClusterKey );
// lReturnValue will be tested by the next BLOCK of code.
} // cluster hive loaded?
else { // The Cluster hive could not be loaded. Treat that as if there
// are no custom resource types.
// A return value of TRUE will allow the uninstall operation to
// continue. Since lReturnValue is NOT ERROR_SUCCESS no additional
// processing will be performed by this function.
fReturnValue = (BOOL) TRUE; } // Was the cluster hive loaded successfully?
// Undo whatever the preceding call to RtlAdjustPrivilege() did.
RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, OriginalState, FALSE, &OriginalState ); } // Initial call to RtlAdjustPrivilege succeeded?
else { // The initial call to RtlAdjustPrivilege FAILED.
ClRtlLogPrint( "In CheckForCustomResources the initial call to RtlAdjustPrivileges failed with status %1!d!.\n", Status );
// A return value of TRUE will allow the uninstall operation to
// continue. Since lReturnValue is NOT ERROR_SUCCESS no additional
// processing will be performed by this function.
fReturnValue = (BOOL) TRUE; } // Did RtlAdjustPrivilege succeed?
} // cluster hive file located?
else { // The Cluster hive file was not located. That implies that there can be
// no custom resources. Since lReturnValue is NOT ERROR_SUCCESS no
// additional processing will be done by this function. A return value
// of TRUE will allow the uninstall operation to continue.
ClRtlLogPrint( "Since the Cluster hive file could not be located CheckForCustomResources will\n" ); ClRtlLogPrint( "report that the uninstall operation may continue.\n" );
fReturnValue = (BOOL) TRUE; } // Was the cluster hive file located?
} // Was the Cluster registry key opened successfully?
// The preceeding code segment may have loaded the cluster hive and
// attempted to open the cluster key. Was it sucessful?
if ( lReturnValue == ERROR_SUCCESS ) { // At this point, the Cluster registry key was opened successfully. Now, attempt
// to open the Resource Type subkey.
HKEY hResourceTypesKey;
CString csResourceTypesRegKey;
csResourceTypesRegKey = CLUSREG_KEYNAME_RESOURCE_TYPES;
lReturnValue = RegOpenKeyEx( hClusterKey, csResourceTypesRegKey, 0, KEY_READ, &hResourceTypesKey );
if ( lReturnValue == ERROR_SUCCESS ) { // The ResourceTypes sub key is open.
// Enumerate the subkeys of the REG_RESTYPES.
FILETIME t_LastWriteTime; WCHAR wszSubKeyName[256]; DWORD dwCharCount; DWORD dwIndex = 0; // index of the first sub-key to enumerate
dwCharCount = sizeof( wszSubKeyName ) / sizeof( wszSubKeyName[0] ); lReturnValue = RegEnumKeyEx( hResourceTypesKey, dwIndex, wszSubKeyName, &dwCharCount, NULL, NULL,// Class of the Key. Not Reqd.
NULL,// Size of the above param.
&t_LastWriteTime );
// RegEnumKeyEx returns ERROR_NO_MORE_ITEMS when there are
// no additional sub-keys to enumerate.
if ( lReturnValue == ERROR_SUCCESS ) { // The initial call to RegEnumKeyEx succeeded. Determine whether
// the sub-key is associated with a non-standard resource type.
// Function TryToRecognizeResourceType builds a list of non-standard resource types
// in the CString variable csNonStandardResourceTypeList.
CString csNonStandardResourceTypeList;
TryToRecognizeResourceType( csNonStandardResourceTypeList, wszSubKeyName );
// The following loop checks the remaining sub-keys in the
// Resource Types registry key.
while ( lReturnValue == ERROR_SUCCESS ) { dwIndex++;
dwCharCount = sizeof( wszSubKeyName ) / sizeof( wszSubKeyName[0] ); lReturnValue = RegEnumKeyEx( hResourceTypesKey, dwIndex, wszSubKeyName, &dwCharCount, NULL, NULL,// Class of the Key. Not Reqd.
NULL,// Size of the above param.
&t_LastWriteTime );
TryToRecognizeResourceType( csNonStandardResourceTypeList, wszSubKeyName ); }
// Were all subkeys of the ResourceTypes registry key "verified"?
if( lReturnValue == ERROR_NO_MORE_ITEMS ) { CString csMessage;
fReturnValue = (BOOL) TRUE;
RegCloseKey( hResourceTypesKey ); RegCloseKey( hClusterKey );
// Were there any non-standard subkeys in the Resource Types registry key?
if ( csNonStandardResourceTypeList.IsEmpty() == (BOOL) FALSE ) { // Non-standard subkeys were found. Ask the user about removal.
// Build and present a message of the form:
//
// The following software packages should be removed before removing
// the Microsoft Cluster Server software.
//
// <list of packages>
//
// Do you want to continue with uninstall?
CString csLastPartOfMessage; CString csMessageBoxTitle;
csLastPartOfMessage.LoadString(IDS_ERR_UNINSTALL_OTHER_SW_EXT); csNonStandardResourceTypeList += csLastPartOfMessage;
// BUGBUG - The next two statements are commented out temporarily because the localization
// changes needed for this have not been approved. Uncomment after NT 5.0.
// Also restore clusocm.rc@v2 and resource.h@v2
// (Vvasu 05-Jan-2000
// csMessageBoxTitle.LoadString(IDS_TITLE_CUSTOM_RESTYPES);
//::MessageBox( NULL,
// csNonStandardResourceTypeList,
// csMessageBoxTitle,
// MB_OK | MB_ICONINFORMATION
// );
ClRtlLogPrint( "CheckForClusterResourceTypes detected custom resources.\n" ); } // Were non-standard resource typed detected?
else { ClRtlLogPrint( "CheckForCustomResourceTypes did not detect any custom resource types,\n" ); } } // Was there an error enumerating the ResourceTypes key?
else { ClRtlLogPrint( "CheckForCustomResourceTypes was unable to enumerate the resource types,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); ClRtlLogPrint( "The error code is %1!d!.\n", lReturnValue );
// An error occured while enumerating the ResourceTypes sub-keys.
RegCloseKey( hResourceTypesKey ); RegCloseKey( hClusterKey );
// The uninstall operation should continue because the installation
// is apparently defective.
fReturnValue = (BOOL) TRUE; } // Was there an error enumerating the ResourceTypes key?
} else { // The initial call to RegEnumKeyEx failed.
// Issue an error message and exit.
RegCloseKey( hClusterKey );
ClRtlLogPrint( "CheckForCustomResourceTypes was unable to enumerate the resource types,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); ClRtlLogPrint( "The error code is %1!d!.\n", lReturnValue );
// A return value of TRUE will allow the uninstall operation to
// continue.
fReturnValue = (BOOL) TRUE;
} // Did RegEnumKeyEx open the ResourceTypes key?
} // Was the ResourceTypes sub key opened?
else { // The ResourceTypes sub key could not be opened.
// Issue an error message and exit.
RegCloseKey( hClusterKey );
ClRtlLogPrint( "CheckForCustomResourceTypes was unable to open the Cluster\\ResourceTypes key,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" );
// A return value of TRUE will allow the uninstall operation to
// continue.
fReturnValue = (BOOL) TRUE; } // Was the ResourceTypes sub key opened?
} // cluster hive opened?
else { // The Cluster registry key could not be opened, even after possibly
// attempting to load the cluster hive.
ClRtlLogPrint( "CheckForCustomResourceTypes was unable to open the Cluster key,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); ClRtlLogPrint( "The error code is %1!d!.\n", lReturnValue );
// A return value of TRUE will allow the uninstall operation to
// continue.
fReturnValue = (BOOL) TRUE; } // Second test whether the Cluster registry key was opened successfully.
if ( fClusterHiveLoadedByThisFunction == (BOOL) TRUE ) { UnloadClusDB(); }
return ( fReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// UnloadClusDB
//
// Routine Description:
// This function unloads the Cluster hive.
//
// Arguments:
// None
//
// Return Value:
// None
//
// Note:
// This function was originally in newsetup\utils.cpp.
//
//--
/////////////////////////////////////////////////////////////////////////////
VOID CClusocmApp::UnloadClusDB( VOID ) { DWORD Status; BOOLEAN WasEnabled;
Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled);
if ( Status == ERROR_SUCCESS ) { LONG lReturnValue;
CString csClusterRegKey;
csClusterRegKey = CLUSREG_KEYNAME_CLUSTER;
lReturnValue = RegUnLoadKeyW(HKEY_LOCAL_MACHINE, csClusterRegKey );
RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled); } }
PWCHAR WellKnownResourceTypes[] = { CLUS_RESTYPE_NAME_GENAPP, CLUS_RESTYPE_NAME_GENSVC, CLUS_RESTYPE_NAME_FTSET, CLUS_RESTYPE_NAME_PHYS_DISK, CLUS_RESTYPE_NAME_IPADDR, CLUS_RESTYPE_NAME_NETNAME, CLUS_RESTYPE_NAME_FILESHR, CLUS_RESTYPE_NAME_PRTSPLR, CLUS_RESTYPE_NAME_TIMESVC, CLUS_RESTYPE_NAME_LKQUORUM, CLUS_RESTYPE_NAME_DHCP, CLUS_RESTYPE_NAME_MSMQ, CLUS_RESTYPE_NAME_NEW_MSMQ, CLUS_RESTYPE_NAME_MSDTC, CLUS_RESTYPE_NAME_WINS, CLUS_RESTYPE_NAME_IIS4, CLUS_RESTYPE_NAME_SMTP, CLUS_RESTYPE_NAME_NNTP, (PWCHAR) UNICODE_NULL };
/////////////////////////////////////////////////////////////////////////////
//++
//
// TryToRecognizeResourceType
//
// Routine Description:
// This function determines whether the resource type whose name is in
// parameter "keyname" is recognized as a standard resource type as defined
// in clusudef.h.
//
// This function builds a list of unrecognized reaource types in the CString
// referenced by parameter "str".
//
// Arguments:
// str - a reference to a CString in which to build a list of unrecognized
// resource types.
// keyname - points to a string that contains the resource type name.
//
// Return Value:
// None
//
// Note:
// This function was excerpted verbatim from newsetup\utils.cpp.
//
//--
/////////////////////////////////////////////////////////////////////////////
VOID CClusocmApp::TryToRecognizeResourceType( CString& str, LPTSTR keyname ) { PWCHAR * ppName = WellKnownResourceTypes;
while ( *ppName != (PWCHAR)UNICODE_NULL ) { if ( lstrcmp( keyname, *ppName) == 0 ) return; ++ppName; }
if ( !str.IsEmpty() ) str += L", "; else { str.LoadString(IDS_ERR_UNINSTALL_OTHER_SW);
str += _T('\n'); } str += keyname; }
/////////////////////////////////////////////////////////////////////////////
//++
//
// GetServiceBinaryPath
//
// Routine Description:
// This function retrieves the fully qualified path to a Service
// from the Service Control Manager.
//
// Arguments:
// lpwszServiceName - points to a wide character string containing the service name
// lptszBinaryPathName - points to a string to receive the fully qualified
// path to the Cluster Service.
//
// Return Value:
// TRUE - The path to the Cluster Service was obtained successfully.
// FALSE - The path to the Cluster Service was not obtained.
//
// Note:
// Calling this function makes sense IFF the Cluster Service is registered
// with the Service Control Manager. Call IsClusterServiceRegistered() to
// ascertain that BEFORE calling GetServiceBinaryPath.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::GetServiceBinaryPath( IN LPWSTR lpwszServiceName, OUT LPTSTR lptszBinaryPathName ) { BOOL fReturnValue;
DWORD dwErrorCode = (DWORD) ERROR_SUCCESS;
if ( lpwszServiceName != (LPWSTR) NULL ) { SC_HANDLE hscServiceMgr; SC_HANDLE hscService;
// Connect to the Service Control Manager and open the specified service
// control manager database.
hscServiceMgr = OpenSCManager( NULL, NULL, GENERIC_READ | GENERIC_WRITE );
// Was the service control manager database opened successfully?
if ( hscServiceMgr != NULL ) { // The service control manager database is open.
// Open a handle to the Service.
hscService = OpenService( hscServiceMgr, lpwszServiceName, GENERIC_READ );
// Was the handle to the service opened?
if ( hscService != NULL ) { // A valid handle to the Service was obtained.
DWORD dwBufferSize;
// Note that the size of the buffer required to get the service configuration
// information was determined empherically to be 240 bytes.
LPQUERY_SERVICE_CONFIG lpServiceConfig;
dwBufferSize = (DWORD) sizeof( QUERY_SERVICE_CONFIG ) + 256; // The delta from the
// size of the structure
// was chosen arbitrarily.
// Attempt to allocate the buffer.
lpServiceConfig = (LPQUERY_SERVICE_CONFIG) LocalAlloc( LMEM_ZEROINIT, dwBufferSize );
// Was the bufer allocated successfully?
if ( lpServiceConfig != (LPQUERY_SERVICE_CONFIG) NULL ) { // The following call to QueryServiceConfig should return something
// usefull. If it fails it is probably because the guess at the size
// of the buffer is too small.
fReturnValue = QueryServiceConfig( hscService, lpServiceConfig, dwBufferSize, (LPDWORD) &dwBufferSize );
// Was the Service configuration info obtained?
if ( fReturnValue == (BOOL) TRUE ) { // Update the output parameter.
_tcscpy( lptszBinaryPathName, lpServiceConfig->lpBinaryPathName );
// lptszBinaryPathName includes the service name. Strip that off.
LPTSTR ptszTemp;
ptszTemp = _tcsrchr( (const wchar_t *) lptszBinaryPathName, (int) _T('\\') );
*ptszTemp = _T('\0');
ClRtlLogPrint( "In GetServiceBinaryPath the first call to QueryServiceConfig succeeded.\n" ); } else { // Was the buffer too small?
dwErrorCode = GetLastError();
if ( dwErrorCode == (DWORD) ERROR_INSUFFICIENT_BUFFER ) { ClRtlLogPrint( "GetServiceBinaryPath is enlarging the buffer for the QUERY_SERVICE_CONFIG structure.\n" );
// Increase the size of the buffer.
HLOCAL hLocalBlock;
// As per RodGa LocalReAlloc is not always reliable. Here that functionality
// is implemented by freeing the buffer for the QUERY_SERVICE_CONFIG struct
// and allocating a nre block using LocalFree and LocalAlloc.
hLocalBlock = LocalFree( lpServiceConfig );
if ( hLocalBlock == (HLOCAL) NULL ) { // The previously allocated block has been released. Now,
// allocate a block of the proper size.
lpServiceConfig = (LPQUERY_SERVICE_CONFIG) LocalAlloc( LMEM_ZEROINIT, (UINT) dwBufferSize );
// Was the larger buffer allocated successfully?
if ( lpServiceConfig != (LPQUERY_SERVICE_CONFIG) NULL ) { // The following call to QueryServiceConfig should return something
// usefull. If it fails it is not because the buffer is too small.
fReturnValue = QueryServiceConfig( hscService, lpServiceConfig, dwBufferSize, (LPDWORD) &dwBufferSize );
// Was the Service configuration info obtained this time.
if ( fReturnValue == (BOOL) TRUE ) { // Update the output parameter.
_tcscpy( lptszBinaryPathName, lpServiceConfig->lpBinaryPathName );
// lptszBinaryPathName includes the service name. Strip that off.
LPTSTR ptszTemp;
ptszTemp = _tcsrchr( (const wchar_t *) lptszBinaryPathName, (int) _T('\\') );
*ptszTemp = _T('\0');
ClRtlLogPrint( "In GetServiceBinaryPath the second call to QueryServiceConfig succeeded.\n" ); } else { // The Service configuration info was not obtained.
dwErrorCode = GetLastError();
ClRtlLogPrint( "In GetServiceBinaryPath the second call to QueryServiceConfig failed\n" ); ClRtlLogPrint( "with error code 0x%1!x!,\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } // second call to QueryServiceConfig succeeded?
} else { // The attempt to enlarge the buffer failed.
dwErrorCode = GetLastError();
ClRtlLogPrint( "GetServiceBinaryPath was unable to enlarge the buffer for the QUERY_SERVICE_CONFIG structure.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } } else { dwErrorCode = GetLastError();
ClRtlLogPrint( "In GetServiceBinaryPath the call to LocalFree failed with error code 0x%1!x!.\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } } else { // Some other error occured.
ClRtlLogPrint( "In GetServiceBinaryPath the first call to QueryServiceConfig failed\n" ); ClRtlLogPrint( "with error code 0x%1!x!.\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } // Was the buffer to small?
} // Service config info obtained from first call to QueryServiceConfig?
// Free the buffer if it was ever allocated successfully.
if ( lpServiceConfig != (LPQUERY_SERVICE_CONFIG) NULL ) { LocalFree( lpServiceConfig ); } } else { // Could not allocate the buffer.
dwErrorCode = GetLastError();
ClRtlLogPrint( "GetServiceBinaryPath could not allocate a buffer for the QUERY_SERVICE_CONFIG structure.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } // Was the buffer allocated successfully?
// Close the handle to the Cluster Service.
CloseServiceHandle( hscService ); } else { // The Service could not be opened.
dwErrorCode = GetLastError();
ClRtlLogPrint( "GetServiceBinaryPath could not open an handle to %1!ws!.\n", lpwszServiceName ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } // Was the Cluster Service opened?
// Close the handle to the Service Control Manager.
CloseServiceHandle( hscServiceMgr ); } else { // The Service Control Manager could not be opened.
dwErrorCode = GetLastError();
ClRtlLogPrint( "GetServiceBinaryPath could not open the Service Control Manager.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode );
fReturnValue = (BOOL) FALSE; } // Was the Service Control Manager opened?
} else { // The service name pointer was bogus.
ClRtlLogPrint( "The service name passed to GetServiceBinaryPath is invalid.\n" );
fReturnValue = FALSE; } // Is the service name legal?
if ( fReturnValue == (BOOL) FALSE ) { // Set the binary path invalid.
*lptszBinaryPathName = _T('\0'); } else { ClRtlLogPrint( "GetServiceBinaryPath located %1!ws! at %2!s!.\n", lpwszServiceName, lptszBinaryPathName ); }
SetLastError( dwErrorCode ); // Set the "last" error code (which may be ERROR_SUCCESS)
// because this function's caller is likely to call GetLastError().
return ( fReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// SetDirectoryIds
//
// Routine Description:
// This function associates the user defined Directory Identifiers in the
// [DestinationDirs] section of the component INF file with particular
// directories, either the default location for NT 5 installations,
// %windir%\cluster, or the location of a previous installation.
//
// Arguments:
// fClusterServiceRegistered - TRUE indicates that Cluster service has
// previously been installed and the files should
// be updated in place.
//
// FALSE - indicates that the Cluster service
// files should be installed into the default
// location.
//
// Return Value:
// TRUE - indicates success
// FALSE - indicates error
//
// Note:
// The [DestinationDirs] section in clusocm.inf contains the following keys:
//
// ClusterFiles = 33001
// ClusterUpgradeFiles = 33002
// ClusterAdminFiles = 33003
// ClusterUninstallFiles = 33004
//
// Those directory IDs were chosen to be larger than DIRID_USER.
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::SetDirectoryIds( BOOL fClusterServiceRegistered ) { BOOL fReturnValue;
TCHAR tszClusterServiceBinaryPath[MAX_PATH];
// Are Cluster service files already present?
if ( fClusterServiceRegistered == (BOOL) TRUE ) { // Cluster service files should be upgraded in place.
// Query the path to the Cluster Service executable from the Service Control Manager.
CString csClusterService;
csClusterService = CLUSTER_SERVICE_NAME;
fReturnValue = GetServiceBinaryPath( (LPWSTR) (LPCTSTR) csClusterService, tszClusterServiceBinaryPath );
if ( fReturnValue == (BOOL) FALSE ) { DWORD dwErrorCode;
dwErrorCode = GetLastError();
ClRtlLogPrint( "In SetDirectoryIds the call to GetServiceBinaryPath failed with error code 0x%1!x!.\n", dwErrorCode ); } } else { // Cluster service files should be installed in the default location.
CString csClusterDirectory;
csClusterDirectory = CLUSTER_DIRECTORY;
if ( ExpandEnvironmentStrings( (LPCTSTR) csClusterDirectory, tszClusterServiceBinaryPath, MAX_PATH ) > 0L ) { fReturnValue = (BOOL) TRUE; } else { // Could not expand the enviornment string. The default location for the
// Cluster service could not be determined.
fReturnValue = (BOOL) FALSE;
DWORD dwErrorCode;
dwErrorCode = GetLastError();
ClRtlLogPrint( "ExpandEnvironmentString returned 0x%1!x! to SetDirectoryIds.\n", dwErrorCode ); } // Was the default location for the Cluster service determined?
} // Where should Cluster service files be installed?
// Was the location into which Cluster service files should be copied obtained?
if ( fReturnValue == (BOOL) TRUE ) { // Associate selected Directory Ids with the path in tszClusterServiceBinaryPath.
// Set the Directory Id for the ClusterFiles key in [DestinationDirs].
fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33001, (PCTSTR) tszClusterServiceBinaryPath );
// Was the Directory Id for the ClusterFiles key set successfully?
if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33001 was set to %1!s!.\n", tszClusterServiceBinaryPath );
// Set the Directory Id for the ClusterUpgradeFiles key in [DestinationDirs].
fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33002, (PCTSTR) tszClusterServiceBinaryPath );
} // Was the Directory Id for the ClusterFiles key set successfully?
// Was the Directory Id for the ClusterUpgradeFiles key set successfully?
if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33002 was set to %1!s!.\n", tszClusterServiceBinaryPath );
// Set the Directory Id for the ClusterAdminFiles key in [DestinationDirs].
fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33003, (PCTSTR) tszClusterServiceBinaryPath );
} // Was the Directory Id for the ClusterUpgradeFiles key set successfully?
// Was the Directory Id for the ClusterAdminFiles key set successfully?
if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33003 was set to %1!s!.\n", tszClusterServiceBinaryPath );
// Set the Directory Id for the ClusterUninstallFiles key in [DestinationDirs].
fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33004, (PCTSTR) tszClusterServiceBinaryPath );
} // Was the Directory Id for the ClusterAdminFiles key set successfully?
// Was the Directory Id for the ClusterUninstallFiles key set successfully?
if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33004 was set to %1!s!.\n", tszClusterServiceBinaryPath );
// Set the Directory Id for the NT4.files.root key in [DestinationDirs].
fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33005, (PCTSTR) tszClusterServiceBinaryPath );
} // Was the Directory Id for the ClusterUninstallFiles key set successfully?
// Was the Directory Id for NT4.files.root key set successfully?
if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33005 was set to %1!s!.\n", tszClusterServiceBinaryPath );
// Append the "private" directory to the path.
// Note, I didn't put this string in the stringtable because it is
// not localizable, and will never change.
_tcscat( tszClusterServiceBinaryPath, _T("\\private") );
// Set the Directory Id for the NT4.files.private key in [DestinationDirs].
fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33006, (PCTSTR) tszClusterServiceBinaryPath );
} // Was the Directory Id for the NT4.files.root key set successfully?
// Was the Directory Id for NT4.files.private key set successfully?
if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33006 was set to %1!s!.\n", tszClusterServiceBinaryPath ); } // Was the Directory Id for NT4.files.private key set successfully?
} // Was the path to the Cluster files determined?
else { ClRtlLogPrint( "SetDirectoryIds could not locate the cluster directory, so it failed.\n" ); }
return ( fReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// UpgradeClusterServiceImagePath
//
// Routine Description:
// This function "upgrades" the ImagePath value in the Cluster Service registry
// key to the location queried from the Service Control Manager.
//
// Arguments:
// None
//
// Return Value:
// NO_ERROR - indicates success
// Any other value is a Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::UpgradeClusterServiceImagePath( void ) { BOOL fReturnValue;
DWORD dwReturnValue;
// Query the path to the Cluster Service from the Service Control Manager.
TCHAR tszClusterServiceBinaryPath[MAX_PATH];
CString csClusterService;
csClusterService = CLUSTER_SERVICE_NAME;
fReturnValue = GetServiceBinaryPath( (LPWSTR) (LPCTSTR) csClusterService, tszClusterServiceBinaryPath );
// Was the path to the Cluster Service obtained?
if ( fReturnValue == (BOOL) TRUE ) { // Set the ImagePath value in the Cluster Service reg key to the location
// obtained from the Service Control Manager.
LONG lReturnValue;
HKEY hClusterServiceKey;
DWORD dwType; DWORD dwSize;
// Attempt to open the Cluster Service reg key.
CString csClusterServiceRegKey;
csClusterServiceRegKey = CLUSREG_KEYNAME_CLUSSVC;
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterServiceRegKey, (DWORD) 0L, // reserved
(REGSAM) KEY_SET_VALUE, &hClusterServiceKey );
// Was the Cluster Service reg key opened?
if ( lReturnValue == (LONG) ERROR_SUCCESS ) { TCHAR tszClusterServicePath[MAX_PATH];
_tcscpy( tszClusterServicePath, tszClusterServiceBinaryPath );
_tcscat( tszClusterServicePath, _T("\\") );
// Append the name of the Cluster Service.
CString csClusterService;
csClusterService = CLUSTER_SERVICE_NAME;
csClusterService += (CString) _T(".exe");
_tcscat( tszClusterServicePath, csClusterService );
DWORD dwImagePathValueLength;
dwImagePathValueLength = (DWORD) ((_tcslen( tszClusterServicePath ) + 1) * sizeof( TCHAR ));
CString csImagePath;
csImagePath = CLUSREG_KEYNAME_IMAGE_PATH;
lReturnValue = RegSetValueEx( hClusterServiceKey, csImagePath, (DWORD) 0L, // reserved
(DWORD) REG_EXPAND_SZ, (CONST BYTE *) tszClusterServicePath, dwImagePathValueLength );
// Was the ImagePath written successfully?
if ( lReturnValue == (LONG) ERROR_SUCCESS ) { dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "UpgradeClusterServiceImagePath succeeded.\n" ); } else { dwReturnValue = GetLastError();
ClRtlLogPrint( "UpgradeClusterServiceImagePath failed with error code 0x%1!x!.\n", dwReturnValue ); } // Was the ImagePath written successfully?
// Close the Cluster Service registry key.
RegCloseKey( hClusterServiceKey ); // do we care about the return value?
} else { dwReturnValue = GetLastError();
ClRtlLogPrint( "UpgradeClusterServiceImagePath failed with error code 0x%1!x!.\n", dwReturnValue ); } // Was the Cluster Service reg key opened?
} // Was the path to the Cluster Service obtained?
else { // Indicate error.
dwReturnValue = GetLastError();
ClRtlLogPrint( "UpgradeClusterServiceImagePath failed with error code 0x%1!x!.\n", dwReturnValue ); }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcQueueFileOps
//
// Routine Description:
// This function processes the OC_QUEUE_FILE_OPS "messages" from the
// Optional Components Manager.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID)
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcQueueFileOps( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue = NO_ERROR;
// Is the handle to the component INF file valid?
if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { // Is this UNATTENDED or ATTENDED?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { // This is UNATTENDED.
ClRtlLogPrint( "In OnOcQueueFileOps this is an UNATTENDED operation.\n" );
dwReturnValue = OnOcQueueFileOpsUnattended( (LPCTSTR) ptszSubComponentId, hSetupFileQueue ); } else { // This is ATTENDED.
ClRtlLogPrint( "In OnOcQueueFileOps this is an ATTENDED operation.\n" );
dwReturnValue = OnOcQueueFileOpsAttended( (LPCTSTR) ptszSubComponentId, hSetupFileQueue ); } // Is this UNATTENDED or ATTENDED?
} else { dwReturnValue = ERROR_FILE_NOT_FOUND;
ClRtlLogPrint( "In OnOcQueueFileOps the handle to the component INF file is bad.\n" ); } // Is the handle to the component INF file valid?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcQueueFileOpsUnattended
//
// Routine Description:
// This function processes the OC_QUEUE_FILE_OPS "messages" from the
// Optional Components Manager during UNATTENDED operations.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID)
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
// Note:
// OC Manager sends OC_QUEUE_FILE_OPS to the component DLL when the Components
// List wizard page is dismissed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcQueueFileOpsUnattended( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue;
// Is this an UPGRADE?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { // This is an unattended UPGRADE.
dwReturnValue = QueueFileOpsUnattendedUpgrade( (LPCTSTR) ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueFileOpsUnattendedUpgrade returned 0x%1!x!.\n", dwReturnValue ); } else { HINF hAnswerFile; // WARNING: NEVER close this handle because clusocm.dll
// did not open it.
// Get a handle to the answer file. WARNING: NEVER close this handle because clusocm.dll
// did not open it.
hAnswerFile = m_SetupInitComponent.HelperRoutines.GetInfHandle( INFINDEX_UNATTENDED, m_SetupInitComponent.HelperRoutines.OcManagerContext );
if ( (hAnswerFile != (HINF) NULL) && (hAnswerFile != (HINF) INVALID_HANDLE_VALUE) ) { ClRtlLogPrint( "In OnOcQueueFileOpsUnattended this is a CLEAN install.\n" );
// Is Cluster service selected? It is probably overkill to check, but
// it is safer to check than to be sorry later.
BOOL fCurrentSelectionState; BOOL fOriginalSelectionState;
fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT );
fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
if ( fCurrentSelectionState == (BOOL) TRUE ) { // Was there a selection state transition?
if ( fCurrentSelectionState != fOriginalSelectionState ) { ClRtlLogPrint( "A selection state transition was detected.\n" );
// A selection state transition has occured. Install files.
dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended,\n", dwReturnValue ); } else { ClRtlLogPrint( "NO selection state transition was detected.\n" );
// The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition?
} else { // Has there been a selection state transition ?
ClRtlLogPrint( "In OnOcQueueFileOpsUnattended Cluster service is not selected for installation.\n" );
if ( fCurrentSelectionState != fOriginalSelectionState ) { // Remove files.
dwReturnValue = QueueRemoveFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueRemoveFileOperations returned 0x%1!x! to OnOcQueueFileOpsUnattended.\n", dwReturnValue ); } else { ClRtlLogPrint( "In OnOcQueueFileOpsUnattended NO selection state transition was detected.\n" );
// The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition ?
} } else { // A handle to the answer file could not be obtained. Treat it as a UPGRADE.
ClRtlLogPrint( "InOnOcQueueFileOpsUnattended the handle to the answer file could not be obtained.\n" );
dwReturnValue = QueueFileOpsUnattendedUpgrade( (LPCTSTR) ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueFileOpsUnattendedUpgrade returned 0x%1!x!.\n", dwReturnValue ); } }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// QueueFileOpsUnattendedUpgrade
//
// Routine Description:
// This function queues the file operations appropriate for an unattended
// upgrade operation.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID)
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::QueueFileOpsUnattendedUpgrade( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue; eClusterInstallState eState;
// Has Cluster service previously been installed?
// GetClusterInstallationState reports on the state of the registry value
// that records the state of the Cluster service installation on NT 5 machines.
// IsClusterServiceRegistered indicates whether the Cluster service is registered on
// BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for
// upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines.
BOOL fClusteringServicePreviouslyInstalled;
ClRtlGetClusterInstallState( NULL, &eState ); if ( ( eState != eClusterInstallStateUnknown ) || ( IsClusterServiceRegistered() == (BOOL) TRUE ) ) { fClusteringServicePreviouslyInstalled = (BOOL) TRUE; } else { fClusteringServicePreviouslyInstalled = (BOOL) FALSE; }
if ( fClusteringServicePreviouslyInstalled == (BOOL) TRUE ) { // Upgrade the ClusteringService files.
dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to QueueFileOpsUnattendedUpgrade.\n", dwReturnValue ); } // Has Cluster service previously been installed?
else { // Since Cluster service has not been previously installed there is
// nothing to do.
ClRtlLogPrint( "In QueueFileOpsUnattendedUpgrade Cluster service has never been installed.\n" );
dwReturnValue = (DWORD) NO_ERROR; } // Has Cluster service previously been installed?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcQueueFileOpsAttended
//
// Routine Description:
// This function processes the OC_QUEUE_FILE_OPS "messages" from the
// Optional Components Manager during ATTENDED operations.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
// hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID)
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcQueueFileOpsAttended( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue; eClusterInstallState eState;
// Is Cluster service selected?
BOOL fCurrentSelectionState; BOOL fOriginalSelectionState;
fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT );
if ( fCurrentSelectionState == (BOOL) TRUE ) { // The subcomponent is selected. Is this a fresh install ?
ClRtlLogPrint( "In OnOcQueueFileOpsAttended the current selection state is TRUE.\n" );
if ( ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) == (DWORDLONG) 0L ) && ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) == (DWORDLONG) 0L ) ) { // SETUPOP_STANDALONE flag clear means running under GUI mode setup.
// SETUPOP_NTUPGRADE flag clear means not performing an upgrade.
// Both flags clear means a fresh install. Do not check for a selection
// state transition.
ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is a CLEAN install.\n" );
dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended.\n", dwReturnValue ); } else { // This is either an upgrade or OC Manager is running stand-alone.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is an UPGRADE.\n" );
// This is an upgrade, if Cluster Server has previously been
// been installed then queue the file copies.
// Has Cluster service perviously been installed? Ask the Service Control
// Manager whether the Cluster Service is registered.
// GetClusterInstallationState reports on the state of the registry value
// that records the state of the Cluster service installation on NT 5 machines.
// IsClusterServiceRegistered indicates whether the Cluster service is registered on
// BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for
// upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines.
BOOL fClusterServiceRegistered;
fClusterServiceRegistered = IsClusterServiceRegistered();
BOOL fClusterServicePreviouslyInstalled;
ClRtlGetClusterInstallState( NULL, &eState ); if ( ( eState != eClusterInstallStateUnknown ) || ( fClusterServiceRegistered == (BOOL) TRUE ) ) { fClusterServicePreviouslyInstalled = (BOOL) TRUE; } else { fClusterServicePreviouslyInstalled = (BOOL) FALSE; }
if ( fClusterServicePreviouslyInstalled == (BOOL) TRUE ) { // Since this is an UPGRADE and Cluster service has been
// installed there is no need to test for a selection state transition.
dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended.\n", dwReturnValue ); } else { ClRtlLogPrint( "In OnOcQueueFileOps attempted an UPGRADE but Cluster service has never been installed.\n" );
dwReturnValue = (DWORD) NO_ERROR; } // Was Cluster service perviously installed?
} else { ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is STANDALONE.\n" );
// This is not an upgrade. That means Add/Remove Programs must be
// running. It is necessary to test for a selection state transition.
fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
// Was there a selection state transition?
if ( fCurrentSelectionState != fOriginalSelectionState ) { ClRtlLogPrint( "A selection state transition was detected.\n" );
// A selection state transition has occured. Install files.
dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended,\n", dwReturnValue ); } else { ClRtlLogPrint( "NO selection state transition was detected.\n" );
// The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition?
} // Is this an UPGRADE?
} // Is this a clean install?
} // Is Cluster service currently selected ?
else { ClRtlLogPrint( "In OnOcQueueFileOpsAttended the current selection state is FALSE.\n" );
// Cluster service is not selected. Is OC Manager running stand-alone ?
// If not, i.e. if GUI mode setup is running, there is nothing to do.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is STANDALONE.\n" );
// SETUPOP_STANDALONE set implies GUI mode setup is not running. If
// there was a selection state change (to unselected) then remove files.
fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
// Has there been a selection state transition ?
if ( fCurrentSelectionState != fOriginalSelectionState ) { // Remove files.
dwReturnValue = QueueRemoveFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue );
ClRtlLogPrint( "QueueRemoveFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended.\n", dwReturnValue ); } else { ClRtlLogPrint( "NO selection state transition was detected.\n" );
// The selection state has not been changed. Perform no action.
dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition ?
} else { // GUI mode setup is running and Cluster service is not selected.
// There is nothing to do.
dwReturnValue = (DWORD) NO_ERROR; } // Is GUI mode setup running ?
} // Is Cluster service currently selected ?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CompleteUninstallingClusteringService
//
// Routine Description:
// This function completes uninstalling ClusteringService by queuing the
// registry operations to delete the Cluster service registry keys,
// cleaning up the Start Menu, removing the Network Provider, and requesting
// a reboot.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::CompleteUninstallingClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; eClusterInstallState ecisInstallState; BOOL bRebootRequired = FALSE;
// Update the "progress text"
CString csProgressText;
csProgressText.LoadString( IDS_REMOVING_CLUS_SERVICE );
m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText );
// Request that the system reboot only if clusdisk has been started.
// We can deduce this by looking at the cluster installation state.
// If the state is not eClusterInstallStateFilesCopied then it means that
// ClusCfg may have run successfully and therefore, clusdisk may have been started.
// We must call ClRtlGetClusterInstallState before calling UninstallRegistryOperations.
// Otherwise it will always return eClusterInstallStateUnknown!
// If ClRtlGetClusterInstallState fails, reboot anyway.
// If it succeeds, reboot only if ClusCfg has completed successfully.
if ( ( ClRtlGetClusterInstallState( NULL, &ecisInstallState ) != ERROR_SUCCESS ) || ( ecisInstallState != eClusterInstallStateFilesCopied ) ) { bRebootRequired = TRUE; }
// Delete registry entries. Queue the base registry operations.
dwReturnValue = UninstallRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId );
ClRtlLogPrint( "UninstallRegistryOperations returned 0x%1!x! to CompleteUninstallingClusteringService.\n", dwReturnValue );
//
// Remove the cluster item from the start menu
//
CString csGroupName; CString csItemName;
csGroupName.LoadString( IDS_START_GROUP_NAME ); csItemName.LoadString( IDS_START_ITEM_NAME );
DeleteLinkFile( CSIDL_COMMON_PROGRAMS, (LPCWSTR) csGroupName, (LPCWSTR) csItemName, (BOOL) FALSE );
// Delete the cluster directory. BUGBUG
//
// Remove the cluster network provider
//
dwReturnValue = RemoveNetworkProvider();
if ( bRebootRequired ) { BOOL fRebootRequestStatus;
// In the following call the value passed in the second parameter
// was chosen arbitrarily. Ocmanage.h implies that the parameter is not used.
fRebootRequestStatus = m_SetupInitComponent.HelperRoutines.SetReboot( m_SetupInitComponent.HelperRoutines.OcManagerContext, (BOOL) TRUE ); }
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OpenClusterRegistryRoot
//
// Routine Description:
// This function retuns a handle to the root key of the Cluster hive.
// It will load the hive if necessary.
//
// Arguments:
// none
//
//
// Return Value:
// If success, handle to the cluster root key. Otherwise, NULL
//
//--
/////////////////////////////////////////////////////////////////////////////
HKEY CClusocmApp::OpenClusterRegistryRoot( void ) { BOOL fReturnValue;
HKEY hClusterKey = NULL; LONG lReturnValue;
// Attempt to open the Cluster Registry key.
CString csClusterRegKey;
csClusterRegKey = CLUSREG_KEYNAME_CLUSTER;
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterRegKey, 0, KEY_READ, &hClusterKey);
if ( lReturnValue != ERROR_SUCCESS ) { ClRtlLogPrint( "In OpenClusterRegistryRoot, the first attempt to open the Cluster key failed.\n" );
// The Cluster hive is not currently loaded. This condition means that
// the cluster service has not been started. Attempt to load the Cluster
// hive so that it can be read.
// First, locate the Cluster hive file. It should be in the location
// specified for the ClusterFiles entry in the [DestinationDirs] section
// of clusocm.inf.
CString csClusterHiveFilePath;
fReturnValue = LocateClusterHiveFile( (CString &) csClusterHiveFilePath );
ClRtlLogPrint( "LocateClusterHiveFile returned 0x%1!x! to OpenClusterRegistryRoot.\n", fReturnValue );
// Was the Cluster hive file located?
if ( fReturnValue == (BOOL) TRUE ) { // The Cluster hive file was located.
// Attempt to load the cluster hive.
BOOLEAN OriginalState;
// I'm not sure what the following function does, but the prototype is
// in sdk\inc\ntrtl.h. Look in stdafx.h for the inclusion of ntrtl.h. I
// replicated the logic that was used in newsetup.h to make it work.
lReturnValue = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OriginalState );
if ( lReturnValue == ERROR_SUCCESS ) { // Attempt to Load the Cluster Hive.
lReturnValue = RegLoadKey( HKEY_LOCAL_MACHINE, csClusterRegKey, csClusterHiveFilePath );
if ( lReturnValue == ERROR_SUCCESS ) {
// Now that the Cluster hive has been loaded, attempt to open the
// Cluster registry key.
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterRegKey, 0, KEY_READ, &hClusterKey );
// lReturnValue will be tested by the next BLOCK of code.
}
// Undo whatever the preceding call to RtlAdjustPrivilege() did.
RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, OriginalState, FALSE, &OriginalState );
if ( lReturnValue != ERROR_SUCCESS ) { // Set the error code.
SetLastError( lReturnValue ); }
} else { // The initial call to RtlAdjustPrivilege FAILED.
SetLastError( lReturnValue );
// A return value of TRUE will allow the uninstall operation to
// continue. Since lReturnValue is NOT ERROR_SUCCESS no additional
// processing will be performed by this function.
} // Did RtlAdjustPrivilege succeed?
} else { // The Cluster hive file was not located. Set last error and return
// a null handle
ClRtlLogPrint( "OpenClusterRegistryRoot couldn't locate the hive files\n" );
SetLastError( ERROR_FILE_NOT_FOUND );
} // Was the cluster hive file located?
} // Was the Cluster registry key opened successfully?
return ( hClusterKey ); } // OpenClusterRegistryRoot
/////////////////////////////////////////////////////////////////////////////
//++
//
// FindNodeNumber
//
// Routine Description:
// This function looks through the cluster's Node key and returns a pointer to
// the node number string of the current node.
//
// Arguments:
// ClusterKey - handle to cluster root key
//
// NodeNumberString - pointer to location that receives the node number
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
PWSTR CClusocmApp::FindNodeNumber( HKEY ClusterKey ) { WCHAR nodeName[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD nodeNameLength = sizeof( nodeName ); WCHAR registryNodeName[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD registryNodeNameLength; HKEY nodesKey = NULL; HKEY nodesEnumKey = NULL; DWORD status; PWSTR nodeNumberString; DWORD nodeNumberStringLength; DWORD index = 0; DWORD dataType;
//
// allocate space for the node number string
//
nodeNumberString = (PWSTR)LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, ( CS_MAX_NODE_ID_LENGTH + 1 ) * sizeof( WCHAR )); if ( nodeNumberString == NULL ) { status = GetLastError(); ClRtlLogPrint( "FindNodeNumber couldn't allocate node number buffer: %1!u!\n", status ); return NULL; }
//
// get our node name and a handle to the root key in the cluster hive
//
if ( !GetComputerName( nodeName, &nodeNameLength )) { status = GetLastError(); ClRtlLogPrint( "FindNodeNumber failed to get computer name: %1!u!\n", status );
goto error_exit; }
status = RegOpenKeyEx( ClusterKey, CLUSREG_KEYNAME_NODES, 0, KEY_READ, &nodesKey );
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "FindNodeName failed to open Nodes key: %1!u!\n", status ); goto error_exit; }
//
// enum the entries under the Nodes key
//
do { nodeNumberStringLength = sizeof( nodeNumberString ); status = RegEnumKeyEx( nodesKey, index, nodeNumberString, &nodeNumberStringLength, NULL, NULL, NULL, NULL );
if ( status == ERROR_NO_MORE_ITEMS ) { ClRtlLogPrint( "FindNodeNumber finished enum'ing the Nodes key\n" ); break; } else if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "FindNodeNumber failed to enum Nodes key: %1!u!\n", status ); goto error_exit; }
//
// open this key and get the Name value
//
status = RegOpenKeyEx( nodesKey, nodeNumberString, 0, KEY_READ, &nodesEnumKey );
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "FindNodeNumber failed to open Nodes key %1!ws!: %2!u!\n", nodeNumberString, status ); goto error_exit;
}
registryNodeNameLength = sizeof( registryNodeName ); status = RegQueryValueEx( nodesEnumKey, CLUSREG_NAME_NODE_NAME, NULL, &dataType, (LPBYTE)registryNodeName, ®istryNodeNameLength );
RegCloseKey( nodesEnumKey ); nodesEnumKey = NULL;
if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "FindNodeNumber failed to get NodeName value: " "status %1!u!, data type %2!u!\n", status, dataType ); goto error_exit;
}
//
// finally, we get to see if this is our node
//
if ( lstrcmpiW( nodeName, registryNodeName ) == 0 ) { break; }
++index; } while ( TRUE );
error_exit: if ( nodesKey != NULL ) { RegCloseKey( nodesKey ); }
if ( nodesEnumKey != NULL ) { RegCloseKey( nodesEnumKey ); }
if ( status != ERROR_SUCCESS || lstrlenW( nodeNumberString ) == 0 ) { LocalFree( nodeNumberString ); nodeNumberString = NULL; }
return nodeNumberString; } // FindNodeNumber
/////////////////////////////////////////////////////////////////////////////
//++
//
// GetConnectionName
//
// Routine Description:
// This function finds the matching connection object based on the IP address
// of the network interface. NOTE that the return value is also pointed to in
// the Adapter Info of the Adapter enum struct. This return value must not be
// freed with LocalFree.
//
// Arguments:
// NetInterfacesKey - handle to key of network interface
//
// AdapterEnum - pointer to struct with adapter/IP interface info
//
// Return Value:
// If successful, pointer to connectoid name. Otherwise, NULL with Win32
// error available from GetLastError().
//
//--
/////////////////////////////////////////////////////////////////////////////
PWSTR CClusocmApp::GetConnectionName( HKEY NetInterfacesGuidKey, PCLRTL_NET_ADAPTER_ENUM AdapterEnum ) { WCHAR ipAddress[ 16 ]; DWORD ipAddressLength = sizeof( ipAddress ); DWORD status; PCLRTL_NET_ADAPTER_INFO adapterInfo; PCLRTL_NET_INTERFACE_INFO interfaceInfo; PWSTR connectoidName = NULL; DWORD dataType;
//
// query for the netIf's IP address
//
status = RegQueryValueEx( NetInterfacesGuidKey, CLUSREG_NAME_NETIFACE_ADDRESS, NULL, &dataType, (LPBYTE)ipAddress, &ipAddressLength );
if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "GetConnectionName failed to get Address value: " "status %1!u!, data type %2!u!\n", status, dataType );
SetLastError( ERROR_INVALID_DATA ); goto error_exit; }
adapterInfo = ClRtlFindNetAdapterByInterfaceAddress( AdapterEnum, ipAddress, &interfaceInfo ); if ( adapterInfo != NULL ) { connectoidName = adapterInfo->ConnectoidName; }
error_exit: return connectoidName; } // GetConnectionName
/////////////////////////////////////////////////////////////////////////////
//++
//
// GetTCPAdapterInfo
//
// Routine Description:
// This function opens a handle to the service controller and tries to
// start the DHCP service. If successful, then TCP is queried for its
// NIC and IP interface info.
//
// Arguments:
// none
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
PCLRTL_NET_ADAPTER_ENUM CClusocmApp::GetTCPAdapterInfo( void ) { SC_HANDLE schSCManager; SC_HANDLE serviceHandle; PCLRTL_NET_ADAPTER_ENUM adapterEnum = NULL; BOOL success; DWORD retryCount; DWORD status;
schSCManager = OpenSCManager(NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_CONNECT); // access required
if ( schSCManager ) {
//
// start DHCP for nodes that are using it. it will cause TCP to start
// as well. For statically configured nodes, starting DHCP causes no
// harm.
//
serviceHandle = OpenService(schSCManager, L"DHCP", SERVICE_START );
if ( serviceHandle ) { success = StartService( serviceHandle, 0, NULL );
if ( !success ) { status = GetLastError(); if ( status == ERROR_SERVICE_ALREADY_RUNNING ) { success = TRUE; } }
if ( success ) {
//
// wait while TCP gets bound to all its adapters and discovers
// its IP interfaces.
//
retryCount = 3; do { Sleep( 2000 ); adapterEnum = ClRtlEnumNetAdapters();
if (adapterEnum != NULL) { break; } } while ( --retryCount != 0 );
if (adapterEnum == NULL) { status = GetLastError(); ClRtlLogPrint( "Failed to obtain local system network config. " "status %1!u! retryCount %2!u!\n", status, retryCount); } } else { ClRtlLogPrint( "StartService returned %1!u! to StartTCP\n", status); } CloseServiceHandle( serviceHandle ); } else { status = GetLastError(); ClRtlLogPrint( "OpenService returned %1!u! to StartTCP\n", status); }
CloseServiceHandle( schSCManager ); } else { status = GetLastError(); ClRtlLogPrint("OpenSCManager returned %1!u! to StartTCP\n", status); }
return adapterEnum; }
/////////////////////////////////////////////////////////////////////////////
//++
//
// RenameConnectionObjects
//
// Routine Description:
// This function walks through the node's network interfaces and changes,
// if necessary, their associated connection objects.
//
// Arguments:
// none
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::RenameConnectionObjects( void ) { HKEY clusterKey; HKEY netInterfacesKey = NULL; HKEY netInterfacesGuidKey = NULL; HKEY networkGuidKey = NULL; HKEY networksKey = NULL; DWORD status; PCLRTL_NET_ADAPTER_ENUM adapterEnum = NULL; PCLRTL_NET_ADAPTER_INFO adapterInfo = NULL; PCLRTL_NET_INTERFACE_INFO adapterIfInfo = NULL; DWORD hiddenAdapterCount = 0; PWSTR ourNodeNumber = NULL; DWORD index = 0; WCHAR netIfGuidString[ CS_NETINTERFACE_ID_LENGTH + 1 ]; DWORD netIfGuidStringLength; WCHAR networkGuidString[ CS_NETWORK_ID_LENGTH + 1 ]; DWORD networkGuidStringLength; WCHAR netIfNodeNumber[ CS_MAX_NODE_ID_LENGTH + 1 ]; DWORD netIfNodeNumberLength; DWORD dataType; PWSTR connectoidName; PWSTR networkName = NULL; DWORD networkNameLength;
//
// initialize COM
//
status = CoInitializeEx( NULL, COINIT_DISABLE_OLE1DDE | COINIT_MULTITHREADED ); if ( !SUCCEEDED( status )) { ClRtlLogPrint( "RenameConnectionObjects Couldn't init COM %1!08X!\n", status ); return status; }
//
// get a handle to the root key in the cluster hive
//
clusterKey = OpenClusterRegistryRoot();
if ( clusterKey == NULL ) { status = GetLastError(); ClRtlLogPrint( "OpenClusterRegistryRoot returned %1!u! to RenameConnectionObjects\n", status ); goto error_exit; }
//
// start TCP if necessary
//
adapterEnum = GetTCPAdapterInfo(); if ( adapterEnum == NULL ) { status = GetLastError();
if ( status == ERROR_SUCCESS ) { ClRtlLogPrint( "No usable network adapters are installed in this system.\n" ); } else { ClRtlLogPrint( "GetTCPAdapterInfo returned %1!u! to RenameConnectionObjects\n", status ); } goto error_exit; }
//
// Ignore all adapters which are hidden or have an address of 0.0.0.0.
//
for (adapterInfo = adapterEnum->AdapterList; adapterInfo != NULL; adapterInfo = adapterInfo->Next ) { if (adapterInfo->Flags & CLRTL_NET_ADAPTER_HIDDEN) { ClRtlLogPrint( "Ignoring hidden adapter '%1!ws!'.\n", adapterInfo->DeviceName ); adapterInfo->Ignore = TRUE; hiddenAdapterCount++; } else { adapterIfInfo = ClRtlGetPrimaryNetInterface(adapterInfo);
if (adapterIfInfo->InterfaceAddress == 0) { ClRtlLogPrint( "Ignoring adapter '%1!ws!' because primary address is 0.0.0.0.\n", adapterInfo->DeviceName ); adapterInfo->Ignore = TRUE; hiddenAdapterCount++; } } }
if ((adapterEnum->AdapterCount - hiddenAdapterCount) == 0) { ClRtlLogPrint( "No usable network adapters are installed in this system.\n" ); status = ERROR_SUCCESS; goto error_exit; }
ClRtlLogPrint( "RenameConnectionObject found %1!u! adapters to process.\n", adapterEnum->AdapterCount - hiddenAdapterCount );
//
// now find our node number by looking through the node key in the cluster
// registry and comparing our netbios name with the names in the registry
//
ourNodeNumber = FindNodeNumber( clusterKey ); if ( ourNodeNumber == NULL ) { status = GetLastError(); ClRtlLogPrint( "FindNodeNumber failed: status %1!u!\n", status ); goto error_exit; }
//
// open the network and network interface keys
//
status = RegOpenKeyEx( clusterKey, CLUSREG_KEYNAME_NETWORKS, 0, KEY_READ, &networksKey );
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open Networks key: %1!u!\n", status ); goto error_exit;
}
status = RegOpenKeyEx( clusterKey, CLUSREG_KEYNAME_NETINTERFACES, 0, KEY_READ, &netInterfacesKey );
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open NetworkInterfaces key: %1!u!\n", status ); goto error_exit;
}
//
// enum the NetworkInterfaces key, looking for interfaces on this node. If
// we find one, get their IP address and find the corresponding network and
// connection object.
//
do { netIfGuidStringLength = sizeof( netIfGuidString ); status = RegEnumKeyEx( netInterfacesKey, index, netIfGuidString, &netIfGuidStringLength, NULL, NULL, NULL, NULL );
if ( status == ERROR_NO_MORE_ITEMS ) { ClRtlLogPrint( "RenameConnectionObjects finished enum'ing the " "NetworkInterfaces key\n" );
status = ERROR_SUCCESS; break; } else if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to enum NetworkInterfaces " "key: %1!u!\n", status ); goto error_exit; }
//
// open this key and get the Node value
//
status = RegOpenKeyEx( netInterfacesKey, netIfGuidString, 0, KEY_READ, &netInterfacesGuidKey );
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open NetworkIntefaces " "subkey %1!ws!: %2!u!\n", netIfGuidString, status ); goto error_exit;
}
netIfNodeNumberLength = sizeof( netIfNodeNumber ); status = RegQueryValueEx( netInterfacesGuidKey, CLUSREG_NAME_NETIFACE_NODE, NULL, &dataType, (LPBYTE)netIfNodeNumber, &netIfNodeNumberLength );
if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Node value: " "status %1!u!, data type %2!u!\n", status, dataType ); status = ERROR_INVALID_DATA; goto error_exit;
}
//
// if this is one our interfaces, then adjust the name of the
// associated connection object
//
if ( lstrcmpiW( ourNodeNumber, netIfNodeNumber ) == 0 ) { ClRtlLogPrint( "Net Interface %1!ws! is on this node.\n", netIfGuidString );
connectoidName = GetConnectionName( netInterfacesGuidKey, adapterEnum );
if ( connectoidName != NULL ) {
ClRtlLogPrint( "Associated connectoid name is '%1!ws!'.\n", connectoidName);
//
// look up network GUID and open its Key to get the network name
//
networkGuidStringLength = sizeof( networkGuidString ); status = RegQueryValueEx( netInterfacesGuidKey, CLUSREG_NAME_NETIFACE_NETWORK, NULL, &dataType, (LPBYTE)networkGuidString, &networkGuidStringLength );
if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Network value: " "status %1!u!, data type %2!u!\n", status, dataType ); status = ERROR_INVALID_DATA; goto error_exit; }
status = RegOpenKeyEx( networksKey, networkGuidString, 0, KEY_READ, &networkGuidKey );
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open networks " "subkey %1!ws!: %2!u!\n", networkGuidString, status ); goto error_exit;
}
//
// query the Name value to get its size, allocate a buffer large
// enough to hold it and request the data contained in the value.
//
networkNameLength = 0; status = RegQueryValueEx( networkGuidKey, CLUSREG_NAME_NET_NAME, NULL, &dataType, (LPBYTE)NULL, &networkNameLength );
if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Network Name " "size: status %1!u!, data type %2!u!\n", status, dataType ); status = ERROR_INVALID_DATA; goto error_exit; }
networkName = (PWCHAR)LocalAlloc( LMEM_FIXED, networkNameLength ); if ( networkName == NULL ) { status = GetLastError(); ClRtlLogPrint( "RenameConnectionObjects failed to alloc space " "for network name: %1!u!\n", status ); goto error_exit; }
status = RegQueryValueEx( networkGuidKey, CLUSREG_NAME_NET_NAME, NULL, NULL, (LPBYTE)networkName, &networkNameLength );
RegCloseKey( networkGuidKey ); networkGuidKey = NULL;
if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Network " "Name: status %1!u!\n", status ); goto error_exit; }
ClRtlLogPrint( "Network name is '%1!ws!'.\n", networkName );
if ( lstrcmpiW( connectoidName, networkName ) != 0 ) { ClRtlLogPrint( "Changing connectoid name to '%1!ws!'.\n", networkName ); ClRtlFindConnectoidByNameAndSetName( connectoidName, networkName ); } } else { ClRtlLogPrint( "RenameConnectionObjects failed to get connection name.\n" ); } }
RegCloseKey( netInterfacesGuidKey ); netInterfacesGuidKey = NULL;
++index; } while ( TRUE );
error_exit: if ( clusterKey != NULL ) { RegCloseKey( clusterKey ); }
if ( netInterfacesKey != NULL ) { RegCloseKey( netInterfacesKey ); }
if ( netInterfacesGuidKey != NULL ) { RegCloseKey( netInterfacesGuidKey ); }
if ( networksKey != NULL ) { RegCloseKey( networksKey ); }
if ( networkGuidKey != NULL ) { RegCloseKey( networkGuidKey ); }
if ( ourNodeNumber != NULL ) { LocalFree( ourNodeNumber ); }
if ( networkName != NULL ) { LocalFree( networkName ); }
if ( adapterEnum != NULL ) { ClRtlFreeNetAdapterEnum( adapterEnum ); }
CoUninitialize();
return status; } // RenameConnectionObjects
/////////////////////////////////////////////////////////////////////////////
//++
//
// CompleteUpgradingClusteringService
//
// Routine Description:
// This function completes the process of upgrading a Cluster service installation.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::CompleteUpgradingClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; eClusterInstallState eState;
// This is an upgrade, if Cluster Server has previously been
// been installed then queue the registry additions.
// GetClusterInstallationState reports on the state of the registry value
// that records the state of the Cluster service installation on NT 5 machines.
// IsClusterServiceRegistered indicates whether the Cluster service is registered on
// BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for
// upgrading NT 4 machines, ClRtlGetClusterInstallState for NT 5 machines.
BOOL fClusterServiceRegistered;
fClusterServiceRegistered = IsClusterServiceRegistered();
ClRtlGetClusterInstallState( NULL, &eState );
if ( ( eState != eClusterInstallStateUnknown ) || ( fClusterServiceRegistered == (BOOL) TRUE ) ) { // Update the progress bar.
m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext );
// Update the "progress text"
CString csProgressText;
csProgressText.LoadString( IDS_UPGRADING_CLUS_SERVICE );
m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); ClRtlLogPrint( "In CompleteUpgradingClusteringService Cluster service has previously been installed.\n" );
// Perform the base registry operations.
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId );
ClRtlLogPrint( "The base call to PerformRegistryOperations returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue );
// Were the base registry operations performed successfully?
CString csUpgrade;
if ( dwReturnValue == (DWORD) NO_ERROR ) { // Perform the registry operations that pertain to UPGRADE.
csUpgrade = UPGRADE_INF_KEY;
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csUpgrade );
ClRtlLogPrint( "The UPGRADE call to PerformRegistryOperations returned 0x%1!x! " "to CompleteUpgradingClusteringService.\n", dwReturnValue ); } // Were the base registry operations performed successfully?
// Were the UPGRADE registry operations performed successfully?
if ( dwReturnValue == (DWORD) NO_ERROR ) { // If the Cluster Service is registered with the Service Control Manager then
// the registry keys that add "Configure Cluster service" to the Welcome UI
// should be eliminated because the service has already been configured.
if ( fClusterServiceRegistered == (BOOL) TRUE ) { CString csClusterService;
csClusterService = CLUSTER_SERVICE_NAME;
CString csRegistered;
csRegistered = REGISTERED_INF_KEY_FRAGMENT;
CString csSectionName;
csSectionName = csUpgrade + (CString) _T(".") + csClusterService + (CString) _T(".") + csRegistered;
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csSectionName );
ClRtlLogPrint( "The REGISTERED call to PerformRegistryOperations returned 0x%1!x! " "to CompleteUpgradingClusteringService.\n", dwReturnValue ); }
// Were the Welcome UI registry operations performed successfully?
if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "In CompleteUpgradingClusteringService the registry operations " "were performed successfully.\n" );
// In NT 4 the ImagePath value in HKLM\System\CurrentControlSet\Services\ClusSvc
// is set to reference clusprxy.exe. In NT 5 it must reference clussvc.exe.
// The ImagePath value in the Cluster Service registry key must
// be set programmatically because the path to the service
// executable image is not known to the component INF file. That
// is because in NT 4 "Cluster Server" as it was known, could be
// installed in an arbitrary location.
// There is no straightforward way to determine whether this upgrade
// is from NT 4 or NT 5. The assumption made here is that if the
// Cluster Service is registered with the Service Control Manager
// then it was upgraded in place, regardless of the OS level. In that
// case it will be safe to set the ImagePath value to reference clussvc.exe
// in that location. If the Cluster Service is not registered with the
// SC Manager then it is safe to leave that reg value alone because when
// cluscfg.exe configures the Cluster service the ImagePath value will
// get written correctly.
if ( fClusterServiceRegistered == (BOOL) TRUE ) { dwReturnValue = UpgradeClusterServiceImagePath();
ClRtlLogPrint( "UpgradeClusterServiceImagePath returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue ); }
// Was the ImagePath set successfully?
if ( dwReturnValue == (DWORD) NO_ERROR ) { // did the connection objects get renamed?
dwReturnValue = RenameConnectionObjects();
ClRtlLogPrint( "RenameConnectionObjects returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue );
if ( dwReturnValue == (DWORD) NO_ERROR ) { //
// set the service controller default failure actions
//
dwReturnValue = ClRtlSetSCMFailureActions( NULL );
ClRtlLogPrint( "ClRtlSetDefaultFailureActions returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue );
if ( dwReturnValue == (DWORD) NO_ERROR ) { // In NT4 the "Display Name" for the Cluster service was
// "Cluster Server". It must be programatically changed to
// "Cluster service".
if ( fClusterServiceRegistered == (BOOL) TRUE ) { CString csClusterService;
csClusterService = CLUSTER_SERVICE_NAME;
SC_HANDLE hscServiceMgr; SC_HANDLE hscService;
// Connect to the Service Control Manager and open the specified service
// control manager database.
hscServiceMgr = OpenSCManager( NULL, NULL, GENERIC_READ | GENERIC_WRITE );
// Was the service control manager database opened successfully?
if ( hscServiceMgr != NULL ) { // The service control manager database is open.
// Get the current display name of the service.
TCHAR tszDisplayName[50]; // arbitrary size
DWORD dwBufferSize = 50;
BOOL fStatus;
fStatus = GetServiceDisplayName( hscServiceMgr, (LPCTSTR) csClusterService, tszDisplayName, &dwBufferSize );
if ( fStatus == (BOOL) TRUE ) { // Is the service name "Cluster service"?
if ( _tcscmp( tszDisplayName, _T("Cluster service") ) != 0 ) { // The display name is not correct. Change it.
// Open a handle to the Service. Allow configuration changes.
hscService = OpenService( hscServiceMgr, (LPWSTR) (LPCTSTR) csClusterService, SERVICE_CHANGE_CONFIG );
// Was the handle to the service opened?
if ( hscService != NULL ) { // A valid handle to the Service was obtained.
fStatus = ChangeServiceConfig( hscService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, _T("Cluster service") );
// Was the change succesful?
if ( fStatus != (BOOL) TRUE ) { dwReturnValue = GetLastError();
ClRtlLogPrint( "Could not change the cluster service display name" " in CompleteUpgradingClusteringService." " Error: 0x%1!x!.\n", dwReturnValue ); }
// Close the handle to the Cluster Service.
CloseServiceHandle( hscService ); } // service opened?
} // display name correct?
} else { // Couldn't get the display name.
dwReturnValue = GetLastError();
ClRtlLogPrint( "Could not query the cluster service display name" " in CompleteUpgradingClusteringService." " Error: 0x%1!x!.\n", dwReturnValue ); }// got display name?
// Close the handle to the Service Control Manager.
CloseServiceHandle( hscServiceMgr ); } } // cluster service registered?
if ( dwReturnValue == (DWORD) NO_ERROR ) { // Register ClAdmWiz.
// First, get the path to the cluster directory.
CString csTemp;
csTemp = CLUSTER_DIRECTORY; TCHAR tszPathToClusterDir[_MAX_PATH]; if ( ExpandEnvironmentStrings( (LPCTSTR) csTemp, tszPathToClusterDir, _MAX_PATH ) > 0L ) { HRESULT hResult;
// Attempt to initialize the COM library.
hResult = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
if ( (hResult == S_OK) || // success
(hResult == S_FALSE) || // already initialized
(hResult == RPC_E_CHANGED_MODE) ) // previous initialization specified
// a different concurrency model
{ DWORD dwRCOrv; // RegisterCOMObject returns ERROR_SUCCESS on success.
dwRCOrv = RegisterCOMObject( TEXT("ClAdmWiz.dll"), tszPathToClusterDir ); if ( dwRCOrv != (DWORD) ERROR_SUCCESS ) { ClRtlLogPrint( "CompleteUpgradingClusteringService failed to register ClAdmWiz.dll with error %1!d!.\n", dwRCOrv ); } // ClAdmWiz registered?
else { // ERROR_SUCCESS and NO_ERROR are coincidentally the same.
ClRtlLogPrint( "CompleteUpgradingClusteringService has registered ClAdmWiz.dll.\n" ); } // ClAdmWiz registered?
// RegisterCOMObject returns ERROR_SUCCESS on success.
dwRCOrv = RegisterCOMObject( TEXT("ClNetREx.dll"), tszPathToClusterDir ); if ( dwRCOrv != (DWORD) ERROR_SUCCESS ) { ClRtlLogPrint( "CompleteUpgradingClusteringService failed to register ClNetREx.dll with error %1!d!.\n", dwRCOrv ); } // ClNetREx registered?
else { // ERROR_SUCCESS and NO_ERROR are coincidentally the same.
ClRtlLogPrint( "CompleteUpgradingClusteringService has registered ClNetREx.dll.\n" ); } // ClNetREx registered?
// RegisterCOMObject returns ERROR_SUCCESS on success.
dwRCOrv = RegisterCOMObject( TEXT("CluAdMMC.dll"), tszPathToClusterDir ); if ( dwRCOrv != (DWORD) ERROR_SUCCESS ) { ClRtlLogPrint( "CompleteUpgradingClusteringService failed to register CluAdMMC.dll with error %1!d!.\n", dwRCOrv ); } // CluAdMMC registered?
else { // ERROR_SUCCESS and NO_ERROR are coincidentally the same.
ClRtlLogPrint( "CompleteUpgradingClusteringService has registered CluAdMMC.dll.\n" ); } // CluAdMMC registered?
// Close the COM library. As per MSDN, each successfull call
// to CoInitializeEx() must be balanced ba a call to CoUnitialize().
CoUninitialize();
} // COM library initialized?
else { // Could not inititlize the COM library.
ClRtlLogPrint( "CompleteUpgradingClusteringService could not initialize the COM library. Error %1!d!.\n", hResult ); } // COM library initialized?
} // path to cluster directory obtained?
else { // Couldn't expand the environment string.
dwReturnValue = GetLastError(); ClRtlLogPrint( "CompleteUpgradingClusteringService could not register ClAdmWiz.dll\n" ); ClRtlLogPrint( "because it could not locate the cluster directory. Error %1!d!.\n", dwReturnValue ); } // path to cluster directory obtained?
}
if ( dwReturnValue == (DWORD) NO_ERROR ) { // Set the ClusterInstallationState reg value to "UPGRADED"
ClRtlSetClusterInstallState( eClusterInstallStateUpgraded ); } } // did the default failure actions get set?
} // did the connection objects get renamed?
} // Was the ImagePath set successfully?
} // Were the Welcome UI registry operations performed successfully?
} // Were the UPGRADE registry operations performed successfully?
} else { ClRtlLogPrint( "In CompleteUpgradingClusteringService since Cluster service " "has never been installed there is nothing to do.\n" );
dwReturnValue = (DWORD) NO_ERROR; } // Has Cluster service previously been installed?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CompleteStandAloneInstallationOfClusteringService
//
// Routine Description:
// This function completes the process of installing Cluster service when
// either Add/Remove Programs is running or sysocmgr.exe was launched from
// a command prompt.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::CompleteStandAloneInstallationOfClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; DWORD_PTR dwResult;
// Update the progress bar.
m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext );
// Update the "progress text"
CString csProgressText;
csProgressText.LoadString( IDS_INSTALLING_CLUS_SERVICE );
m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); // A selection state transition has occured. Perform the base
// registry operations.
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId );
// On a standalone installation, we need to send a WM_SETTINGCHANGE message to all top level windows
// so that any changes we have made to the system environment variables take effect.
// During fresh installation, this happens automatically since there is a reboot.
// See Knowledge Base article Q104011.
SendMessageTimeout( HWND_BROADCAST, // handle to window
WM_SETTINGCHANGE, // message
0, // wparam
(LPARAM) _T("Environment"), // lparam
SMTO_ABORTIFHUNG | SMTO_NORMAL, // send options
100, // time-out duration (ms). Note, each top level window can wait upto this time
&dwResult // return value for synchronous call - we ignore this
);
ClRtlLogPrint( "The base call to PerformRegistryOperations returned 0x%1!x! to CompleteStandAloneInstallationOfClusteringService.\n", dwReturnValue );
// Were the base registry operations performed successfully?
if ( dwReturnValue == (DWORD) NO_ERROR ) { // Queue the registry operations to add "Configure Cluster service"
// to the Welcome UI task list. As per Sharon Montgomery on 12/18/98 the
// Server Solutions will use those same keys.
CString csSectionName;
csSectionName = CLUSREG_KEYNAME_WELCOME_UI;
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csSectionName );
ClRtlLogPrint( "The WelcomeUI call to PerformRegistryOperations returned 0x%1!x! to CompleteStandAloneInstallationOfClusteringService.\n", dwReturnValue );
if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "In CompleteStandAloneInstallationOfClusteringService the registry operations were queued successfully.\n" );
// Since this was a standalone installation launch the Cluster Configuration
// program, cluscfg.exe.
// First, build the command line used to launch cluscfg. The specific
// command depends on whether this is an unattended operation. Regardless
// of whether this is unattended, the first part of the command line is
// the fully qualified path to cluscfg.
BOOL fReturnValue;
TCHAR tszClusCfgCommandLine[MAX_PATH];
fReturnValue = GetPathToClusCfg( tszClusCfgCommandLine );
// Was the path to cluscfg.exe deduced?
if ( fReturnValue == (BOOL) TRUE ) { // Update the progress bar.
m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext );
// Update the "progress text"
CString csProgressText;
csProgressText.LoadString( IDS_CONFIGURING_CLUS_SERVICE );
m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText );
// Is this an unattended operation?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { // This is unattended.
HINF hAnswerFile; // WARNING: NEVER close this handle because clusocm.dll
// did not open it.
// Get a handle to the answer file. WARNING: NEVER close this handle because clusocm.dll
// did not open it.
hAnswerFile = m_SetupInitComponent.HelperRoutines.GetInfHandle( INFINDEX_UNATTENDED, m_SetupInitComponent.HelperRoutines.OcManagerContext ); // Is the handle to the answer file OK?
if ( (hAnswerFile != (HINF) NULL) && (hAnswerFile != (HINF) INVALID_HANDLE_VALUE) ) { // The handle to the answer file is legal.
// tszClusCfgCommandLine is the path to cluscfg.exe. Append the "-UNATTEND"
// command line option.
CString csUnattendCommandLineOption;
csUnattendCommandLineOption = UNATTEND_COMMAND_LINE_OPTION;
_tcscat( tszClusCfgCommandLine, _T(" ") ); _tcscat( tszClusCfgCommandLine, csUnattendCommandLineOption );
// Append the path to the answer file.
_tcscat( tszClusCfgCommandLine, _T(" ") ); _tcscat( tszClusCfgCommandLine, m_SetupInitComponent.SetupData.UnattendFile ); } // handle to answer file OK?
} // unattended?
// Launch cluscfg - tszClusCfgCommandLine contains the command, either
// attended or unattended.
STARTUPINFO StartupInfo;
ZeroMemory( &StartupInfo, sizeof( StartupInfo ) ); StartupInfo.cb = sizeof( StartupInfo );
PROCESS_INFORMATION ProcessInformation;
fReturnValue = CreateProcess( (LPCTSTR) NULL, (LPTSTR) tszClusCfgCommandLine, (LPSECURITY_ATTRIBUTES) NULL, (LPSECURITY_ATTRIBUTES) NULL, (BOOL) FALSE, (DWORD) (CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT), (LPVOID) GetEnvironmentStrings(), (LPCTSTR) NULL, (LPSTARTUPINFO) &StartupInfo, (LPPROCESS_INFORMATION) &ProcessInformation );
// Was the process created?
if ( fReturnValue == (BOOL) TRUE ) { // Wait for cluscfg.exe to complete. As per Pat Styles on 7/6/98,
// an OCM Setup DLL MUST wait for any process it spawns to complete.
DWORD dwWFSOrv;
dwWFSOrv = WaitForSingleObject( ProcessInformation.hProcess, INFINITE ); } else { DWORD dwErrorCode;
dwErrorCode = GetLastError();
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) == (DWORDLONG) 0L ) { CString csMessage;
csMessage.Format( IDS_ERROR_SPAWNING_CLUSCFG, dwErrorCode );
AfxMessageBox( csMessage ); }
ClRtlLogPrint( "Error %1!d! occured attempting to spawn cluscfg.exe.\n", dwErrorCode ); } // Was the process created?
} // Path to cluscfg obtained?
} // WelcomeUI registry operations succeeded?
} // Were the base registry operations performed successfully?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CompleteInstallingClusteringService
//
// Routine Description:
// This function performs much of the processing when OC_COMPLETE_INSTALLATION
// has been received and a clean installation is being performed.
//
// Arguments:
// ptszSubComponentId - points to a string that uniquely identifies a sub-
// component in the component's hiearchy.
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::CompleteInstallingClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue;
// Update the progress bar.
if ( GetStepCount() != 0L ) { // Update the progress meter.
m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext ); }
// Update the "progress text"
CString csProgressText;
csProgressText.LoadString( IDS_INSTALLING_CLUS_SERVICE );
m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText );
// Perform the base registry operations.
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId );
ClRtlLogPrint( "The base call to PerformRegistryOperations returned 0x%1!x! to CompleteInstallingClusteringService.\n", dwReturnValue );
// Were the base registry operations performed successfully?
if ( dwReturnValue == (DWORD) NO_ERROR ) { // Queue the registry operations to add "Configure Cluster service"
// to the Welcome UI task list. As per Sharon Montgomery on 12/18/98, Server
// Solutions will use those same keys.
CString csSectionName;
csSectionName = CLUSREG_KEYNAME_WELCOME_UI;
dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csSectionName );
ClRtlLogPrint( "The WelcomeUI call to PerformRegistryOperations returned 0x%1!x! to CompleteInstallingClusteringService.\n", dwReturnValue ); } // Were the base registry operations performed successfully?
// Were the Welcome UI registry operations performed successfully?
if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "In CompleteInstallingClusteringService for a clean install all registery operations were performed successfully.\n" ); } else { ClRtlLogPrint( "In CompleteInstallingClusteringService for a clean install the registry operations were not performed successfully.\n" ); }
// For unattended setup this needs to write the WelcomeUI reg keys in a way that
// will cause Configure Your Server to launch cluscfg.exe in unattended mode.
// revisions to CompleteInstallingClusteringService may be appropriate prior to this code segment.
// Were all registry operations performed correctly?
if ( dwReturnValue == (DWORD) NO_ERROR ) { LONG lReturnValue; // used below for registry operations
HKEY hKey; // used below for registry operations
// Is this an unattended installation?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { HINF hAnswerFile; // WARNING: NEVER close this handle because clusocm.dll
// did not open it.
// Get a handle to the answer file. WARNING: NEVER close this handle because clusocm.dll
// did not open it.
hAnswerFile = m_SetupInitComponent.HelperRoutines.GetInfHandle( INFINDEX_UNATTENDED, m_SetupInitComponent.HelperRoutines.OcManagerContext );
if ( (hAnswerFile != (HINF) NULL) && (hAnswerFile != (HINF) INVALID_HANDLE_VALUE) ) { // Write the WelcomeUI registry entry to launch cluscfg in unattended mode.
// Note that the registry operations performed when CLUSREG_KEYNAME_WELCOME_UI
// was passed to PerformRegistryOperations() above included creation of
// HKLM\Software\Microsoft\Windows NT\CurrentVersion\Setup\OCManager\ToDoList\Cluster\ConfigCommand.
// All that is necessary is to create the ConfigArgs value with the
// appropriate command line arguments to execute cluscfg.exe in unattended mode.
HKEY hKey;
DWORD dwType;
// Attempt to open an existing key in the registry.
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( "Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\OCManager\\ToDoList\\Cluster" ), (DWORD) 0, // reserved
KEY_SET_VALUE, &hKey );
// Was the Cluster "to do list" registry key opened?
if ( lReturnValue == (LONG) ERROR_SUCCESS ) { // The key is open.
TCHAR tszValue[MAX_PATH];
_tcscpy( tszValue, UNATTEND_COMMAND_LINE_OPTION );
// Append the path to the answer file.
_tcscat( tszValue, _T(" ") ); _tcscat( tszValue, m_SetupInitComponent.SetupData.UnattendFile );
// Set the key.
DWORD dwValueLength;
dwValueLength = (DWORD) ((_tcslen( tszValue ) + 1) * sizeof( TCHAR ));
lReturnValue = RegSetValueEx( hKey, _T( "ConfigArgs" ), (DWORD) 0L, // reserved
(DWORD) REG_EXPAND_SZ, (CONST BYTE *) tszValue, dwValueLength );
// Was the key written successfully?
if ( lReturnValue == (LONG) ERROR_SUCCESS ) { dwReturnValue = (DWORD) NO_ERROR;
ClRtlLogPrint( "CompleteInstallingClusteringService created the ConfigArgs reg key for unattended operation of cluscfg.\n" ); } else { dwReturnValue = GetLastError();
ClRtlLogPrint( "CompleteInstallingClusteringService failed to create the ConfigArgs reg key with error code %1!d!.\n", dwReturnValue ); } // Was the reg key written successfully?
// Close the registry key.
RegCloseKey( hKey ); // do we care about the return value?
} else { // The key could not be opened.
DWORD dwErrorCode;
dwErrorCode = GetLastError();
ClRtlLogPrint( "In CompleteInstallingClusteringService RegOpenKeyEx failed with error code 0x%1!x!.\n", dwErrorCode ); } // Was the key opened?
} // handle to answer file OK?
} // Is this unattended?
else { // This is NOT unattended, so make sure the ConfigArgs value is removed.
// That will prevent any value left from a previous unattended install
// which was never successfully configured from corrupting the behavior
// of Configure Your Server or Add/Remove Programs.
// Attempt to open an existing key in the registry.
lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\OCManager\\ToDoList\\Cluster"), (DWORD) 0, // reserved
KEY_READ, &hKey );
// Was the Cluster "to do list" registry key opened?
if ( lReturnValue == (long) ERROR_SUCCESS ) { // Delete the values (which may not exist).
RegDeleteValue( hKey, (LPCTSTR) TEXT("ConfigArgs") ); } // if ToDoList opened
} // if unattended?
} // registry operations successfull?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// OnOcCleanup
//
// Routine Description:
// This function processes the OC_CLEANUP messages by removing the "private"
// subdirectory in the cluster directory if it is empty and the cluster directory
// itself it it is empty.
//
// Arguments:
// none
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// Any other value is a standard Win32 error code.
//
// Note:
// The "private" subrirectory may exist in the cluster directory if the system
// was upgraded from NT4.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::OnOcCleanup( void ) { ClRtlLogPrint( "Processing OC_CLEANUP.\n" );
DWORD dwReturnValue;
dwReturnValue = CleanupDirectories();
ClRtlLogPrint( "\n\n\n\n" );
// Close the log file.
ClRtlCleanup();
// Set the ClusterLog environment variable back to its' original state.
char szLogFileName[MAX_PATH];
strcpy( szLogFileName, "ClusterLog=" );
if ( *m_szOriginalLogFileName != (char) '\0' ) { strcat( szLogFileName, m_szOriginalLogFileName ); }
_putenv( szLogFileName );
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CleanupDirectories
//
// Routine Description:
// This function removes the "private" subdirectory that may remain in the
// cluster directory if the system has ever been upgraded from NT 4 if that
// directory is empty. It will also remove the cluster directory itself if
// it is empty.
//
// Arguments:
// none
//
// Return Value:
// (DWORD) NO_ERROR - indicates success
// (DWORD) -1L - means that the cluster directory was not empty
// Any other value is a standard Win32 error code.
//
// Note:
// This function assumes that the path to the cluster directory is less than
// MAX_PATH TCHARs long and that function SetupGetTargetPath expects the fifth
// parameter to specify the number of TCHARS (not bytes) in the buffer.
//
// No recovery is attempted if the call to SetupGetTargetPath fails.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::CleanupDirectories( void ) { DWORD dwReturnValue = (DWORD) NO_ERROR;
BOOL fReturnValue;
DWORD dwRequiredSize;
TCHAR tszPathToClusterDirectory[MAX_PATH];
// First, get the path to the cluster directory.
CString csSectionName;
csSectionName = CLUSTER_FILES_INF_KEY;
fReturnValue = SetupGetTargetPath( m_SetupInitComponent.ComponentInfHandle, (PINFCONTEXT) NULL, (PCTSTR) csSectionName, tszPathToClusterDirectory, (DWORD) MAX_PATH, (PDWORD) &dwRequiredSize );
// Was the path to the cluster directory obtained?
if ( fReturnValue == (BOOL) TRUE ) { // Test for the presence of a subdirectory named "private".
TCHAR tszPathToPrivateDirectory[MAX_PATH];
// Append "\private" to the path to the cluster directory that was obtained above.
// Note, I didn't put this string in the stringtable because it is
// not localizable, and will never change.
_tcscpy( tszPathToPrivateDirectory, tszPathToClusterDirectory ); _tcscat( tszPathToPrivateDirectory, _T("\\private") );
HANDLE hFindHandle; WIN32_FIND_DATA FindData;
// Does a file or directory named "private" exist?
hFindHandle = FindFirstFile( (LPCTSTR) tszPathToPrivateDirectory, &FindData );
if ( hFindHandle != (HANDLE) INVALID_HANDLE_VALUE ) { // A file named "private" exists in the cluster directory. Is it a directory?
if ( (FindData.dwFileAttributes & (DWORD) FILE_ATTRIBUTE_DIRECTORY) != (DWORD) 0L ) { // The "private" directory has been located. Determine whether it is empty.
if ( IsDirectoryEmpty( tszPathToPrivateDirectory ) == (BOOL) TRUE ) { // No file was found in the "private" directory. Remove the "private" directory.
if ( RemoveDirectory( (LPCTSTR) tszPathToPrivateDirectory ) == (BOOL) TRUE ) { dwReturnValue = (DWORD) NO_ERROR; } else { // Could not delete the "private" directory even though it was empty.
dwReturnValue = GetLastError();
ClRtlLogPrint( "CleanupDirectories was unable to remove %1!s!. The error code is %2!x!.\n", tszPathToPrivateDirectory, dwReturnValue ); } // Was the "private" directory removed successfully?
} else { ClRtlLogPrint( "CleanupDirectories did not attempt to remove %1!s! because it is not empty.\n", tszPathToPrivateDirectory );
// An error code of -1 means the "private" directory, and thus the
// cluster directory was not empty.
dwReturnValue = (DWORD) -1L; } // Was any file found in the "private" directory?
} else { ClRtlLogPrint( "In CleanupDirectories the file named private is not a directory.\n");
dwReturnValue = (DWORD) ERROR_FILE_NOT_FOUND; } // Is it a directory?
// Close the search that located the "private" directory.
FindClose( hFindHandle ); } else { dwReturnValue = GetLastError();
ClRtlLogPrint( "CleanupDirectories was unable to find any file named private in the cluster directory.\n"); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwReturnValue ); } // Does a file or directory named "private" exist?
// Was the "private" directory absent or removed successfully?
if ( (dwReturnValue == (DWORD) ERROR_FILE_NOT_FOUND) || (dwReturnValue == (DWORD) NO_ERROR) ) { // The "private" directory either did not exist or has been removed.
// Now determine whether the cluster directory is empty and therefore should be removed.
// This code segment assumes that the cluster directory, as specified
// in tszPathToClusterDirectory exists.
if ( IsDirectoryEmpty( tszPathToClusterDirectory ) == (BOOL) TRUE ) { // The cluster directory is empty.
if ( RemoveDirectory( tszPathToClusterDirectory ) == (BOOL) TRUE ) { dwReturnValue = (DWORD) NO_ERROR; } else { // Could not delete the "cluster" directory even though it was empty.
dwReturnValue = GetLastError();
ClRtlLogPrint( "CleanupDirectories was unable to remove %1!s!. The error code is %2!x!.\n", tszPathToClusterDirectory, dwReturnValue ); } // Was the cluster directory removed?
} else { // The cluster directory is not empty.
ClRtlLogPrint( "CleanupDirectories did not attempt to remove %1!s! because it is not empty.\n", tszPathToClusterDirectory );
// An error code of -1 means the cluster directory was not empty.
dwReturnValue = (DWORD) -1L; } // Is the cluster directory empty?
} // Was the "private" directory absent or removed?
} else { dwReturnValue = GetLastError();
ClRtlLogPrint( "CleanupDirectories was unable to obtain the path to the cluster directory.\n"); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwReturnValue ); } // Was the path to the cluster directory obtained?
return ( dwReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// IsDirectoryEmpty
//
// Routine Description:
// This function determines whether a directory is empty.
//
// Arguments:
// ptzDirectoryName - points to a string specifying the directory name.
//
// Return Value:
// TRUE - indicates that the directory is empty
// FALSE - indicates that the directory is not empty
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::IsDirectoryEmpty( LPCTSTR ptszDirectoryName ) { BOOL fDirectoryEmpty;
HANDLE hFindHandle;
WIN32_FIND_DATA FindData;
TCHAR tszFindPath[MAX_PATH];
// Append the file specification to the path supplied.
_tcscpy( tszFindPath, ptszDirectoryName ); _tcscat( tszFindPath, _T("\\*") );
hFindHandle = FindFirstFile( tszFindPath, &FindData );
// Was any file found in the directory?
if ( hFindHandle != (HANDLE) INVALID_HANDLE_VALUE ) { // A file was found. It could be "." or "..".
if ( (_tcscmp( FindData.cFileName, _T(".") ) == 0) || (_tcscmp( FindData.cFileName, _T("..") ) == 0) ) { // The file was either "." or "..". Keep trying.
DWORD dwErrorCode;
if ( FindNextFile( hFindHandle, &FindData ) == (BOOL) TRUE ) { // A file was found. It could be "." or "..".
if ( (_tcscmp( FindData.cFileName, _T(".") ) == 0) || (_tcscmp( FindData.cFileName, _T("..") ) == 0) ) { // The file was either "." or "..". Keep trying.
if ( FindNextFile( hFindHandle, &FindData ) == (BOOL) TRUE ) { // The file cannot possibly be either "." or "..".
fDirectoryEmpty = (BOOL) FALSE; } else { dwErrorCode = GetLastError();
if ( dwErrorCode == (DWORD) ERROR_NO_MORE_FILES ) { fDirectoryEmpty = (BOOL) TRUE; } else { fDirectoryEmpty = (BOOL) FALSE; } } } else { // The private directory is not empty.
fDirectoryEmpty = (BOOL) FALSE; } } else { dwErrorCode = GetLastError();
if ( dwErrorCode == ERROR_NO_MORE_FILES ) { fDirectoryEmpty = (BOOL) TRUE; } else { fDirectoryEmpty = (BOOL) FALSE; } } } else { // The directory is not empty.
fDirectoryEmpty = (BOOL) FALSE; }
// Close the search.
FindClose( hFindHandle ); } else { fDirectoryEmpty = (BOOL) TRUE; }
return ( fDirectoryEmpty ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// GetPathToClusCfg
//
// Routine Description:
// This function deduces the path to the Cluster service configuration
// program, cluscfg.exe.
//
// It assumes that if the Cluster service is registered with the Service
// COntrol Manager then cluscfg.exe is located in the directory with clussvc.exe.
// Otherwise it expands %windir%\cluster and declares that to be the path.
//
// The name of the Cluster service configuration pogram is then appended to
// the path.
//
// Arguments:
// lptszPathToClusCfg - points to a string in which the fully qualified path to
// the Cluster service configuration program is to be placed.
//
// Return Value:
// TRUE - indicates that lptszPathToClusCfg is valid
// FALSE - indicates that an error occured
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusocmApp::GetPathToClusCfg( LPTSTR lptszPathToClusCfg ) { BOOL fReturnValue;
// If the Cluster service is registered assume that cluscfg is in
// the same directory as clussvc.exe and get the path to cluscfg.exe
// from the Service Control Mamager. If the Cluster service is not
// registered, assume cluscfg.exe is in %windir%\cluster.
if ( IsClusterServiceRegistered() == (BOOL) TRUE ) { // Query the path to the Cluster Service executable from the Service Control Manager.
CString csClusterService;
csClusterService = CLUSTER_SERVICE_NAME;
fReturnValue = GetServiceBinaryPath( (LPWSTR) (LPCTSTR) csClusterService, lptszPathToClusCfg ); } else { // Use the default location.
CString csClusterDirectory;
csClusterDirectory = CLUSTER_DIRECTORY;
if ( ExpandEnvironmentStrings( (LPCTSTR) csClusterDirectory, lptszPathToClusCfg, MAX_PATH ) > 0L ) { fReturnValue = (BOOL) TRUE; } else { // Could not expand the enviornment string. The default location for the
// Cluster service could not be determined.
fReturnValue = (BOOL) FALSE; } // Was the default location for cluscfg.exe determined?
} // Where is cluscfg.exe?
// Was the path to cluscfg.exe deduced?
if ( fReturnValue == (BOOL) TRUE ) { // tszPathToClusCfg is the path to cluscfg.exe. Append the program name.
CString csClusterConfigurationProgram;
csClusterConfigurationProgram = CLUSTER_CONFIGURATION_PGM;
_tcscat( lptszPathToClusCfg, _T("\\") ); _tcscat( lptszPathToClusCfg, csClusterConfigurationProgram ); } else { // Set the target string empty.
*lptszPathToClusCfg = _T( '\0' ); }
return ( fReturnValue ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// CalculateStepCount
//
// Routine Description:
// This function calculates the "step count", i.e the number of non-file operations
// to be performed when processing OC_COMPLETE_INSTALLATION.
//
// Arguments:
// ptszSubComponentId
//
// Return Value:
// The number of non-file operation (steps) to perform when processing
// OC_COMPLETE_INSTALLATION
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::CalculateStepCount( LPCTSTR ptszSubComponentId ) { DWORD dwStepCount;
// Is this UNATTENDED or ATTENDED?
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { // This is UNATTENDED.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { // This is UPGRADE. Cluscfg.exe will not be launched, so there is
// one non-file operation to be performed, registry revisions.
dwStepCount = 1L; } else { // This is a clean INSTALL. Cluscfg.exe may be launched.
// Is there a [cluster] section in the answer file? BUGBUG
// so there
// are two non-file operations to be performed, registry revisions
// and execution of cluscfg.
dwStepCount = 2L; } // unattended: upgrade or install?
} else { // This is ATTENDED.
if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) != (DWORDLONG) 0L ) { // This is STANDALONE. Is Cluster service selected?
BOOL fCurrentSelectionState; BOOL fOriginalSelectionState;
fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT ); fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL );
// Was there a selection state transition?
if ( fCurrentSelectionState != fOriginalSelectionState ) { if ( fCurrentSelectionState == (BOOL) TRUE ) { // Currently selected - This is a clean install. There are two
// non-file operations to be performed, registry revisions and
// execution of cluscfg.exe.
dwStepCount = 2L; } else { // Currently not selected - This is an uninstall. There is one
// non-file operation to be performed, registry revisions.
dwStepCount = 1L; } // currently selected?
dwStepCount = 1L; } else { // There was no selection state transition, therefore there
// are zero non-file operations to be performed.
dwStepCount = 0L; } // selection state transition?
} else { // This is GUI mode setup. The two operations that may be performed
// during GUI mode setup are clean install and upgrade. In neither
// case does cluscfg.exe get launched. Thus, there is one non-file
// operation to be performed, registry revisions.
dwStepCount = 1L; } // interactive: standalone or atttended?
} // Is this UNATTENDED or ATTENDED?
return ( dwStepCount ); }
/////////////////////////////////////////////////////////////////////////////
//++
//
// SetStepCount
//
// Routine Description:
// This function sets the value of the m_dwStepCount member of the CClusocmApp object.
//
// Arguments:
// dwStepCount - the value to which the m_dwStepCount member should be set.
//
// Return Value:
// None
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusocmApp::SetStepCount( DWORD dwStepCount ) { m_dwStepCount = dwStepCount; }
/////////////////////////////////////////////////////////////////////////////
//++
//
// GetStepCount
//
// Routine Description:
// This function returns the content of the m_dwStepCount member of the
// CClusocmApp object.
//
// Arguments:
// None
//
// Return Value:
// The content of the m_dwStepCount member.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD CClusocmApp::GetStepCount( void ) { return ( m_dwStepCount ); }
|