|
|
/*
* snapinitem.cxx * * * Copyright (c) 1998-1999 Microsoft Corporation * * PURPOSE: Defines the CSnapinItem class. * * * OWNER: ptousig */ #include <headers.hxx>
#include <atlhost.h>
// class CBaseSnapinItem
// -----------------------------------------------------------------------------
CBaseSnapinItem::CBaseSnapinItem() { Trace(tagBaseSnapinItemTracker, _T("0x%08lX: %S: Creation"), this, SzGetSnapinItemClassName());
m_type = CCT_UNINITIALIZED; m_hscopeitem = 0; m_pComponentData = NULL; m_pitemParent = NULL; m_pitemNext = NULL; m_pitemPrevious = NULL; m_pitemChild = NULL; m_fInserted = FALSE; m_fIsRoot = FALSE; m_fIsGhostRoot = FALSE; m_fWasExpanded = FALSE; }
// -----------------------------------------------------------------------------
// Cleans up the subtree below the item.
//
CBaseSnapinItem::~CBaseSnapinItem() { // Declarations
SC sc = S_OK;
// Do not do anything below if the object is a multiselect data object.
// We can not call FIsMultiSelectDataObject(). Use another criteria.
// $REVIEW (dominicp) Is it possible to have a type set to CCT_UNINITIALIZED and not be a multiselect snapin item?
if (CCT_UNINITIALIZED != m_type) { Trace(tagBaseSnapinItemTracker, _T("0x%08lX: %S: Destroyed"), this, SzGetSnapinItemClassName());
sc = ScDeleteSubTree(FALSE); if (sc) goto Error;
// Remove the item from the tree.
Unlink();
// The Pcookielist is in CBaseSnapin.
if (Psnapin()) { // Root nodes do not addref the frame and so should not release them.
if (FIsRoot() == FALSE) { // Remove the cookie from the list of available cookies.
Pcookielist()->erase(Cookie()); } } }
Cleanup: return; Error: sc.Throw (); goto Cleanup; }
// -----------------------------------------------------------------------------
// Set the HSCOPEITEM of this node.
//
void CBaseSnapinItem::SetHscopeitem(HSCOPEITEM hscopeitem) { // If we already have a HSCOPEITEM, we don't want it to change.
ASSERT(m_hscopeitem == 0 || hscopeitem == 0 || m_hscopeitem == hscopeitem); m_hscopeitem = hscopeitem; }
// -----------------------------------------------------------------------------
// This is the CCF_DISPLAY_NAME clipboard format.
//
SC CBaseSnapinItem::ScWriteDisplayName(IStream *pstream) { SC sc = S_OK;
ASSERT(PstrDisplayName());
sc = pstream->Write(PstrDisplayName()->data(), (PstrDisplayName()->length()+1)*sizeof(TCHAR), NULL); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScWriteDisplayName"), sc); goto Cleanup; }
SC CBaseSnapinItem::ScWriteAnsiName(IStream *pStream ) { SC sc = S_OK;
ASSERT(PstrDisplayName());
USES_CONVERSION; sc = pStream->Write( T2A(PstrDisplayName()->data()), (PstrDisplayName()->length()+1), NULL); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScWriteAnsiName"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Write the Node type's GUID out to a stream in CLSID form.
// This is the CCF_NODETYPE clipboard format.
//
SC CBaseSnapinItem::ScWriteNodeType(IStream *pstream) { return pstream->Write(Pnodetype()->PclsidNodeType(), sizeof(CLSID), NULL); }
// -----------------------------------------------------------------------------
// Write a unique ID to represent this node. This implementation uses the 'this'
// pointer. A SNodeID is simply a blob prefixed by its length (as a DWORD).
//
SC CBaseSnapinItem::ScWriteNodeID(IStream *pstream) { SC sc = S_OK; CBaseSnapinItem *pitemThis = this; DWORD dwSize = sizeof(pitemThis);
// Write the size of the data
sc = pstream->Write(&dwSize, sizeof(dwSize), NULL); if (sc) goto Error;
// Write the data itself.
sc = pstream->Write(&pitemThis, sizeof(pitemThis), NULL); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScWriteNodeID"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Write the Node type's Column Set ID. We use the node guid
// as a default implementation.
//
SC CBaseSnapinItem::ScWriteColumnSetId(IStream *pstream) { SC sc = S_OK; DWORD dwFlags = 0; DWORD dwSize = sizeof(GUID);
// write out an MMC SColumnSetID structure
sc = pstream->Write(&dwFlags, sizeof(dwFlags), NULL); if (sc) goto Error;
sc = pstream->Write(&dwSize, sizeof(dwSize), NULL); if (sc) goto Error;
sc = pstream->Write(Pnodetype()->PclsidNodeType(), sizeof(GUID), NULL); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScWriteColumnSetId"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Write the HSCOPEITEM of this node out to a stream.
// This is the CF_EXCHANGE_ADMIN_HSCOPEITEM clipboard format.
//
SC CBaseSnapinItem::ScWriteAdminHscopeitem(IStream *pstream) { return pstream->Write(&m_hscopeitem, sizeof(m_hscopeitem), NULL); }
// -----------------------------------------------------------------------------
// Write the class ID of the Snapin out to a stream.
// This is the CCF_SNAPIN_CLASSID clipboard format.
//
SC CBaseSnapinItem::ScWriteClsid(IStream *pstream) { return pstream->Write(PclsidSnapin(), sizeof(CLSID), NULL); }
// -----------------------------------------------------------------------------
// Returns the snapin to which this item belongs.
//
CBaseSnapin *CBaseSnapinItem::Psnapin(void) { return m_pSnapin; }
// -----------------------------------------------------------------------------
// Get an IConsole interface.
// It is possible that this item is not associated with a ComponentData. We
// cannot ASSERT in this case, because in many situations we don't mind if it's NULL.
// Without an ASSERT, the worst that will happen is that you will hit an AV
// (which would happen in retail builds anyway) and that is as easy to debug as
// an ASSERT.
//
IConsole *CBaseSnapinItem::IpConsole(void) { if (m_pComponentData) return m_pComponentData->IpConsole(); else return NULL; }
// -----------------------------------------------------------------------------
// Get an IPropertySheetProvider interface.
//
IPropertySheetProvider *CBaseSnapinItem::IpPropertySheetProvider(void) { ASSERT(m_pComponentData); return m_pComponentData->IpPropertySheetProvider(); }
// -----------------------------------------------------------------------------
// Get the CComponentData that is associated with this node.
//
CComponentData *CBaseSnapinItem::PComponentData(void) { ASSERT(m_pComponentData); return m_pComponentData; }
// -----------------------------------------------------------------------------
// Set the given CComponentData as the "owner" of this node.
//
void CBaseSnapinItem::SetComponentData(CComponentData *pComponentData) { if (pComponentData == NULL) { // We are being told to forget our owner
m_pComponentData = NULL; } else if (pComponentData->FIsRealComponentData()) { // Once the "real" owner is set, it shouldn't be changed.
ASSERT(m_pComponentData == NULL || m_pComponentData == pComponentData); m_pComponentData = pComponentData; } }
// -----------------------------------------------------------------------------
// Is the given item one of the children of this node.
//
BOOL CBaseSnapinItem::FIncludesChild(CBaseSnapinItem *pitem) { CBaseSnapinItem *pitemIter = PitemChild(); while (pitemIter) { if (pitemIter == pitem) return TRUE; pitemIter = pitemIter->PitemNext(); } return FALSE; }
// -----------------------------------------------------------------------------
// Add a child to this node.
//
SC CBaseSnapinItem::ScAddChild(CBaseSnapinItem *pitem) { SC sc = S_OK; CBaseSnapinItem *pitemPrevious = NULL;
pitemPrevious = PitemChild(); if (pitemPrevious) { while (pitemPrevious->PitemNext()) pitemPrevious = pitemPrevious->PitemNext(); pitemPrevious->SetNext(pitem); pitem->m_pitemParent = this; // Successfully inserted.
} else { // First child item
SetChild(pitem); }
return sc; }
// -----------------------------------------------------------------------------
// This node will be used to represent another node (aka Ghost root node).
// We don't own the other node, it might not even be from this DLL, the only
// information we can get about this other node has to come from clipboard
// data from the provided 'lpDataObject'.
//
SC CBaseSnapinItem::ScInitializeNamespaceExtension(LPDATAOBJECT lpDataObject, HSCOPEITEM item, CNodeType *pnodetype) { return S_OK; }
// -----------------------------------------------------------------------------
// Called to ask this node to create its children.
//
SC CBaseSnapinItem::ScCreateChildren(void) { return S_OK; }
// -----------------------------------------------------------------------------
// Removes an item from the linked list. Links up the previous and next items,
// if any. If this is the first item in the list, set the parent's child pointer
// to the next item (if it exists.)
//
void CBaseSnapinItem::Unlink() { // Make sure that this item has no children. Wouldn't know what to do with them.
ASSERT(PitemChild() == NULL);
// A real clear way of checking all cases: 8 in all.
if (PitemPrevious()) { if (PitemParent()) { if (PitemNext()) // PitemPrevious() && PitemParent() && PitemNext()
PitemPrevious()->SetNext(PitemNext()); else // PitemPrevious() && PitemParent() && !PitemNext()
PitemPrevious()->SetNext(NULL); } else // !PitemParent()
{ if (PitemNext()) // PitemPrevious() && !PitemParent() && PitemNext()
PitemPrevious()->SetNext(PitemNext()); else // PitemPrevious() && !PitemParent() && !PitemNext()
PitemPrevious()->SetNext(NULL); } } else // !PitemPrevious() - this is the first item in the list.
{ if (PitemParent()) { if (PitemNext()) // !PitemPrevious() && PitemParent() && PitemNext()
{ PitemParent()->SetChild(PitemNext()); PitemNext()->SetPrevious(NULL); } else // !PitemPrevious() && PitemParent() && !PitemNext()
{ // Set the Parent's Child pointer to NULL if we are the (only) child of the parent.
if (PitemParent()->PitemChild() == static_cast<CBaseSnapinItem*>(this)) PitemParent()->SetChild(NULL); } } else // !PitemParent()
{ if (PitemNext()) // !PitemPrevious() && !PitemParent() && PitemNext()
PitemNext()->SetPrevious(NULL); else // !PitemPrevious() && !PitemParent() && !PitemNext()
; // do nothing - already an orphan.
} }
// Clear all the link pointers.
SetNext(NULL); SetPrevious(NULL); // Can't use SetParent() because it ASSERTs.
m_pitemParent = NULL; }
// -----------------------------------------------------------------------------
// Initializes the snapinitem.
//
// The 'pcolinfoex' and 'ccolinfoex' are no longer used. The derived class is
// now responsible for maintaining per-item column information. The default
// implementation of the accessors will get the column information from the
// CBaseSnapin-derived class.
//
SC CBaseSnapinItem::ScInit(CBaseSnapin *pSnapin, CColumnInfoEx *pcolinfoex, INT ccolinfoex, BOOL fIsRoot) { SC sc = S_OK; m_pSnapin = pSnapin; m_fIsRoot = fIsRoot; // Add the cookie to the list of available cookies.
pSnapin->Pcookielist()->insert(Cookie());
return sc; }
// -----------------------------------------------------------------------------
// Initializes a child item.
//
SC CBaseSnapinItem::ScInitializeChild(CBaseSnapinItem* pitem) { return pitem->ScInit(Psnapin(), NULL, 0); }
// -----------------------------------------------------------------------------
// Information obtained from Psnapin().
//
const tstring& CBaseSnapinItem::StrClassName(void) { return Psnapin()->StrClassName();} const CLSID * CBaseSnapinItem::PclsidSnapin(void) { return Psnapin()->PclsidSnapin();} const tstring& CBaseSnapinItem::StrClsidSnapin(void) { return Psnapin()->StrClsidSnapin();} WTL::CBitmap* CBaseSnapinItem::PbitmapImageListSmall(void) { return Psnapin()->PbitmapImageListSmall();} WTL::CBitmap* CBaseSnapinItem::PbitmapImageListLarge(void) { return Psnapin()->PbitmapImageListLarge();} CCookieList * CBaseSnapinItem::Pcookielist(void) { return Psnapin()->Pcookielist();}
// -----------------------------------------------------------------------------
// Deletes the entire subtree rooted at this node, thereby freeing up all the cookies.
// If fDeleteRoot is TRUE we need to delete the root node as well.
//
SC CBaseSnapinItem::ScDeleteSubTree(BOOL fDeleteRoot) { SC sc = S_OK; CBaseSnapinItem * pitem = PitemChild(); CBaseSnapinItem * pitemNext = NULL;
while (pitem) { // We are about to delete 'pitem', keep a pointer to the next one.
pitemNext = pitem->PitemNext();
// Delete the entire subtree including the root node.
sc = pitem->ScDeleteSubTree(TRUE); if (sc) goto Error;
pitem = pitemNext; }
// We don't have any children left.
m_pitemChild = NULL; m_fWasExpanded = FALSE;
// if we have not removed the scope item, do it now
if (fDeleteRoot) { if (m_hscopeitem) { sc = PComponentData()->IpConsoleNameSpace()->DeleteItem(m_hscopeitem, TRUE); if (sc) goto Error; }
Unlink(); // Since we are no longer known by MMC we have to forget our HSCOPEITEM
// so that it doesn't get mis-used. This can happen if you refresh a parent
// of a node that has properties open.
m_hscopeitem = 0; static_cast<IDataObject *>(this)->Release(); }
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScDeleteSubTree"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Get the root of this item tree.
//
CBaseSnapinItem *CBaseSnapinItem::PitemRoot(void) { if (m_pitemParent) return m_pitemParent->PitemRoot(); else return this; }
// -----------------------------------------------------------------------------
// We are the data object. We just return a pointer to the IDataObject of
// ourselves.
//
SC CBaseSnapinItem::ScQueryDataObject(long cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject) { SC sc = S_OK;
//
// The item will remember what type it is at the moment.
//
m_type = type;
//
// This seems like a very twisted way of getting a pointer to ourselves,
// but it is actualy because we want to make sure we play within ATL rules.
// In a way, QueryDataObject is the same thing as a QueryInterface.
//
sc = static_cast<IDataObject *>(this)->QueryInterface(IID_IDataObject, reinterpret_cast<void**>(ppDataObject)); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScQueryDataObject"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Handles the MMCN_SHOW notification. Initializes the default result view headers.
//
SC CBaseSnapinItem::ScOnShow(CComponent *pComponent, BOOL fSelect) { SC sc = S_OK;
ASSERT(pComponent);
if (fSelect) { sc = ScInitializeResultView(pComponent); if (sc) goto Error; }
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScOnShow"), sc); goto Cleanup;
}
// -----------------------------------------------------------------------------
// Called during the MMCN_EXPAND notification sent to IComponentData::Notify. Inserts
// the current item into the scope pane (if it is a container item) using IConsoleNameSpace
// methods. If fExpand is FALSE, don't do anything.
// Chains all sibling items.
//
SC CBaseSnapinItem::ScInsertScopeItem(CComponentData *pComponentData, BOOL fExpand, HSCOPEITEM item) { SC sc = S_OK; CBaseSnapinItem* pitem = this; SCOPEDATAITEM scopedataitem;
if (fExpand == FALSE) goto Cleanup;
while (pitem) { // Only add container items.
if (pitem->FIsContainer()) { pitem->SetComponentData(pComponentData);
ZeroMemory(&scopedataitem, sizeof(SCOPEDATAITEM)); scopedataitem.lParam = pitem->Cookie(); scopedataitem.mask = SDI_STR | SDI_PARAM | SDI_PARENT | SDI_CHILDREN; if (pitem->Iconid() != iconNil) scopedataitem.mask |= SDI_IMAGE; if (pitem->OpenIconid() != iconNil) scopedataitem.mask |= SDI_OPENIMAGE;
// Callback for the display name.
// $REVIEW (ptousig) Why don't we take advantage of the displayname ?
// Callbacks can be pretty inefficient.
scopedataitem.displayname = MMC_CALLBACK; scopedataitem.nImage = pitem->Iconid(); scopedataitem.nOpenImage = pitem->OpenIconid(); scopedataitem.relativeID = item; // If there are no children, MMC will suppress the "+" sign
scopedataitem.cChildren = pitem->FHasChildren() ? 1 : 0;
ASSERT(pComponentData); sc = pComponentData->IpConsoleNameSpace()->InsertItem(&scopedataitem); if (sc) goto Error;
pitem->SetHscopeitem(scopedataitem.ID); }
pitem = pitem->PitemNext(); }
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScInsertScopeItem"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Inserts all child items into the default result list view.
//
SC CBaseSnapinItem::ScInsertResultItem(CComponent *pComponent) { SC sc = S_OK; RESULTDATAITEM resultdataitem;
ASSERT(pComponent && pComponent->IpResultData());
// Add this item
ZeroMemory(&resultdataitem, sizeof(resultdataitem));
resultdataitem.lParam = Cookie(); resultdataitem.mask = RDI_STR | RDI_PARAM | RDI_IMAGE; // Callback for the display name.
resultdataitem.str = MMC_CALLBACK; // Custom icon
resultdataitem.nImage = (int) MMC_CALLBACK;
sc = pComponent->IpResultData()->InsertItem(&resultdataitem); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScInsertResultItem"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Asks MMC to update the display of this item in the result pane.
//
SC CBaseSnapinItem::ScUpdateResultItem(IResultData *ipResultData) { DECLARE_SC(sc, _T("CBaseSnapinItem::ScUpdateResultItem")); HRESULTITEM item = NULL;
ASSERT(ipResultData); sc = ipResultData->FindItemByLParam(Cookie(), &item); if (sc) return (sc);
// If not found, does not exist in this view. Ignore.
if (!item) return (sc);
// $REVIEW (ptousig) Why are we ignoring errors ?
ipResultData->UpdateItem(item);
return sc; }
// -----------------------------------------------------------------------------
// Asks MMC to update the display of this item in the scope pane.
//
SC CBaseSnapinItem::ScUpdateScopeItem(IConsoleNameSpace *ipConsoleNameSpace) { SC sc = S_OK; SCOPEDATAITEM scopedataitem;
ASSERT(FIsContainer());
//
// If this item doesn't have a HSCOPEITEM, then it is not known by MMC
// therefore we would get an "invalid arg" error from the call to SetItem.
//
if (Hscopeitem() == 0) goto Cleanup;
ZeroMemory(&scopedataitem, sizeof(SCOPEDATAITEM)); scopedataitem.ID = Hscopeitem(); scopedataitem.lParam = Cookie(); scopedataitem.mask = SDI_PARAM;
if (Iconid() != iconNil) { scopedataitem.mask |= SDI_IMAGE; ASSERT(FALSE && "Bitmap"); //scopedataitem.nImage = PbitmapImageListSmall()->GetIndex(Iconid());
}
if (OpenIconid() != iconNil) { scopedataitem.mask |= SDI_OPENIMAGE; ASSERT(FALSE && "Bitmap"); //scopedataitem.nOpenImage = PbitmapImageListSmall()->GetIndex(OpenIconid());
}
ASSERT(ipConsoleNameSpace); sc = ipConsoleNameSpace->SetItem(&scopedataitem); if (sc) goto Error;
// Send a notification to update the result pane description bar.
IpConsole()->UpdateAllViews(Pdataobject(), 0, ONVIEWCHANGE_UPDATEDESCRIPTIONBAR);
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScUpdateScopeItem"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// This is where we should tell MMC to remove all the items in the result pane.
//
// $REVIEW (ptousig) Why aren't we doing anything ?
//
SC CBaseSnapinItem::ScRemoveResultItems(LPRESULTDATA ipResultData) { return S_OK; }
// -----------------------------------------------------------------------------
// Provides (to MMC) the display string (or icon) for a given node in the
// scope pane.
//
SC CBaseSnapinItem::ScGetDisplayInfo(LPSCOPEDATAITEM pScopeItem) { SC sc = S_OK;
#ifdef _DEBUG
static tstring str; #endif
ASSERT(pScopeItem);
if (pScopeItem->mask & SDI_STR) { pScopeItem->displayname = (LPTSTR)PstrDisplayName()->data(); #ifdef _DEBUG
if (tagBaseSnapinDebugDisplay.FAny()) { USES_CONVERSION; str = OLE2T(pScopeItem->displayname); str += A2T(SzGetSnapinItemClassName()); // str += this;
// str += Hscopeitem();
pScopeItem->displayname = T2OLE((LPTSTR)str.data()); } #endif
}
if (pScopeItem->mask & SDI_IMAGE) { ASSERT(FALSE && "Bitmap"); //pScopeItem->nImage = PbitmapImageListSmall()->GetIndex(Iconid());
}
return sc; }
// -----------------------------------------------------------------------------
// Provides (to MMC) the display string (or icon) for a given node in the
// result pane.
//
SC CBaseSnapinItem::ScGetDisplayInfo(LPRESULTDATAITEM pResultItem) { DECLARE_SC(sc, _T("CBaseSnapinItem::ScGetDisplayInfo")); static tstring s_sz;
ASSERT(pResultItem);
if (pResultItem->mask & RDI_STR) { // Need to do this explicitly because the same buffer is reused.
pResultItem->str = (LPTSTR)s_sz.data();
// "Old" snapins might be referring to columns that don't exist.
if (pResultItem->nCol < CcolinfoexDisplay()) { Trace(tagBaseSnapinItemTracker, _T("Requesting field data - requested DAT is %d"), PcolinfoexDisplay(pResultItem->nCol)->Dat()); sc= ScGetField( PcolinfoexDisplay(pResultItem->nCol)->Dat(), s_sz); if (sc) return sc;
#ifdef _DEBUG
if (pResultItem->nCol == 0 && tagBaseSnapinDebugDisplay.FAny()) { s_sz = pResultItem->str; // s_sz += this;
} #endif
USES_CONVERSION; pResultItem->str = T2OLE((LPTSTR)s_sz.data()); } }
if (pResultItem->mask & RDI_IMAGE) // $REVIEW for extension snapins.
{ pResultItem->nImage = Iconid(); }
return sc; }
// -----------------------------------------------------------------------------
// Fills in result pane item information needed by MMC. This
// method is used when the results pane is in virtual list mode
// and we will be asking for data by index.
//
SC CBaseSnapinItem::ScGetVirtualDisplayInfo(LPRESULTDATAITEM pResultItem, IResultData *ipResultData) { DECLARE_SC(sc, _T("CBaseSnapinItem::ScGetDisplayInfo")); static tstring s_sz; //$REVIEW
ASSERT(FVirtualResultsPane() && pResultItem);
if (pResultItem->mask & RDI_STR) { sc= ScGetField(pResultItem->nIndex, PcolinfoexDisplay(pResultItem->nCol)->Dat(), s_sz, ipResultData); if (sc) return sc; pResultItem->str = (LPTSTR)s_sz.data(); }
if (pResultItem->mask & RDI_IMAGE) // $REVIEW for extension snapins.
{ pResultItem->nImage = Iconid(); }
return sc; }
// -----------------------------------------------------------------------------
// Returns the default icon ID : Folder
//
LONG CBaseSnapinItem::Iconid(void) { ASSERT(FALSE); return 0; }
// -----------------------------------------------------------------------------
// Returns the default open icon ID : Handles open folder / open RO folder / custom
//
LONG CBaseSnapinItem::OpenIconid(void) { ASSERT(FALSE); return 0; }
// -----------------------------------------------------------------------------
// Initializes the result view. This implementation requires a column info
// structure, and creates the default set of columns.
//
SC CBaseSnapinItem::ScInitializeResultView(CComponent *pComponent) { SC sc = S_OK; INT i = 0;
ASSERT(pComponent && pComponent->IpHeaderCtrl());
// Remove any old column headers (on refresh / view change)
while (!sc) sc = pComponent->IpHeaderCtrl()->DeleteColumn(0); sc = S_OK;
for (i = 0; i < CcolinfoexHeaders(); i++) { Trace(tagBaseSnapinItemTracker, _T("Inserting column with title %s"), PcolinfoexHeaders(i)->strTitle().data()); sc = pComponent->IpHeaderCtrl()->InsertColumn(i, PcolinfoexHeaders(i)->strTitle().data(), PcolinfoexHeaders(i)->NFormat(), PcolinfoexHeaders(i)->NWidth()); // Will get fail if items are already in result pane. This is not an error, happens on refresh.
// $REVIEW (ptousig) Maybe we should remove the items before re-initializing the columns.
if (sc.ToHr() == E_FAIL) sc = S_OK;
if (sc) goto Error; } // remove the NOSORTHEADER option - by default all items will have push-button header controls
sc = pComponent->IpResultData()->ModifyViewStyle((MMC_RESULT_VIEW_STYLE)0, MMC_NOSORTHEADER); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScInitializeResultView"), sc); goto Cleanup;
}
// -----------------------------------------------------------------------------
// MMC wants to know the image strip that should be used for the result pane.
//
SC CBaseSnapinItem::ScOnAddImages(IImageList* ipResultImageList) { SC sc = S_OK;
ASSERT(ipResultImageList);
sc = ipResultImageList->ImageListSetStrip( reinterpret_cast<long*>(static_cast<HBITMAP>(*PbitmapImageListSmall())), reinterpret_cast<long*>(static_cast<HBITMAP>(*PbitmapImageListLarge())), 0, RGB(255, 0, 255)); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScOnAddImages"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Cause this node to refresh itself.
//
// WARNING: The "flicker" effect of this call is very annoying to look at. Do
// not use this as a "silver bullet" solution. If you are adding,
// removing or updating a child then use the appropriate ONVIEWCHANGE_*
// notification.
//
SC CBaseSnapinItem::ScRefreshNode(void) { SC sc = S_OK;
sc = IpConsole()->UpdateAllViews(this, 0, ONVIEWCHANGE_REFRESHCHILDREN); if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScRefreshNode"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// MMC is asking us the type of result pane we want
//
SC CBaseSnapinItem::ScGetResultViewType(LPOLESTR *ppViewType, long *pViewOptions) { DECLARE_SC(sc, TEXT("CBaseSnapinItem::ScGetResultViewType")); // Validate parameters
ASSERT(ppViewType); ASSERT(pViewOptions);
if ( FResultPaneIsOCX()) { tstring strclsidOCX; sc = ScGetOCXCLSID(strclsidOCX); if (sc == S_FALSE) // default to listview.
return sc;
*ppViewType = (LPOLESTR)CoTaskMemAlloc( (strclsidOCX.length()+1) * sizeof(WCHAR));
USES_CONVERSION; wcscpy(*ppViewType, T2COLE(strclsidOCX.data()));
return sc; }
if (FResultPaneIsWeb()) { tstring strURL; sc = ScGetWebURL(strURL); if (sc == S_FALSE) // default to listview.
return sc;
*ppViewType = (LPOLESTR)CoTaskMemAlloc( (strURL.length()+1) * sizeof(WCHAR));
USES_CONVERSION; wcscpy(*ppViewType, T2COLE(strURL.data()));
return sc; }
// Set the default
*pViewOptions = MMC_VIEW_OPTIONS_NONE;
// Check if we are displaying a virtual result pane list
if (FVirtualResultsPane()) // Ask for owner data listview (virtual list box mode).
*pViewOptions = MMC_VIEW_OPTIONS_OWNERDATALIST;
// Check if we should enable multiselect
if (FAllowMultiSelectionForChildren()) *pViewOptions = *pViewOptions | MMC_VIEW_OPTIONS_MULTISELECT;
//
// Return S_FALSE to indicate we want the standard result view.
//
return S_FALSE; }
// -----------------------------------------------------------------------------
// MMC is asking us the type of result pane we want using IComponent2
//
SC CBaseSnapinItem::ScGetResultViewType2(IConsole *pConsole, PRESULT_VIEW_TYPE_INFO pResultViewType) { DECLARE_SC(sc, _T("CBaseSnapinItem::ScGetResultViewType2"));
// Validate parameters
ASSERT(pResultViewType); ASSERT(PstrDisplayName());
LPOLESTR pszViewDesc = (LPOLESTR)CoTaskMemAlloc((PstrDisplayName()->length()+1) * sizeof(WCHAR)); if (! pszViewDesc) return (sc = E_OUTOFMEMORY);
USES_CONVERSION; wcscpy(pszViewDesc, T2COLE(PstrDisplayName()->data())); pResultViewType->pstrPersistableViewDescription = pszViewDesc;
if ( FResultPaneIsOCX()) { tstring strclsidOCX; sc = ScGetOCXCLSID(strclsidOCX); if (sc == S_FALSE) // default to listview.
return sc;
pResultViewType->eViewType = MMC_VIEW_TYPE_OCX; pResultViewType->dwOCXOptions = RVTI_OCX_OPTIONS_NOLISTVIEW;
pResultViewType->pUnkControl = NULL;
if (FCacheOCX()) { pResultViewType->dwOCXOptions = RVTI_OCX_OPTIONS_CACHE_OCX; pResultViewType->pUnkControl = GetCachedOCX(pConsole); }
CComQIPtr<IConsole2> spConsole2(pConsole);
if (! pResultViewType->pUnkControl) { CLSID clsidOCX;
USES_CONVERSION; sc = CLSIDFromString(T2OLE(const_cast<LPTSTR>(strclsidOCX.data())), &clsidOCX); if (sc) return sc;
LPUNKNOWN pUnkControl = NULL; sc = CoCreateInstance(clsidOCX, NULL, CLSCTX_SERVER, IID_IUnknown, (LPVOID*)&pUnkControl); if (sc) return sc;
sc = ScInitOCX(pUnkControl, pConsole);
pResultViewType->pUnkControl = pUnkControl;
if (spConsole2) { tstring strStatusText = L"OCX: "; strStatusText += strclsidOCX; strStatusText += L" Created"; spConsole2->SetStatusText(const_cast<LPOLESTR>(T2COLE(strStatusText.data()))); } } else { pResultViewType->pUnkControl->AddRef(); if (spConsole2) { tstring strStatusText = L"OCX: "; strStatusText += strclsidOCX; strStatusText += L" cached is used"; spConsole2->SetStatusText(const_cast<LPOLESTR>(T2COLE(strStatusText.data()))); } }
return sc; }
if (FResultPaneIsWeb()) { tstring strURL = TEXT("msw"); sc = ScGetWebURL(strURL); if (sc == S_FALSE) // default to listview.
return sc;
pResultViewType->eViewType = MMC_VIEW_TYPE_HTML; pResultViewType->dwHTMLOptions = RVTI_HTML_OPTIONS_NONE | RVTI_HTML_OPTIONS_NOLISTVIEW; LPOLESTR lpszURL = (LPOLESTR)CoTaskMemAlloc( (strURL.length()+1) * sizeof(WCHAR));
USES_CONVERSION; wcscpy(lpszURL, T2COLE(strURL.data()));
pResultViewType->pstrURL = lpszURL;
return sc; }
// Set the default
pResultViewType->dwMiscOptions = RVTI_LIST_OPTIONS_NONE;
// Check if we are displaying a virtual result pane list
if (FVirtualResultsPane()) // Ask for owner data listview (virtual list box mode).
pResultViewType->dwListOptions = RVTI_LIST_OPTIONS_OWNERDATALIST;
// Check if we should enable multiselect
if (FAllowMultiSelectionForChildren()) pResultViewType->dwListOptions |= RVTI_LIST_OPTIONS_MULTISELECT;
if (FAllowPasteForResultItems()) pResultViewType->dwListOptions |= RVTI_LIST_OPTIONS_ALLOWPASTE;
//
// Return S_FALSE to indicate we want the standard result view.
//
return S_FALSE; }
// -----------------------------------------------------------------------------
// MMC is trying to restore the view, see if it is our view description.
//
SC CBaseSnapinItem::ScRestoreResultView(PRESULT_VIEW_TYPE_INFO pResultViewType) { DECLARE_SC(sc, _T("CBaseSnapinItem::ScRestoreResultView"));
// Validate parameters
ASSERT(pResultViewType); ASSERT(pResultViewType->pstrPersistableViewDescription); ASSERT(PstrDisplayName());
LPOLESTR pszViewDesc = pResultViewType->pstrPersistableViewDescription; if (! pszViewDesc) return (sc = E_OUTOFMEMORY);
USES_CONVERSION; if ( 0 != wcscmp(pszViewDesc, T2COLE(PstrDisplayName()->data())) ) return (sc = S_FALSE);
if (! pResultViewType->dwMiscOptions & RVTI_LIST_OPTIONS_NONE) return (sc = S_FALSE);
// Check if we are displaying a virtual result pane list
if (FVirtualResultsPane()) { if (! (pResultViewType->dwListOptions & RVTI_LIST_OPTIONS_OWNERDATALIST)) return (sc = S_FALSE); }
// Check if we should enable multiselect
if (FAllowMultiSelectionForChildren()) { if (! (pResultViewType->dwListOptions & RVTI_LIST_OPTIONS_MULTISELECT) ) return (sc = S_FALSE); }
// Check if result pane items allow paste.
if (FAllowPasteForResultItems()) { if (! (pResultViewType->dwListOptions & RVTI_LIST_OPTIONS_ALLOWPASTE) ) return (sc = S_FALSE); }
return S_OK; }
// -----------------------------------------------------------------------------
// Used to create a property sheet for an item. This uses MMC trickery to
// display property pages on an object that has not yet been added to the MMC
// result view.
//
SC CBaseSnapinItem::ScDisplayPropertySheet(void) { SC sc = S_OK; IPropertySheetCallbackPtr ipPropertySheetCallback; // If fCleanup == TRUE, we need to call Show(-1, 0) on an error.
BOOL fCleanup = FALSE; TCHAR strTitle[256]; CComPtr<IUnknown> spIUnknown;
// Get a pointer to the IPropertySheetProvider interface.
ipPropertySheetCallback = IpPropertySheetProvider(); ASSERT(NULL != ipPropertySheetCallback);
// Create the property pages for this object.
// TRUE for property sheet, not wizard
sc = IpPropertySheetProvider()->CreatePropertySheet(strTitle, TRUE, Cookie(), Pdataobject(), 0); if (sc) goto Error;
// If failure occurs after a successful call to CreatePropertySheet, need to call Show(-1,0). See MMC docs.
fCleanup = TRUE;
sc = ((IComponentData *)PComponentData())->QueryInterface(IID_IUnknown, (void **)&spIUnknown); if (sc) goto Error;
// Add the primary pages for the object.
sc = IpPropertySheetProvider()->AddPrimaryPages(spIUnknown, TRUE, NULL, TRUE); if (sc) goto Error;
//$REVIEW (ptousig) Why is this commented out ?
//sc = IpPropertySheetProvider()->AddExtensionPages();
if (sc) goto Error;
sc = IpPropertySheetProvider()->Show((long) GetActiveWindow(), 0); if (sc) goto Error;
Cleanup: return sc; Error: // If failure occurs after a successful call to CreatePropertySheet, need to call Show(-1,0). See MMC docs.
if (fCleanup) IpPropertySheetProvider()->Show(-1, 0); TraceError(_T("CBaseSnapinItem::ScDisplayPropertySheet"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
void CBaseSnapinItem::SetParent(CBaseSnapinItem *pitemParent) { ASSERT(pitemParent); m_pitemParent = pitemParent; }
// -----------------------------------------------------------------------------
void CBaseSnapinItem::SetNext(CBaseSnapinItem *pitemNext) { m_pitemNext = pitemNext; if (pitemNext) pitemNext->m_pitemPrevious = this; }
// -----------------------------------------------------------------------------
void CBaseSnapinItem::SetPrevious(CBaseSnapinItem *pitemPrevious) { m_pitemPrevious = pitemPrevious; if (pitemPrevious) pitemPrevious->m_pitemNext = this; }
// -----------------------------------------------------------------------------
void CBaseSnapinItem::SetChild(CBaseSnapinItem *pitemChild) { m_pitemChild = pitemChild; if (pitemChild) pitemChild->m_pitemParent = this; }
// -----------------------------------------------------------------------------
// A context menu option was selected.
//
SC CBaseSnapinItem::ScCommand(long nCommandID, CComponent *pComponent) { SC sc = S_OK;
switch (nCommandID) { case IDS_Test: break; #if 0
case idmBarfTraces: sc = Psnapin()->ScOnMenuTraces(); break;
case idmBarfClearDbgScreen: Trace(tagAlways, _T("\x1B[2J")); break;
case idmBarfSCDescription: sc = Psnapin()->ScOnMenuSCDescription(); break;
case idmBarfSettings: DoBarfDialog(); break;
case idmBarfAll: BarfAll(); break;
case idmBarfMemoryDiff: sc = Psnapin()->ScOnMenuMemoryDiff(); break;
case idmBarfValidateMemory: sc = Psnapin()->ScOnMenuMemoryDiff(); break;
case idmBarfTotalMemAllocd: sc = Psnapin()->ScOnMenuTotalMemory(); break;
case idmBarfDebugBreak: DebugBreak(); break; #endif
default: break; }
if (sc) goto Error;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScCommand"), sc); goto Cleanup; }
// -----------------------------------------------------------------------------
// Determines whether any property pages are open on this item. As a side-effect
// if any property sheet is found, it will be given focus. MMC does that by
// itself, we don't have any way of stopping it.
// If the item is a result pane item, we must provide an IComponent to MMC, this
// is the component that will receive then CompareObjects call. In our
// implementation, we don't care which IComponent does the comparison, so pass
// in any IComponent you can get your hands on.
//
SC CBaseSnapinItem::ScIsPropertySheetOpen(BOOL *pfPagesUp, IComponent *ipComponent) { SC sc = S_OK;
ASSERT(pfPagesUp);
*pfPagesUp = FALSE;
if (FIsContainer()) { // Scope pane nodes are owned by the MMC.
// Note: MMC docs says first parameter is the cookie. It is in fact the HSCOPEITEM.
sc = IpPropertySheetProvider()->FindPropertySheet(Hscopeitem(), NULL, Pdataobject()); if (sc) goto Error; } else { // Result pane nodes are owned by the IComponent.
ASSERT(ipComponent);
sc = IpPropertySheetProvider()->FindPropertySheet(Cookie(), ipComponent, Pdataobject()); if (sc) goto Error; }
if (sc == S_FALSE) *pfPagesUp = FALSE; else if (sc == S_OK) *pfPagesUp = TRUE;
Cleanup: return sc; Error: TraceError(_T("CBaseSnapinItem::ScIsPropertySheetOpen"), sc); goto Cleanup; }
// =============================================================================
// Class CBaseMultiSelectSnapinItem
// =============================================================================
UINT CBaseMultiSelectSnapinItem::s_cfMultiSelectSnapins = RegisterClipboardFormat(CCF_MULTI_SELECT_SNAPINS); // Multiselect - list of multi select snapin items in a composite data object
UINT CBaseMultiSelectSnapinItem::s_cfCompositeDataObject = RegisterClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT); // Multi select - used to determine if an object is a composite data object
// -----------------------------------------------------------------------------
// Constructor for CBaseMultiSelectSnapinItem
// -----------------------------------------------------------------------------
CBaseMultiSelectSnapinItem::CBaseMultiSelectSnapinItem() : CBaseSnapinItem() { // Remember we created a multiselect snapin item
Trace(tagBaseMultiSelectSnapinItemTracker, _T("0x%08lX: Creation"), this);
// By default, a multiselect object is not involved in copy/paste operations
// Set the pointer to an array indicating the pasted items to NULL
m_pfPastedWithCut = NULL; }
// -----------------------------------------------------------------------------
// Destructor for CBaseMultiSelectSnapinItem
// -----------------------------------------------------------------------------
CBaseMultiSelectSnapinItem::~CBaseMultiSelectSnapinItem() { // Delete the array indicating the pasted items (if any was allocated)
if (m_pfPastedWithCut) { delete [] m_pfPastedWithCut; m_pfPastedWithCut = NULL; } }
/* CBaseMultiSelectSnapinItem::ScWriteMultiSelectionItemTypes
* * PURPOSE: Implement the CCF_OBJECT_TYPES_IN_MULTI_SELECT clipboard format. * The clipboard data info indicates the types of nodes selected by a multi-select operation. * * PARAMETERS: * IStream * pstream The stream to write to. * * RETURNS: * SC Execution code. */ SC CBaseMultiSelectSnapinItem::ScWriteMultiSelectionItemTypes(IStream * pstream) { // Declarations
SC sc = S_OK; INT nIterator = 0; GUIDSet gsItemTypes;
// Data validation
ASSERT(pstream);
// Remember we created a multiselect snapin item
Trace(tagBaseMultiSelectSnapinItemTracker, _T("Received a request for clipboard data on node types"), this);
// First determine how many GUIDs we have - note that the selection of snapin items was obtained for a particular component
// Therefore the selection contains snapin items which were instanciated from the same snapin and does not include items which may have been added by other snapins
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) { // Local declarations
LPGUID pGuidItemType = (LPGUID)((*PivSelectedItems())[nIterator])->Pnodetype()->PclsidNodeType();
// Determine if the guid can be located in the guid set and if not add it
ASSERT(pGuidItemType); if (gsItemTypes.find(*pGuidItemType) == gsItemTypes.end()) // means not found
gsItemTypes.insert(*pGuidItemType); }
// Write a SMMCObjectTypes data structure
// First: number of found types
{ // Local declarations
DWORD dwcItemTypes = 0;
ASSERT(gsItemTypes.size() > 0); dwcItemTypes = gsItemTypes.size(); sc = pstream->Write(&dwcItemTypes, sizeof(DWORD), NULL); // need l-value
if (sc) goto Error; }
// Write a SMMCObjectTypes data structure
// Second: the guids
{ for (GUIDSet::iterator p = gsItemTypes.begin(); p != gsItemTypes.end(); p++) { sc = pstream->Write(&p, sizeof(GUID), NULL); if (sc) goto Error; } }
Cleanup: return sc; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScWriteMultiSelectionItemTypes()"), sc); goto Cleanup;
}
/* CBaseMultiSelectSnapinItem::ScOnSelect
* * PURPOSE: Forward a selection notification to selected items - select verbs. * * PARAMETERS: * CComponent * pComponent Pointer to the component object. * LPDATAOBJECT lpDataObject Pointer to the multiselect snapin item. * BOOL fScope TRUE if selection in the scope pane. * BOOL fSelect TRUE if selected. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScOnSelect(CComponent * pComponent, LPDATAOBJECT lpDataObject, BOOL fScope, BOOL fSelect) { // Declarations
SC sc = S_OK; INT nIterator = 0;
// Data validation
ASSERT(pComponent); ASSERT(lpDataObject);
// Forward the request to the component as if only the first snapin item had been selected (this will allow us to select verbs)
// MMC finds will select these verbs for all selected snapin items
ASSERT(PivSelectedItemsFirst());
// For this call we have to go back to the component as there is some work to do at the snapin level
// We contact only the first snapin item to select verbs
// $REVIEW (dominicp) MMC recommends doing this. We should probably merge verbs only though. This would be smarter.
sc = pComponent->ScOnSelect(PivSelectedItemsFirst(), fScope, fSelect); if (sc) goto Error;
// We just let the other snapin items know they have been selected
for (nIterator=1; nIterator < PivSelectedItems()->size(); nIterator++) // start at index 1
{ // Get the next item
ASSERT((*PivSelectedItems())[nIterator]);
// Call ScOnSelect for each snapin item - pass pitem as an lpDataObject parameter
sc = (*PivSelectedItems())[nIterator]->ScOnSelect(pComponent, (*PivSelectedItems())[nIterator], fScope, fSelect); if (sc) goto Error; }
Cleanup: return sc; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScOnSelect()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScAddMenuItems
* * PURPOSE: Computes the merged context menu items and sets them. * * PARAMETERS: * CBaseSnapin * pSnapin Pointer to the snapin object. * LPDATAOBJECT lpDataObject Pointer to the multiselect snapin item. * LPCONTEXTMENUCALLBACK ipContextMenuCallback Context menu callback to add menu items. * long * pInsertionAllowed Pointer to insertion flags. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScAddMenuItems(CBaseSnapin * pSnapin, LPDATAOBJECT pDataObject, LPCONTEXTMENUCALLBACK ipContextMenuCallback, long * pInsertionAllowed) { // Declarations
SC sc = S_OK; CBaseSnapinItem * pitem = NULL; INT nIterator = 0; INT nIteratorMenuItems = 0; CSnapinContextMenuItemVectorWrapper cmivwMerged; // vector of merged context menu items
// Data validation
ASSERT(pSnapin); ASSERT(pDataObject); ASSERT(ipContextMenuCallback); ASSERT(pInsertionAllowed);
// Iterate through the snapin items and retrieve a list of context menus - combine the menus across snapin items
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) // start at index 1
{ // Get the snapin item
pitem = (*PivSelectedItems())[nIterator]; ASSERT(pitem);
// Create a vector of menu items
CSnapinContextMenuItemVectorWrapper cmivw; // we will have to merge these context menu item`s
// Determine the menu items for the snapin item
for (nIteratorMenuItems=0; nIteratorMenuItems < pitem->CMenuItem(); nIteratorMenuItems++) { // Declarations
CSnapinContextMenuItem * pcmi = NULL; BOOL fAllowed = FALSE;
// Create a new context menu item
pcmi = new CSnapinContextMenuItem(); if (!pcmi) goto MemoryError;
// Get the context menu item
ASSERT(pitem->Pmenuitem()); sc = pSnapin->ScGetMenuItem(pcmi, pitem, &((pitem->Pmenuitem())[nIteratorMenuItems]), &fAllowed, *pInsertionAllowed); if (sc) goto Error;
// If the context menu item is allowed, add it to the vector
if (fAllowed) { if (nIterator > 0) cmivw.cmiv.push_back(pcmi); // for other items, set a new array and then merge
else cmivwMerged.cmiv.push_back(pcmi); // for the first item, set the merge
} // Otherwise delete the context menu item
else { if (pcmi) { delete pcmi; pcmi = NULL; } } }
// Now combine the menus we found with the merged menus
if (nIterator > 0) MergeMenuItems(&cmivwMerged, &cmivw); }
// Now add the merged menu items
for (nIteratorMenuItems=0; nIteratorMenuItems < cmivwMerged.cmiv.size(); nIteratorMenuItems++) { sc = ipContextMenuCallback->AddItem(&(cmivwMerged.cmiv[nIteratorMenuItems]->cm)); if (sc) goto Error; }
Cleanup: return sc; MemoryError: sc = E_OUTOFMEMORY; goto Error; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScAddMenuItems()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScCommand
* * PURPOSE: Forward an add menu request to selected items. * * PARAMETERS: * CComponent * pComponent Pointer to the component object. * long nCommandID Id of the invoked command. * LPDATAOBJECT pDataObject Pointer to the multiselect snapin item. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScCommand(CComponent * pComponent, long nCommandID, LPDATAOBJECT pDataObject) { // Declarations
SC sc = S_OK; CBaseSnapinItem * pitem = NULL; INT nIterator = 0;
// Data validation
ASSERT(pComponent); ASSERT(pDataObject);
// We forward the call to all items in the default implementation
// This can be overriden if you want to provide bulk operation behaviour
// You will then have to create you own CBaseMultiSelectSnapinItem based multiselect data object
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) { // Get the next item
ASSERT((*PivSelectedItems())[nIterator]);
// Call the menu by going back to the component
sc = pComponent->ScCommand(nCommandID, (*PivSelectedItems())[nIterator]); if (sc) goto Error; }
Cleanup: return sc; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScCommand()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScQueryPagesFor
* * PURPOSE: Indicate that there are properties for a multi selection (in fact a page saying that there are no properties available). * * PARAMETERS: * CComponent * pComponent Pointer to the component object. * LPDATAOBJECT lpDataObject Pointer to the multiselect snapin item. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScQueryPagesFor(CComponent * pComponent, LPDATAOBJECT lpDataObject) { // Declarations
SC sc = S_OK; INT nIterator = 0;
// Data validation
ASSERT(pComponent); ASSERT(lpDataObject);
// By default, no page for a multiselection
return S_FALSE; }
/* CBaseMultiSelectSnapinItem::ScCreatePropertyPages
* * PURPOSE: Display a page saying that there are no properties available. * * PARAMETERS: * CComponent * pComponent Pointer to the component object. * LPDATAOBJECT lpDataObject Pointer to the multiselect snapin item. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScCreatePropertyPages(CComponent * pComponent, LPPROPERTYSHEETCALLBACK ipPropertySheetCallback, long handle, LPDATAOBJECT lpDataObject) { // Data validation
ASSERT(pComponent); ASSERT(ipPropertySheetCallback); ASSERT(lpDataObject);
// By default, no page for a multiselection
return S_FALSE; }
/* CBaseMultiSelectSnapinItem::ScOnPaste
* * PURPOSE: We receive a multiselect snapin item and we must determine which items are okay with the paste. * Also, if the operation involves a cut, we have to return a list of items to be pasted. * We use an array of boolean to indicate the items for which a cut notification has to be sent. * We reuse the existing multiselect snapin item as the multiselect snapin item sent to MMC to identify * the objects which should receive a cut notification. * * PARAMETERS: * CBaseSnapin * psnapin Pointer to the snapin. * CBaseSnapinItem * pitemTarget Target item for the paste. * LPDATAOBJECT lpDataObjectList Pointer to the multiselect snapin item. * LPDATAOBJECT * ppDataObjectPasted If not NULL, we must set this pointer to the multiselect snapin item indicating the objects which need a cut notification. * IConsole * ipConsole Pointer to the console interface. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScOnPaste(CBaseSnapin * pSnapin, CBaseSnapinItem * pitemTarget, LPDATAOBJECT lpDataObjectList, LPDATAOBJECT * ppDataObjectPasted, IConsole * ipConsole) { // Declarations
SC sc = S_OK; INT nIterator = 0; BOOL fAtLeastOnePaste = FALSE;
// Data validation
ASSERT(pSnapin); ASSERT(pitemTarget); ASSERT(lpDataObjectList); ASSERT(ipConsole); // other parameters can not be ASSERTed
// Allocate an array of booleans to indicate the correctly pasted items
ASSERT(PivSelectedItems()->size() > 0); m_pfPastedWithCut = new BOOL[PivSelectedItems()->size()]; if (!m_pfPastedWithCut) goto MemoryError; for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) m_pfPastedWithCut[nIterator] = FALSE;
// Iterate through all the objects and verify that they can be pasted
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) { // Local declarations
DWORD dwCanCopyCut = 0; BOOL fPasted = FALSE;
// Ask the item to copy the underlying object
sc = pitemTarget->ScOnPaste((*PivSelectedItems())[nIterator], ppDataObjectPasted ? TRUE : FALSE, &fPasted); if (sc) goto Error; if (fPasted) { // Remember we pasted at least one item
fAtLeastOnePaste = TRUE; }
// If this was a cut, we need to return to MMC the items that were pasted
// (do not delete the dropped item if we are just adding it to a policy)
if (fPasted && ppDataObjectPasted && !pitemTarget->FIsPolicy()) { // Remember the items which should receive a cut notification
m_pfPastedWithCut[nIterator] = TRUE; } }
// Indicate to MMC the pasted item
if (ppDataObjectPasted) { *ppDataObjectPasted = this; AddRef(); // reuse ourselves as the multiselect data object which indicates the pasted items
}
// Refresh
if (fAtLeastOnePaste) { sc = ipConsole->UpdateAllViews(static_cast<IDataObject *>(pitemTarget), 0, ONVIEWCHANGE_REFRESHCHILDREN); if (sc) goto Error; } Cleanup: return sc; MemoryError: sc = E_OUTOFMEMORY; goto Error; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScOnPaste()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScOnCutOrMove
* * PURPOSE: We are a multiselect snapin item which points to an array of booleans indicating which snapin items need a cut notification. * We must forward a cut notification to these items. * * PARAMETERS: * CBaseSnapin * psnapin Pointer to the snapin. * LPDATAOBJECT lpDataObjectList Pointer to the multiselect snapin item. * IConsoleNameSpace * ipConsoleNameSpace Pointer to the namespace console interface. * IConsole * ipConsole Pointer to the console interface. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScOnCutOrMove(CBaseSnapin * pSnapin, LPDATAOBJECT lpDataObjectList, IConsoleNameSpace * ipConsoleNameSpace, IConsole * ipConsole) { // Declarations
SC sc = S_OK; INT nIterator = 0;
// Data validation
ASSERT(pSnapin); ASSERT(lpDataObjectList); ASSERT(ipConsoleNameSpace); ASSERT(ipConsole);
// Iterate through the items and find those which need a cut notification
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) // start at index 1
{ // Get the next item
ASSERT((*PivSelectedItems())[nIterator]); ASSERT(m_pfPastedWithCut);
// Check if the item needs a cut notification
if (m_pfPastedWithCut[nIterator]) { // Call ScOnCutOrMove for each snapin item - pass pitem as an lpDataObjectList parameter
sc = pSnapin->ScOnCutOrMove((*PivSelectedItems())[nIterator], ipConsoleNameSpace, ipConsole); if (sc) goto Error; } }
Cleanup: return sc; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScOnCutOrMove()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScOnDelete
* * PURPOSE: Forward a delete notification to selected items. * * PARAMETERS: * CComponent * pComponent Pointer to the component. * LPDATAOBJECT lpDataObject Pointer to the multiselect snapin item. * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScOnDelete(CComponent * pComponent, LPDATAOBJECT lpDataObject) { // Declarations
SC sc = S_OK; INT nIterator = 0;
// Data validation
ASSERT(pComponent); ASSERT(lpDataObject);
// Iterate through the items and find those which need a cut notification
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) // start at index 1
{ // Get the next item
ASSERT((*PivSelectedItems())[nIterator]);
// Call ScOnDelete for each snapin item - pass pitem as an lpDataObject parameter
sc = pComponent->ScOnDelete((*PivSelectedItems())[nIterator]); if (sc) goto Error; }
Cleanup: return sc; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScOnDelete()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScExtractMultiSelectObjectFromCompositeMultiSelectObject
* * PURPOSE: On some notifications, MMC gives us a composite object made of multiselect object from different snapins. * This method's goal is to extract a multiselect object from the composite object. * * PARAMETERS: * CBaseSnapin * psnapin Snapin (used to determine which multiselect data object is the right one in the composite multiselect data object). * LPDATAOBJECT pDataObjectComposite Composite data object. * CBaseMultiSelectSnapinItem ** ppBaseMultiSelectSnapinItem Determined multiselect data object. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScExtractMultiSelectObjectFromCompositeMultiSelectObject(CBaseSnapin * psnapin, LPDATAOBJECT pDataObjectComposite, CBaseMultiSelectSnapinItem ** ppBaseMultiSelectSnapinItem) { // Declarations
SC sc = S_OK; STGMEDIUM stgmedium = {TYMED_HGLOBAL, NULL}; FORMATETC formatetc = {(CLIPFORMAT)s_cfMultiSelectSnapins, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; SMMCDataObjects * pSMMCDataObjects = NULL; CBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL; INT nIterator = 0;
// Data validation
ASSERT(pDataObjectComposite); ASSERT(ppBaseMultiSelectSnapinItem); ASSERT(!*ppBaseMultiSelectSnapinItem); ASSERT(psnapin);
// Retrieve data
sc = pDataObjectComposite->GetData(&formatetc, &stgmedium); if (sc) goto Error;
// Lock memory and cast
pSMMCDataObjects = (SMMCDataObjects *)(::GlobalLock(stgmedium.hGlobal)); ASSERT(pSMMCDataObjects);
// What we get here is a composite object made of multiselection sets of snapin items from the same snapin
// We have to locate the multiselection set for our snapin
ASSERT(pSMMCDataObjects->count > 0); for (nIterator=0; nIterator < pSMMCDataObjects->count; nIterator++) { // Local declarations
CLSID clsid;
// Get the class id for the dataobject
ASSERT(pSMMCDataObjects->lpDataObject[nIterator]); sc = ScGetClassID(pSMMCDataObjects->lpDataObject[nIterator], &clsid); if (sc) { // Ignore the error, probably this node does not belong to us
sc = S_OK; } else if (::IsEqualCLSID(clsid, *(psnapin->PclsidSnapin()))) { pBaseMultiSelectSnapinItem = dynamic_cast<CBaseMultiSelectSnapinItem *>(pSMMCDataObjects->lpDataObject[nIterator]); ASSERT(pBaseMultiSelectSnapinItem); break; } }
// Assign the result
ASSERT(pBaseMultiSelectSnapinItem); ASSERT(*(psnapin->PclsidSnapin()) == *(pBaseMultiSelectSnapinItem->PclsidSnapin())); *ppBaseMultiSelectSnapinItem = pBaseMultiSelectSnapinItem;
Cleanup: // Cleanup allocated resources
if (pSMMCDataObjects) ::GlobalUnlock(stgmedium.hGlobal); if (stgmedium.hGlobal) ::GlobalFree(stgmedium.hGlobal); return sc; Error: TraceError(_T("CBaseMultiSelectSnapinItem::ScExtractMultiSelectObjectFromCompositeMultiSelectObject()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScIsPastableDataObject
* * PURPOSE: Determine, using the multiselect object, if a paste operation is acceptable. * * PARAMETERS: * CBaseSnapin * psnapin Pointer to the snapin. * CBaseSnapinItem * pitemTarget Target item for the paste. * LPDATAOBJECT lpDataObjectList Pointer to the multiselect snapin item. * BOOL * pfPastable Must be set to TRUE if the paste operation is acceptable. * * RETURNS: * SC Execution code */ SC CBaseMultiSelectSnapinItem::ScIsPastableDataObject(CBaseSnapin * pSnapin, CBaseSnapinItem * pitemTarget, LPDATAOBJECT lpDataObjectList, BOOL * pfPastable) { // Declarations
SC sc = S_OK; INT nIterator = 0;
// Data validation
ASSERT(pSnapin); ASSERT(pitemTarget); ASSERT(lpDataObjectList); ASSERT(pfPastable);
// Go through each snapin items for the multiselect snapin items
for (nIterator=0; nIterator < PivSelectedItems()->size(); nIterator++) { // Check if we can paste - if any item can not be pasted, then cancel out
sc = pSnapin->ScIsPastableDataObject(pitemTarget, (*(PivSelectedItems()))[nIterator], pfPastable); if (sc) goto Error; if (!*pfPastable) { sc = S_FALSE; goto Cleanup; } }
Cleanup: return sc; Error: *pfPastable = FALSE; TraceError(_T("CBaseMultiSelectSnapinItem::ScIsPastableDataObject()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::MergeMenuItemsVectors
* * PURPOSE: Merges menu item vectors in a smart way. * If a menu item in the merge is not found in the list to add, then remove this item from the merge (one snapin item does not support the operation). * If a menu item in the merge is found in the list to add, logically combine the flags. * * PARAMETERS: * CSnapinContextMenuItemVectorWrapper * pcmivwForMerge Vector of menu items to merge - will contain the result of the merge * CSnapinContextMenuItemVectorWrapper * pcmivwToAdd Vector of menu items to merge - specify what to add here */ void CBaseMultiSelectSnapinItem::MergeMenuItems(CSnapinContextMenuItemVectorWrapper * pcmivwForMerge, CSnapinContextMenuItemVectorWrapper * pcmivwToAdd) { // Declarations
INT nIteratorToAdd = 0; INT nIteratorForMerge = 0;
// Data validation
ASSERT(pcmivwForMerge); ASSERT(pcmivwToAdd);
// Iterate through the merge
for (nIteratorForMerge=0; nIteratorForMerge < pcmivwForMerge->cmiv.size(); nIteratorForMerge++) { // Local declarations
BOOL fFound = FALSE; // if we are not able to find, then we must remove them menu item
// See if we can find in the add a menu item with the same command id and the same insertion point
for (nIteratorToAdd=0; nIteratorToAdd < pcmivwToAdd->cmiv.size(); nIteratorToAdd++) { if (((pcmivwToAdd->cmiv)[nIteratorToAdd]->cm.lCommandID == (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.lCommandID) && ((pcmivwToAdd->cmiv)[nIteratorToAdd]->cm.lInsertionPointID == (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.lInsertionPointID)) { // Set the new default
fFound = TRUE;
// Combine the flags in merge
(pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags = (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags | (pcmivwToAdd->cmiv)[nIteratorToAdd]->cm.fFlags; (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fSpecialFlags = (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fSpecialFlags | (pcmivwToAdd->cmiv)[nIteratorToAdd]->cm.fSpecialFlags;
// Handle flag combining exceptions: MF_ENABLED and MF_GRAYED -> MF_GRAYED
if ((pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags & (MF_ENABLED | MF_GRAYED)) (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags = (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags & ~MF_ENABLED;
// Handle flag combining exceptions: MF_MENUBARBREAK and MF_MENUBREAK -> MF_MENUBREAK
if ((pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags & (MF_MENUBARBREAK | MF_MENUBREAK)) (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags = (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags & ~MF_MENUBARBREAK;
// Handle flag combining exceptions: MF_CHECKED and MF_UNCHECKED -> MF_UNCHECKED
if ((pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags & (MF_CHECKED | MF_UNCHECKED)) (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags = (pcmivwForMerge->cmiv)[nIteratorForMerge]->cm.fFlags & ~MF_CHECKED;
// Stop now
break; } }
// If we were not able to find a menu item from merge in add, we have to remove this menu item because at least one snapin item does not advertise it
if (!fFound) { // Delete the pointed context menu item
if (pcmivwForMerge->cmiv[nIteratorForMerge]) { delete pcmivwForMerge->cmiv[nIteratorForMerge]; pcmivwForMerge->cmiv[nIteratorForMerge] = NULL; } pcmivwForMerge->cmiv.erase(&(pcmivwForMerge->cmiv[nIteratorForMerge])); nIteratorForMerge--; } } }
/* CBaseMultiSelectSnapinItem::ScGetMultiSelectDataObject
* * PURPOSE: Determines if an object is a multiselect data object and casts it. * * PARAMETERS: * LPDATAOBJECT lpDataObject Received data object to cast * CBaseMultiSelectSnapinItem ** ppMultiSelectSnapinItem Pointer to multiselect pointer - set to NULL we can not convert. * * RETURNS SC Execution code. */ SC CBaseMultiSelectSnapinItem::ScGetMultiSelectDataObject(LPDATAOBJECT lpDataObject, CBaseMultiSelectSnapinItem ** ppMultiSelectSnapinItem) { // Declarations
SC sc = S_OK; CLSID clsid;
// Data validation
ASSERT(lpDataObject); ASSERT(ppMultiSelectSnapinItem); ASSERT(!*ppMultiSelectSnapinItem);
// Use the clipboard format to identify the object type
sc = CBaseDataObject::ScGetNodeType(lpDataObject, &clsid); if (sc == SC(DV_E_FORMATETC) ) { SC scNoTrace = sc; sc.Clear(); return scNoTrace; }
if (sc) goto Error;
// If the node type is nodetypeBaseMultiSelect then we know we are a multiselect data object
if (::IsEqualCLSID(clsid, *(nodetypeBaseMultiSelect.PclsidNodeType()))) { ASSERT(dynamic_cast<CBaseSnapinItem *>(lpDataObject)); *ppMultiSelectSnapinItem = dynamic_cast<CBaseMultiSelectSnapinItem *>(lpDataObject); } else *ppMultiSelectSnapinItem = NULL;
Cleanup: return sc; Error: *ppMultiSelectSnapinItem = NULL; TraceError(_T("CBaseMultiSelectSnapinItem::ScGetMultiSelectDataObject()"), sc); goto Cleanup; }
/* CBaseMultiSelectSnapinItem::ScExtractMultiSelectDataObject
* * PURPOSE: Determines if an object is a composite data object and extracts the correct multiselect snapin item from it. * * PARAMETERS: * CBaseSnapin * psnapin Snapin (used to determine which multiselect data object is the right one in the composite multiselect data object). * LPDATAOBJECT lpDataObject Received data object to cast * CBaseMultiSelectSnapinItem ** ppMultiSelectSnapinItem Pointer to multiselect pointer - set to NULL we can not convert. * * RETURNS SC Execution code. */ SC CBaseMultiSelectSnapinItem::ScExtractMultiSelectDataObject(CBaseSnapin * psnapin, LPDATAOBJECT lpDataObject, CBaseMultiSelectSnapinItem ** ppMultiSelectSnapinItem) { // Declarations
SC sc = S_OK; STGMEDIUM stgmedium = {TYMED_HGLOBAL, NULL}; FORMATETC formatetc = {(CLIPFORMAT)s_cfCompositeDataObject, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; BOOL * pfCompositeDataObject = NULL;
// Data validation
ASSERT(lpDataObject); ASSERT(psnapin); ASSERT(ppMultiSelectSnapinItem); ASSERT(!*ppMultiSelectSnapinItem);
// Retrieve data
sc = lpDataObject->GetData(&formatetc, &stgmedium); if (sc) { sc = S_OK; // ignore the error, consider that we are not a composite data object
goto Cleanup; }
// Lock memory and cast
pfCompositeDataObject = (BOOL *)(::GlobalLock(stgmedium.hGlobal)); ASSERT(pfCompositeDataObject);
// If the object is a composite data object then extract the correct multiselect snapin item
if (*pfCompositeDataObject) { sc = ScExtractMultiSelectObjectFromCompositeMultiSelectObject(psnapin, lpDataObject, ppMultiSelectSnapinItem); if (sc) goto Error; }
Cleanup: // Cleanup allocated resources
if (pfCompositeDataObject) ::GlobalUnlock(stgmedium.hGlobal); if (stgmedium.hGlobal) ::GlobalFree(stgmedium.hGlobal); return sc; Error: *ppMultiSelectSnapinItem = NULL; TraceError(_T("CBaseMultiSelectSnapinItem::ScExtractMultiSelectDataObject()"), sc); goto Cleanup; }
|