Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2204 lines
81 KiB

/*
* 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;
}