mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
499 lines
15 KiB
499 lines
15 KiB
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1997-1999 Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
// MSClus.cpp
|
|
//
|
|
// Description:
|
|
// Implementation of the DLL Exports for the MSCLUS automation classes.
|
|
//
|
|
// Author:
|
|
// Charles Stacy Harris (stacyh) 28-Feb-1997
|
|
// Galen Barbee (galenb) July 1998
|
|
//
|
|
// Revision History:
|
|
// July 1998 GalenB Maaaaaajjjjjjjjjoooooorrrr clean up
|
|
//
|
|
// Notes:
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
#include "stdafx.h"
|
|
#include <atlimpl.cpp>
|
|
#include <initguid.h>
|
|
#include <ClusRtl.h>
|
|
#include "ClusterObject.h"
|
|
#include "property.h"
|
|
#include "ClusNeti.h"
|
|
#include "ClusNetw.h"
|
|
#include "ClusRes.h"
|
|
#include "ClusRest.h"
|
|
#include "ClusResg.h"
|
|
#include "ClusNode.h"
|
|
#include "Version.h"
|
|
#include "ClusApp.h"
|
|
#include "Cluster.h"
|
|
|
|
#define IID_DEFINED
|
|
#include "msclus_i.c"
|
|
#undef IID_DEFINED
|
|
|
|
CComModule _Module;
|
|
|
|
BEGIN_OBJECT_MAP(ObjectMap)
|
|
OBJECT_ENTRY(CLSID_ClusApplication, CClusApplication)
|
|
OBJECT_ENTRY(CLSID_Cluster, CCluster)
|
|
OBJECT_ENTRY(CLSID_ClusVersion, CClusVersion)
|
|
OBJECT_ENTRY(CLSID_DomainNames, CDomainNames)
|
|
OBJECT_ENTRY(CLSID_ClusResGroupPreferredOwnerNodes, CClusResGroupPreferredOwnerNodes)
|
|
OBJECT_ENTRY(CLSID_ClusterNames, CClusterNames)
|
|
OBJECT_ENTRY(CLSID_ClusNetInterface, CClusNetInterface)
|
|
OBJECT_ENTRY(CLSID_ClusNetInterfaces, CClusNetInterfaces)
|
|
OBJECT_ENTRY(CLSID_ClusNetwork, CClusNetwork)
|
|
OBJECT_ENTRY(CLSID_ClusNetworks, CClusNetworks)
|
|
OBJECT_ENTRY(CLSID_ClusNetworkNetInterfaces, CClusNetworkNetInterfaces)
|
|
OBJECT_ENTRY(CLSID_ClusNode, CClusNode)
|
|
OBJECT_ENTRY(CLSID_ClusNodes, CClusNodes)
|
|
OBJECT_ENTRY(CLSID_ClusNodeNetInterfaces, CClusNodeNetInterfaces)
|
|
OBJECT_ENTRY(CLSID_ClusProperty, CClusProperty)
|
|
OBJECT_ENTRY(CLSID_ClusProperties, CClusProperties)
|
|
OBJECT_ENTRY(CLSID_ClusRefObject, CClusRefObject)
|
|
OBJECT_ENTRY(CLSID_ClusResDependencies, CClusResDependencies)
|
|
OBJECT_ENTRY(CLSID_ClusResGroup, CClusResGroup)
|
|
OBJECT_ENTRY(CLSID_ClusResGroups, CClusResGroups)
|
|
OBJECT_ENTRY(CLSID_ClusResource, CClusResource)
|
|
OBJECT_ENTRY(CLSID_ClusResources, CClusResources)
|
|
OBJECT_ENTRY(CLSID_ClusResPossibleOwnerNodes, CClusResPossibleOwnerNodes)
|
|
OBJECT_ENTRY(CLSID_ClusResType, CClusResType)
|
|
OBJECT_ENTRY(CLSID_ClusResTypes, CClusResTypes)
|
|
OBJECT_ENTRY(CLSID_ClusResTypeResources, CClusResTypeResources)
|
|
OBJECT_ENTRY(CLSID_ClusResGroupResources, CClusResGroupResources)
|
|
#if CLUSAPI_VERSION >= 0x0500
|
|
OBJECT_ENTRY(CLSID_ClusResTypePossibleOwnerNodes, CClusResTypePossibleOwnerNodes)
|
|
#endif // CLUSAPI_VERSION >= 0x0500
|
|
OBJECT_ENTRY(CLSID_ClusPropertyValue, CClusPropertyValue)
|
|
OBJECT_ENTRY(CLSID_ClusPropertyValues, CClusPropertyValues)
|
|
OBJECT_ENTRY(CLSID_ClusPropertyValueData, CClusPropertyValueData)
|
|
OBJECT_ENTRY(CLSID_ClusPartition, CClusPartition)
|
|
OBJECT_ENTRY(CLSID_ClusPartitions, CClusPartitions)
|
|
OBJECT_ENTRY(CLSID_ClusDisk, CClusDisk)
|
|
OBJECT_ENTRY(CLSID_ClusDisks, CClusDisks)
|
|
OBJECT_ENTRY(CLSID_ClusScsiAddress, CClusScsiAddress)
|
|
OBJECT_ENTRY(CLSID_ClusRegistryKeys, CClusResourceRegistryKeys)
|
|
#if CLUSAPI_VERSION >= 0x0500
|
|
OBJECT_ENTRY(CLSID_ClusCryptoKeys, CClusResourceCryptoKeys)
|
|
#endif // CLUSAPI_VERSION >= 0x0500
|
|
END_OBJECT_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Forward function declarations
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
static void RegisterRegistryCleanUp( void );
|
|
static void UnregisterRegistryCleanUp( void );
|
|
|
|
static const LPWSTR g_ptszRegisterRegistryNodesToDelete[] =
|
|
{
|
|
_T( "software\\classes\\MSCluster.Application" ),
|
|
_T( "software\\classes\\MSCluster.Application.2" ),
|
|
_T( "software\\classes\\MSCluster.Cluster.2" ),
|
|
_T( "software\\classes\\MSCluster.ClusGroupResources" ),
|
|
_T( "software\\classes\\MSCluster.ClusGroupResources.1" ),
|
|
_T( "software\\classes\\MSCluster.ClusGroupOwners" ),
|
|
_T( "software\\classes\\MSCluster.ClusGroupOwners.1" ),
|
|
_T( "software\\classes\\MSCluster.ClusResOwners" ),
|
|
_T( "software\\classes\\MSCluster.ClusResOwners.1" ),
|
|
_T( "software\\classes\\CLSID\\{f2e60717-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60718-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\CLSID\\{f2e60719-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e6071a-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e0-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e1-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e3-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e5-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e7-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e9-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606eb-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606ed-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606ef-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f3-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f5-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f7-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f9-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606fb-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606fd-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606fe-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606ff-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60700-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60702-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60704-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\CLSID\\{f2e606f2-2631-11d1-89f1-00a0c90d061e}" ),
|
|
NULL
|
|
};
|
|
/*
|
|
static const LPWSTR g_ptszUnregisterRegistryNodesToDelete[] =
|
|
{
|
|
// _T( "software\\classes\\typelib\\{f2e606e0-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e2-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e4-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e6-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606e8-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606ea-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606ec-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606ee-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f0-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f2-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f4-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f6-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606f8-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606fa-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606fc-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e606fe-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60700-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60702-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60704-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60706-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60708-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e6070a-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e6070c-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e6070e-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60710-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60712-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60714-2631-11d1-89f1-00a0c90d061e}" ),
|
|
_T( "software\\classes\\interface\\{f2e60716-2631-11d1-89f1-00a0c90d061e}" ),
|
|
NULL
|
|
};
|
|
*/
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// DllMain
|
|
//
|
|
// Description:
|
|
// DLL Entry Point.
|
|
//
|
|
// Arguments:
|
|
// hInstance [IN] - Out instance handle.
|
|
// dwReason [IN] - The reason we are being called.
|
|
// lpReserved [IN] - Don't rightly know what this is...
|
|
//
|
|
// Return Value:
|
|
// TRUE if successful, FALSE if not.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
extern "C" BOOL WINAPI DllMain(
|
|
IN HINSTANCE hInstance,
|
|
IN DWORD dwReason,
|
|
IN LPVOID //lpReserved
|
|
)
|
|
{
|
|
if ( dwReason == DLL_PROCESS_ATTACH )
|
|
{
|
|
#ifdef _DEBUG
|
|
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
|
|
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );
|
|
#endif
|
|
_Module.Init( ObjectMap, hInstance );
|
|
DisableThreadLibraryCalls( hInstance );
|
|
}
|
|
else if ( dwReason == DLL_PROCESS_DETACH )
|
|
{
|
|
_Module.Term();
|
|
}
|
|
|
|
return TRUE; // ok
|
|
|
|
} //*** DllMain()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// DllCanUnloadNow
|
|
//
|
|
// Description:
|
|
// Used to determine whether the DLL can be unloaded by OLE.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// S_OK if we can unload, S_FALSE if we cannot.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDAPI DllCanUnloadNow( void )
|
|
{
|
|
return ( _Module.GetLockCount() == 0 ) ? S_OK : S_FALSE;
|
|
|
|
} //*** DllCanUnloadNow()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// DllGetClassObject
|
|
//
|
|
// Description:
|
|
// Retrieves the class object from a DLL object handler or object
|
|
// application. DllGetClassObject is called from within the
|
|
// CoGetClassObject function when the class context is a DLL.
|
|
//
|
|
// Arguments:
|
|
// rclsid [IN] - CLSID that will associate the correct data and code.
|
|
// riid [IN] - Reference to the identifier of the interface that the
|
|
// caller is to use to communicate with the class object.
|
|
// Usually, this is IID_IClassFactory (defined in the OLE
|
|
// headers as the interface identifier for IClassFactory).
|
|
// ppv [OUT] - Address of pointer variable that receives the interface
|
|
// pointer requested in riid. Upon successful return, *ppv
|
|
// contains the requested interface pointer. If an error
|
|
// occurs, the interface pointer is NULL.
|
|
//
|
|
// Return Value:
|
|
// S_OK if successful, or CLASS_E_CLASSNOTAVAILABLE if not.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDAPI DllGetClassObject(
|
|
IN REFCLSID rclsid,
|
|
IN REFIID riid,
|
|
OUT LPVOID * ppv
|
|
)
|
|
{
|
|
return _Module.GetClassObject( rclsid, riid, ppv );
|
|
|
|
} //*** DllGetClassObject()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// DllRegisterServer
|
|
//
|
|
// Description:
|
|
// Add entries to the system registry.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// S_OK if successful, or other HRESULT error if not.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDAPI DllRegisterServer( void )
|
|
{
|
|
RegisterRegistryCleanUp();
|
|
|
|
//
|
|
// Registers object, typelib and all interfaces in typelib
|
|
//
|
|
return _Module.RegisterServer( TRUE );
|
|
|
|
} //*** DllRegisterServer()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// DllUnregisterServer
|
|
//
|
|
// Description:
|
|
// Removes entries from the system registry.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// S_OK if successful, or other HRESULT error if not.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDAPI DllUnregisterServer( void )
|
|
{
|
|
HRESULT _hr = S_FALSE;
|
|
|
|
UnregisterRegistryCleanUp();
|
|
|
|
//
|
|
// Unregisters object, typelib and all interfaces in typelib
|
|
//
|
|
_hr = _Module.UnregisterServer();
|
|
if ( SUCCEEDED( _hr ) )
|
|
{
|
|
|
|
#if _WIN32_WINNT >= 0x0400
|
|
_hr = UnRegisterTypeLib( LIBID_MSClusterLib, 1, 0, LOCALE_NEUTRAL, SYS_WIN32 );
|
|
#endif
|
|
|
|
} //if: server was unregistered
|
|
|
|
return _hr;
|
|
|
|
} //*** DllUnregisterServer()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// HrGetCluster
|
|
//
|
|
// Description:
|
|
// Common implementation of creating a new cluster object.
|
|
//
|
|
// Arguments:
|
|
// ppCluster [OUT] - Catches the newly created object.
|
|
// pClusRefObject [IN] - Object that wraps the cluster handle.
|
|
//
|
|
// Return Value:
|
|
// S_OK for success
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
HRESULT HrGetCluster(
|
|
OUT ISCluster ** ppCluster,
|
|
IN ISClusRefObject * pClusRefObject
|
|
)
|
|
{
|
|
//ASSERT( ppCluster != NULL );
|
|
ASSERT( pClusRefObject != NULL );
|
|
|
|
HRESULT _hr = E_POINTER;
|
|
|
|
if ( ( ppCluster != NULL ) && ( pClusRefObject != NULL ) )
|
|
{
|
|
HCLUSTER hCluster = NULL;
|
|
|
|
_hr = pClusRefObject->get_Handle( (ULONG_PTR *) &hCluster );
|
|
if ( SUCCEEDED( _hr ) )
|
|
{
|
|
CComObject< CCluster > * pCluster = NULL;
|
|
|
|
_hr = CComObject< CCluster >::CreateInstance( &pCluster );
|
|
if ( SUCCEEDED( _hr ) )
|
|
{
|
|
pCluster->ClusRefObject( pClusRefObject );
|
|
pCluster->Hcluster( hCluster );
|
|
|
|
_hr = pCluster->QueryInterface( IID_ISCluster, (void **) ppCluster );
|
|
}
|
|
}
|
|
}
|
|
|
|
return _hr;
|
|
|
|
} //*** HrGetCluster()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// RegisterRegistryCleanUp
|
|
//
|
|
// Description:
|
|
// Clean up the registry during registration
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
static void RegisterRegistryCleanUp( void )
|
|
{
|
|
int nIndex;;
|
|
|
|
for ( nIndex = 0; ; nIndex++ )
|
|
{
|
|
if ( g_ptszRegisterRegistryNodesToDelete[ nIndex ] == NULL )
|
|
{
|
|
break;
|
|
} // if:
|
|
|
|
RegDelnode( HKEY_LOCAL_MACHINE, g_ptszRegisterRegistryNodesToDelete[ nIndex ] );
|
|
} // for:
|
|
|
|
} //*** RegisterRegistryCleanUp()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// UnregisterRegistryCleanUp
|
|
//
|
|
// Description:
|
|
// Clean up the registry during unregistration
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
static void UnregisterRegistryCleanUp( void )
|
|
{
|
|
return;
|
|
|
|
} //*** UnregisterRegistryCleanUp()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// ClearIDispatchEnum
|
|
//
|
|
// Description:
|
|
// Cleans up an Enum of IDispatch pointers.
|
|
//
|
|
// Arguments:
|
|
// ppVarVect [IN OUT] - The enum to clean up.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void ClearIDispatchEnum(
|
|
IN OUT CComVariant ** ppvarVect
|
|
)
|
|
{
|
|
if ( ppvarVect != NULL )
|
|
{
|
|
size_t cCount = ARRAYSIZE( *ppvarVect );
|
|
size_t iIndex;
|
|
|
|
for ( iIndex = 0; iIndex < cCount; iIndex++ )
|
|
{
|
|
(*ppvarVect[iIndex]).pdispVal->Release();
|
|
} // for:
|
|
|
|
delete [] *ppvarVect;
|
|
*ppvarVect = NULL;
|
|
}
|
|
|
|
} //*** ClearIDispatchEnum()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// ClearVariantEnum
|
|
//
|
|
// Description:
|
|
// Cleans up an Enum of variant values.
|
|
//
|
|
// Arguments:
|
|
// ppVarVect [IN OUT] - The enum to clean up.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void ClearVariantEnum(
|
|
IN OUT CComVariant ** ppvarVect
|
|
)
|
|
{
|
|
if ( ppvarVect != NULL )
|
|
{
|
|
delete [] *ppvarVect;
|
|
*ppvarVect = NULL;
|
|
}
|
|
|
|
} //*** ClearVariantEnum()
|