/////////////////////////////////////////////////////////////////////////////
//
//	Copyright (c) 1999 Microsoft Corporation
//
//	Module Name:
//		RegExt.cpp
//
//	Description:
//		Implementation of routines for extension registration.
//
//	Author:
//		David Potter (DavidP)	March 24, 1999
//
//	Revision History:
//
//	Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <ole2.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define REG_VALUE_ADMIN_EXTENSIONS L"AdminExtensions"

/////////////////////////////////////////////////////////////////////////////
// Static Function Prototypes
/////////////////////////////////////////////////////////////////////////////

static HRESULT RegisterAnyCluAdminExtension(
	IN HCLUSTER			hCluster,
	IN LPCWSTR			pwszKeyName,
	IN const CLSID *	pClsid
	);
static HRESULT RegisterAnyCluAdminExtension(
	IN HKEY				hkey,
	IN const CLSID *	pClsid
	);
static HRESULT UnregisterAnyCluAdminExtension(
	IN HCLUSTER			hCluster,
	IN LPCWSTR			pwszKeyName,
	IN const CLSID *	pClsid
	);
static HRESULT UnregisterAnyCluAdminExtension(
	IN HKEY				hkey,
	IN const CLSID *	pClsid
	);
static DWORD ReadValue(
	IN HKEY			hkey,
	IN LPCWSTR		pwszValueName,
	OUT LPWSTR *	ppwszValue,
	OUT DWORD *		pcbSize
	);

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminClusterExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends the cluster object.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminClusterExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	HKEY		hkey;

	// Get the cluster registry key.
	hkey = GetClusterKey(hCluster, KEY_ALL_ACCESS);
	if ( hkey == NULL )
	{
		hr = GetLastError();
	} // if:  error getting cluster key
	else
	{
		// Register the extension.
		hr = RegisterAnyCluAdminExtension( hkey, pClsid );

		ClusterRegCloseKey( hkey );
	} // else:  GetClusterKey succeeded

	return hr;

} //*** RegisterCluAdminClusterExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminAllNodesExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends all nodes.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllNodesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = RegisterAnyCluAdminExtension( hCluster, L"Nodes", pClsid );

	return hr;

} //*** RegisterCluAdminAllNodesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminAllGroupsExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends all groups.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllGroupsExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = RegisterAnyCluAdminExtension( hCluster, L"Groups", pClsid );

	return hr;

} //*** RegisterCluAdminAllGroupsExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminAllResourcesExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends all resources.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllResourcesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = RegisterAnyCluAdminExtension( hCluster, L"Resources", pClsid );

	return hr;

} //*** RegisterCluAdminAllResourcesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminAllResourceTypesExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends all resource types.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllResourceTypesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = RegisterAnyCluAdminExtension( hCluster, L"ResourceTypes", pClsid );

	return hr;

} //*** RegisterCluAdminAllResourceTypesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminAllNetworksExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends all networks.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllNetworksExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = RegisterAnyCluAdminExtension( hCluster, L"Networks", pClsid );

	return hr;

} //*** RegisterCluAdminAllNetworksExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminAllNetInterfacesExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends all network interfaces.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminAllNetInterfacesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = RegisterAnyCluAdminExtension( hCluster, L"NetInterfaces", pClsid );

	return hr;

} //*** RegisterCluAdminAllNetInterfacesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterCluAdminResourceTypeExtension
//
//	Description:
//		Register with the cluster database a Cluster Administrator Extension
//		DLL that extends resources of a specific type, or the resource type
//		itself.
//
//	Arguments:
//		hCluster			[IN] Handle to the cluster to modify.
//		pwszResourceType	[IN] Resource type name.
//		pClsid				[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI RegisterCluAdminResourceTypeExtension(
	IN HCLUSTER			hCluster,
	IN LPCWSTR			pwszResourceType,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	HKEY		hkey;

	// Get the resource type registry key.
	hkey = GetClusterResourceTypeKey( hCluster, pwszResourceType, KEY_ALL_ACCESS );
	if ( hkey == NULL )
	{
		hr = GetLastError();
	} // if:  error getting resource type key
	else
	{
		// Register the extension.
		hr = RegisterAnyCluAdminExtension( hkey, pClsid );

		ClusterRegCloseKey( hkey );
	} // else:  GetClusterResourceTypeKey succeeded

	return hr;

} //*** RegisterCluAdminResourceTypeExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminClusterExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends the cluster object.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminClusterExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	HKEY		hkey;

	// Get the cluster registry key.
	hkey = GetClusterKey( hCluster, KEY_ALL_ACCESS );
	if ( hkey == NULL )
	{
		hr = GetLastError();
	} // if:  error getting cluster key
	else
	{
		// Unregister the extension.
		hr = UnregisterAnyCluAdminExtension( hkey, pClsid );

		ClusterRegCloseKey( hkey );
	} // else:  GetClusterKey succeeded

	return hr;

} //*** UnregisterCluAdminClusterExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminAllNodesExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends all nodes.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllNodesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = UnregisterAnyCluAdminExtension( hCluster, L"Nodes", pClsid );

	return hr;

} //*** UnregisterCluAdminAllNodesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminAllGroupsExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends all groups.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllGroupsExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = UnregisterAnyCluAdminExtension( hCluster, L"Groups", pClsid );

	return hr;

} //*** UnregisterCluAdminAllGroupsExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminAllResourcesExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends all resources.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllResourcesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = UnregisterAnyCluAdminExtension( hCluster, L"Resources", pClsid );

	return hr;

} //*** UnregisterCluAdminAllResourcesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminAllResourceTypesExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends all resource types.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllResourceTypesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = UnregisterAnyCluAdminExtension( hCluster, L"ResourceTypes", pClsid );

	return hr;

} //*** UnregisterCluAdminAllResourceTypesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminAllNetworksExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends all networks.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllNetworksExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = UnregisterAnyCluAdminExtension( hCluster, L"Networks", pClsid );

	return hr;

} //*** UnregisterCluAdminAllNetworksExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminAllNetInterfacesExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends all network interfaces.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminAllNetInterfacesExtension(
	IN HCLUSTER			hCluster,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;

	hr = UnregisterAnyCluAdminExtension( hCluster, L"NetInterfaces", pClsid );

	return hr;

} //*** UnregisterCluAdminAllNetInterfacesExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterCluAdminResourceTypeExtension
//
//	Description:
//		Unregister with the cluster database a Cluster Administrator Extension
//		DLL that extends resources of a specific type, or the resource type
//		itself.
//
//	Arguments:
//		hCluster			[IN] Handle to the cluster to modify.
//		pwszResourceType	[IN] Resource type name.
//		pClsid				[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
STDAPI UnregisterCluAdminResourceTypeExtension(
	IN HCLUSTER			hCluster,
	IN LPCWSTR			pwszResourceType,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	HKEY		hkey;

	// Get the resource type registry key.
	hkey = GetClusterResourceTypeKey( hCluster, pwszResourceType, KEY_ALL_ACCESS );
	if ( hkey == NULL )
	{
		hr = GetLastError();
	} // if:  error getting resource type key
	else
	{
		// Unregister the extension.
		hr = UnregisterAnyCluAdminExtension( hkey, pClsid );

		ClusterRegCloseKey( hkey );
	} // else:  GetClusterResourceTypeKey succeeded

	return hr;

} //*** UnregisterCluAdminResourceTypeExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterAnyCluAdminExtension
//
//	Description:
//		Register any Cluster Administrator Extension DLL with the cluster
//		database.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pwszKeyName		[IN] Key name.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT RegisterAnyCluAdminExtension(
	IN HCLUSTER			hCluster,
	IN LPCWSTR			pwszKeyName,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	HKEY		hkeyCluster;
	HKEY		hkey;

	// Get the cluster key.
	hkeyCluster = GetClusterKey( hCluster, KEY_ALL_ACCESS );
	if ( hkeyCluster == NULL )
	{
		hr = GetLastError();
	} // if:  error getting cluster key
	else
	{
		// Get the specified key.
		hr = ClusterRegOpenKey( hkeyCluster, pwszKeyName, KEY_ALL_ACCESS, &hkey );
		if ( hr == ERROR_SUCCESS )
		{
			// Register the extension.
			hr = RegisterAnyCluAdminExtension( hkey, pClsid );

			ClusterRegCloseKey( hkey );
		} // else:  GetClusterResourceTypeKey succeeded

		ClusterRegCloseKey( hkeyCluster );
	} // if:  cluster key retrieved successfully

	return hr;

} //*** RegisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	RegisterAnyCluAdminExtension
//
//	Description:
//		Register any Cluster Administrator Extension DLL with the cluster
//		database.
//
//	Arguments:
//		hkey			[IN] Cluster database key.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension registered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT RegisterAnyCluAdminExtension(
	IN HKEY				hkey,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	LPOLESTR	pwszClsid;
	DWORD		cbSize;
	DWORD		cbNewSize;
	LPWSTR		pwszValue;
	LPWSTR		pwszNewValue;
	BOOL		bAlreadyRegistered;

	// Convert the CLSID to a string.
	hr = StringFromCLSID( *pClsid, &pwszClsid );
	if ( hr == S_OK )
	{
		// Read the current value.
		hr = ReadValue( hkey, REG_VALUE_ADMIN_EXTENSIONS, &pwszValue, &cbSize );
		if (hr == S_OK)
		{
			// Check to see if the extension has been registered yet.
			if ( pwszValue == NULL )
			{
				bAlreadyRegistered = FALSE;
			} // if:  empty value found
			else
			{
				LPCWSTR	pwszValueBuf = pwszValue;

				while ( *pwszValueBuf != L'\0' )
				{
					if ( lstrcmpiW( pwszClsid, pwszValueBuf ) == 0 )
					{
						break;
					} // if:  CLSID for this extension already in list
					pwszValueBuf += lstrlenW(pwszValueBuf) + 1;
				} // while:  more strings in the extension list
				bAlreadyRegistered = (*pwszValueBuf != L'\0');
			} // else:  extension value exists

			// Register the extension.
			if ( ! bAlreadyRegistered )
			{
				// Allocate a new buffer.
				cbNewSize = cbSize + (lstrlenW( pwszClsid ) + 1) * sizeof( WCHAR );
				if ( cbSize == 0 ) // Add size of final NULL if first entry.
				{
					cbNewSize += sizeof( WCHAR );
				} // if:  no previous value
				pwszNewValue = reinterpret_cast< LPWSTR >( LocalAlloc( LMEM_FIXED, cbNewSize ) );
				if ( pwszNewValue == NULL )
				{
					hr = GetLastError();
				} // if:  error allocating memory
				else
				{
					LPCWSTR	pwszValueBuf	= pwszValue;
					LPWSTR	pwszNewValueBuf	= pwszNewValue;
					DWORD	cch;
					DWORD	dwType;

					// Copy the existing extensions to the new buffer.
					if ( pwszValue != NULL)
					{
						while ( *pwszValueBuf != L'\0' )
						{
							lstrcpyW( pwszNewValueBuf, pwszValueBuf );
							cch = lstrlenW( pwszValueBuf );
							pwszValueBuf += cch + 1;
							pwszNewValueBuf += cch + 1;
						} // while:  more strings in the extension list
					} // if:  previous value buffer existed

					// Add the new CLSID to the list.
					lstrcpyW( pwszNewValueBuf, pwszClsid );
					pwszNewValueBuf += lstrlenW( pwszClsid ) + 1;
					*pwszNewValueBuf = L'\0';

					// Write the value to the cluster database.
					dwType = REG_MULTI_SZ;
					hr = ClusterRegSetValue(
									hkey,
									REG_VALUE_ADMIN_EXTENSIONS,
									dwType,
									reinterpret_cast< LPBYTE >( pwszNewValue ),
									cbNewSize
									);

					LocalFree( pwszNewValue );
				} // else:  new buffer allocated successfully

			} // if:  extension not registered yet

			LocalFree( pwszValue );
		} // if:  value read successfully

		CoTaskMemFree( pwszClsid );
	} // if:  CLSID converted to a string successfully

	return hr;

} //*** RegisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterAnyCluAdminExtension
//
//	Description:
//		Unregister any Cluster Administrator Extension DLL with the cluster
//		database.
//
//	Arguments:
//		hCluster		[IN] Handle to the cluster to modify.
//		pwszKeyName		[IN] Key name.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT UnregisterAnyCluAdminExtension(
	IN HCLUSTER			hCluster,
	IN LPCWSTR			pwszKeyName,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	HKEY		hkeyCluster;
	HKEY		hkey;

	// Get the cluster key.
	hkeyCluster = GetClusterKey( hCluster, KEY_ALL_ACCESS );
	if ( hkeyCluster == NULL )
	{
		hr = GetLastError();
	} // if:  error getting cluster key
	else
	{
		// Get the specified key.
		hr = ClusterRegOpenKey( hkeyCluster, pwszKeyName, KEY_ALL_ACCESS, &hkey );
		if ( hr == ERROR_SUCCESS )
		{
			// Unregister the extension.
			hr = UnregisterAnyCluAdminExtension( hkey, pClsid );

			ClusterRegCloseKey( hkey );
		} // else:  GetClusterResourceTypeKey succeeded

		ClusterRegCloseKey( hkeyCluster );
	} // if:  cluster key retrieved successfully

	return hr;

} //*** UnregisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	UnregisterAnyCluAdminExtension
//
//	Description:
//		Unregister any Cluster Administrator Extension DLL with the cluster
//		database.
//
//	Arguments:
//		hkey			[IN] Cluster database key.
//		pClsid			[IN] Extension's CLSID.
//
//	Return Value:
//		S_OK			Extension unregistered successfully.
//		Win32 error code if another failure occurred.
//
//--
/////////////////////////////////////////////////////////////////////////////
static HRESULT UnregisterAnyCluAdminExtension(
	IN HKEY				hkey,
	IN const CLSID *	pClsid
	)
{
	HRESULT		hr;
	LPOLESTR	pwszClsid;
	DWORD		cbSize;
	DWORD		cbNewSize;
	LPWSTR		pwszValue;
	LPWSTR		pwszNewValue;
	BOOL		bAlreadyUnregistered;

	// Convert the CLSID to a string.
	hr = StringFromCLSID( *pClsid, &pwszClsid );
	if ( hr == S_OK )
	{
		// Read the current value.
		hr = ReadValue( hkey, REG_VALUE_ADMIN_EXTENSIONS, &pwszValue, &cbSize );
		if ( hr == S_OK )
		{
			// Check to see if the extension has been unregistered yet.
			if ( pwszValue == NULL )
			{
				bAlreadyUnregistered = TRUE;
			} // if:  empty value found
			else
			{
				LPCWSTR pwszValueBuf = pwszValue;

				while ( *pwszValueBuf != L'\0' )
				{
					if ( lstrcmpiW( pwszClsid, pwszValueBuf ) == 0 )
					{
						break;
					} // if:  CLSID for this extension found in list
					pwszValueBuf += lstrlenW( pwszValueBuf ) + 1;
				} // while:  more strings in the extension list
				bAlreadyUnregistered = (*pwszValueBuf == L'\0');
			} // else:  extension value exists

			// Unregister the extension.
			if ( ! bAlreadyUnregistered )
			{
				// Allocate a new buffer.
				cbNewSize = cbSize - (lstrlenW( pwszClsid ) + 1) * sizeof( WCHAR );
				if ( cbNewSize == sizeof( WCHAR ) )
				{
					cbNewSize = 0;
				} // if:  no previous value
				pwszNewValue = reinterpret_cast< LPWSTR >( LocalAlloc( LMEM_FIXED, cbNewSize ) );
				if ( pwszNewValue == NULL )
				{
					hr = GetLastError();
				} // if:  error allocating memory
				else
				{
					LPCWSTR	pwszValueBuf	= pwszValue;
					LPWSTR	pwszNewValueBuf	= pwszNewValue;
					DWORD	dwType;

					// Copy the existing extensions to the new buffer.
					if ( (cbNewSize > 0) && (pwszValue != NULL) )
					{
						while ( *pwszValueBuf != L'\0' )
						{
							if ( lstrcmpiW( pwszClsid, pwszValueBuf ) != 0 )
							{
								lstrcpyW( pwszNewValueBuf, pwszValueBuf );
								pwszNewValueBuf += lstrlenW( pwszNewValueBuf ) + 1;
							} // if:  not CLSID being removed
							pwszValueBuf += lstrlenW( pwszValueBuf ) + 1;
						} // while:  more strings in the extension list
						*pwszNewValueBuf = L'\0';
					} // if:  previous value buffer existed

					// Write the value to the cluster database.
					dwType = REG_MULTI_SZ;
					hr = ClusterRegSetValue(
									hkey,
									REG_VALUE_ADMIN_EXTENSIONS,
									dwType,
									reinterpret_cast< LPBYTE >( pwszNewValue ),
									cbNewSize
									);

					LocalFree( pwszNewValue );
				} // else:  new buffer allocated successfully

			} // if:  extension not unregistered yet

			LocalFree( pwszValue );
		} // if:  value read successfully

		CoTaskMemFree( pwszClsid );
	} // if:  CLSID converted to a string successfully

	return hr;

} //*** UnregisterAnyCluAdminExtension()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	ReadValue
//
//	Description:
//		Reads a value from the cluster database.
//
//	Arguments:
//		hkey [IN]
//			Handle for the key to read from.
//
//		pwszValueName [IN]
//			Name of value to read.
//
//		ppwszValue [OUT]
//			Address of pointer in which to return data.  The string is
//			allocated using LocalAlloc and must be deallocated by the calling
//			LocalFree.
//
//		pcbSize [OUT]
//			Size in bytes of the allocated value buffer.
//
//	Return Value:
//		Any return values from ClusterRegQueryValue or errors from new.
//
//--
/////////////////////////////////////////////////////////////////////////////

