windows-nt/Source/XPSP1/NT/base/cluster/setup/clusocm/clusocm.cpp
2020-09-26 16:20:57 +08:00

6143 lines
218 KiB
C++

/////////////////////////////////////////////////////////////////////////////
//
// 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,
&registryNodeNameLength );
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 );
}