////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000-2001 Microsoft Corporation // // Module Name: // CClusterResource.cpp // // Description: // This file contains the definition of the CClusterResource // class. // // The class CClusterResource represents a cluster manageable // device. It implements the IClusCfgManagedResourceInfo interface. // // Maintained By: // Galen Barbee (GalenB) 13-JUN-2000 // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Include Files ////////////////////////////////////////////////////////////////////////////// #include "Pch.h" #include "CClusterResource.h" #include ////////////////////////////////////////////////////////////////////////////// // Constant Definitions ////////////////////////////////////////////////////////////////////////////// DEFINE_THISCLASS( "CClusterResource" ); //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // CClusterResource class ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::S_HrCreateInstance // // Description: // Create a CClusterResource instance. // // Arguments: // None. // // Return Values: // Pointer to CClusterResource instance. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CClusterResource::S_HrCreateInstance( IUnknown ** ppunkOut ) { TraceFunc( "" ); HRESULT hr = S_OK; CClusterResource * pcr = NULL; if ( ppunkOut == NULL ) { hr = THR( E_POINTER ); goto Cleanup; } // if: pcr = new CClusterResource(); if ( pcr == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if: error allocating object hr = THR( pcr->HrInit() ); if ( FAILED( hr ) ) { goto Cleanup; } // if: HrInit() failed hr = THR( pcr->TypeSafeQI( IUnknown, ppunkOut ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: QI failed Cleanup: if ( FAILED( hr ) ) { LogMsg( L"[SRV] CClusterResource::S_HrCreateInstance() failed. (hr = %#08x)", hr ); } // if: if ( pcr != NULL ) { pcr->Release(); } // if: HRETURN( hr ); } //*** CClusterResource::S_HrCreateInstance ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::CClusterResource // // Description: // Constructor of the CClusterResource class. This initializes // the m_cRef variable to 1 instead of 0 to account of possible // QueryInterface failure in DllGetClassObject. // // Arguments: // None. // // Return Value: // None. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// CClusterResource::CClusterResource( void ) : m_cRef( 1 ) { TraceFunc( "" ); // Increment the count of components in memory so the DLL hosting this // object cannot be unloaded. InterlockedIncrement( &g_cObjects ); Assert( m_dwFlags == 0 ); Assert( m_lcid == 0 ); Assert( m_picccCallback == NULL ); Assert( m_bstrDescription == NULL ); Assert( m_bstrType == NULL ); TraceFuncExit(); } //*** CClusterResource::CClusterResource ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::~CClusterResource // // Description: // Desstructor of the CClusterResource class. // // Arguments: // None. // // Return Value: // None. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// CClusterResource::~CClusterResource( void ) { TraceFunc( "" ); if ( m_picccCallback != NULL ) { m_picccCallback->Release(); } // if: TraceSysFreeString( m_bstrDescription ); TraceSysFreeString( m_bstrType ); // There's going to be one less component in memory. Decrement component count. InterlockedDecrement( &g_cObjects ); TraceFuncExit(); } //*** CClusterResource::~CClusterResource //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // CClusterResource -- IUknkown interface. ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::AddRef // // Description: // Increment the reference count of this object by one. // // Arguments: // None. // // Return Value: // The new reference count. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_( ULONG ) CClusterResource::AddRef( void ) { TraceFunc( "[IUnknown]" ); InterlockedIncrement( &m_cRef ); CRETURN( m_cRef ); } //*** CClusterResource::AddRef ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::Release // // Description: // Decrement the reference count of this object by one. // // Arguments: // None. // // Return Value: // The new reference count. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_( ULONG ) CClusterResource::Release( void ) { TraceFunc( "[IUnknown]" ); LONG cRef; cRef = InterlockedDecrement( &m_cRef ); if ( cRef == 0 ) { TraceDo( delete this ); } // if: reference count equal to zero CRETURN( cRef ); } //*** CClusterResource::Release ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::QueryInterface // // Description: // Query this object for the passed in interface. // // Arguments: // riidIn // Id of interface requested. // // ppvOut // Pointer to the requested interface. // // Return Value: // S_OK // If the interface is available on this object. // // E_NOINTERFACE // If the interface is not available. // // E_POINTER // ppvOut was NULL. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::QueryInterface( REFIID riidIn , void ** ppvOut ) { TraceQIFunc( riidIn, ppvOut ); HRESULT hr = S_OK; // // Validate arguments. // Assert( ppvOut != NULL ); if ( ppvOut == NULL ) { hr = THR( E_POINTER ); goto Cleanup; } // // Handle known interfaces. // if ( IsEqualIID( riidIn, IID_IUnknown ) ) { *ppvOut = static_cast< IClusCfgManagedResourceInfo * >( this ); } // if: IUnknown else if ( IsEqualIID( riidIn, IID_IClusCfgManagedResourceInfo ) ) { *ppvOut = TraceInterface( __THISCLASS__, IClusCfgManagedResourceInfo, this, 0 ); } // else if: IClusCfgManagedResourceInfo else if ( IsEqualIID( riidIn, IID_IClusCfgInitialize ) ) { *ppvOut = TraceInterface( __THISCLASS__, IClusCfgInitialize, this, 0 ); } // else if: IClusCfgInitialize else if ( IsEqualIID( riidIn, IID_IClusCfgLoadResource ) ) { *ppvOut = TraceInterface( __THISCLASS__, IClusCfgLoadResource, this, 0 ); } // else if: IClusCfgLoadResource else { *ppvOut = NULL; hr = E_NOINTERFACE; } // // Add a reference to the interface if successful. // if ( SUCCEEDED( hr ) ) { ((IUnknown *) *ppvOut)->AddRef(); } // if: success Cleanup: QIRETURN_IGNORESTDMARSHALLING( hr, riidIn ); } //*** CClusterResource::QueryInterface //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // CClusterResource -- IClusCfgInitialize interface. ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::Initialize // // Description: // Initialize this component. // // Arguments: // IN IUknown * punkCallbackIn // // IN LCID lcidIn // // Return Value: // S_OK // Success // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::Initialize( IUnknown * punkCallbackIn, LCID lcidIn ) { TraceFunc( "[IClusCfgInitialize]" ); Assert( m_picccCallback == NULL ); HRESULT hr = S_OK; m_lcid = lcidIn; if ( punkCallbackIn == NULL ) { hr = THR( E_POINTER ); goto Cleanup; } // if: hr = THR( punkCallbackIn->TypeSafeQI( IClusCfgCallback, &m_picccCallback ) ); Cleanup: HRETURN( hr ); } //*** CClusterResource::Initialize //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // CClusterResource -- IClusCfgLoadResource interface. ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::LoadResource // // Description: // Initialize this component from the cluster resource. // // Arguments: // // Return Value: // S_OK // Success // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::LoadResource( HCLUSTER hClusterIn, HRESOURCE hResourceIn ) { TraceFunc( "[IClusCfgLoadResource]" ); HRESULT hr = S_OK; DWORD sc; CClusPropList cpl; CLUSPROP_BUFFER_HELPER cpbh; sc = TW32( cpl.ScGetResourceProperties( hResourceIn, CLUSCTL_RESOURCE_GET_COMMON_PROPERTIES ) ); if ( sc != ERROR_SUCCESS ) { goto MakeHr; } // if: sc = TW32( cpl.ScMoveToPropertyByName( L"Description" ) ); if ( sc != ERROR_SUCCESS ) { goto MakeHr; } // if: cpbh = cpl.CbhCurrentValue(); Assert( cpbh.pSyntax->dw == CLUSPROP_SYNTAX_LIST_VALUE_SZ ); m_bstrDescription = TraceSysAllocString( cpbh.pStringValue->sz ); if ( m_bstrDescription == NULL ) { goto OutOfMemory; } // if: sc = TW32( cpl.ScMoveToPropertyByName( L"Name" ) ); if ( sc != ERROR_SUCCESS ) { goto MakeHr; } // if: cpbh = cpl.CbhCurrentValue(); Assert( cpbh.pSyntax->dw == CLUSPROP_SYNTAX_LIST_VALUE_SZ ); m_bstrName = TraceSysAllocString( cpbh.pStringValue->sz ); if ( m_bstrName == NULL ) { goto OutOfMemory; } // if: sc = TW32( cpl.ScMoveToPropertyByName( L"Type" ) ); if ( sc != ERROR_SUCCESS ) { goto MakeHr; } // if: cpbh = cpl.CbhCurrentValue(); Assert( cpbh.pSyntax->dw == CLUSPROP_SYNTAX_LIST_VALUE_SZ ); m_bstrType = TraceSysAllocString( cpbh.pStringValue->sz ); if ( m_bstrType == NULL ) { goto OutOfMemory; } // if: hr = THR( HrIsResourceQuorumCapabile( hResourceIn ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: else if ( hr == S_OK ) { m_dwFlags |= eIsQuorumCapable; } // Do this only if the above is true i.e. Device quorum capable... if( hr == S_OK ) { hr = THR( HrDetermineQuorumJoinable( hResourceIn ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: else if ( hr == S_OK ) { m_dwFlags |= eIsQuorumJoinable; } } goto Cleanup; OutOfMemory: hr = THR( E_OUTOFMEMORY ); STATUS_REPORT_REF( TASKID_Major_Find_Devices, TASKID_Minor_LoadResource, IDS_ERROR_OUTOFMEMORY, IDS_ERROR_OUTOFMEMORY_REF, hr ); goto Cleanup; MakeHr: hr = HRESULT_FROM_WIN32( sc ); Cleanup: HRETURN( hr ); } //*** CClusterResource::LoadResource //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // CClusterResource -- IClusCfgManagedResourceInfo interface. ///////////////////////////////////////////////////////////////////////////// #if 0 // DEAD CODE: GPease 27-JUL-2000 Method Removed /* ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::TransferInformation // // Description: // Transfer node information from another source. // // Arguments: // IN IClusCfgManagedResourceInfo * pccmriIn // // Return Value: // S_OK // Success // // E_OUTOFMEMORY // Out of memory. // // Remarks: // none // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::TransferInformation( IClusCfgManagedResourceInfo * pccmriIn ) { TraceFunc( "" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } //*** CClusterResource::TransferInformation */ #endif // End Dead Code ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::GetUID // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::GetUID( BSTR * pbstrUIDOut ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = S_OK; if ( pbstrUIDOut == NULL ) { hr = THR( E_POINTER ); STATUS_REPORT_REF( TASKID_Major_Find_Devices, TASKID_Minor_ClusterResource_GetUID_Pointer, IDS_ERROR_NULL_POINTER, IDS_ERROR_NULL_POINTER_REF, hr ); goto Cleanup; } // if: *pbstrUIDOut = SysAllocString( m_bstrName ); if ( *pbstrUIDOut == NULL ) { hr = THR( E_OUTOFMEMORY ); STATUS_REPORT_REF( TASKID_Major_Find_Devices, TASKID_Minor_ClusterResource_GetUID_Memory, IDS_ERROR_OUTOFMEMORY, IDS_ERROR_OUTOFMEMORY_REF, hr ); } // if: Cleanup: HRETURN( hr ); } //*** CClusterResource::GetUID ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::GetName // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::GetName( BSTR * pbstrNameOut ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = S_OK; if ( pbstrNameOut != NULL ) { hr = THR( E_POINTER ); STATUS_REPORT_REF( TASKID_Major_Find_Devices, TASKID_Minor_GetName_Pointer, IDS_ERROR_NULL_POINTER, IDS_ERROR_NULL_POINTER_REF, hr ); goto Cleanup; } // if: *pbstrNameOut = SysAllocString( m_bstrName ); if ( *pbstrNameOut == NULL ) { hr = THR( E_OUTOFMEMORY ); STATUS_REPORT_REF( TASKID_Major_Find_Devices, TASKID_Minor_GetName_Memory, IDS_ERROR_OUTOFMEMORY, IDS_ERROR_OUTOFMEMORY_REF, hr ); } // if: Cleanup: HRETURN( hr ); } //*** CClusterResource::GetName ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::SetName // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::SetName( BSTR bstrNameIn ) { TraceFunc1( "[IClusCfgManagedResourceInfo] bstrNameIn = '%ls'", bstrNameIn == NULL ? L"" : bstrNameIn ); HRESULT hr = S_OK; if ( bstrNameIn == NULL ) { hr = THR( E_INVALIDARG ); goto Cleanup; } // if: TraceSysFreeString( m_bstrName ); m_bstrName = NULL; m_bstrName = TraceSysAllocString( bstrNameIn ); if ( m_bstrName == NULL ) { hr = THR( E_OUTOFMEMORY ); STATUS_REPORT_REF( TASKID_Major_Find_Devices, TASKID_Minor_SetName_Cluster_Resource, IDS_ERROR_OUTOFMEMORY, IDS_ERROR_OUTOFMEMORY_REF, hr ); } // if: Cleanup: HRETURN( hr ); } //*** CClusterResource::SetName ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::IsManaged // // Description: // // Arguments: // // Return Value: // S_OK // The device is managed. // // S_FALSE // The device is not managed. // // Win32 error as HRESULT when an error occurs. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::IsManaged( void ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRETURN( S_OK ); } //*** CClusterResource::IsManaged ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::SetManaged // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::SetManaged( BOOL fIsManagedIn ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } //*** CClusterResource::SetManaged ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::IsQuorumDevice // // Description: // // Arguments: // // Return Value: // S_OK // The device is the quorum device. // // S_FALSE // The device is not the quorum device. // // Win32 error as HRESULT when an error occurs. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::IsQuorumDevice( void ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = S_FALSE; if ( m_dwFlags & eIsQuorumDevice ) { hr = S_OK; } // if: HRETURN( hr ); } //*** CClusterResource::IsQuorumDevice ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::SetQuorumedDevice // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::SetQuorumedDevice( BOOL fIsQuorumDeviceIn ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } //*** CClusterResource::SetQuorumedDevice ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::IsDeviceJoinable // // Description: // Does the Quorumable device allow other nodes to join. // // Arguments: // // Return Value: // S_OK // The device is joinable. // // S_FALSE // The device is not joinable. // // Win32 error as HRESULT when an error occurs. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::IsDeviceJoinable( void ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = S_FALSE; if ( m_dwFlags & eIsQuorumJoinable ) { hr = S_OK; } // if: HRETURN( hr ); } //*** CClusterResource::IsDeviceJoijnable ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::SetDeviceJoinable // // Description: // Set the joinable flag. // // Arguments: // // Return Value: // S_OK // The device is joinable. // // S_FALSE // The device is not joinable. // // Win32 error as HRESULT when an error occurs. // // Remarks: // This method should never be called. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::SetDeviceJoinable( BOOL fIsJoinableIn ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRETURN( THR( E_NOTIMPL ) ); } //*** CClusterResource::SetDeviceJoijnable ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::IsQuorumCapable // // Description: // // Arguments: // // Return Value: // S_OK // The device is a quorum capable device. // // S_FALSE // The device is not a quorum capable device. // // Win32 error as HRESULT when an error occurs. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::IsQuorumCapable( void ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = S_FALSE; if ( m_dwFlags & eIsQuorumCapable ) { hr = S_OK; } // if: HRETURN( hr ); } //*** CClusterResource::IsQuorumCapable ////////////////////////////////////////////////////////////////////////// // // CClusterResource::SetQuorumCapable // // Description: // Call this to set whether the resource is capable to be the quorum // resource or not. // // Parameter: // fIsQuorumCapableIn - If TRUE, the resource will be marked as quorum capable. // // Return Values: // S_OK // Call succeeded. // ////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::SetQuorumCapable( BOOL fIsQuorumCapableIn ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } //*** CClusterResource::SetQuorumCapable ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::GetDriveLetterMappings // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::GetDriveLetterMappings( SDriveLetterMapping * pdlmDriveLetterMappingOut ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } //*** CClusterResource::GetDriveLetterMappings ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::SetDriveLetterMappings // // Description: // // Arguments: // // Return Value: // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CClusterResource::SetDriveLetterMappings( SDriveLetterMapping dlmDriveLetterMappingIn ) { TraceFunc( "[IClusCfgManagedResourceInfo]" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } //*** CClusterResource::SetDriveLetterMappings //*************************************************************************// ///////////////////////////////////////////////////////////////////////////// // CClusterResource class -- Private Methods. ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::HrInit // // Description: // Initialize this component. // // Arguments: // None. // // Return Value: // // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CClusterResource::HrInit( void ) { TraceFunc( "" ); HRESULT hr = S_OK; // IUnknown Assert( m_cRef == 1 ); HRETURN( hr ); } //*** CClusterResource::HrInit ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::HrIsResourceQuorumCapabile // // Description: // Is this resource quorum capable? // // Arguments: // None. // // Return Value: // // S_OK // The resource is quorum capable. // // S_FALSE // The resource is not quorum capable. // // Other Win32 error as HRESULT. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CClusterResource::HrIsResourceQuorumCapabile( HRESOURCE hResourceIn ) { TraceFunc( "" ); HRESULT hr = S_FALSE; DWORD sc; DWORD dwFlags; DWORD cbReturned; sc = TW32( ClusterResourceControl( hResourceIn, NULL, CLUSCTL_RESOURCE_GET_CHARACTERISTICS, &dwFlags, sizeof( dwFlags ), NULL, NULL, &cbReturned ) ); if ( sc != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( sc ); goto Cleanup; } // if: if ( dwFlags & ( CLUS_CHAR_QUORUM | CLUS_CHAR_LOCAL_QUORUM | CLUS_CHAR_LOCAL_QUORUM_DEBUG ) ) { hr = S_OK; } // if: Cleanup: HRETURN( hr ); } //*** CClusterResource::HrIsResourceQuorumCapabile ////////////////////////////////////////////////////////////////////////////// //++ // // CClusterResource::HrDetermineQuorumJoinable // // Description: // Is this quorumable resource joinable? // // Arguments: // None. // // Return Value: // // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CClusterResource::HrDetermineQuorumJoinable( HRESOURCE hResourceIn ) { TraceFunc( "" ); HRESULT hr = S_FALSE; DWORD sc; DWORD dwFlags; DWORD cbReturned; sc = TW32( ClusterResourceControl( hResourceIn, NULL, CLUSCTL_RESOURCE_GET_CHARACTERISTICS, &dwFlags, sizeof( dwFlags ), NULL, NULL, &cbReturned ) ); if ( sc != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( sc ); goto Cleanup; } // if: if ( dwFlags & (CLUS_CHAR_QUORUM|CLUS_CHAR_LOCAL_QUORUM_DEBUG)) { hr = S_OK; } // if: Cleanup: HRETURN( hr ); }