1714 lines
59 KiB
C++
1714 lines
59 KiB
C++
/*
|
|
* basesnap.cxx
|
|
*
|
|
*
|
|
* Copyright (c) 1998-1999 Microsoft Corporation
|
|
*
|
|
* PURPOSE: Defines CBaseSnapin.
|
|
*
|
|
*
|
|
* OWNER: ptousig
|
|
*/
|
|
|
|
#include "headers.hxx"
|
|
|
|
#include "basesnap.rgs"
|
|
|
|
// Some substitution strings used to create a registry script on the fly.
|
|
#define szDLLName L"DLLName"
|
|
#define szModule L"Module"
|
|
#define szCLSID_Snapin L"CLSID_Snapin"
|
|
#define szCLSID_About L"CLSID_About"
|
|
#define szClassName L"ClassName"
|
|
#define szSnapinName L"SnapinName"
|
|
#define szCLSID_NodeType L"CLSID_NodeType"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
CBaseSnapin::CBaseSnapin(void)
|
|
{
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
CBaseSnapin::~CBaseSnapin(void)
|
|
{
|
|
// Release all of our root items.
|
|
while (m_ilRootItems.empty() == false)
|
|
{
|
|
static_cast<LPDATAOBJECT>(m_ilRootItems.back())->Release();
|
|
m_ilRootItems.pop_back();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// The simple version of Pitem() simply calls the full version.
|
|
//
|
|
CBaseSnapinItem *CBaseSnapin::Pitem(LPDATAOBJECT lpDataObject, HSCOPEITEM hscopeitem, long cookie)
|
|
{
|
|
return Pitem(NULL, NULL, lpDataObject, hscopeitem, cookie);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// The full version of Pitem() attempts to find an existing CBaseSnapinItem
|
|
// that matches the given parameters. If it can't find any, it will create
|
|
// one.
|
|
//
|
|
// Note: In order to create a new item, one of the two first parameters must
|
|
// be provided.
|
|
//
|
|
CBaseSnapinItem *CBaseSnapin::Pitem(
|
|
CComponentData * pComponentData,
|
|
CComponent * pComponent,
|
|
LPDATAOBJECT lpDataObject,
|
|
HSCOPEITEM hscopeitem,
|
|
long cookie)
|
|
{
|
|
SC sc;
|
|
CBaseSnapinItem *pitem = NULL;
|
|
ItemList::iterator iter;
|
|
|
|
// For debugging purposes, I don't want to modify the 'hscopeitem' parameter,
|
|
// so I make a copy of it.
|
|
HSCOPEITEM hscopeitem2 = hscopeitem;
|
|
|
|
if (cookie)
|
|
{
|
|
//
|
|
// We can simply cast the cookie into a CBaseSnapinItem *.
|
|
//
|
|
pitem = reinterpret_cast<CBaseSnapinItem *>(cookie);
|
|
ASSERT(dynamic_cast<CBaseSnapinItem *>(pitem));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (hscopeitem2 == 0 && lpDataObject == NULL)
|
|
{
|
|
//
|
|
// We are being asked for the stand-alone root.
|
|
//
|
|
ASSERT(pComponentData);
|
|
|
|
pitem = pComponentData->PitemStandaloneRoot();
|
|
if (pitem)
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (lpDataObject)
|
|
{
|
|
CLSID clsid;
|
|
|
|
//
|
|
// Are we the snapin who created this item ?
|
|
//
|
|
sc = CBaseDataObject::ScGetClassID(lpDataObject, &clsid);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
if (::IsEqualCLSID(*PclsidSnapin(), clsid))
|
|
{
|
|
//
|
|
// We created this item, we can simply cast the pointer
|
|
// to a CBaseSnapinItem.
|
|
//
|
|
pitem = dynamic_cast<CBaseSnapinItem *>(lpDataObject);
|
|
ASSERT(pitem);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (lpDataObject && hscopeitem2 == 0)
|
|
{
|
|
//
|
|
// We got an IDataObject *, but we were not given a HSCOPEITEM :-(
|
|
// We'll get it from the CF_EXCHANGE_ADMIN_HSCOPEITEM clipboard format.
|
|
//
|
|
sc = CBaseDataObject::ScGetAdminHscopeitem(lpDataObject, &hscopeitem2);
|
|
if (sc == DV_E_FORMATETC)
|
|
{
|
|
//
|
|
// We don't own this item, we were not given a HSCOPEITEM and it
|
|
// doesn't support CF_EXCHANGE_ADMIN_HSCOPEITEM.
|
|
//
|
|
// $REVIEW (ptousig) Does this ever happen ?
|
|
//
|
|
ASSERT("Does this ever happen ?" && FALSE);
|
|
sc = S_OK;
|
|
}
|
|
else if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
// If the user adds the snapin twice in the same console, we will
|
|
// be asked for two root items. If we are being asked for the same root
|
|
// twice, then the "if (hscopeitem2 == 0 && lpDataObject == NULL)" above
|
|
// will have caught it. If we get this far, it means we are being asked
|
|
// for another root item. So we want to search through our existing
|
|
// list of roots only if we have a HSCOPEITEM.
|
|
if (hscopeitem2)
|
|
{
|
|
//
|
|
// We are going to search through our list of existing root items
|
|
// to find one with this HSCOPEITEM.
|
|
// We can't really use a STL map here because the HSCOPEITEM of the items
|
|
// will change after the root is added to the list. Besides, we don't expect
|
|
// more than a handful of roots anyway.
|
|
//
|
|
for (iter = m_ilRootItems.begin(); iter != m_ilRootItems.end(); iter++)
|
|
{
|
|
if ((*iter)->Hscopeitem() == hscopeitem2)
|
|
{
|
|
pitem = *iter;
|
|
goto Cleanup; // We found it, stop looking
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we reach this point it's because we couldn't find this node.
|
|
// So we create a new one and append it to the end of the list.
|
|
//
|
|
sc = ScCreateRootItem(lpDataObject, hscopeitem2, &pitem);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
ASSERT(pitem);
|
|
|
|
//
|
|
// Initialize the new root
|
|
//
|
|
if (pComponentData)
|
|
pitem->SetComponentData(pComponentData);
|
|
|
|
sc = pitem->ScInit(this, NULL, 0, TRUE);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// Add this new item to our list of roots.
|
|
static_cast<LPDATAOBJECT>(pitem)->AddRef();
|
|
m_ilRootItems.push_back(pitem);
|
|
|
|
// If this is a standalone root, better tell the component data about it.
|
|
if (hscopeitem2 == 0 && lpDataObject == NULL)
|
|
{
|
|
ASSERT(pComponentData);
|
|
pComponentData->SetPitemStandaloneRoot(pitem);
|
|
}
|
|
|
|
if (lpDataObject)
|
|
{
|
|
BOOL fIsOwned = FALSE;
|
|
CNodeType *pnodetype = NULL;
|
|
|
|
// $REVIEW (ptousig) There's a better way of doing this.
|
|
// We know we don't own that node, but ScInitializeNamespaceExtension expects
|
|
// a CNodeType so we have to call this to get one.
|
|
sc = ScIsOwnedDataObject(lpDataObject, &fIsOwned, &pnodetype);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// Initialize the root item's code from the dataobject of the parent item.
|
|
pitem->SetIsGhostRoot(TRUE);
|
|
sc = pitem->ScInitializeNamespaceExtension(lpDataObject, hscopeitem2, pnodetype);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
// Make sure this item knows its HSCOPEITEM
|
|
if (hscopeitem2)
|
|
pitem->SetHscopeitem(hscopeitem2);
|
|
|
|
ASSERT(pitem);
|
|
return pitem;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::Pitem"), sc);
|
|
MMCErrorBox(sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Returns whether a dataobject is "owned" by this snapin. It does that
|
|
// by looking at the list of nodetypes this snapin says it can create, if
|
|
// this node is one of these, we assume we own it.
|
|
// As a side-effect, this function also returns the nodetype of the node.
|
|
//
|
|
// $REVIEW (ptousig) This is not an accurate test, we need to use the CCF_SNAPIN_CLASSID.
|
|
//
|
|
SC CBaseSnapin::ScIsOwnedDataObject(LPDATAOBJECT pdataobject, BOOL *pfIsOwned, CNodeType **ppnodetype)
|
|
{
|
|
SC sc;
|
|
BOOL fIsOwned = FALSE;
|
|
CNodeType * pnodetype = NULL;
|
|
CLSID clsid;
|
|
INT isnr = 0;
|
|
|
|
ASSERT(pdataobject);
|
|
ASSERT(pfIsOwned);
|
|
ASSERT(ppnodetype);
|
|
|
|
// Get the nodetype, in guid format, of the data object.
|
|
sc = CBaseDataObject::ScGetNodeType(pdataobject, &clsid);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
for (isnr = 0; isnr < Csnr(); isnr ++)
|
|
{
|
|
if (IsEqualCLSID(*(Psnr(isnr)->pnodetype->PclsidNodeType()), clsid))
|
|
{
|
|
// We found the CLSID, keep track of its nodetype.
|
|
pnodetype = Psnr(isnr)->pnodetype;
|
|
SNRTypes snrtypes = Psnr(isnr)->snrtypes;
|
|
|
|
// Do we enumerate nodes of this type? If so, we must be the owner.
|
|
if ((snrtypes & snrEnumSP) || (snrtypes & snrEnumRP) || (snrtypes & snrEnumSM))
|
|
fIsOwned = TRUE;
|
|
break; // exit the loop.
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
*pfIsOwned = fIsOwned;
|
|
*ppnodetype = pnodetype;
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScIsOwnedDataObject"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// -----------------------------------------------------------------------------
|
|
// Debug menu options
|
|
//
|
|
SnapinMenuItem CBaseSnapin::s_rgmenuitemBase[] =
|
|
{
|
|
{IDS_Test, IDS_Test, 0, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, 0, dwMenuNeverGray, dwMenuNeverChecked},
|
|
#if 0
|
|
{idsBarfTraces, idsBarfTracesStatusText, idmBarfTraces, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfClearDbgScreen, idsBarfClearDbgScreenStatusText, idmBarfClearDbgScreen, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfSCDescription, idsBarfSCDescriptionStatusText, idmBarfSCDescription, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{NULL, idsBarfSeparatorStatusText, idmBarfSeparator1, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, MF_SEPARATOR, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfSettings, idsBarfSettingsStatusText, idmBarfSettings, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfAll, idsBarfAllStatusText, idmBarfAll, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfMemoryChkpoint, idsBarfMemoryChkpointStatusText, idmBarfMemoryChkpoint, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfMemoryDiff, idsBarfMemoryDiffStatusText, idmBarfMemoryDiff, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfValidateMemory, idsBarfValidateMemoryStatusText, idmBarfValidateMemory, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfTotalMemAllocd, idsBarfTotalMemAllocdStatusText, idmBarfTotalMemAllocd, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{NULL, idsBarfSeparatorStatusText, idmBarfSeparator3, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, MF_SEPARATOR, dwMenuNeverGray, dwMenuNeverChecked},
|
|
{idsBarfDebugBreak, idsBarfDebugBreakStatusText, idmBarfDebugBreak, CCM_INSERTIONPOINTID_PRIMARY_TASK, NULL, dwMenuAlwaysEnable, dwMenuNeverGray, dwMenuNeverChecked},
|
|
#endif
|
|
};
|
|
|
|
INT CBaseSnapin::s_cMenuItemBase = CMENUITEM(s_rgmenuitemBase);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
SnapinMenuItem *CBaseSnapin::PmenuitemBase(void)
|
|
{
|
|
return s_rgmenuitemBase;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
INT CBaseSnapin::CMenuItemBase(void)
|
|
{
|
|
return s_cMenuItemBase;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// This table allows us to map a MMC verb to a specific bit in a dword.
|
|
//
|
|
VerbMap CBaseSnapin::s_rgverbmap[] =
|
|
{
|
|
{ vmOpen, MMC_VERB_OPEN},
|
|
{ vmCopy, MMC_VERB_COPY},
|
|
{ vmPaste, MMC_VERB_PASTE},
|
|
{ vmDelete, MMC_VERB_DELETE},
|
|
{ vmProperties, MMC_VERB_PROPERTIES},
|
|
{ vmRename, MMC_VERB_RENAME},
|
|
{ vmRefresh, MMC_VERB_REFRESH},
|
|
{ vmPrint, MMC_VERB_PRINT},
|
|
{ vmCut, MMC_VERB_CUT},
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Accesses a given entry in the VerbMap table.
|
|
//
|
|
VerbMap *CBaseSnapin::Pverbmap(INT i)
|
|
{
|
|
ASSERT(i>=0 && i<Cverbmap());
|
|
return &(s_rgverbmap[i]);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Returns the number of entries in the VerbMap table.
|
|
//
|
|
INT CBaseSnapin::Cverbmap(void)
|
|
{
|
|
return(sizeof(s_rgverbmap) / sizeof(VerbMap));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Has the icon information been initialized ?
|
|
BOOL CBaseSnapin::s_fBaseSnapinInitialized = FALSE;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// The bitmaps containing all the icons
|
|
WTL::CBitmap CBaseSnapin::s_bmpImage16;
|
|
WTL::CBitmap CBaseSnapin::s_bmpImage32;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// The Registrar
|
|
//
|
|
CRegistrar CBaseSnapin::s_registrar;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Initializes the global bitmaps (once) as well as the per-snapin bitmaps.
|
|
//
|
|
SC CBaseSnapin::ScInitBitmaps(void)
|
|
{
|
|
DECLARE_SC(sc, _T("CBaseSnapin::ScInitBitmaps"));
|
|
|
|
// Once for the whole app...
|
|
if (s_fBaseSnapinInitialized == FALSE)
|
|
{
|
|
sc = BmpImage16().LoadBitmap(IDB_NODES16) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = BmpImage32().LoadBitmap(IDB_NODES32) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
|
|
s_fBaseSnapinInitialized = TRUE;
|
|
}
|
|
|
|
if (BitmapSmall().IsNull())
|
|
{
|
|
sc = BitmapSmall().LoadBitmap(IDB_NODES16) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
if (BitmapLarge().IsNull())
|
|
{
|
|
sc = BitmapLarge().LoadBitmap(IDB_NODES32) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
if (BitmapStaticSmall().IsNull())
|
|
{
|
|
sc = BitmapStaticSmall().LoadBitmap(IDB_FOLDER16) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
if (BitmapStaticSmallOpen().IsNull())
|
|
{
|
|
sc = BitmapStaticSmallOpen().LoadBitmap(IDB_FOLDER16OP) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
if (BitmapStaticLarge().IsNull())
|
|
{
|
|
sc = BitmapStaticLarge().LoadBitmap(IDB_FOLDER32) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
inline HBITMAP CopyBitmap (HBITMAP hbm)
|
|
{
|
|
return ((HBITMAP) CopyImage ((HANDLE) hbm, IMAGE_BITMAP, 0, 0, 0));
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// MMC wants icons to persist in the .msc file.
|
|
//
|
|
SC CBaseSnapin::ScGetStaticFolderImage(HBITMAP *phSmallImage, HBITMAP *phSmallImageOpen, HBITMAP *phLargeImage, COLORREF *pcMask)
|
|
{
|
|
ASSERT(phSmallImage && phSmallImageOpen && phLargeImage && pcMask);
|
|
|
|
*phSmallImage = CopyBitmap (BitmapStaticSmall());
|
|
*phSmallImageOpen = CopyBitmap (BitmapStaticSmallOpen());
|
|
*phLargeImage = CopyBitmap (BitmapStaticLarge());
|
|
*pcMask = RGB (255, 0, 255);
|
|
return S_OK;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Compares two fields for sorting by MMC. This applies to regular result items, i.e. not to virtual
|
|
// list snapin items. Our snapins can override the compare and impact the sort. We use a smart compare
|
|
// to guess if the field is numeric. If so, we perform a numeric compare.
|
|
// Otherwise we use case insensitive string compare.
|
|
//
|
|
// On input, *pnResult contains the column number to compare.
|
|
// On output, *pnResult contains the result of our comparison:
|
|
// -1 means A is smaller than B
|
|
// 0 means A is equal to B
|
|
// 1 means A is greater than B
|
|
//
|
|
SC CBaseSnapin::ScCompare(MMC_COOKIE cookieA, MMC_COOKIE cookieB, INT nColumn, INT * pnResult)
|
|
{
|
|
// Declarations
|
|
SC sc; // execution code
|
|
CBaseSnapinItem * pitemA = NULL; // snapin item A
|
|
CBaseSnapinItem * pitemB = NULL; // snapin item B
|
|
tstring strBufferA; // field A
|
|
tstring strBufferB; // field B
|
|
LONG lValueA = 0; // numeric value for field A
|
|
LONG lValueB = 0; // numeric value for field B
|
|
|
|
// Validate data
|
|
ASSERT(pnResult);
|
|
|
|
// Check that these cookies are not special cookies
|
|
ASSERT(IS_SPECIAL_COOKIE(cookieA) == FALSE);
|
|
ASSERT(IS_SPECIAL_COOKIE(cookieB) == FALSE);
|
|
|
|
// Cast the cookies into snapin items
|
|
pitemA = reinterpret_cast<CBaseSnapinItem *>(cookieA);
|
|
pitemB = reinterpret_cast<CBaseSnapinItem *>(cookieB);
|
|
|
|
// Get the fields from the snapin items
|
|
sc = pitemA->ScGetField(pitemA->PcolinfoexDisplay(nColumn)->Dat(), strBufferA);
|
|
if (sc)
|
|
goto Error;
|
|
sc = pitemB->ScGetField(pitemB->PcolinfoexDisplay(nColumn)->Dat(), strBufferB);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
ASSERT(FALSE && "Use Dat and compare data type properly");
|
|
|
|
// Use the default string compare (case-insensitive)
|
|
*pnResult = _tcsicmp(strBufferA.data(), strBufferB.data());
|
|
if (*pnResult < 0)
|
|
*pnResult = -1; // string A < string B
|
|
else if (*pnResult > 0)
|
|
*pnResult = 1;
|
|
else
|
|
*pnResult = 0;
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScCompare()"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* CBaseMultiSelectSnapinItem::ScCreateMultiSelectionDataObject
|
|
*
|
|
* PURPOSE: Creates a multiselect data object - we store a list of selected object in this special object.
|
|
*
|
|
* PARAMETERS:
|
|
* LPDATAOBJECT * ppDataObject Pointer to a pointer to the multiselect snapin item to create.
|
|
* CComponent * pComponent Pointer to the component object.
|
|
*
|
|
* RETURNS:
|
|
* SC Execution code
|
|
*/
|
|
SC
|
|
CBaseSnapin::ScCreateMultiSelectionDataObject(LPDATAOBJECT * ppDataObject, CComponent * pComponent)
|
|
{
|
|
// Declarations
|
|
SC sc ; // execution code
|
|
HRESULT hr = S_FALSE; // local execution code
|
|
RESULTDATAITEM rdi; // a selected item
|
|
BOOL fFoundASelection = FALSE; // did we find at least one selected item
|
|
CBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL; // multiselect dataobject
|
|
// are we processing the first object of the selected object set
|
|
// Data validation
|
|
ASSERT(ppDataObject);
|
|
ASSERT(*ppDataObject == NULL);
|
|
ASSERT(pComponent);
|
|
|
|
// Allocate a typed multiselection data object
|
|
sc = ScAllocateMultiSelectionDataObject(&pBaseMultiSelectSnapinItem);
|
|
if (sc)
|
|
goto Error;
|
|
ASSERT(pBaseMultiSelectSnapinItem);
|
|
|
|
// Assign the snapin - important for some clipboard formats
|
|
pBaseMultiSelectSnapinItem->SetSnapin(this);
|
|
|
|
// Identify the selected items
|
|
::ZeroMemory(&rdi, sizeof(rdi));
|
|
rdi.nIndex = -1; // first item requested
|
|
rdi.nState = LVIS_SELECTED; // only the selected items requested
|
|
rdi.mask = RDI_STATE | RDI_INDEX | RDI_PARAM; // state, cookie and index
|
|
|
|
// Get the result data interface
|
|
ASSERT(pComponent->IpResultData()); // verify we have an IResultData interface
|
|
while (S_OK == (hr = pComponent->IpResultData()->GetNextItem(&rdi)))
|
|
{
|
|
// Local declarations
|
|
CBaseSnapinItem * pitem = NULL;
|
|
|
|
// Make sure we got a cookie for the item
|
|
ASSERT(rdi.lParam);
|
|
pitem = reinterpret_cast<CBaseSnapinItem *>(rdi.lParam);
|
|
ASSERT(pitem);
|
|
|
|
// Add the item to the list managed by our multiselect data object
|
|
ASSERT(pBaseMultiSelectSnapinItem->PivSelectedItems());
|
|
pBaseMultiSelectSnapinItem->PivSelectedItems()->push_back(pitem);
|
|
|
|
// Remember we found a selected object
|
|
fFoundASelection = TRUE;
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
sc = hr;
|
|
goto Error;
|
|
}
|
|
|
|
// Let the component it is in multiselect mode
|
|
*(pComponent->PpMultiSelectSnapinItem()) = pBaseMultiSelectSnapinItem;
|
|
|
|
// Make sure we found at least one selected object, otherwise we should never have been called in the first place
|
|
ASSERT(fFoundASelection);
|
|
|
|
// Set the result
|
|
*ppDataObject = pBaseMultiSelectSnapinItem;
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
if (pBaseMultiSelectSnapinItem)
|
|
delete pBaseMultiSelectSnapinItem;
|
|
pBaseMultiSelectSnapinItem = NULL;
|
|
|
|
TraceError(_T("CBaseSnapin::ScCreateMultiSelectionDataObject()"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* CBaseMultiSelectSnapinItem::ScAllocateMultiSelectionDataObject
|
|
*
|
|
* PURPOSE: Allocates a multiselect data object - we store a list of selected object in this special object.
|
|
*
|
|
* PARAMETERS:
|
|
* CBaseMultiSelectSnapinItem ** ppBaseMultiSelectSnapinItem Pointer to a pointer to the multiselect snapin item to allocate.
|
|
*
|
|
* RETURNS:
|
|
* SC Execution code
|
|
*/
|
|
SC
|
|
CBaseSnapin::ScAllocateMultiSelectionDataObject(CBaseMultiSelectSnapinItem ** ppBaseMultiSelectSnapinItem)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
t_itemBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL; // create multiselect snapin item
|
|
|
|
// Data validation
|
|
ASSERT(ppBaseMultiSelectSnapinItem);
|
|
ASSERT(!*ppBaseMultiSelectSnapinItem);
|
|
|
|
// Allocate the object
|
|
sc = ScCreateItemQuick(&pBaseMultiSelectSnapinItem);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// Assign the result
|
|
*ppBaseMultiSelectSnapinItem = pBaseMultiSelectSnapinItem;
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScAllocateMultiSelectionDataObject()"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Returns the version information string for the snapin.
|
|
// The output string must be allocated with CoTaskMemAlloc.
|
|
//
|
|
SC CBaseSnapin::ScGetSnapinVersion(LPOLESTR *lpVersion)
|
|
{
|
|
DECLARE_SC(sc, _T("CBaseSnapin::ScGetSnapinVersion"));
|
|
sc = ScCheckPointers(lpVersion);
|
|
if (sc)
|
|
return sc;
|
|
|
|
*lpVersion = CoTaskDupString(L"");
|
|
if ((*lpVersion) == NULL)
|
|
return (E_OUTOFMEMORY);
|
|
|
|
return sc;
|
|
}
|
|
|
|
SC CBaseSnapin::ScGetProvider(LPOLESTR *lpName)
|
|
{
|
|
DECLARE_SC(sc, _T("CBaseSnapin::ScGetProvider"));
|
|
sc = ScCheckPointers(lpName);
|
|
if (sc)
|
|
return sc;
|
|
|
|
*lpName = CoTaskDupString(L"Microsoft");
|
|
if ((*lpName) == NULL)
|
|
return (E_OUTOFMEMORY);
|
|
|
|
return sc;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Creates an ATL Registrar script for the snapin and registers/unregisters it.
|
|
// If fRegister is TRUE then we are registering, FALSE we are unregistering.
|
|
//
|
|
SC CBaseSnapin::ScRegister(BOOL fRegister)
|
|
{
|
|
SC sc ;
|
|
HRESULT hr = S_OK;
|
|
INT i = 0;
|
|
const INT cchMaxLen = 256;
|
|
TCHAR szFileName[cchMaxLen];
|
|
CStr cstrTemp;
|
|
|
|
tstring strVersion;
|
|
const INT cchMaxRegScript = 10000;
|
|
tstring strRegScript;
|
|
tstring strFmtSnapinRegScript;
|
|
tstring strSnapinAboutRegScript;
|
|
tstring strSnapinNodeTypes;
|
|
tstring strExtensionScript;
|
|
tstring strPropertySheetScript;
|
|
tstring strToolBarScript;
|
|
tstring strNameSpaceScript;
|
|
tstring strContextMenuScript;
|
|
tstring strTemp;
|
|
tstring strSnapinName;
|
|
tstring strStandalone;
|
|
|
|
|
|
// Hacks for version, snapin name.
|
|
|
|
// Version number eg 6.3523.0.0
|
|
strVersion = _T("6.3523.0.0");
|
|
|
|
strSnapinAboutRegScript = szSnapinAboutRegScript + strVersion;
|
|
cstrTemp.Format(szSnapinAboutRegScript, strVersion.data());
|
|
strSnapinAboutRegScript = cstrTemp;
|
|
|
|
strSnapinName.LoadString(_Module.GetModuleInstance(), IdsName());
|
|
// strSnapinName = _T("Sample Framework Snapin");
|
|
|
|
GetModuleFileName(_Module.GetModuleInstance(), szFileName, cchMaxLen);
|
|
|
|
// Create the Standalone key only if we're a standalone snapin.
|
|
if (FStandalone())
|
|
strStandalone = szStandalone;
|
|
|
|
// Format all the extension node stuff.
|
|
for (i = 0; i<Csnr(); i++)
|
|
{
|
|
tstring strNodeTypeName;
|
|
CNodeType *pnodetype = Psnr()[i].pnodetype;
|
|
SNRTypes snrtypes = Psnr()[i].snrtypes;
|
|
|
|
// Get the name of the node type
|
|
if (! pnodetype->StrName().empty())
|
|
{
|
|
strTemp = pnodetype->StrName();
|
|
strNodeTypeName = pnodetype->StrClsidNodeType();
|
|
strNodeTypeName += _T(" = s '");
|
|
strNodeTypeName += pnodetype->StrName();
|
|
strNodeTypeName += _T("'");
|
|
}
|
|
else
|
|
strNodeTypeName = pnodetype->StrClsidNodeType();
|
|
|
|
// Add the ID of the node to the NodeTypes key if we enumerate it.
|
|
tstring strSnapinNodeTypesTemp;
|
|
|
|
if ( (snrtypes & snrEnumSP) || (snrtypes & snrEnumRP) || (snrtypes & snrEnumSM))
|
|
{
|
|
// Add the opening brace the first time around.
|
|
if (strSnapinNodeTypes.empty())
|
|
strSnapinNodeTypes = szSnapinNodeTypeOpen;
|
|
|
|
cstrTemp.Format(szFmtSnapinNodeType, pnodetype->StrClsidNodeType().data());
|
|
strSnapinNodeTypesTemp = cstrTemp;
|
|
strSnapinNodeTypes += strSnapinNodeTypesTemp;
|
|
}
|
|
|
|
// Needed because this is a for loop.
|
|
strContextMenuScript = _T("");
|
|
|
|
// Menu extensions
|
|
if ( snrtypes & snrExtCM)
|
|
{
|
|
cstrTemp.Format(szfmtSingleExtension, szSingleExtension);
|
|
strContextMenuScript = cstrTemp;
|
|
}
|
|
|
|
// Property Page extensions
|
|
strPropertySheetScript = _T("");
|
|
if ( snrtypes & snrExtPS)
|
|
{
|
|
cstrTemp.Format(szfmtSingleExtension, szSingleExtension);
|
|
strPropertySheetScript = cstrTemp;
|
|
}
|
|
|
|
// Toolbar extensions
|
|
strToolBarScript = _T("");
|
|
if ( snrtypes & snrExtTB)
|
|
{
|
|
cstrTemp.Format(szfmtSingleExtension, szSingleExtension);
|
|
strToolBarScript = cstrTemp;
|
|
}
|
|
|
|
// Namespace extensions
|
|
strNameSpaceScript = _T("");
|
|
if ( snrtypes & snrExtNS)
|
|
{
|
|
cstrTemp.Format(szfmtSingleExtension, szSingleExtension);
|
|
strNameSpaceScript = cstrTemp;
|
|
}
|
|
|
|
cstrTemp.Format(szfmtAllExtensions, strNodeTypeName.data(), strNameSpaceScript.data() , strContextMenuScript.data() , strPropertySheetScript.data() , strToolBarScript.data());
|
|
|
|
strExtensionScript += cstrTemp;
|
|
}
|
|
|
|
// Add a closing brace to the NodeTypes key if needed
|
|
if (! strSnapinNodeTypes.empty())
|
|
strSnapinNodeTypes += szSnapinNodeTypeClose;
|
|
|
|
// Need to concatenate these to form the real fmt string!
|
|
strFmtSnapinRegScript = szfmtSnapinRegScript1;
|
|
strFmtSnapinRegScript += szfmtSnapinRegScript2;
|
|
|
|
cstrTemp.Format(strFmtSnapinRegScript.data(), strVersion.data(), strVersion.data(), strStandalone.data(), strSnapinNodeTypes.data(), strExtensionScript.data());
|
|
|
|
strRegScript = strSnapinAboutRegScript;
|
|
strRegScript += cstrTemp;
|
|
|
|
USES_CONVERSION;
|
|
|
|
// Set all the replacement parameter values.
|
|
// $REVIEW (ptousig) DLLName is always set to Exadmin !!!
|
|
sc = Registrar().ClearReplacements( );
|
|
sc = Registrar().AddReplacement(szDLLName, L"Snapins");
|
|
sc = Registrar().AddReplacement(szModule, T2COLE(szFileName));
|
|
sc = Registrar().AddReplacement(szCLSID_Snapin, T2COLE(StrClsidSnapin().data()));
|
|
sc = Registrar().AddReplacement(szCLSID_About, T2COLE(StrClsidAbout().data()));
|
|
sc = Registrar().AddReplacement(szClassName, T2COLE(StrClassName().data()));
|
|
sc = Registrar().AddReplacement(szSnapinName, T2COLE(strSnapinName.data()));
|
|
sc = Registrar().AddReplacement(szCLSID_NodeType, L"");
|
|
|
|
if (fRegister)
|
|
{
|
|
LPCOLESTR lpOleStr = T2COLE(strRegScript.data());
|
|
sc = Registrar().StringRegister(lpOleStr);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
hr = Registrar().StringUnregister(strRegScript.data());
|
|
if (hr == DISP_E_EXCEPTION)
|
|
{
|
|
//
|
|
// When trying to unregister a snapin that wasn't registered in
|
|
// the first place, the Registrar returns a DISP_E_EXCEPTION.
|
|
// I don't know why, seems to be a bug in the Registrar. Our
|
|
// solution for now is to ignore the error, not very clean but
|
|
// effective. We are un-registering a snapin that wasn't registered
|
|
// anyway.
|
|
//
|
|
hr = S_OK;
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
//
|
|
// Some other error occured
|
|
//
|
|
sc = hr;
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScRegister"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
#if 0
|
|
#ifdef _DEBUG
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Displays the Traces menu (in debug only)
|
|
//
|
|
SC CBaseSnapin::ScOnMenuTraces(void)
|
|
{
|
|
DoTraceDialog();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Provides a description of a given SC Code (in debug only)
|
|
//
|
|
SC CBaseSnapin::ScOnMenuSCDescription(void)
|
|
{
|
|
CStr strPrompt(_T("Enter the Status Code (sc)"));
|
|
CSTR (strAnswer, 10);
|
|
CAskStringDialog dlg;
|
|
ID id;
|
|
SC sc;
|
|
|
|
dlg.SetPrompt(&strPrompt);
|
|
dlg.SetAnswer(&strAnswer);
|
|
strAnswer.BlankString();
|
|
id = dlg.IdModal();
|
|
if (id != IDOK)
|
|
return(sc = S_OK);
|
|
|
|
//
|
|
// Convert the text the user entered into a SC.
|
|
//
|
|
sc = (SC) strAnswer.strtoul(NULL, 16);
|
|
MbbErrorBox(sc);
|
|
return sc;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Debug stuff (in debug only)
|
|
//
|
|
SC CBaseSnapin::ScOnMenuMemoryDiff(void)
|
|
{
|
|
#ifdef USE_BARFMEM
|
|
CBaseWaitCursor wc;
|
|
CBarfMemory::DumpMarked();
|
|
#endif // USE_BARFMEM
|
|
return S_OK;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Validates Memory (in debug only)
|
|
//
|
|
SC CBaseSnapin::ScOnMenuValidateMemory(void)
|
|
{
|
|
#ifdef USE_BARFMEM
|
|
CBaseWaitCursor wc;
|
|
//Ensure output even if tagMemoryCorruption not turned on
|
|
Trace(&tagAlways, _T("CBaseSnapin::ScOnMenuValidateMemory() - Validating memory ..."));
|
|
ValidateMemory(&tagAlways);
|
|
Trace(&tagAlways, _T("CBaseSnapin::ScOnMenuValidateMemory() - ... Done validating memory."));
|
|
#endif // USE_BARFMEM
|
|
return S_OK;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Displays Dialog Box of Total Memory Allocated (in debug only)
|
|
//
|
|
SC CBaseSnapin::ScOnMenuTotalMemory(void)
|
|
{
|
|
#ifdef USE_BARFMEM
|
|
INT nAlloc;
|
|
INT nBytes;
|
|
CSTR (str, cchMaxLine);
|
|
CBaseWaitCursor wc;
|
|
|
|
TotalMemory(&nAlloc, &nBytes);
|
|
str.Format(_T("Memory currently allocated:\n\n%d allocations\n%d bytes"), nAlloc, nBytes);
|
|
MbbErrorBox(str.Sz(), MB_OK | MB_ICONINFORMATION);
|
|
#endif // USE_BARFMEM
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
#endif //#if 0
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Given two pointers to dataObjects, are they the same?
|
|
// Returns S_OK if objects A and B are the same, S_FALSE if they are different.
|
|
//
|
|
// $REVIEW (ptousig) Will there ever be a case where two different
|
|
// dataobjects should be considered equal ?
|
|
//
|
|
SC CBaseSnapin::ScCompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB)
|
|
{
|
|
SC sc = S_FALSE;
|
|
BOOL fOwnedA = FALSE;
|
|
BOOL fOwnedB = FALSE;
|
|
CNodeType *pnodetypeA = NULL;
|
|
CNodeType *pnodetypeB = NULL;
|
|
|
|
// Check if one of the pointers is NULL.
|
|
if (!lpDataObjectA || !lpDataObjectB)
|
|
{
|
|
// This happens when one of the objects is new.
|
|
// See bug 117170.
|
|
sc = S_FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (lpDataObjectA == lpDataObjectB)
|
|
{
|
|
// If both pointers are the same, then obviously they are
|
|
// the same object.
|
|
sc = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Do we own both dataobjects.
|
|
sc = ScIsOwnedDataObject(lpDataObjectA, &fOwnedA, &pnodetypeA);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
sc = ScIsOwnedDataObject(lpDataObjectB, &fOwnedB, &pnodetypeB);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
if (fOwnedA == FALSE || fOwnedB == FALSE)
|
|
{
|
|
// We don't own at least of the dataobjects. They are either
|
|
// different or we are not qualified to compare them.
|
|
sc = S_FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Since we own both dataobjects, and the pointers are different
|
|
// then we can conclude that they represent different objects.
|
|
sc = S_FALSE;
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScCompareObjects"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Handler of AddMenuItems we ask the item for its menu items. And in debug
|
|
// mode we add debug menu items if the item wants us to.
|
|
//
|
|
SC CBaseSnapin::ScAddMenuItems(LPDATAOBJECT lpDataObject, LPCONTEXTMENUCALLBACK ipContextMenuCallback, long *pInsertionAllowed)
|
|
{
|
|
// Declarations
|
|
SC sc;
|
|
CBaseSnapinItem * pitem = NULL;
|
|
CBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL;
|
|
|
|
// Data validation
|
|
ASSERT(lpDataObject);
|
|
ASSERT(ipContextMenuCallback);
|
|
ASSERT(pInsertionAllowed);
|
|
|
|
// See if we can extract the multi select data object from the composite data object
|
|
sc = CBaseMultiSelectSnapinItem::ScExtractMultiSelectDataObject(this, lpDataObject, &pBaseMultiSelectSnapinItem);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// If we actually had a composite data object and we were able to find our multiselect snapin item
|
|
if (pBaseMultiSelectSnapinItem)
|
|
{
|
|
// Call ScAddMenuItems for the multiselect object for menu merging
|
|
sc = pBaseMultiSelectSnapinItem->ScAddMenuItems(this, lpDataObject, ipContextMenuCallback, pInsertionAllowed);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
// Handle the normal case - PItem() does more work than a simple cast to verify that the snapin item belongs to the snapin etc.
|
|
pitem = Pitem(lpDataObject);
|
|
ASSERT(pitem);
|
|
|
|
sc = ScAddMenuItems(pitem, *pInsertionAllowed, ipContextMenuCallback, pitem->Pmenuitem(), pitem->CMenuItem());
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScAddMenuItems"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Constructor for CSnapinContextMenuItem
|
|
//
|
|
CSnapinContextMenuItem::CSnapinContextMenuItem(void)
|
|
{
|
|
// Initialize members
|
|
::ZeroMemory(&cm, sizeof(cm));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Destructor for CSnapinContextMenuItemVectorWrapper
|
|
//
|
|
CSnapinContextMenuItemVectorWrapper::~CSnapinContextMenuItemVectorWrapper(void)
|
|
{
|
|
// Declarations
|
|
INT nIterator = 0;
|
|
|
|
// Go through all the referenced CSnapinContextMenuItem objects and delete them
|
|
for (nIterator=0; nIterator < cmiv.size(); nIterator++)
|
|
{
|
|
if (cmiv[nIterator])
|
|
{
|
|
delete cmiv[nIterator];
|
|
cmiv[nIterator] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Adds menu items to the context menu.
|
|
//
|
|
SC
|
|
CBaseSnapin::ScAddMenuItems(CBaseSnapinItem * pitem, long lInsertionAllowed, LPCONTEXTMENUCALLBACK ipContextMenuCallback, SnapinMenuItem * rgmenuitem, INT cmenuitem)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
BOOL fAllowed = TRUE;
|
|
INT nIterator = 0;
|
|
|
|
// Data validation
|
|
ASSERT(pitem);
|
|
ASSERT(ipContextMenuCallback);
|
|
// ASSERT(rgmenuitem); sometimes there is no menu
|
|
|
|
// Go through the different menu items
|
|
for (nIterator=0; nIterator < cmenuitem; nIterator++)
|
|
{
|
|
// Local declarations
|
|
CSnapinContextMenuItem cmi;
|
|
|
|
// Get the menu item
|
|
sc = ScGetMenuItem(&cmi, pitem, &(rgmenuitem[nIterator]), &fAllowed, lInsertionAllowed);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// If the menu item is allowed, then add it
|
|
if (fAllowed)
|
|
{
|
|
sc = ipContextMenuCallback->AddItem(&(cmi.cm));
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScAddMenuItems"), sc);
|
|
goto Cleanup;
|
|
};
|
|
|
|
/* CBaseSnapin::ScGetMenuItem
|
|
*
|
|
* PURPOSE: Sets a menu item struct for a particular snapin item and for a particular source menu item of that snapin item.
|
|
*
|
|
* PARAMETERS:
|
|
* CSnapinContextMenuItem * pcmiReturned Menu item struct to set
|
|
* CBaseSnapinItem * pitem Snapin item from which the source menu item comes
|
|
* MenuItem * pMenuItemSource Source menu item
|
|
* BOOL * pfAllowed TRUE if the menu item allowed?
|
|
*
|
|
* RETURNS:
|
|
* SC Execution code
|
|
*/
|
|
SC
|
|
CBaseSnapin::ScGetMenuItem(CSnapinContextMenuItem * pcmiReturned, CBaseSnapinItem * pitem, SnapinMenuItem * pMenuItemSource, BOOL * pfAllowed, long lInsertionAllowed)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
LONG fFlags = 0;
|
|
DWORD dwFlagsMenuDisable = 0;
|
|
DWORD dwFlagsMenuGray = 0;
|
|
DWORD dwFlagsMenuChecked = 0;
|
|
|
|
// Data validation
|
|
ASSERT(pcmiReturned);
|
|
ASSERT(pitem);
|
|
ASSERT(pMenuItemSource);
|
|
ASSERT(pfAllowed);
|
|
|
|
// Set default
|
|
*pfAllowed = TRUE;
|
|
|
|
// Get the flags from the snapin item
|
|
dwFlagsMenuDisable = pitem->DwFlagsMenuDisable();
|
|
dwFlagsMenuGray = pitem->DwFlagsMenuGray();
|
|
dwFlagsMenuChecked = pitem->DwFlagsMenuChecked();
|
|
|
|
// Disabled means don't show at all. Not the same as MMC's MF_DISABLED.
|
|
if (dwFlagsMenuDisable & pMenuItemSource->dwFlagsDisable)
|
|
{
|
|
*pfAllowed = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Check if the menu item should be allowed
|
|
switch (pMenuItemSource->lInsertionPointID)
|
|
{
|
|
case CCM_INSERTIONPOINTID_PRIMARY_TOP:
|
|
*pfAllowed = (lInsertionAllowed & CCM_INSERTIONALLOWED_TOP) != 0;
|
|
break;
|
|
|
|
case CCM_INSERTIONPOINTID_PRIMARY_NEW:
|
|
case CCM_INSERTIONPOINTID_3RDPARTY_NEW:
|
|
*pfAllowed = ((lInsertionAllowed & CCM_INSERTIONALLOWED_NEW) != 0) && pitem->FHasComponentData();
|
|
break;
|
|
|
|
case CCM_INSERTIONPOINTID_PRIMARY_TASK:
|
|
case CCM_INSERTIONPOINTID_3RDPARTY_TASK:
|
|
*pfAllowed = (lInsertionAllowed & CCM_INSERTIONALLOWED_TASK) != 0;
|
|
break;
|
|
|
|
case CCM_INSERTIONPOINTID_PRIMARY_VIEW:
|
|
*pfAllowed = (lInsertionAllowed & CCM_INSERTIONALLOWED_VIEW) != 0;
|
|
break;
|
|
}
|
|
|
|
// If the menu item is not to be enabled, then discard
|
|
if (!*pfAllowed)
|
|
goto Cleanup;
|
|
|
|
// Set flags
|
|
if (dwFlagsMenuGray & pMenuItemSource->dwFlagsGray)
|
|
fFlags |= MF_GRAYED;
|
|
else
|
|
fFlags |= MF_ENABLED;
|
|
if (dwFlagsMenuChecked & pMenuItemSource->dwFlagsChecked)
|
|
fFlags |= MF_CHECKED;
|
|
else
|
|
fFlags |= MF_UNCHECKED;
|
|
|
|
// Set other parameters
|
|
if (pMenuItemSource->idsName)
|
|
pcmiReturned->strName.LoadString(_Module.GetResourceInstance(), pMenuItemSource->idsName);
|
|
if (pMenuItemSource->idsStatusBarText)
|
|
pcmiReturned->strStatusBarText.LoadString(_Module.GetResourceInstance(), pMenuItemSource->idsStatusBarText);
|
|
|
|
pcmiReturned->cm.strName = (LPWSTR)pcmiReturned->strName.data();
|
|
pcmiReturned->cm.strStatusBarText = (LPWSTR)pcmiReturned->strStatusBarText.data();
|
|
pcmiReturned->cm.lCommandID = pMenuItemSource->lCommandID;
|
|
pcmiReturned->cm.lInsertionPointID = pMenuItemSource->lInsertionPointID;
|
|
pcmiReturned->cm.fFlags = fFlags;
|
|
pcmiReturned->cm.fSpecialFlags = pMenuItemSource->fSpecialFlags;
|
|
|
|
Cleanup:
|
|
return sc;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Load the icon for the snapin and return it to MMC.
|
|
//
|
|
SC CBaseSnapin::ScGetSnapinImage(HICON *phAppIcon)
|
|
{
|
|
SC sc ;
|
|
|
|
ASSERT(phAppIcon);
|
|
|
|
if (phAppIcon == NULL)
|
|
{
|
|
sc = E_INVALIDARG;
|
|
goto Error;
|
|
}
|
|
|
|
if (Idi() == 0)
|
|
{
|
|
// There is no icon for this snapin
|
|
*phAppIcon = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*phAppIcon = ::LoadIcon(_Module.GetModuleInstance(), MAKEINTRESOURCE(Idi()));
|
|
if (*phAppIcon == NULL)
|
|
{
|
|
sc = ScFromWin32(GetLastError());
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScGetSnapinImage"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Release the given item, but only if it is one of the root items.
|
|
// Used during MMCN_REMOVE_CHILDREN. We are being told that the item
|
|
// that we are "ghosting" is either being destroyed or all of its children
|
|
// are being destroyed. In either case, we don't need the ghost item
|
|
// anymore.
|
|
//
|
|
SC CBaseSnapin::ScReleaseIfRootItem(CBaseSnapinItem *pitem)
|
|
{
|
|
SC sc ;
|
|
ItemList::iterator iter;
|
|
|
|
ASSERT(pitem);
|
|
|
|
for (iter = m_ilRootItems.begin(); iter != m_ilRootItems.end(); iter++)
|
|
{
|
|
if (pitem == (*iter))
|
|
{
|
|
m_ilRootItems.erase(iter);
|
|
static_cast<LPDATAOBJECT>(pitem)->Release();
|
|
break;
|
|
}
|
|
}
|
|
return sc;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Determines whether the specified dataobject is pastable.
|
|
//
|
|
SC
|
|
CBaseSnapin::ScIsPastableDataObject(CBaseSnapinItem * pitemTarget, LPDATAOBJECT lpDataObject, BOOL * pfPastable)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
BOOL fPastable = FALSE;
|
|
CNodeType * pnodetype = NULL;
|
|
CLSID clsid;
|
|
INT isnr = 0;
|
|
|
|
// Validate parameters
|
|
ASSERT(pitemTarget);
|
|
ASSERT(lpDataObject);
|
|
ASSERT(pfPastable);
|
|
|
|
// Is this an MMC node?
|
|
// Get the nodetype, in guid format, of the data object.
|
|
sc = CBaseDataObject::ScGetNodeType(lpDataObject, &clsid);
|
|
if (sc == DV_E_FORMATETC || sc == E_NOTIMPL)
|
|
{
|
|
// Not an MMC Node
|
|
fPastable = FALSE;
|
|
sc = S_FALSE; // override the execution code
|
|
goto Cleanup;
|
|
}
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// Verify that the snapin item class type is acceptable to the destination
|
|
for (isnr=0; isnr < Csnr(); isnr++)
|
|
{
|
|
// Find a class id match
|
|
if (IsEqualCLSID(*(Psnr(isnr)->pnodetype->PclsidNodeType()), clsid)) // found the CLSID
|
|
{
|
|
// SNR verification
|
|
SNRTypes snrtypes = Psnr(isnr)->snrtypes;
|
|
|
|
// $REVIEW (ptousig) So all nodes are pastable ? Sounds to me like
|
|
// snrEnumSP and snrEnumRP shouldn't be in here.
|
|
if ( (snrtypes & snrEnumSP) || (snrtypes & snrEnumRP) || (snrtypes & snrPaste))
|
|
fPastable = TRUE;
|
|
break; // exit the loop.
|
|
}
|
|
}
|
|
|
|
// If it seems we can paste, ask the target item if we can paste here
|
|
if (fPastable)
|
|
{
|
|
sc = pitemTarget->ScOnQueryPaste(lpDataObject, &fPastable);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
// Assign result
|
|
*pfPastable = fPastable;
|
|
return sc;
|
|
Error:
|
|
fPastable = FALSE;
|
|
TraceError(_T("CBaseSnapin::ScIsPastableDataObject()"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Find out which verb should be the default.
|
|
//
|
|
MMC_CONSOLE_VERB CBaseSnapin::MmcverbDefault(LPDATAOBJECT lpDataObject)
|
|
{
|
|
// Ask the item.
|
|
return Pitem(lpDataObject)->MmcverbDefault();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Find out the verbs that are allowed on this item.
|
|
//
|
|
SC CBaseSnapin::ScGetVerbs(LPDATAOBJECT lpDataObject, DWORD * pdwVerbs)
|
|
{
|
|
// Ask the item.
|
|
return Pitem(lpDataObject)->ScGetVerbs(pdwVerbs);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Returns the path to the help file for this snapin.
|
|
//
|
|
SC CBaseSnapin::ScGetHelpTopic(tstring& strCompiledHelpFile)
|
|
{
|
|
DECLARE_SC(sc, _T("CBaseSnapin::ScGetHelpTopic"));
|
|
DWORD dwLen = 0;
|
|
const int cchMaxLen = 256;
|
|
TCHAR szFileName[cchMaxLen];
|
|
|
|
//
|
|
// Get the full path to the current module
|
|
//
|
|
dwLen = ::GetModuleFileName(_Module.GetModuleInstance() , szFileName, cchMaxLen);
|
|
if (dwLen == 0)
|
|
return (ScFromWin32(GetLastError()));
|
|
|
|
strCompiledHelpFile = szFileName;
|
|
//
|
|
// Replace the extension with .CHM
|
|
//
|
|
int nDotPos = strCompiledHelpFile.rfind(_T("."));
|
|
strCompiledHelpFile.erase(nDotPos);
|
|
strCompiledHelpFile += _T(".CHM");
|
|
|
|
return sc;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// We are being told that something has changed on the item pointed
|
|
// by lParam.
|
|
//
|
|
SC CBaseSnapin::ScOnPropertyChange(BOOL fScope, LPARAM lParam, IConsoleNameSpace *ipConsoleNameSpace, IConsole *ipConsole)
|
|
{
|
|
SC sc ;
|
|
LPDATAOBJECT pdataobject = NULL;
|
|
CBaseSnapinItem * pitem = NULL;
|
|
|
|
pdataobject = reinterpret_cast<LPDATAOBJECT>(lParam);
|
|
ASSERT(pdataobject);
|
|
|
|
pitem = Pitem(pdataobject);
|
|
ASSERT(pitem);
|
|
|
|
sc = pitem->ScOnPropertyChange();
|
|
if (sc)
|
|
goto Error;
|
|
|
|
if (pitem->FIsContainer())
|
|
{
|
|
// Container items should be updated just once.
|
|
sc = pitem->ScUpdateScopeItem(ipConsoleNameSpace);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
// Result item. Need to update the item in all views.
|
|
// Call out to all the views to update themselves.
|
|
sc = ipConsole->UpdateAllViews(pdataobject, 0, ONVIEWCHANGE_UPDATERESULTITEM);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScOnPropertyChange"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// The user has changed the name of an item.
|
|
//
|
|
SC CBaseSnapin::ScOnRename(LPDATAOBJECT lpDataObject, const tstring& strNewName, IConsole *ipConsole)
|
|
{
|
|
SC sc= S_FALSE;
|
|
CBaseSnapinItem *pitem = NULL;
|
|
|
|
ASSERT(lpDataObject);
|
|
|
|
pitem = Pitem(lpDataObject);
|
|
ASSERT(pitem);
|
|
|
|
if (strNewName.empty())
|
|
{
|
|
// ScOnQueryPaste should prevent this from ever happening
|
|
sc = E_UNEXPECTED;
|
|
goto Error;
|
|
}
|
|
|
|
if (strNewName.length() == 0)
|
|
goto Cleanup;
|
|
|
|
// Tell the object to rename
|
|
// If it returns S_FALSE, the rename was not done.
|
|
sc = pitem->ScOnRename(strNewName);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
if (sc == S_OK)
|
|
{
|
|
// If this was renamed, reload our children
|
|
sc = ipConsole->UpdateAllViews(static_cast<IDataObject *>(pitem), 0, ONVIEWCHANGE_REFRESHCHILDREN);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScOnRename"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Received on a paste command.
|
|
//
|
|
// lpDataObject is the node receiving the past command.
|
|
// lpDataObjectList are the nodes being pasted.
|
|
// ppDataObjectPasted is where we answer the list of nodes that were successfully pasted.
|
|
//
|
|
SC CBaseSnapin::ScOnPaste(LPDATAOBJECT lpDataObject, LPDATAOBJECT lpDataObjectList, LPDATAOBJECT * ppDataObjectPasted, IConsole * ipConsole)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
CBaseSnapinItem * pitemTarget = NULL;
|
|
DWORD dwCanCopyCut = 0;
|
|
BOOL fPasted = FALSE;
|
|
CBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL;
|
|
|
|
// Handle special case
|
|
if (!lpDataObjectList)
|
|
{
|
|
// ScOnQueryPaste should prevent this from ever happening
|
|
sc = E_UNEXPECTED;
|
|
goto Error;
|
|
}
|
|
|
|
// Data validation
|
|
ASSERT(lpDataObject);
|
|
ASSERT(lpDataObjectList);
|
|
ASSERT(ipConsole);
|
|
// other parameters can not be ASSERTed
|
|
|
|
// Get the target item
|
|
pitemTarget = Pitem(lpDataObject);
|
|
ASSERT(pitemTarget);
|
|
|
|
// Determine if this is a multiselect data object
|
|
sc = CBaseMultiSelectSnapinItem::ScGetMultiSelectDataObject(lpDataObjectList, &pBaseMultiSelectSnapinItem);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// If we received a multiselect snapin item
|
|
if (pBaseMultiSelectSnapinItem)
|
|
{
|
|
// Call ScOnPaste for the multiselect object for dispatch
|
|
sc = pBaseMultiSelectSnapinItem->ScOnPaste(this, pitemTarget, lpDataObjectList, ppDataObjectPasted, ipConsole);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
// Ask the item to copy the underlying object
|
|
sc = pitemTarget->ScOnPaste(lpDataObjectList, ppDataObjectPasted ? TRUE : FALSE, &fPasted);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// If the object was pasted
|
|
if (fPasted)
|
|
{
|
|
// 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 (ppDataObjectPasted && !pitemTarget->FIsPolicy())
|
|
{
|
|
*ppDataObjectPasted = lpDataObjectList;
|
|
(*ppDataObjectPasted)->AddRef();
|
|
}
|
|
|
|
// Reload our children
|
|
sc = ipConsole->UpdateAllViews(static_cast<IDataObject *>(pitemTarget), 0, ONVIEWCHANGE_REFRESHCHILDREN);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScOnPaste"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// We need to figure out whether we will allow pasting of this object.
|
|
// lpDataObject is the target object.
|
|
// lpDataObjectList are the objects being pasted.
|
|
//
|
|
SC CBaseSnapin::ScOnQueryPaste(LPDATAOBJECT lpDataObject, LPDATAOBJECT lpDataObjectList, LPDWORD pdwFlags)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
CBaseSnapinItem * pitemTarget = NULL;
|
|
BOOL fCanPaste = FALSE;
|
|
CNodeType * pnodetype = NULL;
|
|
CBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL;
|
|
|
|
// Data validation
|
|
ASSERT(lpDataObject);
|
|
ASSERT(lpDataObjectList);
|
|
ASSERT(pdwFlags);
|
|
|
|
// Get the target item
|
|
pitemTarget = Pitem(lpDataObject);
|
|
ASSERT(pitemTarget);
|
|
|
|
// Determine if this is a multiselect data object
|
|
// Determine if this is a multiselect data object
|
|
sc = CBaseMultiSelectSnapinItem::ScGetMultiSelectDataObject(lpDataObjectList, &pBaseMultiSelectSnapinItem);
|
|
if (sc == SC(DV_E_FORMATETC) )
|
|
{
|
|
sc = S_FALSE; // Cant paste.
|
|
return sc;
|
|
}
|
|
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// If we received a multiselect snapin item
|
|
if (pBaseMultiSelectSnapinItem)
|
|
{
|
|
// Call ScOnCutOrMove for the multiselect object for dispatch
|
|
sc = pBaseMultiSelectSnapinItem->ScIsPastableDataObject(this, pitemTarget, lpDataObjectList, &fCanPaste);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
// Determine if the parse operation is acceptable
|
|
// Here lpDataObjectList is only one item
|
|
sc = ScIsPastableDataObject(pitemTarget, lpDataObjectList, &fCanPaste);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
// Determine if we can paste
|
|
if (!fCanPaste)
|
|
sc = S_FALSE; // indicate no pasting
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScOnQueryPaste()"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// We need to figure out whether we can handle the given dataobject that is from
|
|
// different process.
|
|
// lpDataObject is the target object.
|
|
// lpDataObjectList are the objects being pasted.
|
|
//
|
|
SC CBaseSnapin::ScOnCanPasteOutOfProcDataObject(LPBOOL pbCanHandle)
|
|
{
|
|
// Declarations
|
|
DECLARE_SC(sc, TEXT("CBaseSnapin::ScOnCanPasteOutOfProcDataObject"));
|
|
sc = ScCheckPointers(pbCanHandle);
|
|
if (sc)
|
|
return sc;
|
|
|
|
*pbCanHandle = TRUE;
|
|
|
|
return sc;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
SC CBaseSnapin::ScOnCutOrMove(LPDATAOBJECT lpDataObjectList, IConsoleNameSpace * ipConsoleNameSpace, IConsole * ipConsole)
|
|
{
|
|
// Declarations
|
|
SC sc ;
|
|
CBaseSnapinItem * pitem = NULL;
|
|
CBaseMultiSelectSnapinItem * pBaseMultiSelectSnapinItem = NULL;
|
|
|
|
// Data validation
|
|
ASSERT(lpDataObjectList);
|
|
ASSERT(ipConsoleNameSpace);
|
|
ASSERT(ipConsole);
|
|
|
|
// Determine if this is a multiselect data object
|
|
sc = CBaseMultiSelectSnapinItem::ScGetMultiSelectDataObject(lpDataObjectList, &pBaseMultiSelectSnapinItem);
|
|
if (sc)
|
|
goto Error;
|
|
|
|
// If we received a multiselect snapin item
|
|
if (pBaseMultiSelectSnapinItem)
|
|
{
|
|
// Call ScOnCutOrMove for the multiselect object for dispatch
|
|
sc = pBaseMultiSelectSnapinItem->ScOnCutOrMove(this, lpDataObjectList, ipConsoleNameSpace, ipConsole);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
else
|
|
{
|
|
// Handle the normal case - PItem() does more work than a simple cast to verify that the snapin item belongs to the snapin etc.
|
|
pitem = Pitem(lpDataObjectList);
|
|
ASSERT(pitem);
|
|
|
|
// Ask the item to delete the underlying object.
|
|
sc = pitem->ScOnCutOrMove();
|
|
if (sc)
|
|
goto Error;
|
|
if (sc == S_FALSE)
|
|
goto Cleanup;
|
|
|
|
if (pitem->FIsContainer())
|
|
{
|
|
// Container items need to be deleted from the document
|
|
// Delete the item and everything below it.
|
|
if (pitem->Hscopeitem())
|
|
{
|
|
sc = ipConsoleNameSpace->DeleteItem(pitem->Hscopeitem(), TRUE);
|
|
if (sc)
|
|
goto Error;
|
|
pitem->SetHscopeitem(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Leaf items need to be deleted from the views.
|
|
sc = ipConsole->UpdateAllViews(lpDataObjectList, 0, ONVIEWCHANGE_DELETESINGLEITEM);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
// Delete this item, MMC is still using it. Below ScDeleteSubTree
|
|
// will release the object so Addref it. We got this through
|
|
// the Pitem() call which just type cast the dataobject.
|
|
pitem->AddRef();
|
|
sc = pitem->ScDeleteSubTree(TRUE);
|
|
if (sc)
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
return sc;
|
|
Error:
|
|
TraceError(_T("CBaseSnapin::ScOnCutOrMove"), sc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// This method allows a CComponentData to tell us it is being
|
|
// destroyed. Any item referring to this CD as its owner will
|
|
// have their owner pointer nulled.
|
|
SC CBaseSnapin::ScOwnerDying(CComponentData *pComponentData)
|
|
{
|
|
ItemList::iterator iter;
|
|
for (iter = m_ilRootItems.begin(); iter != m_ilRootItems.end(); iter++)
|
|
{
|
|
if ((*iter)->FHasComponentData() && (*iter)->PComponentData() == pComponentData)
|
|
(*iter)->SetComponentData(NULL);
|
|
}
|
|
return S_OK;
|
|
}
|