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.
1335 lines
33 KiB
1335 lines
33 KiB
//
|
|
// cmponent.cpp : Declaration of Component.
|
|
//
|
|
// This COM object is primarily concerned with
|
|
// the result pane items.
|
|
//
|
|
// Cory West <[email protected]>
|
|
// Copyright (c) Microsoft Corporation 1997
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "macros.h"
|
|
USE_HANDLE_MACROS("SCHMMGMT(cmponent.cpp)")
|
|
|
|
#include "dataobj.h"
|
|
#include "cmponent.h" // Component
|
|
#include "compdata.h" // ComponentData
|
|
#include "schmutil.h"
|
|
#include "attrgen.hpp"
|
|
|
|
|
|
#include "stdcmpnt.cpp" // CComponent
|
|
|
|
|
|
|
|
//
|
|
// These arrays describe the result pane layout for when
|
|
// any particular object is selected.
|
|
//
|
|
|
|
UINT
|
|
g_aColumns0[5] = {
|
|
|
|
IDS_COLUMN_NAME,
|
|
IDS_COLUMN_TYPE,
|
|
IDS_COLUMN_STATUS,
|
|
IDS_COLUMN_DESCRIPTION,
|
|
0
|
|
};
|
|
|
|
UINT
|
|
g_aColumns1[5] = {
|
|
|
|
IDS_COLUMN_NAME,
|
|
IDS_COLUMN_SYNTAX,
|
|
IDS_COLUMN_STATUS,
|
|
IDS_COLUMN_DESCRIPTION,
|
|
0
|
|
};
|
|
|
|
UINT
|
|
g_aColumns2[6] = {
|
|
|
|
IDS_COLUMN_NAME,
|
|
IDS_COLUMN_TYPE,
|
|
IDS_COLUMN_SYSTEM,
|
|
IDS_COLUMN_DESCRIPTION,
|
|
IDS_COLUMN_PARENT,
|
|
0
|
|
};
|
|
|
|
UINT
|
|
g_aColumns3[2] =
|
|
{
|
|
IDS_COLUMN_NAME,
|
|
0
|
|
};
|
|
|
|
UINT*
|
|
g_Columns[SCHMMGMT_NUMTYPES] = {
|
|
|
|
g_aColumns3, // SCHMMGMT_SCHMMGMT
|
|
g_aColumns0, // SCHMMGMT_CLASSES
|
|
g_aColumns1, // SCHMMGMT_ATTRIBUTES
|
|
g_aColumns2, // SCHMMGMT_CLASS
|
|
g_aColumns0, // SCHMMGMT_ATTRIBUTE // @@ Is this used?
|
|
};
|
|
|
|
UINT** g_aColumns = g_Columns;
|
|
|
|
//
|
|
// These control the column widths, which I will not change.
|
|
//
|
|
|
|
int g_aColumnWidths0[4] = {150,150,75,150};
|
|
int g_aColumnWidths1[5] = {150,75,75,150,150};
|
|
int g_aColumnWidths2[1] = {150};
|
|
|
|
int* g_ColumnWidths[SCHMMGMT_NUMTYPES] = {
|
|
|
|
g_aColumnWidths2, // SCHMMGMT_SCHMMGMT
|
|
g_aColumnWidths0, // SCHMMGMT_CLASSES
|
|
g_aColumnWidths0, // SCHMMGMT_ATTRIBUTES
|
|
g_aColumnWidths1, // SCHMMGMT_CLASS
|
|
g_aColumnWidths0, // SCHMMGMT_ATTRIBUTE
|
|
};
|
|
|
|
int** g_aColumnWidths = g_ColumnWidths;
|
|
|
|
//
|
|
// Constructors and destructors.
|
|
//
|
|
|
|
Component::Component()
|
|
: m_pSvcMgmtToolbar( NULL ),
|
|
m_pSchmMgmtToolbar( NULL ),
|
|
m_pControlbar( NULL ),
|
|
m_bDirty(false)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
|
|
m_pViewedCookie = NULL;
|
|
}
|
|
|
|
Component::~Component()
|
|
{
|
|
TRACE_METHOD(Component,Destructor);
|
|
VERIFY( SUCCEEDED(ReleaseAll()) );
|
|
}
|
|
|
|
HRESULT Component::ReleaseAll()
|
|
{
|
|
MFC_TRY;
|
|
|
|
TRACE_METHOD(Component,ReleaseAll);
|
|
|
|
SAFE_RELEASE(m_pSvcMgmtToolbar);
|
|
SAFE_RELEASE(m_pSchmMgmtToolbar);
|
|
SAFE_RELEASE(m_pControlbar);
|
|
|
|
return CComponent::ReleaseAll();
|
|
|
|
MFC_CATCH;
|
|
}
|
|
|
|
//
|
|
// Support routines in ISchmMgmtComponent.
|
|
//
|
|
|
|
|
|
HRESULT
|
|
Component::LoadColumns(
|
|
Cookie* pcookie
|
|
) {
|
|
|
|
TEST_NONNULL_PTR_PARAM(pcookie);
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
return LoadColumnsFromArrays( (INT)(pcookie->m_objecttype) );
|
|
}
|
|
|
|
HRESULT
|
|
Component::OnViewChange(
|
|
LPDATAOBJECT,
|
|
LPARAM data,
|
|
LPARAM function
|
|
)
|
|
/***
|
|
|
|
This is called when IConsole->UpdateAllViews() is called.
|
|
The data is a schema object type as follows:
|
|
|
|
|
|
if function == 0 (SCHMMGMT_UPDATEVIEW_REFRESH)
|
|
|
|
SCHMMGMT_ATTIBUTES - We need to refresh the attributes
|
|
folder displays.
|
|
SCHMMGMT_CLASS - We need to refresh _ALL_ class attribute
|
|
displays. We don't try and trace the inheritance
|
|
graphs and do a selective refresh, that's too complicated.
|
|
SCHMMGMT_SCHMMGMT - Refresh EVERYTHING because we reloaded
|
|
the schema cache.
|
|
|
|
else if function == 1 (SCHMMGMT_UPDATEVIEW_DELETE_RESULT_ITEM)
|
|
|
|
data is the Cookie pointer
|
|
***/
|
|
{
|
|
//
|
|
// Refresh this result view.
|
|
//
|
|
if ( function == SCHMMGMT_UPDATEVIEW_REFRESH )
|
|
{
|
|
if ( m_pViewedCookie ) {
|
|
|
|
if ( ( data == m_pViewedCookie->m_objecttype ) ||
|
|
( data == SCHMMGMT_SCHMMGMT ) ) {
|
|
|
|
m_pResultData->DeleteAllRsltItems();
|
|
PopulateListbox( m_pViewedCookie );
|
|
|
|
}
|
|
}
|
|
}
|
|
else if ( function == SCHMMGMT_UPDATEVIEW_DELETE_RESULT_ITEM )
|
|
{
|
|
HRESULTITEM item;
|
|
// FUTURE-2002-03/94/2002-dantra-Although this is a safe usage of ZeroMemory, suggest changing
|
|
// the definition of item to HRESULTITEM item = {0}; and removing the ZeroMemory call.
|
|
ZeroMemory( &item, sizeof(HRESULTITEM) );
|
|
|
|
HRESULT hr = m_pResultData->FindItemByLParam( data, &item );
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = m_pResultData->DeleteItem( item, 0 );
|
|
ASSERT( SUCCEEDED(hr) );
|
|
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
Component::OnNotifySelect( LPDATAOBJECT lpDataObject, BOOL )
|
|
/***
|
|
|
|
This called in response to MMCN_SELECT.
|
|
This routine will set the default verb and enable the toolbar buttons.
|
|
|
|
***/
|
|
{
|
|
CCookie* pBaseParentCookie = NULL;
|
|
HRESULT hr = ExtractData( lpDataObject,
|
|
CSchmMgmtDataObject::m_CFRawCookie,
|
|
OUT reinterpret_cast<PBYTE>(&pBaseParentCookie),
|
|
sizeof(pBaseParentCookie) );
|
|
|
|
ASSERT( SUCCEEDED(hr) );
|
|
Cookie* pParentCookie = ActiveCookie(pBaseParentCookie);
|
|
ASSERT( NULL != pParentCookie );
|
|
|
|
m_pConsoleVerb->SetVerbState(MMC_VERB_REFRESH,ENABLED,TRUE);
|
|
|
|
switch ( pParentCookie->m_objecttype ) {
|
|
|
|
case SCHMMGMT_CLASSES:
|
|
case SCHMMGMT_ATTRIBUTES:
|
|
|
|
break;
|
|
|
|
case SCHMMGMT_CLASS:
|
|
{
|
|
//
|
|
// Set the default verb to display the properties of the selected object.
|
|
//
|
|
|
|
m_pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, TRUE);
|
|
m_pConsoleVerb->SetDefaultVerb(MMC_VERB_PROPERTIES);
|
|
|
|
// if the schema class is defunct and the forest version is Whistler or higher
|
|
// then allow delete
|
|
/* Feature was removed for Whistler
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
if ( Scope.GetBasePathsInfo()->GetForestBehaviorVersion() >= 2)
|
|
{
|
|
SchemaObject *pSchemaObject = Scope.g_SchemaCache.LookupSchemaObjectByCN(
|
|
pParentCookie->strSchemaObject,
|
|
SCHMMGMT_CLASS );
|
|
|
|
if ( pSchemaObject &&
|
|
pSchemaObject->isDefunct )
|
|
{
|
|
m_pConsoleVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, TRUE);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
break;
|
|
|
|
case SCHMMGMT_ATTRIBUTE:
|
|
|
|
if ( ( pParentCookie->m_objecttype == SCHMMGMT_ATTRIBUTE ) &&
|
|
( pParentCookie->pParentCookie ) &&
|
|
( pParentCookie->pParentCookie->m_objecttype == SCHMMGMT_ATTRIBUTES ) ) {
|
|
|
|
//
|
|
// Set the default verb to display the properties of the selected object.
|
|
//
|
|
|
|
m_pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, TRUE);
|
|
m_pConsoleVerb->SetDefaultVerb(MMC_VERB_PROPERTIES);
|
|
|
|
// if the schema class is defunct and the forest version is Whistler or higher
|
|
// then allow delete
|
|
/* Feature was removed for Whistler
|
|
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
if ( Scope.GetBasePathsInfo()->GetForestBehaviorVersion() >= 2)
|
|
{
|
|
SchemaObject *pSchemaObject = Scope.g_SchemaCache.LookupSchemaObjectByCN(
|
|
pParentCookie->strSchemaObject,
|
|
SCHMMGMT_ATTRIBUTE );
|
|
|
|
if ( pSchemaObject &&
|
|
pSchemaObject->isDefunct )
|
|
{
|
|
m_pConsoleVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, TRUE);
|
|
}
|
|
}
|
|
*/
|
|
} else {
|
|
|
|
m_pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, TRUE);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Otherwise set the default verb to open/expand the folder.
|
|
//
|
|
|
|
m_pConsoleVerb->SetDefaultVerb(MMC_VERB_OPEN);
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
Component::Show(
|
|
CCookie* pcookie,
|
|
LPARAM arg,
|
|
HSCOPEITEM hScopeItem
|
|
)
|
|
/***
|
|
|
|
This is called in response to MMCN_SHOW.
|
|
|
|
***/
|
|
{
|
|
TEST_NONNULL_PTR_PARAM(pcookie);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
if ( TRUE == arg ) // showing...
|
|
{
|
|
if( QueryComponentDataRef().IsSetDelayedRefreshOnShow() )
|
|
{
|
|
HSCOPEITEM hItem = QueryComponentDataRef().GetDelayedRefreshOnShowItem();
|
|
ASSERT( hItem == hScopeItem );
|
|
|
|
QueryComponentDataRef().SetDelayedRefreshOnShow( NULL );
|
|
|
|
hr = m_pConsole->SelectScopeItem( hItem ); // will call GetResultViewType & Show
|
|
ASSERT_BREAK_ON_FAILED_HRESULT(hr);
|
|
}
|
|
else if( QueryComponentDataRef().IsErrorSet() )
|
|
{
|
|
CComPtr<IUnknown> pUnknown;
|
|
CComPtr<IMessageView> pMessageView;
|
|
|
|
hr = m_pConsole->QueryResultView(&pUnknown);
|
|
ASSERT_BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
hr = pUnknown->QueryInterface(IID_IMessageView, (PVOID*)&pMessageView);
|
|
ASSERT_BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
pMessageView->SetTitleText( CComBSTR( QueryComponentDataRef().GetErrorTitle() ) );
|
|
pMessageView->SetBodyText( CComBSTR( QueryComponentDataRef().GetErrorText() ) );
|
|
pMessageView->SetIcon(Icon_Error);
|
|
}
|
|
else
|
|
{
|
|
m_pViewedCookie = (Cookie*)pcookie;
|
|
LoadColumns( m_pViewedCookie );
|
|
|
|
hr = PopulateListbox( m_pViewedCookie );
|
|
}
|
|
}
|
|
else // hiding...
|
|
{
|
|
if( !QueryComponentDataRef().IsErrorSet() )
|
|
{
|
|
if ( NULL == m_pResultData )
|
|
{
|
|
ASSERT( FALSE );
|
|
hr = E_UNEXPECTED;
|
|
break;
|
|
}
|
|
|
|
m_pViewedCookie = NULL;
|
|
}
|
|
}
|
|
|
|
} while( FALSE );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
Component::OnNotifyAddImages(
|
|
LPDATAOBJECT,
|
|
LPIMAGELIST lpImageList,
|
|
HSCOPEITEM
|
|
)
|
|
/***
|
|
|
|
This routine is called in response to MMCN_ADD_IMAGES. Here's
|
|
what mmc.idl says about this:
|
|
|
|
Sent to IComponent to add images for the result pane. The
|
|
primary snapin should add images for both folders and leaf items.
|
|
|
|
arg = ptr to result panes IImageList.
|
|
param = HSCOPEITEM of selected/deselected item
|
|
|
|
***/
|
|
{
|
|
return QueryComponentDataRef().LoadIcons(lpImageList,TRUE);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
Component::OnNotifyDelete(
|
|
LPDATAOBJECT lpDataObject)
|
|
{
|
|
CThemeContextActivator activator;
|
|
|
|
CCookie* pBaseParentCookie = NULL;
|
|
|
|
HRESULT hr = ExtractData( lpDataObject,
|
|
CSchmMgmtDataObject::m_CFRawCookie,
|
|
reinterpret_cast<PBYTE>(&pBaseParentCookie),
|
|
sizeof(pBaseParentCookie) );
|
|
ASSERT( SUCCEEDED(hr) );
|
|
|
|
Cookie* pParentCookie = ActiveCookie(pBaseParentCookie);
|
|
ASSERT( NULL != pParentCookie );
|
|
|
|
UINT promptID = 0;
|
|
LPARAM updateType = SCHMMGMT_CLASS;
|
|
|
|
if (pParentCookie->m_objecttype == SCHMMGMT_CLASS)
|
|
{
|
|
promptID = IDS_DELETE_CLASS_PROMPT;
|
|
updateType = SCHMMGMT_CLASS;
|
|
}
|
|
else if (pParentCookie->m_objecttype == SCHMMGMT_ATTRIBUTE)
|
|
{
|
|
promptID = IDS_DELETE_ATTR_PROMPT;
|
|
updateType = SCHMMGMT_ATTRIBUTES;
|
|
}
|
|
else
|
|
{
|
|
// We should never get called to delete anything but
|
|
// class and attribute nodes
|
|
|
|
ASSERT(FALSE);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if( IDYES == AfxMessageBox( promptID, MB_YESNO | MB_ICONWARNING ))
|
|
{
|
|
hr = DeleteAttribute(pParentCookie);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Remove the node from the UI
|
|
|
|
hr = m_pConsole->UpdateAllViews( lpDataObject,
|
|
(LPARAM)pParentCookie,
|
|
SCHMMGMT_UPDATEVIEW_DELETE_RESULT_ITEM );
|
|
ASSERT( SUCCEEDED(hr) );
|
|
}
|
|
else
|
|
{
|
|
CString szDeleteError;
|
|
szDeleteError.Format(IDS_ERRMSG_DELETE_FAILED_ATTRIBUTE, GetErrorMessage(hr, TRUE));
|
|
|
|
DoErrMsgBox( ::GetActiveWindow(), TRUE, szDeleteError );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
Component::DeleteAttribute(
|
|
Cookie* pcookie
|
|
)
|
|
/***
|
|
|
|
This deletes an attribute from the schema
|
|
|
|
***/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
if ( !pcookie )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
|
|
SchemaObject* pObject = Scope.g_SchemaCache.LookupSchemaObjectByCN(
|
|
pcookie->strSchemaObject,
|
|
SCHMMGMT_ATTRIBUTE );
|
|
|
|
if ( !pObject )
|
|
{
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
CString szAdsPath;
|
|
Scope.GetSchemaObjectPath( pObject->commonName, szAdsPath );
|
|
|
|
hr = DeleteObject( szAdsPath, pcookie, g_AttributeFilter );
|
|
} while (false);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
Component::PopulateListbox(
|
|
Cookie* pcookie
|
|
)
|
|
/***
|
|
|
|
This populates the result pane when the result pane
|
|
contains data that is not directly derived from the
|
|
data in the scope pane.
|
|
|
|
***/
|
|
{
|
|
switch ( pcookie->m_objecttype ) {
|
|
|
|
case SCHMMGMT_SCHMMGMT:
|
|
case SCHMMGMT_CLASSES:
|
|
|
|
//
|
|
// We don't care about these - the result
|
|
// pane contains only scope items.
|
|
//
|
|
|
|
break;
|
|
|
|
case SCHMMGMT_ATTRIBUTES:
|
|
|
|
//
|
|
// List the specified items in the result pane
|
|
// with some informational data.
|
|
//
|
|
|
|
return FastInsertAttributeResultCookies(
|
|
pcookie );
|
|
|
|
break;
|
|
|
|
case SCHMMGMT_CLASS:
|
|
|
|
//
|
|
// This results in the attributes used in this
|
|
// class and other class data being displayed.
|
|
//
|
|
|
|
return FastInsertClassAttributesResults( pcookie );
|
|
break;
|
|
|
|
|
|
case SCHMMGMT_ATTRIBUTE:
|
|
|
|
//
|
|
// This is not a scope pane item and can have no
|
|
// corresponding result pane data!!
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Component::FastInsertAttributeResultCookies(
|
|
Cookie* pParentCookie
|
|
)
|
|
/***
|
|
|
|
When the "Attributes" folder is selected, this puts
|
|
the attributes in the result pane.
|
|
|
|
pParentCookie is the cookie for the parent object.
|
|
|
|
This routine is similar to
|
|
ComponentData::FastInsertClassScopeCookies.
|
|
|
|
****/
|
|
{
|
|
|
|
HRESULT hr;
|
|
SchemaObject *pObject, *pHead;
|
|
Cookie *pNewCookie;
|
|
RESULTDATAITEM ResultItem;
|
|
LPCWSTR lpcszMachineName = pParentCookie->QueryNonNULLMachineName();
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
|
|
//
|
|
// Initialize the result item.
|
|
//
|
|
|
|
|
|
// FUTURE-2002-03/94/2002-dantra-Although this is a safe usage of ZeroMemory, suggest changing
|
|
// the definition RESULTDATAITEM ResultItem = {0} and removing the ZeroMemory call.
|
|
::ZeroMemory( &ResultItem, sizeof( ResultItem ) );
|
|
ResultItem.nCol = 0;
|
|
ResultItem.mask = RDI_STR | RDI_IMAGE | RDI_PARAM;
|
|
ResultItem.str = MMC_CALLBACK;
|
|
ResultItem.nImage = iIconAttribute;
|
|
//
|
|
// Rather than having a clean class interface to the cache, we
|
|
// walk the cache data structures ourselves. This isn't super
|
|
// clean, but it's simple.
|
|
//
|
|
// Since we do this, we have to make sure that the cache is loaded.
|
|
//
|
|
|
|
Scope.g_SchemaCache.LoadCache();
|
|
|
|
pObject = Scope.g_SchemaCache.pSortedAttribs;
|
|
|
|
//
|
|
// If there's no sorted list, we can't insert anything!!!!
|
|
//
|
|
|
|
if ( !pObject ) {
|
|
ASSERT( FALSE );
|
|
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_NO_SCHEMA_PATH );
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Delete whatever was in the view before
|
|
// and do the insert.
|
|
//
|
|
|
|
pHead = pObject;
|
|
|
|
do {
|
|
|
|
if ( Scope.m_fViewDefunct || !pObject->isDefunct )
|
|
{
|
|
//
|
|
// Insert this result.
|
|
//
|
|
|
|
pNewCookie= new Cookie( SCHMMGMT_ATTRIBUTE,
|
|
lpcszMachineName );
|
|
|
|
if ( pNewCookie ) {
|
|
|
|
pNewCookie->pParentCookie = pParentCookie;
|
|
pNewCookie->strSchemaObject = pObject->commonName;
|
|
|
|
pParentCookie->m_listScopeCookieBlocks.AddHead(
|
|
(CBaseCookieBlock*)pNewCookie
|
|
);
|
|
|
|
ResultItem.lParam = reinterpret_cast<LPARAM>((CCookie*)pNewCookie);
|
|
hr = m_pResultData->InsertItem( &ResultItem );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
|
|
pNewCookie->SetResultHandle( ResultItem.itemID );
|
|
|
|
} else {
|
|
|
|
delete pNewCookie;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
pObject = pObject->pSortedListFlink;
|
|
|
|
} while ( pObject != pHead );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Component::FastInsertClassAttributesResults(
|
|
Cookie* pClassCookie
|
|
)
|
|
/***
|
|
|
|
This routine displays all the attributes for a class.
|
|
|
|
***/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SchemaObject *pObject, *pTop;
|
|
CString top = L"top";
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
|
|
//
|
|
// Call the attribute display routine. This routine
|
|
// will call itself recursively to display the
|
|
// inheritance structure of the class.
|
|
//
|
|
|
|
pObject = Scope.g_SchemaCache.LookupSchemaObjectByCN(
|
|
pClassCookie->strSchemaObject,
|
|
SCHMMGMT_CLASS );
|
|
|
|
if ( pObject ) {
|
|
|
|
CStringList szProcessedList;
|
|
hr = RecursiveDisplayClassAttributesResults(
|
|
pClassCookie,
|
|
pObject,
|
|
szProcessedList);
|
|
|
|
Scope.g_SchemaCache.ReleaseRef( pObject );
|
|
}
|
|
|
|
//
|
|
// Process "top" just once.
|
|
//
|
|
|
|
pTop = Scope.g_SchemaCache.LookupSchemaObject( top, SCHMMGMT_CLASS );
|
|
|
|
if ( pTop ) {
|
|
|
|
ProcessResultList( pClassCookie, pTop->systemMayContain, TRUE, TRUE, pTop );
|
|
ProcessResultList( pClassCookie, pTop->mayContain, TRUE, FALSE, pTop );
|
|
ProcessResultList( pClassCookie, pTop->systemMustContain, FALSE, TRUE, pTop );
|
|
ProcessResultList( pClassCookie, pTop->mustContain, FALSE, FALSE, pTop );
|
|
|
|
Scope.g_SchemaCache.ReleaseRef( pTop );
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
Component::RecursiveDisplayClassAttributesResults(
|
|
Cookie *pParentCookie,
|
|
SchemaObject* pObject,
|
|
CStringList& szProcessedList
|
|
)
|
|
/***
|
|
|
|
Display all the attributes for this class.
|
|
|
|
***/
|
|
{
|
|
|
|
ListEntry *pList;
|
|
SchemaObject *pInheritFrom;
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
|
|
//
|
|
// Don't process "top" here since everyone inherits from it.
|
|
//
|
|
|
|
if ( pObject->ldapDisplayName == L"top" ) {
|
|
return S_OK;
|
|
}
|
|
|
|
DebugTrace( L"RecursiveDisplayClassAttributesResults: %ls\n",
|
|
const_cast<LPWSTR>((LPCTSTR)pObject->ldapDisplayName) );
|
|
|
|
//
|
|
// Insert all the attributes for this class.
|
|
// The second parameter dictates whether these
|
|
// are optional or not. The third parameter
|
|
// is the source of the attribute.
|
|
//
|
|
|
|
ProcessResultList( pParentCookie, pObject->systemMayContain, TRUE, TRUE, pObject );
|
|
ProcessResultList( pParentCookie, pObject->mayContain, TRUE, FALSE, pObject );
|
|
ProcessResultList( pParentCookie, pObject->systemMustContain, FALSE, TRUE, pObject );
|
|
ProcessResultList( pParentCookie, pObject->mustContain, FALSE, FALSE, pObject );
|
|
|
|
//
|
|
// For each auxiliary class, insert those attributes.
|
|
//
|
|
|
|
pList = pObject->systemAuxiliaryClass;
|
|
|
|
while ( pList ) {
|
|
|
|
pInheritFrom = Scope.g_SchemaCache.LookupSchemaObject( pList->Attribute,
|
|
SCHMMGMT_CLASS ,
|
|
Scope.m_fViewDefunct);
|
|
//
|
|
// Don't recursively process the item if we already processed it
|
|
//
|
|
if ( pInheritFrom && szProcessedList.Find(pList->Attribute) == NULL) {
|
|
RecursiveDisplayClassAttributesResults( pParentCookie, pInheritFrom, szProcessedList );
|
|
szProcessedList.AddTail(pList->Attribute);
|
|
Scope.g_SchemaCache.ReleaseRef( pInheritFrom );
|
|
}
|
|
|
|
pList = pList->pNext;
|
|
}
|
|
|
|
pList = pObject->auxiliaryClass;
|
|
|
|
while ( pList ) {
|
|
|
|
pInheritFrom = Scope.g_SchemaCache.LookupSchemaObject( pList->Attribute,
|
|
SCHMMGMT_CLASS,
|
|
Scope.m_fViewDefunct);
|
|
//
|
|
// Don't recursively process the item if we already processed it
|
|
//
|
|
if ( pInheritFrom && szProcessedList.Find(pList->Attribute) == NULL ) {
|
|
RecursiveDisplayClassAttributesResults( pParentCookie, pInheritFrom, szProcessedList );
|
|
szProcessedList.AddTail(pList->Attribute);
|
|
Scope.g_SchemaCache.ReleaseRef( pInheritFrom );
|
|
}
|
|
|
|
pList = pList->pNext;
|
|
}
|
|
|
|
//
|
|
// If this is an inherited class, insert those attributes.
|
|
//
|
|
|
|
pInheritFrom = Scope.g_SchemaCache.LookupSchemaObject( pObject->subClassOf,
|
|
SCHMMGMT_CLASS,
|
|
Scope.m_fViewDefunct);
|
|
if ( pInheritFrom ) {
|
|
RecursiveDisplayClassAttributesResults( pParentCookie, pInheritFrom, szProcessedList );
|
|
Scope.g_SchemaCache.ReleaseRef( pInheritFrom );
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
Component::ProcessResultList(
|
|
Cookie *pParentCookie,
|
|
ListEntry *pList,
|
|
BOOLEAN fOptional,
|
|
BOOLEAN fSystem,
|
|
SchemaObject* pSrcObject
|
|
)
|
|
{
|
|
|
|
HRESULT hr;
|
|
Cookie *pNewCookie;
|
|
RESULTDATAITEM ResultItem;
|
|
LPCWSTR lpcszMachineName = pParentCookie->QueryNonNULLMachineName();
|
|
ListEntry *pCurrent = NULL;
|
|
SchemaObject *pAttribute=NULL;
|
|
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
//
|
|
// Initialize the result item.
|
|
//
|
|
|
|
// FUTURE-2002-03/94/2002-dantra-Although this is a safe usage of ZeroMemory, suggest changing
|
|
// the definition RESULTDATAITEM ResultItem = {0} and removing the ZeroMemory call.
|
|
::ZeroMemory( &ResultItem, sizeof( ResultItem ) );
|
|
ResultItem.nCol = 0;
|
|
ResultItem.mask = RDI_STR | RDI_IMAGE | RDI_PARAM;
|
|
ResultItem.str = MMC_CALLBACK;
|
|
ResultItem.nImage = iIconAttribute;
|
|
|
|
for (pCurrent = pList ; pCurrent != NULL; pCurrent = pCurrent->pNext)
|
|
{
|
|
|
|
//
|
|
// Point to the actual attribute.
|
|
//
|
|
pAttribute = Scope.g_SchemaCache.LookupSchemaObject(
|
|
pCurrent->Attribute,
|
|
SCHMMGMT_ATTRIBUTE,
|
|
Scope.m_fViewDefunct);
|
|
|
|
|
|
|
|
if(pAttribute==NULL)
|
|
{
|
|
ASSERT(pAttribute!=NULL); // All attributes should be in the cache
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Make a new cookie.
|
|
//
|
|
|
|
pNewCookie = new Cookie( SCHMMGMT_ATTRIBUTE,
|
|
lpcszMachineName );
|
|
|
|
if ( pNewCookie )
|
|
{
|
|
//
|
|
// Record the optional status and the source.
|
|
//
|
|
|
|
if ( fOptional ) {
|
|
pNewCookie->Mandatory = FALSE;
|
|
} else {
|
|
pNewCookie->Mandatory = TRUE;
|
|
}
|
|
|
|
if ( fSystem ) {
|
|
pNewCookie->System = TRUE;
|
|
} else {
|
|
pNewCookie->System = FALSE;
|
|
}
|
|
|
|
pNewCookie->strSrcSchemaObject = pSrcObject->commonName;
|
|
pNewCookie->pParentCookie = pParentCookie;
|
|
|
|
pNewCookie->strSchemaObject = pAttribute->commonName;
|
|
Scope.g_SchemaCache.ReleaseRef( pAttribute );
|
|
|
|
//
|
|
// Insert the result pane item.
|
|
//
|
|
|
|
pParentCookie->m_listScopeCookieBlocks.AddHead(
|
|
(CBaseCookieBlock*)pNewCookie
|
|
);
|
|
|
|
ResultItem.lParam = reinterpret_cast<LPARAM>((CCookie*)pNewCookie);
|
|
hr = m_pResultData->InsertItem( &ResultItem );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pNewCookie->SetResultHandle( ResultItem.itemID );
|
|
}
|
|
else
|
|
{
|
|
delete pNewCookie;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
Component::AddMenuItems(
|
|
LPDATAOBJECT,
|
|
LPCONTEXTMENUCALLBACK piCallback,
|
|
long* pInsertionsAllowed
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (*pInsertionsAllowed & CCM_INSERTIONALLOWED_VIEW)
|
|
{
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
hr=_InsertMenuHelper
|
|
(
|
|
piCallback,
|
|
CCM_INSERTIONPOINTID_PRIMARY_VIEW,
|
|
VIEW_DEFUNCT_OBJECTS,
|
|
TRUE,
|
|
Scope.m_fViewDefunct
|
|
);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
Component::Command(
|
|
long lCommandID,
|
|
LPDATAOBJECT obj
|
|
)
|
|
{
|
|
switch ( lCommandID )
|
|
{
|
|
case VIEW_DEFUNCT_OBJECTS:
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
Scope.m_fViewDefunct=!Scope.m_fViewDefunct;
|
|
OnNotifyRefresh(obj);
|
|
CCookie* pBaseParentCookie = NULL;
|
|
HRESULT hr = ExtractData( obj,
|
|
CSchmMgmtDataObject::m_CFRawCookie,
|
|
reinterpret_cast<PBYTE>(&pBaseParentCookie),
|
|
sizeof(pBaseParentCookie) );
|
|
if( SUCCEEDED(hr) && pBaseParentCookie )
|
|
{
|
|
this->m_pConsole->SelectScopeItem(pBaseParentCookie->m_hScopeItem);
|
|
}
|
|
|
|
// The only real info we save is the menu state
|
|
// so let's flag that a save might be required
|
|
m_bDirty=true;
|
|
|
|
break;
|
|
}
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
HRESULT Component::OnNotifyRefresh(LPDATAOBJECT obj)
|
|
{
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
|
|
Scope.RefreshScopeView();
|
|
m_pConsole->UpdateAllViews(
|
|
obj,
|
|
SCHMMGMT_SCHMMGMT,
|
|
SCHMMGMT_UPDATEVIEW_REFRESH);
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT Component::OnNotifySnapinHelp (LPDATAOBJECT)
|
|
{
|
|
// return ShowHelpTopic( L"sag_adschema.htm" );
|
|
|
|
CComQIPtr<IDisplayHelp,&IID_IDisplayHelp> spDisplayHelp = m_pConsole;
|
|
if ( !spDisplayHelp )
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
CString strHelpTopic = L"ADConcepts.chm::/sag_adschema.htm";
|
|
HRESULT hr = spDisplayHelp->ShowTopic (T2OLE ((LPWSTR)(LPCWSTR) strHelpTopic));
|
|
ASSERT (SUCCEEDED (hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT Component::OnNotifyContextHelp (LPDATAOBJECT)
|
|
{
|
|
// return ShowHelpTopic( L"schmmgmt_top.htm" );
|
|
|
|
CComQIPtr<IDisplayHelp,&IID_IDisplayHelp> spDisplayHelp = m_pConsole;
|
|
if ( !spDisplayHelp )
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
CString strHelpTopic = L"ADConcepts.chm::/schmmgmt_top.htm";
|
|
HRESULT hr = spDisplayHelp->ShowTopic (T2OLE ((LPWSTR)(LPCWSTR) strHelpTopic));
|
|
ASSERT (SUCCEEDED (hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
Component::QueryPagesFor(
|
|
LPDATAOBJECT pDataObject )
|
|
{
|
|
|
|
MFC_TRY;
|
|
|
|
if ( NULL == pDataObject ) {
|
|
ASSERT(FALSE);
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
CCookie* pBaseParentCookie = NULL;
|
|
|
|
hr = ExtractData( pDataObject,
|
|
CSchmMgmtDataObject::m_CFRawCookie,
|
|
reinterpret_cast<PBYTE>(&pBaseParentCookie),
|
|
sizeof(pBaseParentCookie) );
|
|
|
|
ASSERT( SUCCEEDED(hr) );
|
|
|
|
Cookie* pParentCookie = ActiveCookie(pBaseParentCookie);
|
|
ASSERT( NULL != pParentCookie );
|
|
|
|
if ( ( pParentCookie->m_objecttype == SCHMMGMT_ATTRIBUTE ) &&
|
|
( pParentCookie->pParentCookie ) &&
|
|
( pParentCookie->pParentCookie->m_objecttype == SCHMMGMT_ATTRIBUTES ) ) {
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
|
|
MFC_CATCH;
|
|
}
|
|
|
|
//
|
|
// This adds pages to the property sheet if appropriate.
|
|
// The handle parameter must be saved in the property page
|
|
// object to notify the parent when modified.
|
|
//
|
|
|
|
STDMETHODIMP
|
|
Component::CreatePropertyPages(
|
|
LPPROPERTYSHEETCALLBACK pCallBack,
|
|
LONG_PTR,
|
|
LPDATAOBJECT pDataObject )
|
|
{
|
|
|
|
MFC_TRY;
|
|
CWaitCursor wait;
|
|
|
|
//
|
|
// Validate the parameters.
|
|
//
|
|
|
|
if ( ( NULL == pCallBack ) ||
|
|
( NULL == pDataObject ) ) {
|
|
|
|
ASSERT(FALSE);
|
|
return E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Make sure this is a class object that we are calling up.
|
|
//
|
|
|
|
CCookie* pBaseParentCookie = NULL;
|
|
|
|
HRESULT hr = ExtractData( pDataObject,
|
|
CSchmMgmtDataObject::m_CFRawCookie,
|
|
reinterpret_cast<PBYTE>(&pBaseParentCookie),
|
|
sizeof(pBaseParentCookie) );
|
|
ASSERT( SUCCEEDED(hr) );
|
|
|
|
Cookie* pParentCookie = ActiveCookie(pBaseParentCookie);
|
|
ASSERT( NULL != pParentCookie );
|
|
ASSERT( pParentCookie->m_objecttype == SCHMMGMT_ATTRIBUTE );
|
|
|
|
//
|
|
// Create the page.
|
|
//
|
|
|
|
HPROPSHEETPAGE hPage;
|
|
AttributeGeneralPage *pGeneralPage =
|
|
new AttributeGeneralPage( this, pDataObject );
|
|
|
|
if ( pGeneralPage )
|
|
{
|
|
pGeneralPage->Load( *pParentCookie );
|
|
MMCPropPageCallback( &pGeneralPage->m_psp );
|
|
hPage= MyCreatePropertySheetPage( &pGeneralPage->m_psp );
|
|
hr = pCallBack->AddPage( hPage );
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
MFC_CATCH;
|
|
}
|
|
|
|
|
|
|
|
HRESULT __stdcall
|
|
Component::Compare(
|
|
LPARAM,
|
|
MMC_COOKIE cookieA,
|
|
MMC_COOKIE cookieB,
|
|
int* result)
|
|
{
|
|
if (!result)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!m_pViewedCookie)
|
|
{
|
|
ASSERT(false);
|
|
*result = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
Cookie* c1 =
|
|
(Cookie*) ActiveBaseCookie(reinterpret_cast<CCookie*>(cookieA));
|
|
|
|
Cookie* c2 =
|
|
(Cookie*) ActiveBaseCookie(reinterpret_cast<CCookie*>(cookieB));
|
|
|
|
PWSTR t1 = QueryBaseComponentDataRef().QueryResultColumnText(*c1, *result);
|
|
PWSTR t2 = QueryBaseComponentDataRef().QueryResultColumnText(*c2, *result);
|
|
|
|
// All columns use a case-insensitive comparison, as many columns contain
|
|
// display names from the directory (which are case-insensitive). That we
|
|
// also use a case insensitive compare for the other columns is harmless.
|
|
|
|
// another trick: we are inverting the results from the compare. This is
|
|
// because we initially insert the items in the list in sorted order. So
|
|
// the first sort request from the user really is intended to reverse-sort
|
|
// the list.
|
|
|
|
*result = -(_wcsicmp(t1, t2));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Component::GetResultViewType(MMC_COOKIE cookie,
|
|
BSTR* ppViewType,
|
|
long* pViewOptions)
|
|
{
|
|
MFC_TRY;
|
|
if( QueryComponentDataRef().IsErrorSet() )
|
|
{
|
|
*pViewOptions = MMC_VIEW_OPTIONS_NOLISTVIEWS;
|
|
|
|
LPOLESTR psz = NULL;
|
|
StringFromCLSID(CLSID_MessageView, &psz);
|
|
|
|
USES_CONVERSION;
|
|
|
|
if (psz != NULL)
|
|
{
|
|
*ppViewType = psz;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return CComponent::GetResultViewType( cookie, ppViewType, pViewOptions );
|
|
}
|
|
|
|
MFC_CATCH;
|
|
}
|
|
|
|
|
|
HRESULT SaveDWordHelper(IStream* pStm, DWORD dw)
|
|
{
|
|
ULONG nBytesWritten;
|
|
HRESULT hr = pStm->Write((void*)&dw, sizeof(DWORD),&nBytesWritten);
|
|
if (nBytesWritten < sizeof(DWORD))
|
|
hr = STG_E_CANTSAVE;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT LoadDWordHelper(IStream* pStm, DWORD* pdw)
|
|
{
|
|
ULONG nBytesRead;
|
|
HRESULT hr = pStm->Read((void*)pdw,sizeof(DWORD), &nBytesRead);
|
|
ASSERT(nBytesRead == sizeof(DWORD));
|
|
return hr;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//// IPersistStream interface members
|
|
|
|
|
|
STDMETHODIMP Component::GetClassID(CLSID *pClassID)
|
|
{
|
|
ASSERT(pClassID != NULL);
|
|
|
|
// Copy the CLSID for this snapin
|
|
*pClassID=CLSID_SchmMgmt;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP Component::IsDirty()
|
|
{
|
|
return m_bDirty ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
// IMPORTANT NOTICE: this value has to be bumped up EVERY time
|
|
// a change is made to the stream format
|
|
#define DS_STREAM_VERSION ((DWORD)0x01)
|
|
|
|
STDMETHODIMP Component::Load(IStream *pStm)
|
|
{
|
|
ASSERT(pStm);
|
|
|
|
// read the version ##
|
|
DWORD dwVersion;
|
|
HRESULT hr = LoadDWordHelper(pStm, &dwVersion);
|
|
if ( FAILED(hr) ||(dwVersion != DS_STREAM_VERSION) ) return E_FAIL;
|
|
|
|
// read m_fViewDefunct
|
|
DWORD auxView;
|
|
hr = LoadDWordHelper(pStm, &auxView);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
Scope.m_fViewDefunct = (bool)auxView;
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
STDMETHODIMP Component::Save(IStream *pStm, BOOL fClearDirty)
|
|
{
|
|
ASSERT(pStm);
|
|
|
|
// write the version ##
|
|
HRESULT hr = SaveDWordHelper(pStm, DS_STREAM_VERSION);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
ComponentData& Scope = QueryComponentDataRef();
|
|
hr = SaveDWordHelper(pStm, Scope.m_fViewDefunct);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
|
|
if(fClearDirty) m_bDirty=false;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP Component::GetSizeMax(ULARGE_INTEGER *pcbSize)
|
|
{
|
|
ASSERT(pcbSize);
|
|
ASSERT(FALSE);
|
|
|
|
//
|
|
// Arbitrary values but I don't think we ever get called
|
|
//
|
|
pcbSize->LowPart = 0xffff;
|
|
pcbSize->HighPart= 0x0;
|
|
return S_OK;
|
|
}
|