///////////////////////////////////////////////////////////////////////////// // // 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 #include #include #include #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. // // // // Do you want to continue with uninstall? CString csLastPartOfMessage; CString csMessageBoxTitle; csLastPartOfMessage.LoadString(IDS_ERR_UNINSTALL_OTHER_SW_EXT); csNonStandardResourceTypeList += csLastPartOfMessage; // BUGBUG - The next two statements are commented out temporarily because the localization // changes needed for this have not been approved. Uncomment after NT 5.0. // Also restore clusocm.rc@v2 and resource.h@v2 // (Vvasu 05-Jan-2000 // csMessageBoxTitle.LoadString(IDS_TITLE_CUSTOM_RESTYPES); //::MessageBox( NULL, // csNonStandardResourceTypeList, // csMessageBoxTitle, // MB_OK | MB_ICONINFORMATION // ); ClRtlLogPrint( "CheckForClusterResourceTypes detected custom resources.\n" ); } // Were non-standard resource typed detected? else { ClRtlLogPrint( "CheckForCustomResourceTypes did not detect any custom resource types,\n" ); } } // Was there an error enumerating the ResourceTypes key? else { ClRtlLogPrint( "CheckForCustomResourceTypes was unable to enumerate the resource types,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); ClRtlLogPrint( "The error code is %1!d!.\n", lReturnValue ); // An error occured while enumerating the ResourceTypes sub-keys. RegCloseKey( hResourceTypesKey ); RegCloseKey( hClusterKey ); // The uninstall operation should continue because the installation // is apparently defective. fReturnValue = (BOOL) TRUE; } // Was there an error enumerating the ResourceTypes key? } else { // The initial call to RegEnumKeyEx failed. // Issue an error message and exit. RegCloseKey( hClusterKey ); ClRtlLogPrint( "CheckForCustomResourceTypes was unable to enumerate the resource types,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); ClRtlLogPrint( "The error code is %1!d!.\n", lReturnValue ); // A return value of TRUE will allow the uninstall operation to // continue. fReturnValue = (BOOL) TRUE; } // Did RegEnumKeyEx open the ResourceTypes key? } // Was the ResourceTypes sub key opened? else { // The ResourceTypes sub key could not be opened. // Issue an error message and exit. RegCloseKey( hClusterKey ); ClRtlLogPrint( "CheckForCustomResourceTypes was unable to open the Cluster\\ResourceTypes key,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); // A return value of TRUE will allow the uninstall operation to // continue. fReturnValue = (BOOL) TRUE; } // Was the ResourceTypes sub key opened? } // cluster hive opened? else { // The Cluster registry key could not be opened, even after possibly // attempting to load the cluster hive. ClRtlLogPrint( "CheckForCustomResourceTypes was unable to open the Cluster key,\n" ); ClRtlLogPrint( "implying that no custom resource types exist, so the uninstall will continue.\n" ); ClRtlLogPrint( "The error code is %1!d!.\n", lReturnValue ); // A return value of TRUE will allow the uninstall operation to // continue. fReturnValue = (BOOL) TRUE; } // Second test whether the Cluster registry key was opened successfully. if ( fClusterHiveLoadedByThisFunction == (BOOL) TRUE ) { UnloadClusDB(); } return ( fReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // UnloadClusDB // // Routine Description: // This function unloads the Cluster hive. // // Arguments: // None // // Return Value: // None // // Note: // This function was originally in newsetup\utils.cpp. // //-- ///////////////////////////////////////////////////////////////////////////// VOID CClusocmApp::UnloadClusDB( VOID ) { DWORD Status; BOOLEAN WasEnabled; Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled); if ( Status == ERROR_SUCCESS ) { LONG lReturnValue; CString csClusterRegKey; csClusterRegKey = CLUSREG_KEYNAME_CLUSTER; lReturnValue = RegUnLoadKeyW(HKEY_LOCAL_MACHINE, csClusterRegKey ); RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled); } } PWCHAR WellKnownResourceTypes[] = { CLUS_RESTYPE_NAME_GENAPP, CLUS_RESTYPE_NAME_GENSVC, CLUS_RESTYPE_NAME_FTSET, CLUS_RESTYPE_NAME_PHYS_DISK, CLUS_RESTYPE_NAME_IPADDR, CLUS_RESTYPE_NAME_NETNAME, CLUS_RESTYPE_NAME_FILESHR, CLUS_RESTYPE_NAME_PRTSPLR, CLUS_RESTYPE_NAME_TIMESVC, CLUS_RESTYPE_NAME_LKQUORUM, CLUS_RESTYPE_NAME_DHCP, CLUS_RESTYPE_NAME_MSMQ, CLUS_RESTYPE_NAME_NEW_MSMQ, CLUS_RESTYPE_NAME_MSDTC, CLUS_RESTYPE_NAME_WINS, CLUS_RESTYPE_NAME_IIS4, CLUS_RESTYPE_NAME_SMTP, CLUS_RESTYPE_NAME_NNTP, (PWCHAR) UNICODE_NULL }; ///////////////////////////////////////////////////////////////////////////// //++ // // TryToRecognizeResourceType // // Routine Description: // This function determines whether the resource type whose name is in // parameter "keyname" is recognized as a standard resource type as defined // in clusudef.h. // // This function builds a list of unrecognized reaource types in the CString // referenced by parameter "str". // // Arguments: // str - a reference to a CString in which to build a list of unrecognized // resource types. // keyname - points to a string that contains the resource type name. // // Return Value: // None // // Note: // This function was excerpted verbatim from newsetup\utils.cpp. // //-- ///////////////////////////////////////////////////////////////////////////// VOID CClusocmApp::TryToRecognizeResourceType( CString& str, LPTSTR keyname ) { PWCHAR * ppName = WellKnownResourceTypes; while ( *ppName != (PWCHAR)UNICODE_NULL ) { if ( lstrcmp( keyname, *ppName) == 0 ) return; ++ppName; } if ( !str.IsEmpty() ) str += L", "; else { str.LoadString(IDS_ERR_UNINSTALL_OTHER_SW); str += _T('\n'); } str += keyname; } ///////////////////////////////////////////////////////////////////////////// //++ // // GetServiceBinaryPath // // Routine Description: // This function retrieves the fully qualified path to a Service // from the Service Control Manager. // // Arguments: // lpwszServiceName - points to a wide character string containing the service name // lptszBinaryPathName - points to a string to receive the fully qualified // path to the Cluster Service. // // Return Value: // TRUE - The path to the Cluster Service was obtained successfully. // FALSE - The path to the Cluster Service was not obtained. // // Note: // Calling this function makes sense IFF the Cluster Service is registered // with the Service Control Manager. Call IsClusterServiceRegistered() to // ascertain that BEFORE calling GetServiceBinaryPath. // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusocmApp::GetServiceBinaryPath( IN LPWSTR lpwszServiceName, OUT LPTSTR lptszBinaryPathName ) { BOOL fReturnValue; DWORD dwErrorCode = (DWORD) ERROR_SUCCESS; if ( lpwszServiceName != (LPWSTR) NULL ) { SC_HANDLE hscServiceMgr; SC_HANDLE hscService; // Connect to the Service Control Manager and open the specified service // control manager database. hscServiceMgr = OpenSCManager( NULL, NULL, GENERIC_READ | GENERIC_WRITE ); // Was the service control manager database opened successfully? if ( hscServiceMgr != NULL ) { // The service control manager database is open. // Open a handle to the Service. hscService = OpenService( hscServiceMgr, lpwszServiceName, GENERIC_READ ); // Was the handle to the service opened? if ( hscService != NULL ) { // A valid handle to the Service was obtained. DWORD dwBufferSize; // Note that the size of the buffer required to get the service configuration // information was determined empherically to be 240 bytes. LPQUERY_SERVICE_CONFIG lpServiceConfig; dwBufferSize = (DWORD) sizeof( QUERY_SERVICE_CONFIG ) + 256; // The delta from the // size of the structure // was chosen arbitrarily. // Attempt to allocate the buffer. lpServiceConfig = (LPQUERY_SERVICE_CONFIG) LocalAlloc( LMEM_ZEROINIT, dwBufferSize ); // Was the bufer allocated successfully? if ( lpServiceConfig != (LPQUERY_SERVICE_CONFIG) NULL ) { // The following call to QueryServiceConfig should return something // usefull. If it fails it is probably because the guess at the size // of the buffer is too small. fReturnValue = QueryServiceConfig( hscService, lpServiceConfig, dwBufferSize, (LPDWORD) &dwBufferSize ); // Was the Service configuration info obtained? if ( fReturnValue == (BOOL) TRUE ) { // Update the output parameter. _tcscpy( lptszBinaryPathName, lpServiceConfig->lpBinaryPathName ); // lptszBinaryPathName includes the service name. Strip that off. LPTSTR ptszTemp; ptszTemp = _tcsrchr( (const wchar_t *) lptszBinaryPathName, (int) _T('\\') ); *ptszTemp = _T('\0'); ClRtlLogPrint( "In GetServiceBinaryPath the first call to QueryServiceConfig succeeded.\n" ); } else { // Was the buffer too small? dwErrorCode = GetLastError(); if ( dwErrorCode == (DWORD) ERROR_INSUFFICIENT_BUFFER ) { ClRtlLogPrint( "GetServiceBinaryPath is enlarging the buffer for the QUERY_SERVICE_CONFIG structure.\n" ); // Increase the size of the buffer. HLOCAL hLocalBlock; // As per RodGa LocalReAlloc is not always reliable. Here that functionality // is implemented by freeing the buffer for the QUERY_SERVICE_CONFIG struct // and allocating a nre block using LocalFree and LocalAlloc. hLocalBlock = LocalFree( lpServiceConfig ); if ( hLocalBlock == (HLOCAL) NULL ) { // The previously allocated block has been released. Now, // allocate a block of the proper size. lpServiceConfig = (LPQUERY_SERVICE_CONFIG) LocalAlloc( LMEM_ZEROINIT, (UINT) dwBufferSize ); // Was the larger buffer allocated successfully? if ( lpServiceConfig != (LPQUERY_SERVICE_CONFIG) NULL ) { // The following call to QueryServiceConfig should return something // usefull. If it fails it is not because the buffer is too small. fReturnValue = QueryServiceConfig( hscService, lpServiceConfig, dwBufferSize, (LPDWORD) &dwBufferSize ); // Was the Service configuration info obtained this time. if ( fReturnValue == (BOOL) TRUE ) { // Update the output parameter. _tcscpy( lptszBinaryPathName, lpServiceConfig->lpBinaryPathName ); // lptszBinaryPathName includes the service name. Strip that off. LPTSTR ptszTemp; ptszTemp = _tcsrchr( (const wchar_t *) lptszBinaryPathName, (int) _T('\\') ); *ptszTemp = _T('\0'); ClRtlLogPrint( "In GetServiceBinaryPath the second call to QueryServiceConfig succeeded.\n" ); } else { // The Service configuration info was not obtained. dwErrorCode = GetLastError(); ClRtlLogPrint( "In GetServiceBinaryPath the second call to QueryServiceConfig failed\n" ); ClRtlLogPrint( "with error code 0x%1!x!,\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } // second call to QueryServiceConfig succeeded? } else { // The attempt to enlarge the buffer failed. dwErrorCode = GetLastError(); ClRtlLogPrint( "GetServiceBinaryPath was unable to enlarge the buffer for the QUERY_SERVICE_CONFIG structure.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } } else { dwErrorCode = GetLastError(); ClRtlLogPrint( "In GetServiceBinaryPath the call to LocalFree failed with error code 0x%1!x!.\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } } else { // Some other error occured. ClRtlLogPrint( "In GetServiceBinaryPath the first call to QueryServiceConfig failed\n" ); ClRtlLogPrint( "with error code 0x%1!x!.\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } // Was the buffer to small? } // Service config info obtained from first call to QueryServiceConfig? // Free the buffer if it was ever allocated successfully. if ( lpServiceConfig != (LPQUERY_SERVICE_CONFIG) NULL ) { LocalFree( lpServiceConfig ); } } else { // Could not allocate the buffer. dwErrorCode = GetLastError(); ClRtlLogPrint( "GetServiceBinaryPath could not allocate a buffer for the QUERY_SERVICE_CONFIG structure.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } // Was the buffer allocated successfully? // Close the handle to the Cluster Service. CloseServiceHandle( hscService ); } else { // The Service could not be opened. dwErrorCode = GetLastError(); ClRtlLogPrint( "GetServiceBinaryPath could not open an handle to %1!ws!.\n", lpwszServiceName ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } // Was the Cluster Service opened? // Close the handle to the Service Control Manager. CloseServiceHandle( hscServiceMgr ); } else { // The Service Control Manager could not be opened. dwErrorCode = GetLastError(); ClRtlLogPrint( "GetServiceBinaryPath could not open the Service Control Manager.\n" ); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwErrorCode ); fReturnValue = (BOOL) FALSE; } // Was the Service Control Manager opened? } else { // The service name pointer was bogus. ClRtlLogPrint( "The service name passed to GetServiceBinaryPath is invalid.\n" ); fReturnValue = FALSE; } // Is the service name legal? if ( fReturnValue == (BOOL) FALSE ) { // Set the binary path invalid. *lptszBinaryPathName = _T('\0'); } else { ClRtlLogPrint( "GetServiceBinaryPath located %1!ws! at %2!s!.\n", lpwszServiceName, lptszBinaryPathName ); } SetLastError( dwErrorCode ); // Set the "last" error code (which may be ERROR_SUCCESS) // because this function's caller is likely to call GetLastError(). return ( fReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // SetDirectoryIds // // Routine Description: // This function associates the user defined Directory Identifiers in the // [DestinationDirs] section of the component INF file with particular // directories, either the default location for NT 5 installations, // %windir%\cluster, or the location of a previous installation. // // Arguments: // fClusterServiceRegistered - TRUE indicates that Cluster service has // previously been installed and the files should // be updated in place. // // FALSE - indicates that the Cluster service // files should be installed into the default // location. // // Return Value: // TRUE - indicates success // FALSE - indicates error // // Note: // The [DestinationDirs] section in clusocm.inf contains the following keys: // // ClusterFiles = 33001 // ClusterUpgradeFiles = 33002 // ClusterAdminFiles = 33003 // ClusterUninstallFiles = 33004 // // Those directory IDs were chosen to be larger than DIRID_USER. //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusocmApp::SetDirectoryIds( BOOL fClusterServiceRegistered ) { BOOL fReturnValue; TCHAR tszClusterServiceBinaryPath[MAX_PATH]; // Are Cluster service files already present? if ( fClusterServiceRegistered == (BOOL) TRUE ) { // Cluster service files should be upgraded in place. // Query the path to the Cluster Service executable from the Service Control Manager. CString csClusterService; csClusterService = CLUSTER_SERVICE_NAME; fReturnValue = GetServiceBinaryPath( (LPWSTR) (LPCTSTR) csClusterService, tszClusterServiceBinaryPath ); if ( fReturnValue == (BOOL) FALSE ) { DWORD dwErrorCode; dwErrorCode = GetLastError(); ClRtlLogPrint( "In SetDirectoryIds the call to GetServiceBinaryPath failed with error code 0x%1!x!.\n", dwErrorCode ); } } else { // Cluster service files should be installed in the default location. CString csClusterDirectory; csClusterDirectory = CLUSTER_DIRECTORY; if ( ExpandEnvironmentStrings( (LPCTSTR) csClusterDirectory, tszClusterServiceBinaryPath, MAX_PATH ) > 0L ) { fReturnValue = (BOOL) TRUE; } else { // Could not expand the enviornment string. The default location for the // Cluster service could not be determined. fReturnValue = (BOOL) FALSE; DWORD dwErrorCode; dwErrorCode = GetLastError(); ClRtlLogPrint( "ExpandEnvironmentString returned 0x%1!x! to SetDirectoryIds.\n", dwErrorCode ); } // Was the default location for the Cluster service determined? } // Where should Cluster service files be installed? // Was the location into which Cluster service files should be copied obtained? if ( fReturnValue == (BOOL) TRUE ) { // Associate selected Directory Ids with the path in tszClusterServiceBinaryPath. // Set the Directory Id for the ClusterFiles key in [DestinationDirs]. fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33001, (PCTSTR) tszClusterServiceBinaryPath ); // Was the Directory Id for the ClusterFiles key set successfully? if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33001 was set to %1!s!.\n", tszClusterServiceBinaryPath ); // Set the Directory Id for the ClusterUpgradeFiles key in [DestinationDirs]. fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33002, (PCTSTR) tszClusterServiceBinaryPath ); } // Was the Directory Id for the ClusterFiles key set successfully? // Was the Directory Id for the ClusterUpgradeFiles key set successfully? if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33002 was set to %1!s!.\n", tszClusterServiceBinaryPath ); // Set the Directory Id for the ClusterAdminFiles key in [DestinationDirs]. fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33003, (PCTSTR) tszClusterServiceBinaryPath ); } // Was the Directory Id for the ClusterUpgradeFiles key set successfully? // Was the Directory Id for the ClusterAdminFiles key set successfully? if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33003 was set to %1!s!.\n", tszClusterServiceBinaryPath ); // Set the Directory Id for the ClusterUninstallFiles key in [DestinationDirs]. fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33004, (PCTSTR) tszClusterServiceBinaryPath ); } // Was the Directory Id for the ClusterAdminFiles key set successfully? // Was the Directory Id for the ClusterUninstallFiles key set successfully? if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33004 was set to %1!s!.\n", tszClusterServiceBinaryPath ); // Set the Directory Id for the NT4.files.root key in [DestinationDirs]. fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33005, (PCTSTR) tszClusterServiceBinaryPath ); } // Was the Directory Id for the ClusterUninstallFiles key set successfully? // Was the Directory Id for NT4.files.root key set successfully? if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33005 was set to %1!s!.\n", tszClusterServiceBinaryPath ); // Append the "private" directory to the path. // Note, I didn't put this string in the stringtable because it is // not localizable, and will never change. _tcscat( tszClusterServiceBinaryPath, _T("\\private") ); // Set the Directory Id for the NT4.files.private key in [DestinationDirs]. fReturnValue = SetupSetDirectoryId( m_SetupInitComponent.ComponentInfHandle, 33006, (PCTSTR) tszClusterServiceBinaryPath ); } // Was the Directory Id for the NT4.files.root key set successfully? // Was the Directory Id for NT4.files.private key set successfully? if ( fReturnValue = (BOOL) TRUE ) { ClRtlLogPrint( "Directory Id 33006 was set to %1!s!.\n", tszClusterServiceBinaryPath ); } // Was the Directory Id for NT4.files.private key set successfully? } // Was the path to the Cluster files determined? else { ClRtlLogPrint( "SetDirectoryIds could not locate the cluster directory, so it failed.\n" ); } return ( fReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // UpgradeClusterServiceImagePath // // Routine Description: // This function "upgrades" the ImagePath value in the Cluster Service registry // key to the location queried from the Service Control Manager. // // Arguments: // None // // Return Value: // NO_ERROR - indicates success // Any other value is a Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::UpgradeClusterServiceImagePath( void ) { BOOL fReturnValue; DWORD dwReturnValue; // Query the path to the Cluster Service from the Service Control Manager. TCHAR tszClusterServiceBinaryPath[MAX_PATH]; CString csClusterService; csClusterService = CLUSTER_SERVICE_NAME; fReturnValue = GetServiceBinaryPath( (LPWSTR) (LPCTSTR) csClusterService, tszClusterServiceBinaryPath ); // Was the path to the Cluster Service obtained? if ( fReturnValue == (BOOL) TRUE ) { // Set the ImagePath value in the Cluster Service reg key to the location // obtained from the Service Control Manager. LONG lReturnValue; HKEY hClusterServiceKey; DWORD dwType; DWORD dwSize; // Attempt to open the Cluster Service reg key. CString csClusterServiceRegKey; csClusterServiceRegKey = CLUSREG_KEYNAME_CLUSSVC; lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterServiceRegKey, (DWORD) 0L, // reserved (REGSAM) KEY_SET_VALUE, &hClusterServiceKey ); // Was the Cluster Service reg key opened? if ( lReturnValue == (LONG) ERROR_SUCCESS ) { TCHAR tszClusterServicePath[MAX_PATH]; _tcscpy( tszClusterServicePath, tszClusterServiceBinaryPath ); _tcscat( tszClusterServicePath, _T("\\") ); // Append the name of the Cluster Service. CString csClusterService; csClusterService = CLUSTER_SERVICE_NAME; csClusterService += (CString) _T(".exe"); _tcscat( tszClusterServicePath, csClusterService ); DWORD dwImagePathValueLength; dwImagePathValueLength = (DWORD) ((_tcslen( tszClusterServicePath ) + 1) * sizeof( TCHAR )); CString csImagePath; csImagePath = CLUSREG_KEYNAME_IMAGE_PATH; lReturnValue = RegSetValueEx( hClusterServiceKey, csImagePath, (DWORD) 0L, // reserved (DWORD) REG_EXPAND_SZ, (CONST BYTE *) tszClusterServicePath, dwImagePathValueLength ); // Was the ImagePath written successfully? if ( lReturnValue == (LONG) ERROR_SUCCESS ) { dwReturnValue = (DWORD) NO_ERROR; ClRtlLogPrint( "UpgradeClusterServiceImagePath succeeded.\n" ); } else { dwReturnValue = GetLastError(); ClRtlLogPrint( "UpgradeClusterServiceImagePath failed with error code 0x%1!x!.\n", dwReturnValue ); } // Was the ImagePath written successfully? // Close the Cluster Service registry key. RegCloseKey( hClusterServiceKey ); // do we care about the return value? } else { dwReturnValue = GetLastError(); ClRtlLogPrint( "UpgradeClusterServiceImagePath failed with error code 0x%1!x!.\n", dwReturnValue ); } // Was the Cluster Service reg key opened? } // Was the path to the Cluster Service obtained? else { // Indicate error. dwReturnValue = GetLastError(); ClRtlLogPrint( "UpgradeClusterServiceImagePath failed with error code 0x%1!x!.\n", dwReturnValue ); } return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // OnOcQueueFileOps // // Routine Description: // This function processes the OC_QUEUE_FILE_OPS "messages" from the // Optional Components Manager. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID) // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::OnOcQueueFileOps( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue = NO_ERROR; // Is the handle to the component INF file valid? if ( (m_SetupInitComponent.ComponentInfHandle != (HINF) INVALID_HANDLE_VALUE) && (m_SetupInitComponent.ComponentInfHandle != (HINF) NULL) ) { // Is this UNATTENDED or ATTENDED? if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { // This is UNATTENDED. ClRtlLogPrint( "In OnOcQueueFileOps this is an UNATTENDED operation.\n" ); dwReturnValue = OnOcQueueFileOpsUnattended( (LPCTSTR) ptszSubComponentId, hSetupFileQueue ); } else { // This is ATTENDED. ClRtlLogPrint( "In OnOcQueueFileOps this is an ATTENDED operation.\n" ); dwReturnValue = OnOcQueueFileOpsAttended( (LPCTSTR) ptszSubComponentId, hSetupFileQueue ); } // Is this UNATTENDED or ATTENDED? } else { dwReturnValue = ERROR_FILE_NOT_FOUND; ClRtlLogPrint( "In OnOcQueueFileOps the handle to the component INF file is bad.\n" ); } // Is the handle to the component INF file valid? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // OnOcQueueFileOpsUnattended // // Routine Description: // This function processes the OC_QUEUE_FILE_OPS "messages" from the // Optional Components Manager during UNATTENDED operations. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID) // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // // Note: // OC Manager sends OC_QUEUE_FILE_OPS to the component DLL when the Components // List wizard page is dismissed. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::OnOcQueueFileOpsUnattended( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue; // Is this an UPGRADE? if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { // This is an unattended UPGRADE. dwReturnValue = QueueFileOpsUnattendedUpgrade( (LPCTSTR) ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueFileOpsUnattendedUpgrade returned 0x%1!x!.\n", dwReturnValue ); } else { HINF hAnswerFile; // WARNING: NEVER close this handle because clusocm.dll // did not open it. // Get a handle to the answer file. WARNING: NEVER close this handle because clusocm.dll // did not open it. hAnswerFile = m_SetupInitComponent.HelperRoutines.GetInfHandle( INFINDEX_UNATTENDED, m_SetupInitComponent.HelperRoutines.OcManagerContext ); if ( (hAnswerFile != (HINF) NULL) && (hAnswerFile != (HINF) INVALID_HANDLE_VALUE) ) { ClRtlLogPrint( "In OnOcQueueFileOpsUnattended this is a CLEAN install.\n" ); // Is Cluster service selected? It is probably overkill to check, but // it is safer to check than to be sorry later. BOOL fCurrentSelectionState; BOOL fOriginalSelectionState; fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT ); fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL ); if ( fCurrentSelectionState == (BOOL) TRUE ) { // Was there a selection state transition? if ( fCurrentSelectionState != fOriginalSelectionState ) { ClRtlLogPrint( "A selection state transition was detected.\n" ); // A selection state transition has occured. Install files. dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended,\n", dwReturnValue ); } else { ClRtlLogPrint( "NO selection state transition was detected.\n" ); // The selection state has not been changed. Perform no action. dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition? } else { // Has there been a selection state transition ? ClRtlLogPrint( "In OnOcQueueFileOpsUnattended Cluster service is not selected for installation.\n" ); if ( fCurrentSelectionState != fOriginalSelectionState ) { // Remove files. dwReturnValue = QueueRemoveFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueRemoveFileOperations returned 0x%1!x! to OnOcQueueFileOpsUnattended.\n", dwReturnValue ); } else { ClRtlLogPrint( "In OnOcQueueFileOpsUnattended NO selection state transition was detected.\n" ); // The selection state has not been changed. Perform no action. dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition ? } } else { // A handle to the answer file could not be obtained. Treat it as a UPGRADE. ClRtlLogPrint( "InOnOcQueueFileOpsUnattended the handle to the answer file could not be obtained.\n" ); dwReturnValue = QueueFileOpsUnattendedUpgrade( (LPCTSTR) ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueFileOpsUnattendedUpgrade returned 0x%1!x!.\n", dwReturnValue ); } } return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // QueueFileOpsUnattendedUpgrade // // Routine Description: // This function queues the file operations appropriate for an unattended // upgrade operation. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID) // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::QueueFileOpsUnattendedUpgrade( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue; eClusterInstallState eState; // Has Cluster service previously been installed? // GetClusterInstallationState reports on the state of the registry value // that records the state of the Cluster service installation on NT 5 machines. // IsClusterServiceRegistered indicates whether the Cluster service is registered on // BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for // upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines. BOOL fClusteringServicePreviouslyInstalled; ClRtlGetClusterInstallState( NULL, &eState ); if ( ( eState != eClusterInstallStateUnknown ) || ( IsClusterServiceRegistered() == (BOOL) TRUE ) ) { fClusteringServicePreviouslyInstalled = (BOOL) TRUE; } else { fClusteringServicePreviouslyInstalled = (BOOL) FALSE; } if ( fClusteringServicePreviouslyInstalled == (BOOL) TRUE ) { // Upgrade the ClusteringService files. dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to QueueFileOpsUnattendedUpgrade.\n", dwReturnValue ); } // Has Cluster service previously been installed? else { // Since Cluster service has not been previously installed there is // nothing to do. ClRtlLogPrint( "In QueueFileOpsUnattendedUpgrade Cluster service has never been installed.\n" ); dwReturnValue = (DWORD) NO_ERROR; } // Has Cluster service previously been installed? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // OnOcQueueFileOpsAttended // // Routine Description: // This function processes the OC_QUEUE_FILE_OPS "messages" from the // Optional Components Manager during ATTENDED operations. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // hSetupFileQueue - a HSPFILEQ (typedefed in setupapi.h to PVOID) // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::OnOcQueueFileOpsAttended( IN LPCTSTR ptszSubComponentId, IN OUT HSPFILEQ hSetupFileQueue ) { DWORD dwReturnValue; eClusterInstallState eState; // Is Cluster service selected? BOOL fCurrentSelectionState; BOOL fOriginalSelectionState; fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT ); if ( fCurrentSelectionState == (BOOL) TRUE ) { // The subcomponent is selected. Is this a fresh install ? ClRtlLogPrint( "In OnOcQueueFileOpsAttended the current selection state is TRUE.\n" ); if ( ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) == (DWORDLONG) 0L ) && ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) == (DWORDLONG) 0L ) ) { // SETUPOP_STANDALONE flag clear means running under GUI mode setup. // SETUPOP_NTUPGRADE flag clear means not performing an upgrade. // Both flags clear means a fresh install. Do not check for a selection // state transition. ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is a CLEAN install.\n" ); dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended.\n", dwReturnValue ); } else { // This is either an upgrade or OC Manager is running stand-alone. if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is an UPGRADE.\n" ); // This is an upgrade, if Cluster Server has previously been // been installed then queue the file copies. // Has Cluster service perviously been installed? Ask the Service Control // Manager whether the Cluster Service is registered. // GetClusterInstallationState reports on the state of the registry value // that records the state of the Cluster service installation on NT 5 machines. // IsClusterServiceRegistered indicates whether the Cluster service is registered on // BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for // upgrading NT 4 machines, GetClusterInstallationState for NT 5 machines. BOOL fClusterServiceRegistered; fClusterServiceRegistered = IsClusterServiceRegistered(); BOOL fClusterServicePreviouslyInstalled; ClRtlGetClusterInstallState( NULL, &eState ); if ( ( eState != eClusterInstallStateUnknown ) || ( fClusterServiceRegistered == (BOOL) TRUE ) ) { fClusterServicePreviouslyInstalled = (BOOL) TRUE; } else { fClusterServicePreviouslyInstalled = (BOOL) FALSE; } if ( fClusterServicePreviouslyInstalled == (BOOL) TRUE ) { // Since this is an UPGRADE and Cluster service has been // installed there is no need to test for a selection state transition. dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended.\n", dwReturnValue ); } else { ClRtlLogPrint( "In OnOcQueueFileOps attempted an UPGRADE but Cluster service has never been installed.\n" ); dwReturnValue = (DWORD) NO_ERROR; } // Was Cluster service perviously installed? } else { ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is STANDALONE.\n" ); // This is not an upgrade. That means Add/Remove Programs must be // running. It is necessary to test for a selection state transition. fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL ); // Was there a selection state transition? if ( fCurrentSelectionState != fOriginalSelectionState ) { ClRtlLogPrint( "A selection state transition was detected.\n" ); // A selection state transition has occured. Install files. dwReturnValue = QueueInstallFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueInstallFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended,\n", dwReturnValue ); } else { ClRtlLogPrint( "NO selection state transition was detected.\n" ); // The selection state has not been changed. Perform no action. dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition? } // Is this an UPGRADE? } // Is this a clean install? } // Is Cluster service currently selected ? else { ClRtlLogPrint( "In OnOcQueueFileOpsAttended the current selection state is FALSE.\n" ); // Cluster service is not selected. Is OC Manager running stand-alone ? // If not, i.e. if GUI mode setup is running, there is nothing to do. if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) != (DWORDLONG) 0L ) { ClRtlLogPrint( "In OnOcQueueFileOpsAttended this is STANDALONE.\n" ); // SETUPOP_STANDALONE set implies GUI mode setup is not running. If // there was a selection state change (to unselected) then remove files. fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL ); // Has there been a selection state transition ? if ( fCurrentSelectionState != fOriginalSelectionState ) { // Remove files. dwReturnValue = QueueRemoveFileOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId, hSetupFileQueue ); ClRtlLogPrint( "QueueRemoveFileOperations returned 0x%1!x! to OnOcQueueFileOpsAttended.\n", dwReturnValue ); } else { ClRtlLogPrint( "NO selection state transition was detected.\n" ); // The selection state has not been changed. Perform no action. dwReturnValue = (DWORD) NO_ERROR; } // Was there a selection state transition ? } else { // GUI mode setup is running and Cluster service is not selected. // There is nothing to do. dwReturnValue = (DWORD) NO_ERROR; } // Is GUI mode setup running ? } // Is Cluster service currently selected ? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // CompleteUninstallingClusteringService // // Routine Description: // This function completes uninstalling ClusteringService by queuing the // registry operations to delete the Cluster service registry keys, // cleaning up the Start Menu, removing the Network Provider, and requesting // a reboot. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::CompleteUninstallingClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; eClusterInstallState ecisInstallState; BOOL bRebootRequired = FALSE; // Update the "progress text" CString csProgressText; csProgressText.LoadString( IDS_REMOVING_CLUS_SERVICE ); m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); // Request that the system reboot only if clusdisk has been started. // We can deduce this by looking at the cluster installation state. // If the state is not eClusterInstallStateFilesCopied then it means that // ClusCfg may have run successfully and therefore, clusdisk may have been started. // We must call ClRtlGetClusterInstallState before calling UninstallRegistryOperations. // Otherwise it will always return eClusterInstallStateUnknown! // If ClRtlGetClusterInstallState fails, reboot anyway. // If it succeeds, reboot only if ClusCfg has completed successfully. if ( ( ClRtlGetClusterInstallState( NULL, &ecisInstallState ) != ERROR_SUCCESS ) || ( ecisInstallState != eClusterInstallStateFilesCopied ) ) { bRebootRequired = TRUE; } // Delete registry entries. Queue the base registry operations. dwReturnValue = UninstallRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId ); ClRtlLogPrint( "UninstallRegistryOperations returned 0x%1!x! to CompleteUninstallingClusteringService.\n", dwReturnValue ); // // Remove the cluster item from the start menu // CString csGroupName; CString csItemName; csGroupName.LoadString( IDS_START_GROUP_NAME ); csItemName.LoadString( IDS_START_ITEM_NAME ); DeleteLinkFile( CSIDL_COMMON_PROGRAMS, (LPCWSTR) csGroupName, (LPCWSTR) csItemName, (BOOL) FALSE ); // Delete the cluster directory. BUGBUG // // Remove the cluster network provider // dwReturnValue = RemoveNetworkProvider(); if ( bRebootRequired ) { BOOL fRebootRequestStatus; // In the following call the value passed in the second parameter // was chosen arbitrarily. Ocmanage.h implies that the parameter is not used. fRebootRequestStatus = m_SetupInitComponent.HelperRoutines.SetReboot( m_SetupInitComponent.HelperRoutines.OcManagerContext, (BOOL) TRUE ); } return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // OpenClusterRegistryRoot // // Routine Description: // This function retuns a handle to the root key of the Cluster hive. // It will load the hive if necessary. // // Arguments: // none // // // Return Value: // If success, handle to the cluster root key. Otherwise, NULL // //-- ///////////////////////////////////////////////////////////////////////////// HKEY CClusocmApp::OpenClusterRegistryRoot( void ) { BOOL fReturnValue; HKEY hClusterKey = NULL; LONG lReturnValue; // Attempt to open the Cluster Registry key. CString csClusterRegKey; csClusterRegKey = CLUSREG_KEYNAME_CLUSTER; lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterRegKey, 0, KEY_READ, &hClusterKey); if ( lReturnValue != ERROR_SUCCESS ) { ClRtlLogPrint( "In OpenClusterRegistryRoot, the first attempt to open the Cluster key failed.\n" ); // The Cluster hive is not currently loaded. This condition means that // the cluster service has not been started. Attempt to load the Cluster // hive so that it can be read. // First, locate the Cluster hive file. It should be in the location // specified for the ClusterFiles entry in the [DestinationDirs] section // of clusocm.inf. CString csClusterHiveFilePath; fReturnValue = LocateClusterHiveFile( (CString &) csClusterHiveFilePath ); ClRtlLogPrint( "LocateClusterHiveFile returned 0x%1!x! to OpenClusterRegistryRoot.\n", fReturnValue ); // Was the Cluster hive file located? if ( fReturnValue == (BOOL) TRUE ) { // The Cluster hive file was located. // Attempt to load the cluster hive. BOOLEAN OriginalState; // I'm not sure what the following function does, but the prototype is // in sdk\inc\ntrtl.h. Look in stdafx.h for the inclusion of ntrtl.h. I // replicated the logic that was used in newsetup.h to make it work. lReturnValue = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OriginalState ); if ( lReturnValue == ERROR_SUCCESS ) { // Attempt to Load the Cluster Hive. lReturnValue = RegLoadKey( HKEY_LOCAL_MACHINE, csClusterRegKey, csClusterHiveFilePath ); if ( lReturnValue == ERROR_SUCCESS ) { // Now that the Cluster hive has been loaded, attempt to open the // Cluster registry key. lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, csClusterRegKey, 0, KEY_READ, &hClusterKey ); // lReturnValue will be tested by the next BLOCK of code. } // Undo whatever the preceding call to RtlAdjustPrivilege() did. RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, OriginalState, FALSE, &OriginalState ); if ( lReturnValue != ERROR_SUCCESS ) { // Set the error code. SetLastError( lReturnValue ); } } else { // The initial call to RtlAdjustPrivilege FAILED. SetLastError( lReturnValue ); // A return value of TRUE will allow the uninstall operation to // continue. Since lReturnValue is NOT ERROR_SUCCESS no additional // processing will be performed by this function. } // Did RtlAdjustPrivilege succeed? } else { // The Cluster hive file was not located. Set last error and return // a null handle ClRtlLogPrint( "OpenClusterRegistryRoot couldn't locate the hive files\n" ); SetLastError( ERROR_FILE_NOT_FOUND ); } // Was the cluster hive file located? } // Was the Cluster registry key opened successfully? return ( hClusterKey ); } // OpenClusterRegistryRoot ///////////////////////////////////////////////////////////////////////////// //++ // // FindNodeNumber // // Routine Description: // This function looks through the cluster's Node key and returns a pointer to // the node number string of the current node. // // Arguments: // ClusterKey - handle to cluster root key // // NodeNumberString - pointer to location that receives the node number // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// PWSTR CClusocmApp::FindNodeNumber( HKEY ClusterKey ) { WCHAR nodeName[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD nodeNameLength = sizeof( nodeName ); WCHAR registryNodeName[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD registryNodeNameLength; HKEY nodesKey = NULL; HKEY nodesEnumKey = NULL; DWORD status; PWSTR nodeNumberString; DWORD nodeNumberStringLength; DWORD index = 0; DWORD dataType; // // allocate space for the node number string // nodeNumberString = (PWSTR)LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, ( CS_MAX_NODE_ID_LENGTH + 1 ) * sizeof( WCHAR )); if ( nodeNumberString == NULL ) { status = GetLastError(); ClRtlLogPrint( "FindNodeNumber couldn't allocate node number buffer: %1!u!\n", status ); return NULL; } // // get our node name and a handle to the root key in the cluster hive // if ( !GetComputerName( nodeName, &nodeNameLength )) { status = GetLastError(); ClRtlLogPrint( "FindNodeNumber failed to get computer name: %1!u!\n", status ); goto error_exit; } status = RegOpenKeyEx( ClusterKey, CLUSREG_KEYNAME_NODES, 0, KEY_READ, &nodesKey ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "FindNodeName failed to open Nodes key: %1!u!\n", status ); goto error_exit; } // // enum the entries under the Nodes key // do { nodeNumberStringLength = sizeof( nodeNumberString ); status = RegEnumKeyEx( nodesKey, index, nodeNumberString, &nodeNumberStringLength, NULL, NULL, NULL, NULL ); if ( status == ERROR_NO_MORE_ITEMS ) { ClRtlLogPrint( "FindNodeNumber finished enum'ing the Nodes key\n" ); break; } else if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "FindNodeNumber failed to enum Nodes key: %1!u!\n", status ); goto error_exit; } // // open this key and get the Name value // status = RegOpenKeyEx( nodesKey, nodeNumberString, 0, KEY_READ, &nodesEnumKey ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "FindNodeNumber failed to open Nodes key %1!ws!: %2!u!\n", nodeNumberString, status ); goto error_exit; } registryNodeNameLength = sizeof( registryNodeName ); status = RegQueryValueEx( nodesEnumKey, CLUSREG_NAME_NODE_NAME, NULL, &dataType, (LPBYTE)registryNodeName, ®istryNodeNameLength ); RegCloseKey( nodesEnumKey ); nodesEnumKey = NULL; if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "FindNodeNumber failed to get NodeName value: " "status %1!u!, data type %2!u!\n", status, dataType ); goto error_exit; } // // finally, we get to see if this is our node // if ( lstrcmpiW( nodeName, registryNodeName ) == 0 ) { break; } ++index; } while ( TRUE ); error_exit: if ( nodesKey != NULL ) { RegCloseKey( nodesKey ); } if ( nodesEnumKey != NULL ) { RegCloseKey( nodesEnumKey ); } if ( status != ERROR_SUCCESS || lstrlenW( nodeNumberString ) == 0 ) { LocalFree( nodeNumberString ); nodeNumberString = NULL; } return nodeNumberString; } // FindNodeNumber ///////////////////////////////////////////////////////////////////////////// //++ // // GetConnectionName // // Routine Description: // This function finds the matching connection object based on the IP address // of the network interface. NOTE that the return value is also pointed to in // the Adapter Info of the Adapter enum struct. This return value must not be // freed with LocalFree. // // Arguments: // NetInterfacesKey - handle to key of network interface // // AdapterEnum - pointer to struct with adapter/IP interface info // // Return Value: // If successful, pointer to connectoid name. Otherwise, NULL with Win32 // error available from GetLastError(). // //-- ///////////////////////////////////////////////////////////////////////////// PWSTR CClusocmApp::GetConnectionName( HKEY NetInterfacesGuidKey, PCLRTL_NET_ADAPTER_ENUM AdapterEnum ) { WCHAR ipAddress[ 16 ]; DWORD ipAddressLength = sizeof( ipAddress ); DWORD status; PCLRTL_NET_ADAPTER_INFO adapterInfo; PCLRTL_NET_INTERFACE_INFO interfaceInfo; PWSTR connectoidName = NULL; DWORD dataType; // // query for the netIf's IP address // status = RegQueryValueEx( NetInterfacesGuidKey, CLUSREG_NAME_NETIFACE_ADDRESS, NULL, &dataType, (LPBYTE)ipAddress, &ipAddressLength ); if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "GetConnectionName failed to get Address value: " "status %1!u!, data type %2!u!\n", status, dataType ); SetLastError( ERROR_INVALID_DATA ); goto error_exit; } adapterInfo = ClRtlFindNetAdapterByInterfaceAddress( AdapterEnum, ipAddress, &interfaceInfo ); if ( adapterInfo != NULL ) { connectoidName = adapterInfo->ConnectoidName; } error_exit: return connectoidName; } // GetConnectionName ///////////////////////////////////////////////////////////////////////////// //++ // // GetTCPAdapterInfo // // Routine Description: // This function opens a handle to the service controller and tries to // start the DHCP service. If successful, then TCP is queried for its // NIC and IP interface info. // // Arguments: // none // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// PCLRTL_NET_ADAPTER_ENUM CClusocmApp::GetTCPAdapterInfo( void ) { SC_HANDLE schSCManager; SC_HANDLE serviceHandle; PCLRTL_NET_ADAPTER_ENUM adapterEnum = NULL; BOOL success; DWORD retryCount; DWORD status; schSCManager = OpenSCManager(NULL, // machine (NULL == local) NULL, // database (NULL == default) SC_MANAGER_CONNECT); // access required if ( schSCManager ) { // // start DHCP for nodes that are using it. it will cause TCP to start // as well. For statically configured nodes, starting DHCP causes no // harm. // serviceHandle = OpenService(schSCManager, L"DHCP", SERVICE_START ); if ( serviceHandle ) { success = StartService( serviceHandle, 0, NULL ); if ( !success ) { status = GetLastError(); if ( status == ERROR_SERVICE_ALREADY_RUNNING ) { success = TRUE; } } if ( success ) { // // wait while TCP gets bound to all its adapters and discovers // its IP interfaces. // retryCount = 3; do { Sleep( 2000 ); adapterEnum = ClRtlEnumNetAdapters(); if (adapterEnum != NULL) { break; } } while ( --retryCount != 0 ); if (adapterEnum == NULL) { status = GetLastError(); ClRtlLogPrint( "Failed to obtain local system network config. " "status %1!u! retryCount %2!u!\n", status, retryCount); } } else { ClRtlLogPrint( "StartService returned %1!u! to StartTCP\n", status); } CloseServiceHandle( serviceHandle ); } else { status = GetLastError(); ClRtlLogPrint( "OpenService returned %1!u! to StartTCP\n", status); } CloseServiceHandle( schSCManager ); } else { status = GetLastError(); ClRtlLogPrint("OpenSCManager returned %1!u! to StartTCP\n", status); } return adapterEnum; } ///////////////////////////////////////////////////////////////////////////// //++ // // RenameConnectionObjects // // Routine Description: // This function walks through the node's network interfaces and changes, // if necessary, their associated connection objects. // // Arguments: // none // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::RenameConnectionObjects( void ) { HKEY clusterKey; HKEY netInterfacesKey = NULL; HKEY netInterfacesGuidKey = NULL; HKEY networkGuidKey = NULL; HKEY networksKey = NULL; DWORD status; PCLRTL_NET_ADAPTER_ENUM adapterEnum = NULL; PCLRTL_NET_ADAPTER_INFO adapterInfo = NULL; PCLRTL_NET_INTERFACE_INFO adapterIfInfo = NULL; DWORD hiddenAdapterCount = 0; PWSTR ourNodeNumber = NULL; DWORD index = 0; WCHAR netIfGuidString[ CS_NETINTERFACE_ID_LENGTH + 1 ]; DWORD netIfGuidStringLength; WCHAR networkGuidString[ CS_NETWORK_ID_LENGTH + 1 ]; DWORD networkGuidStringLength; WCHAR netIfNodeNumber[ CS_MAX_NODE_ID_LENGTH + 1 ]; DWORD netIfNodeNumberLength; DWORD dataType; PWSTR connectoidName; PWSTR networkName = NULL; DWORD networkNameLength; // // initialize COM // status = CoInitializeEx( NULL, COINIT_DISABLE_OLE1DDE | COINIT_MULTITHREADED ); if ( !SUCCEEDED( status )) { ClRtlLogPrint( "RenameConnectionObjects Couldn't init COM %1!08X!\n", status ); return status; } // // get a handle to the root key in the cluster hive // clusterKey = OpenClusterRegistryRoot(); if ( clusterKey == NULL ) { status = GetLastError(); ClRtlLogPrint( "OpenClusterRegistryRoot returned %1!u! to RenameConnectionObjects\n", status ); goto error_exit; } // // start TCP if necessary // adapterEnum = GetTCPAdapterInfo(); if ( adapterEnum == NULL ) { status = GetLastError(); if ( status == ERROR_SUCCESS ) { ClRtlLogPrint( "No usable network adapters are installed in this system.\n" ); } else { ClRtlLogPrint( "GetTCPAdapterInfo returned %1!u! to RenameConnectionObjects\n", status ); } goto error_exit; } // // Ignore all adapters which are hidden or have an address of 0.0.0.0. // for (adapterInfo = adapterEnum->AdapterList; adapterInfo != NULL; adapterInfo = adapterInfo->Next ) { if (adapterInfo->Flags & CLRTL_NET_ADAPTER_HIDDEN) { ClRtlLogPrint( "Ignoring hidden adapter '%1!ws!'.\n", adapterInfo->DeviceName ); adapterInfo->Ignore = TRUE; hiddenAdapterCount++; } else { adapterIfInfo = ClRtlGetPrimaryNetInterface(adapterInfo); if (adapterIfInfo->InterfaceAddress == 0) { ClRtlLogPrint( "Ignoring adapter '%1!ws!' because primary address is 0.0.0.0.\n", adapterInfo->DeviceName ); adapterInfo->Ignore = TRUE; hiddenAdapterCount++; } } } if ((adapterEnum->AdapterCount - hiddenAdapterCount) == 0) { ClRtlLogPrint( "No usable network adapters are installed in this system.\n" ); status = ERROR_SUCCESS; goto error_exit; } ClRtlLogPrint( "RenameConnectionObject found %1!u! adapters to process.\n", adapterEnum->AdapterCount - hiddenAdapterCount ); // // now find our node number by looking through the node key in the cluster // registry and comparing our netbios name with the names in the registry // ourNodeNumber = FindNodeNumber( clusterKey ); if ( ourNodeNumber == NULL ) { status = GetLastError(); ClRtlLogPrint( "FindNodeNumber failed: status %1!u!\n", status ); goto error_exit; } // // open the network and network interface keys // status = RegOpenKeyEx( clusterKey, CLUSREG_KEYNAME_NETWORKS, 0, KEY_READ, &networksKey ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open Networks key: %1!u!\n", status ); goto error_exit; } status = RegOpenKeyEx( clusterKey, CLUSREG_KEYNAME_NETINTERFACES, 0, KEY_READ, &netInterfacesKey ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open NetworkInterfaces key: %1!u!\n", status ); goto error_exit; } // // enum the NetworkInterfaces key, looking for interfaces on this node. If // we find one, get their IP address and find the corresponding network and // connection object. // do { netIfGuidStringLength = sizeof( netIfGuidString ); status = RegEnumKeyEx( netInterfacesKey, index, netIfGuidString, &netIfGuidStringLength, NULL, NULL, NULL, NULL ); if ( status == ERROR_NO_MORE_ITEMS ) { ClRtlLogPrint( "RenameConnectionObjects finished enum'ing the " "NetworkInterfaces key\n" ); status = ERROR_SUCCESS; break; } else if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to enum NetworkInterfaces " "key: %1!u!\n", status ); goto error_exit; } // // open this key and get the Node value // status = RegOpenKeyEx( netInterfacesKey, netIfGuidString, 0, KEY_READ, &netInterfacesGuidKey ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open NetworkIntefaces " "subkey %1!ws!: %2!u!\n", netIfGuidString, status ); goto error_exit; } netIfNodeNumberLength = sizeof( netIfNodeNumber ); status = RegQueryValueEx( netInterfacesGuidKey, CLUSREG_NAME_NETIFACE_NODE, NULL, &dataType, (LPBYTE)netIfNodeNumber, &netIfNodeNumberLength ); if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Node value: " "status %1!u!, data type %2!u!\n", status, dataType ); status = ERROR_INVALID_DATA; goto error_exit; } // // if this is one our interfaces, then adjust the name of the // associated connection object // if ( lstrcmpiW( ourNodeNumber, netIfNodeNumber ) == 0 ) { ClRtlLogPrint( "Net Interface %1!ws! is on this node.\n", netIfGuidString ); connectoidName = GetConnectionName( netInterfacesGuidKey, adapterEnum ); if ( connectoidName != NULL ) { ClRtlLogPrint( "Associated connectoid name is '%1!ws!'.\n", connectoidName); // // look up network GUID and open its Key to get the network name // networkGuidStringLength = sizeof( networkGuidString ); status = RegQueryValueEx( netInterfacesGuidKey, CLUSREG_NAME_NETIFACE_NETWORK, NULL, &dataType, (LPBYTE)networkGuidString, &networkGuidStringLength ); if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Network value: " "status %1!u!, data type %2!u!\n", status, dataType ); status = ERROR_INVALID_DATA; goto error_exit; } status = RegOpenKeyEx( networksKey, networkGuidString, 0, KEY_READ, &networkGuidKey ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to open networks " "subkey %1!ws!: %2!u!\n", networkGuidString, status ); goto error_exit; } // // query the Name value to get its size, allocate a buffer large // enough to hold it and request the data contained in the value. // networkNameLength = 0; status = RegQueryValueEx( networkGuidKey, CLUSREG_NAME_NET_NAME, NULL, &dataType, (LPBYTE)NULL, &networkNameLength ); if ( status != ERROR_SUCCESS || dataType != REG_SZ ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Network Name " "size: status %1!u!, data type %2!u!\n", status, dataType ); status = ERROR_INVALID_DATA; goto error_exit; } networkName = (PWCHAR)LocalAlloc( LMEM_FIXED, networkNameLength ); if ( networkName == NULL ) { status = GetLastError(); ClRtlLogPrint( "RenameConnectionObjects failed to alloc space " "for network name: %1!u!\n", status ); goto error_exit; } status = RegQueryValueEx( networkGuidKey, CLUSREG_NAME_NET_NAME, NULL, NULL, (LPBYTE)networkName, &networkNameLength ); RegCloseKey( networkGuidKey ); networkGuidKey = NULL; if ( status != ERROR_SUCCESS ) { ClRtlLogPrint( "RenameConnectionObjects failed to get Network " "Name: status %1!u!\n", status ); goto error_exit; } ClRtlLogPrint( "Network name is '%1!ws!'.\n", networkName ); if ( lstrcmpiW( connectoidName, networkName ) != 0 ) { ClRtlLogPrint( "Changing connectoid name to '%1!ws!'.\n", networkName ); ClRtlFindConnectoidByNameAndSetName( connectoidName, networkName ); } } else { ClRtlLogPrint( "RenameConnectionObjects failed to get connection name.\n" ); } } RegCloseKey( netInterfacesGuidKey ); netInterfacesGuidKey = NULL; ++index; } while ( TRUE ); error_exit: if ( clusterKey != NULL ) { RegCloseKey( clusterKey ); } if ( netInterfacesKey != NULL ) { RegCloseKey( netInterfacesKey ); } if ( netInterfacesGuidKey != NULL ) { RegCloseKey( netInterfacesGuidKey ); } if ( networksKey != NULL ) { RegCloseKey( networksKey ); } if ( networkGuidKey != NULL ) { RegCloseKey( networkGuidKey ); } if ( ourNodeNumber != NULL ) { LocalFree( ourNodeNumber ); } if ( networkName != NULL ) { LocalFree( networkName ); } if ( adapterEnum != NULL ) { ClRtlFreeNetAdapterEnum( adapterEnum ); } CoUninitialize(); return status; } // RenameConnectionObjects ///////////////////////////////////////////////////////////////////////////// //++ // // CompleteUpgradingClusteringService // // Routine Description: // This function completes the process of upgrading a Cluster service installation. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::CompleteUpgradingClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; eClusterInstallState eState; // This is an upgrade, if Cluster Server has previously been // been installed then queue the registry additions. // GetClusterInstallationState reports on the state of the registry value // that records the state of the Cluster service installation on NT 5 machines. // IsClusterServiceRegistered indicates whether the Cluster service is registered on // BOTH NT 4 and NT 5 machines. Both tests are required: IsClusterServiceRegistered for // upgrading NT 4 machines, ClRtlGetClusterInstallState for NT 5 machines. BOOL fClusterServiceRegistered; fClusterServiceRegistered = IsClusterServiceRegistered(); ClRtlGetClusterInstallState( NULL, &eState ); if ( ( eState != eClusterInstallStateUnknown ) || ( fClusterServiceRegistered == (BOOL) TRUE ) ) { // Update the progress bar. m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext ); // Update the "progress text" CString csProgressText; csProgressText.LoadString( IDS_UPGRADING_CLUS_SERVICE ); m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); ClRtlLogPrint( "In CompleteUpgradingClusteringService Cluster service has previously been installed.\n" ); // Perform the base registry operations. dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId ); ClRtlLogPrint( "The base call to PerformRegistryOperations returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue ); // Were the base registry operations performed successfully? CString csUpgrade; if ( dwReturnValue == (DWORD) NO_ERROR ) { // Perform the registry operations that pertain to UPGRADE. csUpgrade = UPGRADE_INF_KEY; dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csUpgrade ); ClRtlLogPrint( "The UPGRADE call to PerformRegistryOperations returned 0x%1!x! " "to CompleteUpgradingClusteringService.\n", dwReturnValue ); } // Were the base registry operations performed successfully? // Were the UPGRADE registry operations performed successfully? if ( dwReturnValue == (DWORD) NO_ERROR ) { // If the Cluster Service is registered with the Service Control Manager then // the registry keys that add "Configure Cluster service" to the Welcome UI // should be eliminated because the service has already been configured. if ( fClusterServiceRegistered == (BOOL) TRUE ) { CString csClusterService; csClusterService = CLUSTER_SERVICE_NAME; CString csRegistered; csRegistered = REGISTERED_INF_KEY_FRAGMENT; CString csSectionName; csSectionName = csUpgrade + (CString) _T(".") + csClusterService + (CString) _T(".") + csRegistered; dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csSectionName ); ClRtlLogPrint( "The REGISTERED call to PerformRegistryOperations returned 0x%1!x! " "to CompleteUpgradingClusteringService.\n", dwReturnValue ); } // Were the Welcome UI registry operations performed successfully? if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "In CompleteUpgradingClusteringService the registry operations " "were performed successfully.\n" ); // In NT 4 the ImagePath value in HKLM\System\CurrentControlSet\Services\ClusSvc // is set to reference clusprxy.exe. In NT 5 it must reference clussvc.exe. // The ImagePath value in the Cluster Service registry key must // be set programmatically because the path to the service // executable image is not known to the component INF file. That // is because in NT 4 "Cluster Server" as it was known, could be // installed in an arbitrary location. // There is no straightforward way to determine whether this upgrade // is from NT 4 or NT 5. The assumption made here is that if the // Cluster Service is registered with the Service Control Manager // then it was upgraded in place, regardless of the OS level. In that // case it will be safe to set the ImagePath value to reference clussvc.exe // in that location. If the Cluster Service is not registered with the // SC Manager then it is safe to leave that reg value alone because when // cluscfg.exe configures the Cluster service the ImagePath value will // get written correctly. if ( fClusterServiceRegistered == (BOOL) TRUE ) { dwReturnValue = UpgradeClusterServiceImagePath(); ClRtlLogPrint( "UpgradeClusterServiceImagePath returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue ); } // Was the ImagePath set successfully? if ( dwReturnValue == (DWORD) NO_ERROR ) { // did the connection objects get renamed? dwReturnValue = RenameConnectionObjects(); ClRtlLogPrint( "RenameConnectionObjects returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue ); if ( dwReturnValue == (DWORD) NO_ERROR ) { // // set the service controller default failure actions // dwReturnValue = ClRtlSetSCMFailureActions( NULL ); ClRtlLogPrint( "ClRtlSetDefaultFailureActions returned 0x%1!x! to " "CompleteUpgradingClusteringService.\n", dwReturnValue ); if ( dwReturnValue == (DWORD) NO_ERROR ) { // In NT4 the "Display Name" for the Cluster service was // "Cluster Server". It must be programatically changed to // "Cluster service". if ( fClusterServiceRegistered == (BOOL) TRUE ) { CString csClusterService; csClusterService = CLUSTER_SERVICE_NAME; SC_HANDLE hscServiceMgr; SC_HANDLE hscService; // Connect to the Service Control Manager and open the specified service // control manager database. hscServiceMgr = OpenSCManager( NULL, NULL, GENERIC_READ | GENERIC_WRITE ); // Was the service control manager database opened successfully? if ( hscServiceMgr != NULL ) { // The service control manager database is open. // Get the current display name of the service. TCHAR tszDisplayName[50]; // arbitrary size DWORD dwBufferSize = 50; BOOL fStatus; fStatus = GetServiceDisplayName( hscServiceMgr, (LPCTSTR) csClusterService, tszDisplayName, &dwBufferSize ); if ( fStatus == (BOOL) TRUE ) { // Is the service name "Cluster service"? if ( _tcscmp( tszDisplayName, _T("Cluster service") ) != 0 ) { // The display name is not correct. Change it. // Open a handle to the Service. Allow configuration changes. hscService = OpenService( hscServiceMgr, (LPWSTR) (LPCTSTR) csClusterService, SERVICE_CHANGE_CONFIG ); // Was the handle to the service opened? if ( hscService != NULL ) { // A valid handle to the Service was obtained. fStatus = ChangeServiceConfig( hscService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, _T("Cluster service") ); // Was the change succesful? if ( fStatus != (BOOL) TRUE ) { dwReturnValue = GetLastError(); ClRtlLogPrint( "Could not change the cluster service display name" " in CompleteUpgradingClusteringService." " Error: 0x%1!x!.\n", dwReturnValue ); } // Close the handle to the Cluster Service. CloseServiceHandle( hscService ); } // service opened? } // display name correct? } else { // Couldn't get the display name. dwReturnValue = GetLastError(); ClRtlLogPrint( "Could not query the cluster service display name" " in CompleteUpgradingClusteringService." " Error: 0x%1!x!.\n", dwReturnValue ); }// got display name? // Close the handle to the Service Control Manager. CloseServiceHandle( hscServiceMgr ); } } // cluster service registered? if ( dwReturnValue == (DWORD) NO_ERROR ) { // Register ClAdmWiz. // First, get the path to the cluster directory. CString csTemp; csTemp = CLUSTER_DIRECTORY; TCHAR tszPathToClusterDir[_MAX_PATH]; if ( ExpandEnvironmentStrings( (LPCTSTR) csTemp, tszPathToClusterDir, _MAX_PATH ) > 0L ) { HRESULT hResult; // Attempt to initialize the COM library. hResult = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); if ( (hResult == S_OK) || // success (hResult == S_FALSE) || // already initialized (hResult == RPC_E_CHANGED_MODE) ) // previous initialization specified // a different concurrency model { DWORD dwRCOrv; // RegisterCOMObject returns ERROR_SUCCESS on success. dwRCOrv = RegisterCOMObject( TEXT("ClAdmWiz.dll"), tszPathToClusterDir ); if ( dwRCOrv != (DWORD) ERROR_SUCCESS ) { ClRtlLogPrint( "CompleteUpgradingClusteringService failed to register ClAdmWiz.dll with error %1!d!.\n", dwRCOrv ); } // ClAdmWiz registered? else { // ERROR_SUCCESS and NO_ERROR are coincidentally the same. ClRtlLogPrint( "CompleteUpgradingClusteringService has registered ClAdmWiz.dll.\n" ); } // ClAdmWiz registered? // RegisterCOMObject returns ERROR_SUCCESS on success. dwRCOrv = RegisterCOMObject( TEXT("ClNetREx.dll"), tszPathToClusterDir ); if ( dwRCOrv != (DWORD) ERROR_SUCCESS ) { ClRtlLogPrint( "CompleteUpgradingClusteringService failed to register ClNetREx.dll with error %1!d!.\n", dwRCOrv ); } // ClNetREx registered? else { // ERROR_SUCCESS and NO_ERROR are coincidentally the same. ClRtlLogPrint( "CompleteUpgradingClusteringService has registered ClNetREx.dll.\n" ); } // ClNetREx registered? // RegisterCOMObject returns ERROR_SUCCESS on success. dwRCOrv = RegisterCOMObject( TEXT("CluAdMMC.dll"), tszPathToClusterDir ); if ( dwRCOrv != (DWORD) ERROR_SUCCESS ) { ClRtlLogPrint( "CompleteUpgradingClusteringService failed to register CluAdMMC.dll with error %1!d!.\n", dwRCOrv ); } // CluAdMMC registered? else { // ERROR_SUCCESS and NO_ERROR are coincidentally the same. ClRtlLogPrint( "CompleteUpgradingClusteringService has registered CluAdMMC.dll.\n" ); } // CluAdMMC registered? // Close the COM library. As per MSDN, each successfull call // to CoInitializeEx() must be balanced ba a call to CoUnitialize(). CoUninitialize(); } // COM library initialized? else { // Could not inititlize the COM library. ClRtlLogPrint( "CompleteUpgradingClusteringService could not initialize the COM library. Error %1!d!.\n", hResult ); } // COM library initialized? } // path to cluster directory obtained? else { // Couldn't expand the environment string. dwReturnValue = GetLastError(); ClRtlLogPrint( "CompleteUpgradingClusteringService could not register ClAdmWiz.dll\n" ); ClRtlLogPrint( "because it could not locate the cluster directory. Error %1!d!.\n", dwReturnValue ); } // path to cluster directory obtained? } if ( dwReturnValue == (DWORD) NO_ERROR ) { // Set the ClusterInstallationState reg value to "UPGRADED" ClRtlSetClusterInstallState( eClusterInstallStateUpgraded ); } } // did the default failure actions get set? } // did the connection objects get renamed? } // Was the ImagePath set successfully? } // Were the Welcome UI registry operations performed successfully? } // Were the UPGRADE registry operations performed successfully? } else { ClRtlLogPrint( "In CompleteUpgradingClusteringService since Cluster service " "has never been installed there is nothing to do.\n" ); dwReturnValue = (DWORD) NO_ERROR; } // Has Cluster service previously been installed? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // CompleteStandAloneInstallationOfClusteringService // // Routine Description: // This function completes the process of installing Cluster service when // either Add/Remove Programs is running or sysocmgr.exe was launched from // a command prompt. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::CompleteStandAloneInstallationOfClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; DWORD_PTR dwResult; // Update the progress bar. m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext ); // Update the "progress text" CString csProgressText; csProgressText.LoadString( IDS_INSTALLING_CLUS_SERVICE ); m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); // A selection state transition has occured. Perform the base // registry operations. dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId ); // On a standalone installation, we need to send a WM_SETTINGCHANGE message to all top level windows // so that any changes we have made to the system environment variables take effect. // During fresh installation, this happens automatically since there is a reboot. // See Knowledge Base article Q104011. SendMessageTimeout( HWND_BROADCAST, // handle to window WM_SETTINGCHANGE, // message 0, // wparam (LPARAM) _T("Environment"), // lparam SMTO_ABORTIFHUNG | SMTO_NORMAL, // send options 100, // time-out duration (ms). Note, each top level window can wait upto this time &dwResult // return value for synchronous call - we ignore this ); ClRtlLogPrint( "The base call to PerformRegistryOperations returned 0x%1!x! to CompleteStandAloneInstallationOfClusteringService.\n", dwReturnValue ); // Were the base registry operations performed successfully? if ( dwReturnValue == (DWORD) NO_ERROR ) { // Queue the registry operations to add "Configure Cluster service" // to the Welcome UI task list. As per Sharon Montgomery on 12/18/98 the // Server Solutions will use those same keys. CString csSectionName; csSectionName = CLUSREG_KEYNAME_WELCOME_UI; dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csSectionName ); ClRtlLogPrint( "The WelcomeUI call to PerformRegistryOperations returned 0x%1!x! to CompleteStandAloneInstallationOfClusteringService.\n", dwReturnValue ); if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "In CompleteStandAloneInstallationOfClusteringService the registry operations were queued successfully.\n" ); // Since this was a standalone installation launch the Cluster Configuration // program, cluscfg.exe. // First, build the command line used to launch cluscfg. The specific // command depends on whether this is an unattended operation. Regardless // of whether this is unattended, the first part of the command line is // the fully qualified path to cluscfg. BOOL fReturnValue; TCHAR tszClusCfgCommandLine[MAX_PATH]; fReturnValue = GetPathToClusCfg( tszClusCfgCommandLine ); // Was the path to cluscfg.exe deduced? if ( fReturnValue == (BOOL) TRUE ) { // Update the progress bar. m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext ); // Update the "progress text" CString csProgressText; csProgressText.LoadString( IDS_CONFIGURING_CLUS_SERVICE ); m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); // Is this an unattended operation? if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { // This is unattended. HINF hAnswerFile; // WARNING: NEVER close this handle because clusocm.dll // did not open it. // Get a handle to the answer file. WARNING: NEVER close this handle because clusocm.dll // did not open it. hAnswerFile = m_SetupInitComponent.HelperRoutines.GetInfHandle( INFINDEX_UNATTENDED, m_SetupInitComponent.HelperRoutines.OcManagerContext ); // Is the handle to the answer file OK? if ( (hAnswerFile != (HINF) NULL) && (hAnswerFile != (HINF) INVALID_HANDLE_VALUE) ) { // The handle to the answer file is legal. // tszClusCfgCommandLine is the path to cluscfg.exe. Append the "-UNATTEND" // command line option. CString csUnattendCommandLineOption; csUnattendCommandLineOption = UNATTEND_COMMAND_LINE_OPTION; _tcscat( tszClusCfgCommandLine, _T(" ") ); _tcscat( tszClusCfgCommandLine, csUnattendCommandLineOption ); // Append the path to the answer file. _tcscat( tszClusCfgCommandLine, _T(" ") ); _tcscat( tszClusCfgCommandLine, m_SetupInitComponent.SetupData.UnattendFile ); } // handle to answer file OK? } // unattended? // Launch cluscfg - tszClusCfgCommandLine contains the command, either // attended or unattended. STARTUPINFO StartupInfo; ZeroMemory( &StartupInfo, sizeof( StartupInfo ) ); StartupInfo.cb = sizeof( StartupInfo ); PROCESS_INFORMATION ProcessInformation; fReturnValue = CreateProcess( (LPCTSTR) NULL, (LPTSTR) tszClusCfgCommandLine, (LPSECURITY_ATTRIBUTES) NULL, (LPSECURITY_ATTRIBUTES) NULL, (BOOL) FALSE, (DWORD) (CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT), (LPVOID) GetEnvironmentStrings(), (LPCTSTR) NULL, (LPSTARTUPINFO) &StartupInfo, (LPPROCESS_INFORMATION) &ProcessInformation ); // Was the process created? if ( fReturnValue == (BOOL) TRUE ) { // Wait for cluscfg.exe to complete. As per Pat Styles on 7/6/98, // an OCM Setup DLL MUST wait for any process it spawns to complete. DWORD dwWFSOrv; dwWFSOrv = WaitForSingleObject( ProcessInformation.hProcess, INFINITE ); } else { DWORD dwErrorCode; dwErrorCode = GetLastError(); if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) == (DWORDLONG) 0L ) { CString csMessage; csMessage.Format( IDS_ERROR_SPAWNING_CLUSCFG, dwErrorCode ); AfxMessageBox( csMessage ); } ClRtlLogPrint( "Error %1!d! occured attempting to spawn cluscfg.exe.\n", dwErrorCode ); } // Was the process created? } // Path to cluscfg obtained? } // WelcomeUI registry operations succeeded? } // Were the base registry operations performed successfully? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // CompleteInstallingClusteringService // // Routine Description: // This function performs much of the processing when OC_COMPLETE_INSTALLATION // has been received and a clean installation is being performed. // // Arguments: // ptszSubComponentId - points to a string that uniquely identifies a sub- // component in the component's hiearchy. // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::CompleteInstallingClusteringService( IN LPCTSTR ptszSubComponentId ) { DWORD dwReturnValue; // Update the progress bar. if ( GetStepCount() != 0L ) { // Update the progress meter. m_SetupInitComponent.HelperRoutines.TickGauge( m_SetupInitComponent.HelperRoutines.OcManagerContext ); } // Update the "progress text" CString csProgressText; csProgressText.LoadString( IDS_INSTALLING_CLUS_SERVICE ); m_SetupInitComponent.HelperRoutines.SetProgressText( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) csProgressText ); // Perform the base registry operations. dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, ptszSubComponentId ); ClRtlLogPrint( "The base call to PerformRegistryOperations returned 0x%1!x! to CompleteInstallingClusteringService.\n", dwReturnValue ); // Were the base registry operations performed successfully? if ( dwReturnValue == (DWORD) NO_ERROR ) { // Queue the registry operations to add "Configure Cluster service" // to the Welcome UI task list. As per Sharon Montgomery on 12/18/98, Server // Solutions will use those same keys. CString csSectionName; csSectionName = CLUSREG_KEYNAME_WELCOME_UI; dwReturnValue = PerformRegistryOperations( m_SetupInitComponent.ComponentInfHandle, csSectionName ); ClRtlLogPrint( "The WelcomeUI call to PerformRegistryOperations returned 0x%1!x! to CompleteInstallingClusteringService.\n", dwReturnValue ); } // Were the base registry operations performed successfully? // Were the Welcome UI registry operations performed successfully? if ( dwReturnValue == (DWORD) NO_ERROR ) { ClRtlLogPrint( "In CompleteInstallingClusteringService for a clean install all registery operations were performed successfully.\n" ); } else { ClRtlLogPrint( "In CompleteInstallingClusteringService for a clean install the registry operations were not performed successfully.\n" ); } // For unattended setup this needs to write the WelcomeUI reg keys in a way that // will cause Configure Your Server to launch cluscfg.exe in unattended mode. // revisions to CompleteInstallingClusteringService may be appropriate prior to this code segment. // Were all registry operations performed correctly? if ( dwReturnValue == (DWORD) NO_ERROR ) { LONG lReturnValue; // used below for registry operations HKEY hKey; // used below for registry operations // Is this an unattended installation? if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { HINF hAnswerFile; // WARNING: NEVER close this handle because clusocm.dll // did not open it. // Get a handle to the answer file. WARNING: NEVER close this handle because clusocm.dll // did not open it. hAnswerFile = m_SetupInitComponent.HelperRoutines.GetInfHandle( INFINDEX_UNATTENDED, m_SetupInitComponent.HelperRoutines.OcManagerContext ); if ( (hAnswerFile != (HINF) NULL) && (hAnswerFile != (HINF) INVALID_HANDLE_VALUE) ) { // Write the WelcomeUI registry entry to launch cluscfg in unattended mode. // Note that the registry operations performed when CLUSREG_KEYNAME_WELCOME_UI // was passed to PerformRegistryOperations() above included creation of // HKLM\Software\Microsoft\Windows NT\CurrentVersion\Setup\OCManager\ToDoList\Cluster\ConfigCommand. // All that is necessary is to create the ConfigArgs value with the // appropriate command line arguments to execute cluscfg.exe in unattended mode. HKEY hKey; DWORD dwType; // Attempt to open an existing key in the registry. lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( "Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\OCManager\\ToDoList\\Cluster" ), (DWORD) 0, // reserved KEY_SET_VALUE, &hKey ); // Was the Cluster "to do list" registry key opened? if ( lReturnValue == (LONG) ERROR_SUCCESS ) { // The key is open. TCHAR tszValue[MAX_PATH]; _tcscpy( tszValue, UNATTEND_COMMAND_LINE_OPTION ); // Append the path to the answer file. _tcscat( tszValue, _T(" ") ); _tcscat( tszValue, m_SetupInitComponent.SetupData.UnattendFile ); // Set the key. DWORD dwValueLength; dwValueLength = (DWORD) ((_tcslen( tszValue ) + 1) * sizeof( TCHAR )); lReturnValue = RegSetValueEx( hKey, _T( "ConfigArgs" ), (DWORD) 0L, // reserved (DWORD) REG_EXPAND_SZ, (CONST BYTE *) tszValue, dwValueLength ); // Was the key written successfully? if ( lReturnValue == (LONG) ERROR_SUCCESS ) { dwReturnValue = (DWORD) NO_ERROR; ClRtlLogPrint( "CompleteInstallingClusteringService created the ConfigArgs reg key for unattended operation of cluscfg.\n" ); } else { dwReturnValue = GetLastError(); ClRtlLogPrint( "CompleteInstallingClusteringService failed to create the ConfigArgs reg key with error code %1!d!.\n", dwReturnValue ); } // Was the reg key written successfully? // Close the registry key. RegCloseKey( hKey ); // do we care about the return value? } else { // The key could not be opened. DWORD dwErrorCode; dwErrorCode = GetLastError(); ClRtlLogPrint( "In CompleteInstallingClusteringService RegOpenKeyEx failed with error code 0x%1!x!.\n", dwErrorCode ); } // Was the key opened? } // handle to answer file OK? } // Is this unattended? else { // This is NOT unattended, so make sure the ConfigArgs value is removed. // That will prevent any value left from a previous unattended install // which was never successfully configured from corrupting the behavior // of Configure Your Server or Add/Remove Programs. // Attempt to open an existing key in the registry. lReturnValue = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\OCManager\\ToDoList\\Cluster"), (DWORD) 0, // reserved KEY_READ, &hKey ); // Was the Cluster "to do list" registry key opened? if ( lReturnValue == (long) ERROR_SUCCESS ) { // Delete the values (which may not exist). RegDeleteValue( hKey, (LPCTSTR) TEXT("ConfigArgs") ); } // if ToDoList opened } // if unattended? } // registry operations successfull? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // OnOcCleanup // // Routine Description: // This function processes the OC_CLEANUP messages by removing the "private" // subdirectory in the cluster directory if it is empty and the cluster directory // itself it it is empty. // // Arguments: // none // // Return Value: // (DWORD) NO_ERROR - indicates success // Any other value is a standard Win32 error code. // // Note: // The "private" subrirectory may exist in the cluster directory if the system // was upgraded from NT4. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::OnOcCleanup( void ) { ClRtlLogPrint( "Processing OC_CLEANUP.\n" ); DWORD dwReturnValue; dwReturnValue = CleanupDirectories(); ClRtlLogPrint( "\n\n\n\n" ); // Close the log file. ClRtlCleanup(); // Set the ClusterLog environment variable back to its' original state. char szLogFileName[MAX_PATH]; strcpy( szLogFileName, "ClusterLog=" ); if ( *m_szOriginalLogFileName != (char) '\0' ) { strcat( szLogFileName, m_szOriginalLogFileName ); } _putenv( szLogFileName ); return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // CleanupDirectories // // Routine Description: // This function removes the "private" subdirectory that may remain in the // cluster directory if the system has ever been upgraded from NT 4 if that // directory is empty. It will also remove the cluster directory itself if // it is empty. // // Arguments: // none // // Return Value: // (DWORD) NO_ERROR - indicates success // (DWORD) -1L - means that the cluster directory was not empty // Any other value is a standard Win32 error code. // // Note: // This function assumes that the path to the cluster directory is less than // MAX_PATH TCHARs long and that function SetupGetTargetPath expects the fifth // parameter to specify the number of TCHARS (not bytes) in the buffer. // // No recovery is attempted if the call to SetupGetTargetPath fails. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::CleanupDirectories( void ) { DWORD dwReturnValue = (DWORD) NO_ERROR; BOOL fReturnValue; DWORD dwRequiredSize; TCHAR tszPathToClusterDirectory[MAX_PATH]; // First, get the path to the cluster directory. CString csSectionName; csSectionName = CLUSTER_FILES_INF_KEY; fReturnValue = SetupGetTargetPath( m_SetupInitComponent.ComponentInfHandle, (PINFCONTEXT) NULL, (PCTSTR) csSectionName, tszPathToClusterDirectory, (DWORD) MAX_PATH, (PDWORD) &dwRequiredSize ); // Was the path to the cluster directory obtained? if ( fReturnValue == (BOOL) TRUE ) { // Test for the presence of a subdirectory named "private". TCHAR tszPathToPrivateDirectory[MAX_PATH]; // Append "\private" to the path to the cluster directory that was obtained above. // Note, I didn't put this string in the stringtable because it is // not localizable, and will never change. _tcscpy( tszPathToPrivateDirectory, tszPathToClusterDirectory ); _tcscat( tszPathToPrivateDirectory, _T("\\private") ); HANDLE hFindHandle; WIN32_FIND_DATA FindData; // Does a file or directory named "private" exist? hFindHandle = FindFirstFile( (LPCTSTR) tszPathToPrivateDirectory, &FindData ); if ( hFindHandle != (HANDLE) INVALID_HANDLE_VALUE ) { // A file named "private" exists in the cluster directory. Is it a directory? if ( (FindData.dwFileAttributes & (DWORD) FILE_ATTRIBUTE_DIRECTORY) != (DWORD) 0L ) { // The "private" directory has been located. Determine whether it is empty. if ( IsDirectoryEmpty( tszPathToPrivateDirectory ) == (BOOL) TRUE ) { // No file was found in the "private" directory. Remove the "private" directory. if ( RemoveDirectory( (LPCTSTR) tszPathToPrivateDirectory ) == (BOOL) TRUE ) { dwReturnValue = (DWORD) NO_ERROR; } else { // Could not delete the "private" directory even though it was empty. dwReturnValue = GetLastError(); ClRtlLogPrint( "CleanupDirectories was unable to remove %1!s!. The error code is %2!x!.\n", tszPathToPrivateDirectory, dwReturnValue ); } // Was the "private" directory removed successfully? } else { ClRtlLogPrint( "CleanupDirectories did not attempt to remove %1!s! because it is not empty.\n", tszPathToPrivateDirectory ); // An error code of -1 means the "private" directory, and thus the // cluster directory was not empty. dwReturnValue = (DWORD) -1L; } // Was any file found in the "private" directory? } else { ClRtlLogPrint( "In CleanupDirectories the file named private is not a directory.\n"); dwReturnValue = (DWORD) ERROR_FILE_NOT_FOUND; } // Is it a directory? // Close the search that located the "private" directory. FindClose( hFindHandle ); } else { dwReturnValue = GetLastError(); ClRtlLogPrint( "CleanupDirectories was unable to find any file named private in the cluster directory.\n"); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwReturnValue ); } // Does a file or directory named "private" exist? // Was the "private" directory absent or removed successfully? if ( (dwReturnValue == (DWORD) ERROR_FILE_NOT_FOUND) || (dwReturnValue == (DWORD) NO_ERROR) ) { // The "private" directory either did not exist or has been removed. // Now determine whether the cluster directory is empty and therefore should be removed. // This code segment assumes that the cluster directory, as specified // in tszPathToClusterDirectory exists. if ( IsDirectoryEmpty( tszPathToClusterDirectory ) == (BOOL) TRUE ) { // The cluster directory is empty. if ( RemoveDirectory( tszPathToClusterDirectory ) == (BOOL) TRUE ) { dwReturnValue = (DWORD) NO_ERROR; } else { // Could not delete the "cluster" directory even though it was empty. dwReturnValue = GetLastError(); ClRtlLogPrint( "CleanupDirectories was unable to remove %1!s!. The error code is %2!x!.\n", tszPathToClusterDirectory, dwReturnValue ); } // Was the cluster directory removed? } else { // The cluster directory is not empty. ClRtlLogPrint( "CleanupDirectories did not attempt to remove %1!s! because it is not empty.\n", tszPathToClusterDirectory ); // An error code of -1 means the cluster directory was not empty. dwReturnValue = (DWORD) -1L; } // Is the cluster directory empty? } // Was the "private" directory absent or removed? } else { dwReturnValue = GetLastError(); ClRtlLogPrint( "CleanupDirectories was unable to obtain the path to the cluster directory.\n"); ClRtlLogPrint( "The error code is 0x%1!x!.\n", dwReturnValue ); } // Was the path to the cluster directory obtained? return ( dwReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // IsDirectoryEmpty // // Routine Description: // This function determines whether a directory is empty. // // Arguments: // ptzDirectoryName - points to a string specifying the directory name. // // Return Value: // TRUE - indicates that the directory is empty // FALSE - indicates that the directory is not empty // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusocmApp::IsDirectoryEmpty( LPCTSTR ptszDirectoryName ) { BOOL fDirectoryEmpty; HANDLE hFindHandle; WIN32_FIND_DATA FindData; TCHAR tszFindPath[MAX_PATH]; // Append the file specification to the path supplied. _tcscpy( tszFindPath, ptszDirectoryName ); _tcscat( tszFindPath, _T("\\*") ); hFindHandle = FindFirstFile( tszFindPath, &FindData ); // Was any file found in the directory? if ( hFindHandle != (HANDLE) INVALID_HANDLE_VALUE ) { // A file was found. It could be "." or "..". if ( (_tcscmp( FindData.cFileName, _T(".") ) == 0) || (_tcscmp( FindData.cFileName, _T("..") ) == 0) ) { // The file was either "." or "..". Keep trying. DWORD dwErrorCode; if ( FindNextFile( hFindHandle, &FindData ) == (BOOL) TRUE ) { // A file was found. It could be "." or "..". if ( (_tcscmp( FindData.cFileName, _T(".") ) == 0) || (_tcscmp( FindData.cFileName, _T("..") ) == 0) ) { // The file was either "." or "..". Keep trying. if ( FindNextFile( hFindHandle, &FindData ) == (BOOL) TRUE ) { // The file cannot possibly be either "." or "..". fDirectoryEmpty = (BOOL) FALSE; } else { dwErrorCode = GetLastError(); if ( dwErrorCode == (DWORD) ERROR_NO_MORE_FILES ) { fDirectoryEmpty = (BOOL) TRUE; } else { fDirectoryEmpty = (BOOL) FALSE; } } } else { // The private directory is not empty. fDirectoryEmpty = (BOOL) FALSE; } } else { dwErrorCode = GetLastError(); if ( dwErrorCode == ERROR_NO_MORE_FILES ) { fDirectoryEmpty = (BOOL) TRUE; } else { fDirectoryEmpty = (BOOL) FALSE; } } } else { // The directory is not empty. fDirectoryEmpty = (BOOL) FALSE; } // Close the search. FindClose( hFindHandle ); } else { fDirectoryEmpty = (BOOL) TRUE; } return ( fDirectoryEmpty ); } ///////////////////////////////////////////////////////////////////////////// //++ // // GetPathToClusCfg // // Routine Description: // This function deduces the path to the Cluster service configuration // program, cluscfg.exe. // // It assumes that if the Cluster service is registered with the Service // COntrol Manager then cluscfg.exe is located in the directory with clussvc.exe. // Otherwise it expands %windir%\cluster and declares that to be the path. // // The name of the Cluster service configuration pogram is then appended to // the path. // // Arguments: // lptszPathToClusCfg - points to a string in which the fully qualified path to // the Cluster service configuration program is to be placed. // // Return Value: // TRUE - indicates that lptszPathToClusCfg is valid // FALSE - indicates that an error occured // //-- ///////////////////////////////////////////////////////////////////////////// BOOL CClusocmApp::GetPathToClusCfg( LPTSTR lptszPathToClusCfg ) { BOOL fReturnValue; // If the Cluster service is registered assume that cluscfg is in // the same directory as clussvc.exe and get the path to cluscfg.exe // from the Service Control Mamager. If the Cluster service is not // registered, assume cluscfg.exe is in %windir%\cluster. if ( IsClusterServiceRegistered() == (BOOL) TRUE ) { // Query the path to the Cluster Service executable from the Service Control Manager. CString csClusterService; csClusterService = CLUSTER_SERVICE_NAME; fReturnValue = GetServiceBinaryPath( (LPWSTR) (LPCTSTR) csClusterService, lptszPathToClusCfg ); } else { // Use the default location. CString csClusterDirectory; csClusterDirectory = CLUSTER_DIRECTORY; if ( ExpandEnvironmentStrings( (LPCTSTR) csClusterDirectory, lptszPathToClusCfg, MAX_PATH ) > 0L ) { fReturnValue = (BOOL) TRUE; } else { // Could not expand the enviornment string. The default location for the // Cluster service could not be determined. fReturnValue = (BOOL) FALSE; } // Was the default location for cluscfg.exe determined? } // Where is cluscfg.exe? // Was the path to cluscfg.exe deduced? if ( fReturnValue == (BOOL) TRUE ) { // tszPathToClusCfg is the path to cluscfg.exe. Append the program name. CString csClusterConfigurationProgram; csClusterConfigurationProgram = CLUSTER_CONFIGURATION_PGM; _tcscat( lptszPathToClusCfg, _T("\\") ); _tcscat( lptszPathToClusCfg, csClusterConfigurationProgram ); } else { // Set the target string empty. *lptszPathToClusCfg = _T( '\0' ); } return ( fReturnValue ); } ///////////////////////////////////////////////////////////////////////////// //++ // // CalculateStepCount // // Routine Description: // This function calculates the "step count", i.e the number of non-file operations // to be performed when processing OC_COMPLETE_INSTALLATION. // // Arguments: // ptszSubComponentId // // Return Value: // The number of non-file operation (steps) to perform when processing // OC_COMPLETE_INSTALLATION // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::CalculateStepCount( LPCTSTR ptszSubComponentId ) { DWORD dwStepCount; // Is this UNATTENDED or ATTENDED? if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_BATCH) != (DWORDLONG) 0L ) { // This is UNATTENDED. if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_NTUPGRADE) != (DWORDLONG) 0L ) { // This is UPGRADE. Cluscfg.exe will not be launched, so there is // one non-file operation to be performed, registry revisions. dwStepCount = 1L; } else { // This is a clean INSTALL. Cluscfg.exe may be launched. // Is there a [cluster] section in the answer file? BUGBUG // so there // are two non-file operations to be performed, registry revisions // and execution of cluscfg. dwStepCount = 2L; } // unattended: upgrade or install? } else { // This is ATTENDED. if ( (m_SetupInitComponent.SetupData.OperationFlags & (DWORDLONG) SETUPOP_STANDALONE) != (DWORDLONG) 0L ) { // This is STANDALONE. Is Cluster service selected? BOOL fCurrentSelectionState; BOOL fOriginalSelectionState; fCurrentSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, (LPCTSTR) ptszSubComponentId, (UINT) OCSELSTATETYPE_CURRENT ); fOriginalSelectionState = m_SetupInitComponent.HelperRoutines.QuerySelectionState( m_SetupInitComponent.HelperRoutines.OcManagerContext, ptszSubComponentId, (UINT) OCSELSTATETYPE_ORIGINAL ); // Was there a selection state transition? if ( fCurrentSelectionState != fOriginalSelectionState ) { if ( fCurrentSelectionState == (BOOL) TRUE ) { // Currently selected - This is a clean install. There are two // non-file operations to be performed, registry revisions and // execution of cluscfg.exe. dwStepCount = 2L; } else { // Currently not selected - This is an uninstall. There is one // non-file operation to be performed, registry revisions. dwStepCount = 1L; } // currently selected? dwStepCount = 1L; } else { // There was no selection state transition, therefore there // are zero non-file operations to be performed. dwStepCount = 0L; } // selection state transition? } else { // This is GUI mode setup. The two operations that may be performed // during GUI mode setup are clean install and upgrade. In neither // case does cluscfg.exe get launched. Thus, there is one non-file // operation to be performed, registry revisions. dwStepCount = 1L; } // interactive: standalone or atttended? } // Is this UNATTENDED or ATTENDED? return ( dwStepCount ); } ///////////////////////////////////////////////////////////////////////////// //++ // // SetStepCount // // Routine Description: // This function sets the value of the m_dwStepCount member of the CClusocmApp object. // // Arguments: // dwStepCount - the value to which the m_dwStepCount member should be set. // // Return Value: // None // //-- ///////////////////////////////////////////////////////////////////////////// void CClusocmApp::SetStepCount( DWORD dwStepCount ) { m_dwStepCount = dwStepCount; } ///////////////////////////////////////////////////////////////////////////// //++ // // GetStepCount // // Routine Description: // This function returns the content of the m_dwStepCount member of the // CClusocmApp object. // // Arguments: // None // // Return Value: // The content of the m_dwStepCount member. // //-- ///////////////////////////////////////////////////////////////////////////// DWORD CClusocmApp::GetStepCount( void ) { return ( m_dwStepCount ); }