static DWORD ReadValue(
	IN HKEY			hkey,
	IN LPCWSTR		pwszValueName,
	OUT LPWSTR *	ppwszValue,
	OUT DWORD *		pcbSize
	)
{
	DWORD		dwStatus;
	DWORD		cbSize;
	DWORD		dwType;
	LPWSTR		pwszValue;

	*ppwszValue = NULL;
	*pcbSize = 0;

	// Get the length of the value.
	dwStatus = ClusterRegQueryValue(
					hkey,
					pwszValueName,
					&dwType,
					NULL,
					&cbSize
					);
	if (	(dwStatus != ERROR_SUCCESS)
		&&	(dwStatus != ERROR_MORE_DATA) )
	{
		if ( dwStatus  == ERROR_FILE_NOT_FOUND )
		{
			dwStatus = ERROR_SUCCESS;
		} // if:  value not found
		return dwStatus;
	} // if:  error occurred

	if ( cbSize > 0 )
	{
		// Allocate a value string.
		pwszValue = reinterpret_cast< LPWSTR >( LocalAlloc( LMEM_FIXED, cbSize ) );
		if ( pwszValue == NULL )
		{
			dwStatus = GetLastError();
			return dwStatus;
		} // if:  error allocating memory

		// Read the the value.
		dwStatus = ClusterRegQueryValue(
						hkey,
						pwszValueName,
						&dwType,
						reinterpret_cast< LPBYTE >( pwszValue ),
						&cbSize
						);
		if ( dwStatus != ERROR_SUCCESS )
		{
			LocalFree( pwszValue );
			pwszValue = NULL;
			cbSize = 0;
		} // if:  error occurred

		*ppwszValue = pwszValue;
		*pcbSize = cbSize;
	} // if:  value is not empty

	return dwStatus;

} //*** ReadValue()
