// // cmponent.cpp : Declaration of Component. // // This COM object is primarily concerned with // the result pane items. // // Cory West // 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 ) { 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); 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; 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(&pBaseParentCookie), sizeof(pBaseParentCookie) ); ASSERT( SUCCEEDED(hr) ); Cookie* pParentCookie = ActiveCookie(pBaseParentCookie); ASSERT( NULL != pParentCookie ); 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 pUnknown; CComPtr 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) { CCookie* pBaseParentCookie = NULL; HRESULT hr = ExtractData( lpDataObject, CSchmMgmtDataObject::m_CFRawCookie, reinterpret_cast(&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. // ::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 { // // 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((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((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 ); // // 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 ); // // 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 ); 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 = pList; SchemaObject *pAttribute; ComponentData& Scope = QueryComponentDataRef(); // // Initialize the result item. // ::ZeroMemory( &ResultItem, sizeof( ResultItem ) ); ResultItem.nCol = 0; ResultItem.mask = RDI_STR | RDI_IMAGE | RDI_PARAM; ResultItem.str = MMC_CALLBACK; ResultItem.nImage = iIconAttribute; while ( pCurrent ) { // // 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; // // Point to the actual attribute. // pAttribute = Scope.g_SchemaCache.LookupSchemaObject( pCurrent->Attribute, SCHMMGMT_ATTRIBUTE ); if ( pAttribute ) { pNewCookie->strSchemaObject = pAttribute->commonName; Scope.g_SchemaCache.ReleaseRef( pAttribute ); } // // Insert the result pane item. // pParentCookie->m_listScopeCookieBlocks.AddHead( (CBaseCookieBlock*)pNewCookie ); ResultItem.lParam = reinterpret_cast((CCookie*)pNewCookie); hr = m_pResultData->InsertItem( &ResultItem ); if ( SUCCEEDED(hr) ) { pNewCookie->SetResultHandle( ResultItem.itemID ); } else { delete pNewCookie; } } pCurrent = pCurrent->pNext; } return S_OK; } STDMETHODIMP Component::AddMenuItems( LPDATAOBJECT, LPCONTEXTMENUCALLBACK, long* ) { return S_OK; } STDMETHODIMP Component::Command( long, LPDATAOBJECT ) { return S_OK; } HRESULT Component::OnNotifySnapinHelp (LPDATAOBJECT) { // return ShowHelpTopic( L"sag_adschema.htm" ); CComQIPtr 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 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(&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(&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= CreatePropertySheetPage( &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(cookieA)); Cookie* c2 = (Cookie*) ActiveBaseCookie(reinterpret_cast(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; }