1902 lines
51 KiB
C++
1902 lines
51 KiB
C++
//____________________________________________________________________________
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
//
|
|
// File: SnapIn.cpp
|
|
//
|
|
// Contents:
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 9/11/1996 RaviR Created
|
|
//
|
|
//____________________________________________________________________________
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "util.h"
|
|
#include "NodeMgr.h"
|
|
#include "regutil.h"
|
|
#include "regkeyex.h"
|
|
#include "tstring.h"
|
|
#include "about.h"
|
|
#include "bitmap.h"
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/*
|
|
* define our own Win64 symbol to make it easy to include 64-bit only
|
|
* code in the 32-bit build, so we can exercise some code on 32-bit Windows
|
|
* where the debuggers are better
|
|
*/
|
|
#ifdef _WIN64
|
|
#define MMC_WIN64
|
|
#endif
|
|
|
|
#ifdef MMC_WIN64
|
|
#include "wow64reg.h" // for REG_OPTION_OPEN_32BITKEY
|
|
#endif
|
|
|
|
#ifdef DBG
|
|
#ifdef MMC_WIN64
|
|
CTraceTag tagSnapinAnalysis64 (_T("64/32-bit interop"), _T("Snap-in analysis"));
|
|
CTraceTag tagVerboseSnapinAnalysis64 (_T("64/32-bit interop"), _T("Snap-in analysis (verbose)"));
|
|
#endif // MMC_WIN64
|
|
#endif
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* tstringFromCLSID
|
|
*
|
|
* Returns the text representation of a CLSID in a tstring.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
tstring tstringFromCLSID (REFCLSID clsid)
|
|
{
|
|
WCHAR wzCLSID[40];
|
|
int nChars = StringFromGUID2 (clsid, wzCLSID, countof(wzCLSID));
|
|
if (nChars == 0)
|
|
return tstring();
|
|
|
|
USES_CONVERSION;
|
|
return (W2T (wzCLSID));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* GetModuleVersion
|
|
*
|
|
* Reads the version resource in a module and returns the version string.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
DWORD GetModuleVersion (LPCTSTR pszModule, LPTSTR pszBuffer)
|
|
{
|
|
static bool fAttemptedVersionDllLoad = false;
|
|
static DWORD (APIENTRY* pfnGetFileVersionInfoSize)(LPCTSTR, LPDWORD) = NULL;
|
|
static BOOL (APIENTRY* pfnGetFileVersionInfo)(LPCTSTR, DWORD, DWORD, LPVOID) = NULL;
|
|
static BOOL (APIENTRY* pfnVerQueryValue)(LPBYTE, LPCTSTR, LPVOID*, PUINT) = NULL;
|
|
|
|
if (!fAttemptedVersionDllLoad)
|
|
{
|
|
/*
|
|
* only try once
|
|
*/
|
|
fAttemptedVersionDllLoad = true;
|
|
|
|
HINSTANCE hinst = LoadLibrary (_T("version.dll"));
|
|
|
|
if (hinst != NULL)
|
|
{
|
|
#ifdef UNICODE
|
|
(FARPROC&)pfnGetFileVersionInfoSize = GetProcAddress (hinst, "GetFileVersionInfoSizeW");
|
|
(FARPROC&)pfnGetFileVersionInfo = GetProcAddress (hinst, "GetFileVersionInfoW");
|
|
(FARPROC&)pfnVerQueryValue = GetProcAddress (hinst, "VerQueryValueW");
|
|
#else
|
|
(FARPROC&)pfnGetFileVersionInfoSize = GetProcAddress (hinst, "GetFileVersionInfoSizeA");
|
|
(FARPROC&)pfnGetFileVersionInfo = GetProcAddress (hinst, "GetFileVersionInfoA");
|
|
(FARPROC&)pfnVerQueryValue = GetProcAddress (hinst, "VerQueryValueA");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
*pszBuffer = 0;
|
|
|
|
if (pfnGetFileVersionInfoSize != NULL)
|
|
{
|
|
ASSERT (pfnGetFileVersionInfo != NULL);
|
|
ASSERT (pfnVerQueryValue != NULL);
|
|
|
|
ULONG lUnused;
|
|
DWORD cbVerInfo = pfnGetFileVersionInfoSize (pszModule, &lUnused);
|
|
|
|
if (cbVerInfo > 0)
|
|
{
|
|
LPBYTE pbVerInfo = new BYTE[cbVerInfo];
|
|
VS_FIXEDFILEINFO* pffi;
|
|
|
|
if (pfnGetFileVersionInfo != NULL && pfnVerQueryValue != NULL &&
|
|
pfnGetFileVersionInfo (pszModule, NULL, cbVerInfo, pbVerInfo) &&
|
|
pfnVerQueryValue (pbVerInfo, _T("\\"), (void**) &pffi, (UINT*)&lUnused))
|
|
{
|
|
wsprintf (pszBuffer, _T("%d.%d.%d.%d"),
|
|
HIWORD (pffi->dwFileVersionMS),
|
|
LOWORD (pffi->dwFileVersionMS),
|
|
HIWORD (pffi->dwFileVersionLS),
|
|
LOWORD (pffi->dwFileVersionLS));
|
|
}
|
|
|
|
delete[] pbVerInfo;
|
|
}
|
|
}
|
|
|
|
return (lstrlen (pszBuffer));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* SafeWriteProfileString
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
inline void SafeWritePrivateProfileString (
|
|
LPCTSTR pszSection,
|
|
LPCTSTR pszKey,
|
|
LPCTSTR psz,
|
|
LPCTSTR pszFile)
|
|
{
|
|
if (!WritePrivateProfileString (pszSection, pszKey, psz, pszFile))
|
|
THROW_ON_FAIL (HRESULT_FROM_WIN32 (GetLastError()));
|
|
}
|
|
|
|
|
|
//____________________________________________________________________________
|
|
//
|
|
// Member: CSnapIn::CSnapIn, Constructor
|
|
//
|
|
// History: 9/19/1996 RaviR Created
|
|
//____________________________________________________________________________
|
|
//
|
|
|
|
// {E6DFFF74-6FE7-11d0-B509-00C04FD9080A}
|
|
const GUID IID_CSnapIn =
|
|
{ 0xe6dfff74, 0x6fe7, 0x11d0, { 0xb5, 0x9, 0x0, 0xc0, 0x4f, 0xd9, 0x8, 0xa } };
|
|
|
|
// {7A85B79C-BDED-11d1-A4FA-00C04FB6DD2C}
|
|
static const GUID GUID_EnableAllExtensions =
|
|
{ 0x7a85b79c, 0xbded, 0x11d1, { 0xa4, 0xfa, 0x0, 0xc0, 0x4f, 0xb6, 0xdd, 0x2c } };
|
|
|
|
DEBUG_DECLARE_INSTANCE_COUNTER(CSnapIn);
|
|
|
|
CSnapIn::CSnapIn()
|
|
:m_pExtSI(NULL), m_dwFlags(SNAPIN_ENABLE_ALL_EXTS), m_ExtPersistor(*this)
|
|
{
|
|
TRACE_CONSTRUCTOR(CSnapIn);
|
|
|
|
#ifdef DBG
|
|
dbg_cRef = 0;
|
|
#endif
|
|
|
|
DEBUG_INCREMENT_INSTANCE_COUNTER(CSnapIn);
|
|
}
|
|
|
|
|
|
CSnapIn::~CSnapIn()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::~CSnapIn"));
|
|
|
|
Dbg(DEB_USER1, _T("CSnapIn::~CSnapIn\n"));
|
|
|
|
sc = ScDestroyExtensionList();
|
|
if (sc)
|
|
{
|
|
}
|
|
|
|
#ifdef DBG
|
|
ASSERT(dbg_cRef <= 0);
|
|
#endif
|
|
|
|
DEBUG_DECREMENT_INSTANCE_COUNTER(CSnapIn);
|
|
}
|
|
|
|
|
|
DEBUG_DECLARE_INSTANCE_COUNTER(CExtSI);
|
|
|
|
CExtSI::CExtSI(CSnapIn* pSnapIn)
|
|
: m_pSnapIn(pSnapIn), m_pNext(NULL), m_dwFlags(0)
|
|
{
|
|
ASSERT(pSnapIn != NULL);
|
|
m_pSnapIn->AddRef();
|
|
|
|
DEBUG_INCREMENT_INSTANCE_COUNTER(CExtSI);
|
|
}
|
|
|
|
CExtSI::~CExtSI(void)
|
|
{
|
|
SAFE_RELEASE(m_pSnapIn);
|
|
delete m_pNext;
|
|
|
|
DEBUG_DECREMENT_INSTANCE_COUNTER(CExtSI);
|
|
}
|
|
|
|
|
|
CSnapInsCache::CSnapInsCache()
|
|
: m_bIsDirty(FALSE), m_bUpdateHelpColl(false)
|
|
{
|
|
}
|
|
|
|
CSnapInsCache::~CSnapInsCache()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::~CSnapInsCache"));
|
|
|
|
// destruction will remove all snapins, but ask them to release extensions first,
|
|
// this will break all circular references (else such snapins objects will be leaked).
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
// get pointer to the snapin
|
|
CSnapIn* pSnapIn = it->second;
|
|
sc = ScCheckPointers( pSnapIn, E_UNEXPECTED );
|
|
if (sc)
|
|
{
|
|
sc.TraceAndClear();
|
|
continue;
|
|
}
|
|
|
|
// ask snapin to destroy extension list
|
|
sc = pSnapIn->ScDestroyExtensionList();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::ScIsDirty
|
|
*
|
|
* PURPOSE: returns dirty status of Snapin Cache
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code [SC(S_OK) - if dirty, SC(S_FALSE) else]
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapInsCache::ScIsDirty()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::ScIsDirty"));
|
|
|
|
TraceDirtyFlag(TEXT("CSnapInsCache"), m_bIsDirty);
|
|
sc = m_bIsDirty ? SC(S_OK) : SC(S_FALSE);
|
|
|
|
return sc;
|
|
}
|
|
|
|
void CSnapInsCache::SetDirty(BOOL bIsDirty)
|
|
{
|
|
m_bIsDirty = bIsDirty;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::Purge
|
|
*
|
|
* PURPOSE: Cleanup Snapin Cache by usage information
|
|
* Uses sphisticated algorithm to find out which snapins are not used
|
|
* see ScMarkExternallyReferencedSnapins() for description
|
|
* removes snapins which are not referenced externally
|
|
*
|
|
* PARAMETERS:
|
|
* BOOL bExtensionsOnly
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
void CSnapInsCache::Purge(BOOL bExtensionsOnly)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::Purge"));
|
|
|
|
int iSnapIn;
|
|
|
|
// Delete all extensions marked as deleted
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
CSnapIn* pSnapIn = it->second;
|
|
ASSERT(pSnapIn != NULL);
|
|
if (it->second != NULL)
|
|
it->second->PurgeExtensions();
|
|
}
|
|
|
|
if (bExtensionsOnly)
|
|
return;
|
|
|
|
// Delete all snapins that have no external references
|
|
sc = ScMarkExternallyReferencedSnapins();
|
|
if (sc)
|
|
return; // error occured - do not remove anything
|
|
|
|
// remove not referenced
|
|
for ( it = m_snapins.begin(); it != m_snapins.end(); )
|
|
{
|
|
CSnapIn *pSnapin = it->second;
|
|
sc = ScCheckPointers( pSnapin, E_UNEXPECTED );
|
|
if (sc)
|
|
return;
|
|
|
|
bool bExternallyReferenced;
|
|
sc = pSnapin->ScTempState_IsExternallyReferenced( bExternallyReferenced );
|
|
if (sc)
|
|
return;
|
|
|
|
if ( !bExternallyReferenced )
|
|
{
|
|
// destory extension list - it will break all circular references if such exist
|
|
// (note- extension list is not needed anymore - snapin is not used anyway)
|
|
sc = pSnapin->ScDestroyExtensionList();
|
|
if (sc)
|
|
return;
|
|
|
|
// remove snapin from the cache;
|
|
// in combination with the call above this will delete the object.
|
|
it = m_snapins.erase( it );
|
|
}
|
|
else
|
|
{
|
|
++it; // go to the next snapin
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::ScMarkExternallyReferencedSnapins
|
|
*
|
|
* PURPOSE: Marks all snapins in cache according to presence of external references
|
|
* This is done by following algorithm:
|
|
* 1) For each snapin in the cache, all extensions have a temporary reference
|
|
* count incremented. Thus, at the end of this step, each snapin's temp
|
|
* ref count is equal to the number of snapins it extends.
|
|
|
|
2) Each snapin compares the temp reference count to the total number of
|
|
references to it, taking into account the fact that the snapin cache
|
|
itself holds a reference to each snapin. If the total references exceed
|
|
the temp references, this indicates that the snapin has one or more
|
|
external references to it.
|
|
* Such a snapin is marked as "Externally referenced" as well as all its
|
|
* extensions.
|
|
*
|
|
* At the end of the process each snapin has a boolean flag indicating if
|
|
* the snapin is externally referenced. This flag is used in subsequential cache cleanup,
|
|
* or help topic building operation.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapInsCache::ScMarkExternallyReferencedSnapins()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::ScMarkExternallyReferencedSnapins"));
|
|
|
|
// 1. reset the reference calculation data
|
|
for ( map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it )
|
|
{
|
|
CSnapIn *pSnapin = it->second;
|
|
sc = ScCheckPointers( pSnapin, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = pSnapin->ScTempState_ResetReferenceCalculationData();
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
// 2. update internal reference counts
|
|
for ( it = m_snapins.begin(); it != m_snapins.end(); ++it )
|
|
{
|
|
CSnapIn *pSnapin = it->second;
|
|
sc = ScCheckPointers( pSnapin, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = pSnapin->ScTempState_UpdateInternalReferenceCounts();
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
// now the snapins which have more references than internal ones do clearly
|
|
// have direct external references
|
|
// we can mark them and their extensions as "referenced"
|
|
|
|
// 3. mark snapins with external references
|
|
// Note: this step must occur after step 2 completes for ALL snapins.
|
|
for ( it = m_snapins.begin(); it != m_snapins.end(); ++it )
|
|
{
|
|
CSnapIn *pSnapin = it->second;
|
|
sc = ScCheckPointers( pSnapin, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = pSnapin->ScTempState_MarkIfExternallyReferenced();
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::ScGetSnapIn
|
|
*
|
|
* PURPOSE: either finds the snapin in cache , either creates the new one
|
|
*
|
|
* PARAMETERS:
|
|
* REFCLSID rclsid - class id of snapin
|
|
* CSnapIn** ppSnapIn - result
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapInsCache::ScGetSnapIn(REFCLSID rclsid, CSnapIn** ppSnapIn)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::ScGetSnapIn"));
|
|
|
|
// first - parameter check
|
|
sc = ScCheckPointers(ppSnapIn);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// second - initialization
|
|
*ppSnapIn = NULL;
|
|
|
|
//
|
|
// See if it already exists.
|
|
//
|
|
sc = ScFindSnapIn(rclsid, ppSnapIn);
|
|
if (!sc.IsError())
|
|
return sc; // jus return OK if we have it
|
|
|
|
//
|
|
// Create a new one & cache it
|
|
//
|
|
try
|
|
{
|
|
// Allocate the object
|
|
CComObject<CSnapIn> *pSnapin = NULL;
|
|
sc = CComObject<CSnapIn>::CreateInstance(&pSnapin);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// be sure we didn't get the NULL
|
|
sc = ScCheckPointers(pSnapin, E_UNEXPECTED);
|
|
if (sc)
|
|
return sc;
|
|
|
|
CSnapInPtr spSnapin = pSnapin;
|
|
|
|
|
|
// Copy the object impl clsid
|
|
spSnapin->SetSnapInCLSID(rclsid);
|
|
|
|
//
|
|
// Cache the object.
|
|
//
|
|
// note - this insertion also AddRef's the pointer
|
|
m_snapins[rclsid] = spSnapin;
|
|
|
|
*ppSnapIn = spSnapin.Detach(); // transfer reference to caller
|
|
}
|
|
catch( std::bad_alloc )
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
#ifdef DBG
|
|
void CSnapInsCache::DebugDump(void)
|
|
{
|
|
TRACE(_T("===========Dump of SnapinsCache ===============\n"));
|
|
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
OLECHAR strGUID[64];
|
|
|
|
CSnapIn* pSnapIn = it->second;
|
|
|
|
StringFromGUID2(pSnapIn->GetSnapInCLSID(), strGUID, countof(strGUID));
|
|
#ifdef DBG
|
|
TRACE(_T("%s: RefCnt = %d, %s\n"), strGUID, pSnapIn->m_dwRef,
|
|
pSnapIn->HasNameSpaceChanged() ? _T("NameSpace changed") : _T("No change"));
|
|
#endif
|
|
|
|
CExtSI* pExt = pSnapIn->GetExtensionSnapIn();
|
|
while (pExt != NULL)
|
|
{
|
|
StringFromGUID2(pExt->GetSnapIn()->GetSnapInCLSID(), strGUID, countof(strGUID));
|
|
#ifdef DBG
|
|
// TODO: go to registry to see the type of extension:
|
|
// these flags are not updated consistently
|
|
TRACE(_T(" %s: %s%s Extends(%s%s%s%s)\n"), strGUID,
|
|
pExt->IsNew() ? _T("New ") : _T(""),
|
|
pExt->IsMarkedForDeletion() ? _T("Deleted ") : _T(""),
|
|
pExt->ExtendsNameSpace() ? _T("NameSpace ") : _T(""),
|
|
pExt->ExtendsContextMenu() ? _T("Menu ") : _T(""),
|
|
pExt->ExtendsToolBar() ? _T("ToolBar ") : _T(""),
|
|
pExt->ExtendsPropertySheet() ? _T("Properties") : _T(""),
|
|
pExt->ExtendsView() ? _T("View") : _T(""),
|
|
pExt->ExtendsTask() ? _T("Task") : _T("")
|
|
);
|
|
#endif
|
|
|
|
pExt = pExt->Next();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::ScFindSnapIn
|
|
*
|
|
* PURPOSE: finds the snapin by class id and returns AddRef'ed pointer
|
|
*
|
|
* PARAMETERS:
|
|
* REFCLSID rclsid - class id of the snapin
|
|
* CSnapIn** ppSnapIn - resulting pointer
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapInsCache::ScFindSnapIn(REFCLSID rclsid, CSnapIn** ppSnapIn)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::ScFindSnapIn"));
|
|
|
|
// first - parameter check
|
|
sc = ScCheckPointers(ppSnapIn);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// second - initialization
|
|
*ppSnapIn = NULL;
|
|
|
|
// and now wee will se if we have one
|
|
map_t::iterator it = m_snapins.find(rclsid);
|
|
|
|
if (it == m_snapins.end())
|
|
return E_FAIL; // not assigning to sc, since it's not really an error condition
|
|
|
|
// be sure we do not return the NULL
|
|
sc = ScCheckPointers(it->second, E_UNEXPECTED);
|
|
if (sc)
|
|
return sc;
|
|
|
|
*ppSnapIn = it->second;
|
|
(*ppSnapIn)->AddRef();
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
#ifdef TEMP_SNAPIN_MGRS_WORK
|
|
// Get all extensions.
|
|
void CSnapInsCache::GetAllExtensions(CSnapIn* pSI)
|
|
{
|
|
if (!pSI)
|
|
return;
|
|
|
|
CExtensionsCache extnsCache;
|
|
HRESULT hr = MMCGetExtensionsForSnapIn(pSI->GetSnapInCLSID(), extnsCache);
|
|
ASSERT(SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
CExtensionsCacheIterator it(extnsCache);
|
|
|
|
for (; it.IsEnd() == FALSE; it.Advance())
|
|
{
|
|
CSnapInPtr spSITemp;
|
|
hr = GetSnapIn(it.GetKey(), &spSITemp);
|
|
ASSERT(SUCCEEDED(hr));
|
|
pSI->AddExtension(spSITemp);
|
|
}
|
|
}
|
|
#endif // TEMP_SNAPIN_MGRS_WORK
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::ScSave
|
|
*
|
|
* PURPOSE: saves contents of Snapin Cache to IStream
|
|
*
|
|
* PARAMETERS:
|
|
* IStream* pStream - save to this stream
|
|
* BOOL bClearDirty - reset dirty flag after save
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapInsCache::ScSave(IStream* pStream, BOOL bClearDirty)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::ScSave"));
|
|
|
|
// check the params
|
|
sc = ScCheckPointers(pStream);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// iterate ans save all snapins
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
CSnapIn* pSnapIn = it->second;
|
|
ASSERT(pSnapIn != NULL);
|
|
if (pSnapIn != NULL)
|
|
{
|
|
sc = pSnapIn->Save(pStream, bClearDirty);
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
}
|
|
|
|
// terminating marker
|
|
ULONG bytesWritten;
|
|
sc = pStream->Write(&GUID_NULL, sizeof(GUID_NULL), &bytesWritten);
|
|
if (sc)
|
|
return sc;
|
|
|
|
ASSERT(bytesWritten == sizeof(GUID_NULL));
|
|
|
|
if (bClearDirty)
|
|
SetDirty(FALSE);
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CSnapInsCache::Persist
|
|
*
|
|
* PURPOSE: Persists the CSnapInsCache to the specified persistor.
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CSnapInsCache::Persist(CPersistor& persistor)
|
|
{
|
|
if (persistor.IsStoring())
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
CSnapIn* pSnapIn = it->second;
|
|
ASSERT(pSnapIn != NULL);
|
|
if (pSnapIn != NULL)
|
|
persistor.Persist(*pSnapIn);
|
|
}
|
|
else
|
|
{
|
|
XMLListCollectionBase::Persist(persistor);
|
|
SetDirty(FALSE);
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CSnapInsCache::OnNewElement
|
|
*
|
|
* PURPOSE: called for each saved instance found in XML file.
|
|
* creates and uploads new snapin entry
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CSnapInsCache::OnNewElement(CPersistor& persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::OnNewElement"));
|
|
ASSERT(persistor.IsLoading());
|
|
|
|
CLSID clsid;
|
|
CPersistor persistorSnapin(persistor, XML_TAG_SNAPIN);
|
|
persistor.PersistAttribute(XML_ATTR_SNAPIN_CLSID, clsid);
|
|
|
|
// create and upload snapin
|
|
CSnapInPtr spSnapIn;
|
|
sc = ScGetSnapIn(clsid, &spSnapIn);
|
|
if (sc) // failed to creatre
|
|
sc.Throw();
|
|
if (spSnapIn != NULL)
|
|
spSnapIn->PersistLoad(persistor,this);
|
|
else // OK reported, pointer still NULL
|
|
sc.Throw(E_POINTER);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapInsCache::ScLoad
|
|
*
|
|
* PURPOSE: loads snapin cache from IStream
|
|
*
|
|
* PARAMETERS:
|
|
* IStream* pStream - stream to load from
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapInsCache::ScLoad(IStream* pStream)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapInsCache::ScLoad"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(pStream);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// loop thru saved snapins...
|
|
do
|
|
{
|
|
CLSID clsid;
|
|
ULONG bytesRead;
|
|
sc = pStream->Read(&clsid, sizeof(clsid), &bytesRead);
|
|
if (sc)
|
|
return sc;
|
|
|
|
ASSERT(bytesRead == sizeof(clsid));
|
|
|
|
// ... until special marker is found
|
|
if (clsid == GUID_NULL)
|
|
{
|
|
SetDirty(FALSE);
|
|
return S_OK;
|
|
}
|
|
|
|
// creale new snapin
|
|
CSnapInPtr spSnapIn;
|
|
sc = ScGetSnapIn(clsid, &spSnapIn);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// recheck the pointer
|
|
sc = ScCheckPointers(spSnapIn, E_UNEXPECTED);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// load the contents of snapin
|
|
sc = spSnapIn->Load(this, pStream);
|
|
if (sc)
|
|
return sc;
|
|
} while (true);
|
|
return E_FAIL; // shouldl never get here.
|
|
}
|
|
|
|
|
|
static void WriteSnapInCLSID (
|
|
CSnapIn* pSnapIn,
|
|
LPCTSTR pszSection,
|
|
LPCTSTR pszKeyPrefix,
|
|
LPCTSTR pszFilename)
|
|
{
|
|
tstring strKey = _T("CLSID");
|
|
|
|
if (pszKeyPrefix != NULL)
|
|
strKey = pszKeyPrefix + strKey;
|
|
|
|
tstring strCLSID = tstringFromCLSID (pSnapIn->GetSnapInCLSID());
|
|
SafeWritePrivateProfileString (pszSection, strKey.data(), strCLSID.data(), pszFilename);
|
|
}
|
|
|
|
|
|
static void WriteSnapInName (
|
|
CSnapIn* pSnapIn,
|
|
LPCTSTR pszSection,
|
|
LPCTSTR pszKeyPrefix,
|
|
LPCTSTR pszFilename)
|
|
{
|
|
tstring strKey = _T("Name");
|
|
|
|
if (pszKeyPrefix != NULL)
|
|
strKey = pszKeyPrefix + strKey;
|
|
|
|
WTL::CString strName;
|
|
SC sc = pSnapIn->ScGetSnapInName (strName);
|
|
|
|
if (sc.IsError() || strName.IsEmpty())
|
|
strName = _T("<unknown>");
|
|
|
|
SafeWritePrivateProfileString (pszSection, strKey.data(), strName, pszFilename);
|
|
}
|
|
|
|
|
|
static void AppendString (tstring& str, LPCTSTR pszToAppend)
|
|
{
|
|
if (!str.empty())
|
|
str += _T(", ");
|
|
|
|
str += pszToAppend;
|
|
}
|
|
|
|
|
|
static void WriteExtensionType (
|
|
DWORD dwExtensionFlags,
|
|
LPCTSTR pszSection,
|
|
LPCTSTR pszKeyPrefix,
|
|
LPCTSTR pszFilename)
|
|
{
|
|
tstring strKey = _T("Type");
|
|
|
|
if (pszKeyPrefix != NULL)
|
|
strKey = pszKeyPrefix + strKey;
|
|
|
|
struct {
|
|
CExtSI::EXTSI_FLAGS flag;
|
|
LPCTSTR pszDescription;
|
|
} FlagMap[] = {
|
|
{ CExtSI::EXT_TYPE_REQUIRED, _T("required") },
|
|
{ CExtSI::EXT_TYPE_STATIC, _T("static") },
|
|
{ CExtSI::EXT_TYPE_DYNAMIC, _T("dynamic") },
|
|
{ CExtSI::EXT_TYPE_NAMESPACE, _T("namespace") },
|
|
{ CExtSI::EXT_TYPE_CONTEXTMENU, _T("context menu") },
|
|
{ CExtSI::EXT_TYPE_TOOLBAR, _T("toolbar") },
|
|
{ CExtSI::EXT_TYPE_PROPERTYSHEET, _T("property sheet") },
|
|
{ CExtSI::EXT_TYPE_TASK, _T("taskpad") },
|
|
{ CExtSI::EXT_TYPE_VIEW, _T("view") },
|
|
};
|
|
|
|
tstring strType;
|
|
|
|
for (int i = 0; i < countof (FlagMap); i++)
|
|
{
|
|
if (dwExtensionFlags & FlagMap[i].flag)
|
|
AppendString (strType, FlagMap[i].pszDescription);
|
|
}
|
|
|
|
SafeWritePrivateProfileString (pszSection, strKey.data(), strType.data(), pszFilename);
|
|
}
|
|
|
|
|
|
HRESULT CSnapInsCache::Dump (LPCTSTR pszDumpFile)
|
|
{
|
|
static const TCHAR szStandaloneSection[] = _T("Standalone Snap-ins");
|
|
static const TCHAR szStandaloneCountKey[] = _T("StandaloneCount");
|
|
|
|
HRESULT hr = S_OK;
|
|
int cStandalones = 0;
|
|
|
|
try
|
|
{
|
|
/*
|
|
* no stand-alone snap-ins found yet (write it now so it's at the
|
|
* beginning of the section, for human readability)
|
|
*/
|
|
SafeWritePrivateProfileString (szStandaloneSection, szStandaloneCountKey, _T("0"), pszDumpFile);
|
|
|
|
/*
|
|
* dump each snap-in to the file
|
|
*/
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
CSnapIn* pSnapIn = it->second;
|
|
ASSERT(pSnapIn != NULL);
|
|
if (pSnapIn == NULL)
|
|
continue;
|
|
|
|
pSnapIn->Dump (pszDumpFile, this);
|
|
|
|
/*
|
|
* if this is a stand-alone, update the "Standalone Snap-ins" section
|
|
*/
|
|
if (pSnapIn->IsStandAlone())
|
|
{
|
|
TCHAR szKeyPrefix[16];
|
|
wsprintf (szKeyPrefix, _T("Standalone%d."), ++cStandalones);
|
|
|
|
WriteSnapInCLSID (pSnapIn, szStandaloneSection, szKeyPrefix, pszDumpFile);
|
|
WriteSnapInName (pSnapIn, szStandaloneSection, szKeyPrefix, pszDumpFile);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if we found stand-alones, update the count
|
|
*/
|
|
if (cStandalones > 0)
|
|
{
|
|
TCHAR szStandaloneCount[6];
|
|
_itot (cStandalones, szStandaloneCount, 10);
|
|
SafeWritePrivateProfileString (szStandaloneSection, szStandaloneCountKey,
|
|
szStandaloneCount, pszDumpFile);
|
|
}
|
|
}
|
|
catch (_com_error& err)
|
|
{
|
|
hr = err.Error();
|
|
ASSERT (false && "Caught _com_error");
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CSnapInsCache::ScCheckSnapinAvailability
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CSnapInsCache::ScCheckSnapinAvailability (CAvailableSnapinInfo& asi)
|
|
{
|
|
DECLARE_SC (sc, _T("CSnapInsCache::ScCheckSnapinAvailability"));
|
|
|
|
#ifdef MMC_WIN64
|
|
asi.m_cTotalSnapins = m_snapins.size();
|
|
asi.m_vAvailableSnapins.clear();
|
|
|
|
/*
|
|
* destroy any existing imagelist
|
|
*/
|
|
if (asi.m_himl)
|
|
ImageList_Destroy (asi.m_himl);
|
|
|
|
/*
|
|
* if we're interested in 32-bit snap-ins, make sure the registry APIs
|
|
* go to the 32-bit registry hive.
|
|
*/
|
|
const REGSAM samDesired = (asi.m_f32Bit) ? KEY_READ | REG_OPTION_OPEN_32BITKEY
|
|
: KEY_READ;
|
|
|
|
CRegKey keyClsid;
|
|
sc = ScFromWin32 (keyClsid.Open (HKEY_CLASSES_ROOT, _T("CLSID"), samDesired));
|
|
if (sc)
|
|
return (sc);
|
|
|
|
CStr strUnknownSnapinName;
|
|
VERIFY (strUnknownSnapinName.LoadString (GetStringModule(), IDS_UnknownSnapinName));
|
|
|
|
/*
|
|
* create an imagelist, tracing (but not aborting) on failure
|
|
*/
|
|
const int nImageListFolder = 0;
|
|
WTL::CImageList iml;
|
|
if (!iml.Create (IDB_FOLDER_16, 16 /*cx*/, 4 /*cGrow*/, RGB(255,0,255) /*crMask*/))
|
|
sc.FromLastError().TraceAndClear();
|
|
|
|
/*
|
|
* for each snap-in in the cache...
|
|
*/
|
|
for (map_t::iterator it = m_snapins.begin(); it != m_snapins.end(); ++it)
|
|
{
|
|
/*
|
|
* ...check to see if there's an HKCR\CLSID\{clsid}\InprocServer32
|
|
* entry for it. If there is, we'll assume the snap-in is "available".
|
|
*/
|
|
tstring strSnapinClsid = tstringFromCLSID (it->first);
|
|
tstring strInprocServerKey = strSnapinClsid + _T("\\InprocServer32");
|
|
|
|
CRegKey keyInprocServer;
|
|
LONG lResult = keyInprocServer.Open (keyClsid, strInprocServerKey.data(), samDesired);
|
|
bool fSnapinAvailable = (lResult == ERROR_SUCCESS);
|
|
|
|
/*
|
|
* if the snap-in's available, get it's name and put it in the
|
|
* available snap-ins collection
|
|
*/
|
|
if (fSnapinAvailable)
|
|
{
|
|
CBasicSnapinInfo bsi;
|
|
bsi.m_clsid = it->first;
|
|
CSnapIn*pSnapin = it->second;
|
|
|
|
/*
|
|
* get the snap-in's name
|
|
*/
|
|
WTL::CString strSnapinName;
|
|
if ((pSnapin != NULL) && !pSnapin->ScGetSnapInName(strSnapinName).IsError())
|
|
bsi.m_strName = strSnapinName;
|
|
else
|
|
bsi.m_strName = strUnknownSnapinName; // "<unknown>"
|
|
|
|
/*
|
|
* Get the snap-in's image from its about object
|
|
* (failures here aren't fatal and don't need to be traced).
|
|
* We'll use a generic folder icon if we can't get an image
|
|
* from the snap-in's about object.
|
|
*/
|
|
CLSID clsidAbout;
|
|
CSnapinAbout snapinAbout;
|
|
|
|
if (!iml.IsNull())
|
|
{
|
|
if (!ScGetAboutFromSnapinCLSID(bsi.m_clsid, clsidAbout).IsError() &&
|
|
snapinAbout.GetBasicInformation (clsidAbout))
|
|
{
|
|
/*
|
|
* the bitmaps returned by GetSmallImages are owned by
|
|
* the CSnapinAbout object (don't need to delete here)
|
|
*/
|
|
HBITMAP hbmSmall;
|
|
HBITMAP hbmSmallOpen; // unused here, but required for GetSmallImages
|
|
COLORREF crMask;
|
|
snapinAbout.GetSmallImages (&hbmSmall, &hbmSmallOpen, &crMask);
|
|
|
|
/*
|
|
* ImageList_AddMasked will mess up the background of
|
|
* its input bitmap, but the input bitmap won't be
|
|
* reused, so we don't need to make a copy like we
|
|
* usually do.
|
|
*/
|
|
WTL::CBitmap bmpSmall = CopyBitmap (hbmSmall);
|
|
|
|
if (!bmpSmall.IsNull())
|
|
bsi.m_nImageIndex = iml.Add (bmpSmall, crMask);
|
|
else
|
|
bsi.m_nImageIndex = nImageListFolder;
|
|
}
|
|
else
|
|
bsi.m_nImageIndex = nImageListFolder;
|
|
}
|
|
|
|
/*
|
|
* put it in the available snap-ins collection
|
|
*/
|
|
asi.m_vAvailableSnapins.push_back (bsi);
|
|
}
|
|
|
|
#ifdef DBG
|
|
if (fSnapinAvailable)
|
|
Trace (tagVerboseSnapinAnalysis64,
|
|
_T(" available: %s (image=%d)"),
|
|
asi.m_vAvailableSnapins.back().m_strName.data(),
|
|
asi.m_vAvailableSnapins.back().m_nImageIndex);
|
|
else
|
|
Trace (tagVerboseSnapinAnalysis64, _T("unavailable: %s"), strSnapinClsid.data());
|
|
#endif
|
|
}
|
|
|
|
Trace (tagSnapinAnalysis64, _T("%d-bit snap-in analysis: %d total, %d available"), asi.m_f32Bit ? 32 : 64, asi.m_cTotalSnapins, asi.m_vAvailableSnapins.size());
|
|
|
|
/*
|
|
* give the imagelist to the CAvailableSnapinInfo
|
|
*/
|
|
asi.m_himl = iml.Detach();
|
|
#else
|
|
sc = E_NOTIMPL;
|
|
#endif // !MMC_WIN64
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
void CSnapIn::MarkExtensionDeleted(CSnapIn* pSnapIn)
|
|
{
|
|
ASSERT(pSnapIn != NULL);
|
|
|
|
CExtSI* pExt = m_pExtSI;
|
|
|
|
while (pExt != NULL)
|
|
{
|
|
if (pExt->GetSnapIn() == pSnapIn)
|
|
{
|
|
pExt->MarkDeleted();
|
|
return;
|
|
}
|
|
|
|
pExt = pExt->Next();
|
|
}
|
|
|
|
// wasn't in the list !
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Delete all extensions marked for deletion
|
|
// Also reset any New flags
|
|
//
|
|
void CSnapIn::PurgeExtensions()
|
|
{
|
|
CExtSI* pExt = m_pExtSI;
|
|
CExtSI* pExtPrev = NULL;
|
|
|
|
// step through linked list, deleting marked nodes
|
|
while (pExt != NULL)
|
|
{
|
|
if (pExt->IsMarkedForDeletion())
|
|
{
|
|
CExtSI *pExtNext = pExt->Next();
|
|
|
|
if (pExtPrev)
|
|
pExtPrev->SetNext(pExtNext);
|
|
else
|
|
m_pExtSI = pExtNext;
|
|
|
|
// clear next link so extensions doesn't take the whole chain
|
|
// with it when it is deleted
|
|
pExt->SetNext(NULL);
|
|
delete pExt;
|
|
|
|
pExt = pExtNext;
|
|
}
|
|
else
|
|
{
|
|
pExt->SetNew(FALSE);
|
|
|
|
pExtPrev = pExt;
|
|
pExt = pExt->Next();
|
|
}
|
|
}
|
|
}
|
|
|
|
CExtSI* CSnapIn::FindExtension(const CLSID& rclsid)
|
|
{
|
|
CExtSI* pExt = m_pExtSI;
|
|
while (pExt != NULL && !IsEqualCLSID(rclsid, pExt->GetSnapIn()->GetSnapInCLSID()))
|
|
{
|
|
pExt = pExt->Next();
|
|
}
|
|
|
|
return pExt;
|
|
}
|
|
|
|
|
|
CExtSI* CSnapIn::AddExtension(CSnapIn* pSI)
|
|
{
|
|
|
|
CExtSI* pExtSI = new CExtSI(pSI);
|
|
ASSERT(pExtSI != NULL);
|
|
if ( pExtSI == NULL )
|
|
return NULL;
|
|
|
|
// insert extension in increasing GUID order
|
|
CExtSI* pExtPrev = NULL;
|
|
CExtSI* pExtTemp = m_pExtSI;
|
|
|
|
while (pExtTemp != NULL && pExtTemp->GetCLSID() < pExtSI->GetCLSID())
|
|
{
|
|
pExtPrev = pExtTemp;
|
|
pExtTemp = pExtTemp->Next();
|
|
}
|
|
|
|
if (pExtPrev == NULL)
|
|
{
|
|
pExtSI->SetNext(m_pExtSI);
|
|
m_pExtSI = pExtSI;
|
|
}
|
|
else
|
|
{
|
|
pExtSI->SetNext(pExtPrev->Next());
|
|
pExtPrev->SetNext(pExtSI);
|
|
}
|
|
|
|
// mark as new
|
|
pExtSI->SetNew();
|
|
|
|
return pExtSI;
|
|
}
|
|
|
|
HRESULT CSnapIn::Save(IStream* pStream, BOOL fClearDirty)
|
|
{
|
|
ASSERT(pStream != NULL);
|
|
if (pStream == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
ULONG bytesWritten;
|
|
HRESULT hr = pStream->Write(&GetSnapInCLSID(), sizeof(CLSID), &bytesWritten);
|
|
ASSERT(SUCCEEDED(hr) && bytesWritten == sizeof(CLSID));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// If all extensions are enabled, then write special guid & flag and return
|
|
if (AreAllExtensionsEnabled())
|
|
{
|
|
hr = pStream->Write(&GUID_EnableAllExtensions, sizeof(GUID), &bytesWritten);
|
|
ASSERT(SUCCEEDED(hr) && bytesWritten == sizeof(GUID));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
int iSnapInEnable = DoesSnapInEnableAll() ? 1 : 0;
|
|
hr = pStream->Write(&iSnapInEnable, sizeof(int), &bytesWritten);
|
|
ASSERT(SUCCEEDED(hr) && bytesWritten == sizeof(int));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
if (m_pExtSI)
|
|
{
|
|
hr = m_pExtSI->Save(pStream, fClearDirty);
|
|
ASSERT(SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
// NULL guid to terminate extensions list
|
|
hr = pStream->Write(&GUID_NULL, sizeof(GUID_NULL), &bytesWritten);
|
|
ASSERT(SUCCEEDED(hr) && bytesWritten == sizeof(GUID_NULL));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HKEY CSnapIn::OpenKey (REGSAM samDesired /*=KEY_ALL_ACCESS*/) const
|
|
{
|
|
MMC_ATL::CRegKey SnapInKey;
|
|
MMC_ATL::CRegKey AllSnapInsKey;
|
|
|
|
if (AllSnapInsKey.Open (HKEY_LOCAL_MACHINE, SNAPINS_KEY, samDesired) == ERROR_SUCCESS)
|
|
{
|
|
OLECHAR szItemKey[40];
|
|
int nChars = StringFromGUID2 (m_clsidSnapIn, szItemKey, countof(szItemKey));
|
|
if (nChars == 0)
|
|
return NULL;
|
|
|
|
USES_CONVERSION;
|
|
SnapInKey.Open (AllSnapInsKey, OLE2T(szItemKey), samDesired);
|
|
}
|
|
|
|
return (SnapInKey.Detach());
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CSnapIn::Persist
|
|
*
|
|
* PURPOSE: Persists the CSnapIn to the specified persistor.
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CSnapIn::Persist(CPersistor& persistor)
|
|
{
|
|
if (persistor.IsStoring())
|
|
persistor.PersistAttribute(XML_ATTR_SNAPIN_CLSID, *const_cast<GUID*>(&GetSnapInCLSID()));
|
|
|
|
BOOL bAreAllExtensionsEnabled = AreAllExtensionsEnabled();
|
|
persistor.PersistAttribute(XML_ATTR_SNAPIN_EXTN_ENABLED, CXMLBoolean(bAreAllExtensionsEnabled));
|
|
SetAllExtensionsEnabled(bAreAllExtensionsEnabled);
|
|
|
|
if(bAreAllExtensionsEnabled) // if all extensions are enabled, don't save anything else.
|
|
return;
|
|
|
|
// save the extension information if it exists
|
|
persistor.Persist(m_ExtPersistor);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CSnapIn::ScGetSnapInName
|
|
//
|
|
// Synopsis: Return the name of this snapin.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: SC
|
|
//
|
|
//--------------------------------------------------------------------
|
|
SC CSnapIn::ScGetSnapInName (WTL::CString& strSnapInName) const
|
|
{
|
|
DECLARE_SC(sc, _T("CSnapIn::ScGetSnapInName"));
|
|
|
|
sc = ScGetSnapinNameFromRegistry (m_clsidSnapIn, strSnapInName);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
DWORD CSnapIn::GetSnapInModule(TCHAR* pBuf, DWORD dwBufLen) const
|
|
{
|
|
ASSERT(pBuf != NULL && dwBufLen != NULL);
|
|
|
|
tstring strKeyName = g_szCLSID;
|
|
strKeyName += _T("\\");
|
|
strKeyName += tstringFromCLSID (m_clsidSnapIn);
|
|
strKeyName += _T("\\");
|
|
strKeyName += _T("InprocServer32");
|
|
|
|
*pBuf = 0;
|
|
|
|
MMC_ATL::CRegKey keyServer;
|
|
if (keyServer.Open (HKEY_CLASSES_ROOT, strKeyName.data(), KEY_QUERY_VALUE) == ERROR_SUCCESS)
|
|
{
|
|
TCHAR szModule[MAX_PATH];
|
|
DWORD cchModule = countof(szModule);
|
|
|
|
if (keyServer.QueryValue (szModule, NULL, &cchModule) == ERROR_SUCCESS)
|
|
ExpandEnvironmentStrings (szModule, pBuf, dwBufLen);
|
|
}
|
|
|
|
return (lstrlen (pBuf));
|
|
}
|
|
|
|
|
|
HRESULT CSnapIn::Load(CSnapInsCache* pCache, IStream* pStream, CExtSI*& pExtSI)
|
|
{
|
|
ASSERT(pStream != NULL);
|
|
if (pStream == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// Clear default enabling of all extensions. The true state will be
|
|
// determined from the persisted data.
|
|
SetAllExtensionsEnabled(FALSE);
|
|
|
|
// Read CLSID
|
|
CLSID clsid;
|
|
ULONG bytesRead;
|
|
HRESULT hr = pStream->Read(&clsid, sizeof(clsid), &bytesRead);
|
|
ASSERT(SUCCEEDED(hr) && bytesRead == sizeof(clsid));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (bytesRead != sizeof(clsid))
|
|
return hr = E_FAIL;
|
|
|
|
if (clsid == GUID_NULL)
|
|
return S_OK;
|
|
|
|
// If special "Enable all" guid encountered, read flag to see if
|
|
// snapin or user enabled all and return
|
|
if (clsid == GUID_EnableAllExtensions)
|
|
{
|
|
SetAllExtensionsEnabled();
|
|
|
|
int iSnapInEnable;
|
|
hr = pStream->Read(&iSnapInEnable, sizeof(int), &bytesRead);
|
|
ASSERT(SUCCEEDED(hr) && bytesRead == sizeof(int));
|
|
|
|
if (iSnapInEnable)
|
|
SetSnapInEnablesAll();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Read extension type flags
|
|
DWORD dwExtTypes;
|
|
hr = pStream->Read(&dwExtTypes, sizeof(DWORD), &bytesRead);
|
|
ASSERT(SUCCEEDED(hr) && bytesRead == sizeof(DWORD));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (pExtSI != NULL)
|
|
{
|
|
hr = Load(pCache, pStream, pExtSI);
|
|
ASSERT(hr == S_OK);
|
|
return hr == S_OK ? S_OK : E_FAIL;
|
|
}
|
|
|
|
CSnapInPtr spSnapIn;
|
|
SC sc = pCache->ScGetSnapIn(clsid, &spSnapIn);
|
|
if (sc)
|
|
return sc.ToHr();
|
|
|
|
ASSERT(spSnapIn != NULL);
|
|
|
|
pExtSI = new CExtSI(spSnapIn);
|
|
ASSERT(pExtSI != NULL);
|
|
if ( pExtSI == NULL )
|
|
return E_OUTOFMEMORY;
|
|
|
|
pExtSI->SetExtensionTypes(dwExtTypes);
|
|
|
|
hr = Load(pCache, pStream, pExtSI->Next());
|
|
ASSERT(hr == S_OK);
|
|
return hr == S_OK ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT CSnapIn::Load(CSnapInsCache* pCache, IStream* pStream)
|
|
{
|
|
HRESULT hr = Load(pCache, pStream, m_pExtSI);
|
|
ASSERT(SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
bool CSnapIn::IsStandAlone () const
|
|
{
|
|
MMC_ATL::CRegKey StandAloneKey;
|
|
MMC_ATL::CRegKey ItemKey;
|
|
ItemKey.Attach (OpenKey (KEY_READ));
|
|
|
|
if (ItemKey.m_hKey != NULL)
|
|
StandAloneKey.Open (ItemKey, g_szStandAlone, KEY_READ);
|
|
|
|
return (StandAloneKey.m_hKey != NULL);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CSnapIn::Dump
|
|
*
|
|
* Dumps the information about this snap-in to a INI-style file. The
|
|
* format is:
|
|
*
|
|
* [{clsid}]
|
|
* Name=<NameString from registry>
|
|
* Module=<dll name>
|
|
* Version=<dll version number>
|
|
* Standalone=<1 if standalone, 0 if extension>
|
|
* ExtensionCount=<number of extensions>
|
|
* Extension1={clsid} (extension name)
|
|
* ...
|
|
* ExtensionN={clsid} (extension name)
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
HRESULT CSnapIn::Dump (LPCTSTR pszDumpFile, CSnapInsCache* pCache)
|
|
{
|
|
/*
|
|
* use the CLSID as the section name
|
|
*/
|
|
const tstring strSection = tstringFromCLSID (m_clsidSnapIn);
|
|
|
|
/*
|
|
* write Name
|
|
*/
|
|
WriteSnapInName (this, strSection.data(), NULL, pszDumpFile);
|
|
|
|
/*
|
|
* write Module
|
|
*/
|
|
TCHAR szModule[MAX_PATH];
|
|
bool fFoundModule = (GetSnapInModule (szModule, countof (szModule)) != 0);
|
|
|
|
if (!fFoundModule)
|
|
lstrcpy (szModule, _T("<unknown>"));
|
|
|
|
SafeWritePrivateProfileString (strSection.data(), _T("Module"), szModule, pszDumpFile);
|
|
|
|
/*
|
|
* write Version
|
|
*/
|
|
TCHAR szVersion[64];
|
|
if (!fFoundModule || !GetModuleVersion (szModule, szVersion))
|
|
lstrcpy (szVersion, _T("<unknown>"));
|
|
|
|
SafeWritePrivateProfileString (strSection.data(), _T("Version"), szVersion, pszDumpFile);
|
|
|
|
/*
|
|
* write Standalone
|
|
*/
|
|
SafeWritePrivateProfileString (strSection.data(), _T("Standalone"),
|
|
IsStandAlone() ? _T("1") : _T("0"),
|
|
pszDumpFile);
|
|
|
|
/*
|
|
* make sure the extension chain has been built
|
|
*/
|
|
if (AreAllExtensionsEnabled())
|
|
{
|
|
/*
|
|
* Calling LoadRequiredExtensions with SNAPIN_SNAPIN_ENABLES_ALL set
|
|
* will result in SNAPIN_ENABLE_ALL_EXTS being cleared, which we don't
|
|
* want (rswaney).
|
|
*
|
|
* This happens because we haven't created the snap-in, so we can't
|
|
* pass an IComponentData from which LoadRequiredExtensions can QI
|
|
* for IRequiredExtensions. LoadRequiredExtensions uses
|
|
* IRequiredExtensions to determine whether SNAPIN_ENABLE_ALL_EXTS
|
|
* should be cleared or set. Since there's no IRequiredExtensions,
|
|
* SNAPIN_ENABLE_ALL_EXTS would be cleared.
|
|
*/
|
|
SetSnapInEnablesAll (false);
|
|
|
|
LoadRequiredExtensions (this, NULL, pCache);
|
|
}
|
|
|
|
/*
|
|
* write ExtensionCount
|
|
*/
|
|
TCHAR szExtCount[8];
|
|
CExtSI* pExt;
|
|
int cExtensions = 0;
|
|
|
|
// count the extensions
|
|
for (pExt = m_pExtSI; pExt != NULL; pExt = pExt->Next())
|
|
cExtensions++;
|
|
|
|
_itot (cExtensions, szExtCount, 10);
|
|
SafeWritePrivateProfileString (strSection.data(), _T("ExtensionCount"), szExtCount, pszDumpFile);
|
|
|
|
/*
|
|
* build up a cache of the extensions for this snap-in
|
|
*/
|
|
CExtensionsCache ExtCache;
|
|
MMCGetExtensionsForSnapIn (m_clsidSnapIn, ExtCache);
|
|
|
|
/*
|
|
* write extensions
|
|
*/
|
|
int i;
|
|
for (i = 0, pExt = m_pExtSI; i < cExtensions; i++, pExt = pExt->Next())
|
|
{
|
|
TCHAR szKeyPrefix[20];
|
|
wsprintf (szKeyPrefix, _T("Extension%d."), i+1);
|
|
|
|
DWORD dwExtFlags = ExtCache[pExt->GetSnapIn()->m_clsidSnapIn];
|
|
|
|
WriteSnapInCLSID (pExt->GetSnapIn(), strSection.data(), szKeyPrefix, pszDumpFile);
|
|
WriteSnapInName (pExt->GetSnapIn(), strSection.data(), szKeyPrefix, pszDumpFile);
|
|
WriteExtensionType (dwExtFlags, strSection.data(), szKeyPrefix, pszDumpFile);
|
|
}
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
HRESULT CExtSI::Save(IStream* pStream, BOOL fClearDirty)
|
|
{
|
|
ASSERT(pStream != NULL);
|
|
if (pStream == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
// Save extension CLSID
|
|
ULONG bytesWritten;
|
|
HRESULT hr = pStream->Write(&GetCLSID(), sizeof(CLSID), &bytesWritten);
|
|
ASSERT(SUCCEEDED(hr) && bytesWritten == sizeof(CLSID));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Save extension types
|
|
DWORD dwExtTypes = m_dwFlags & EXT_TYPES_MASK;
|
|
hr = pStream->Write(&dwExtTypes, sizeof(DWORD), &bytesWritten);
|
|
ASSERT(SUCCEEDED(hr) && bytesWritten == sizeof(DWORD));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (m_pNext == NULL)
|
|
return S_OK;
|
|
|
|
hr = m_pNext->Save(pStream, fClearDirty);
|
|
ASSERT(SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
return S_OK;
|
|
}
|
|
|
|
void CExtSI::Persist(CPersistor& persistor)
|
|
{
|
|
// create an "Extension" object beneath the "Extensions" object.
|
|
CPersistor persistorExtension(persistor, XML_TAG_SNAPIN_EXTENSION);
|
|
|
|
persistorExtension.PersistAttribute(XML_ATTR_SNAPIN_CLSID, *const_cast<GUID*>(&GetCLSID()));
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CExtSI::PersistNew
|
|
*
|
|
* PURPOSE: called to create and persist new extension entry
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
* CSnapIn& snapParent : parent to whom the extension belongs
|
|
* CSnapInsCache& snapCache : cache to put new (extension) snapin to
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CExtSI::PersistNew(CPersistor &persistor, CSnapIn& snapParent, CSnapInsCache& snapCache)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CExtSI::PersistNew"));
|
|
|
|
CLSID clsid;
|
|
CPersistor persistorExtension(persistor, XML_TAG_SNAPIN_EXTENSION);
|
|
persistorExtension.PersistAttribute(XML_ATTR_SNAPIN_CLSID, clsid);
|
|
|
|
CSnapInPtr spSnapIn;
|
|
sc = snapCache.ScGetSnapIn(clsid, &spSnapIn);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// create new extension entry
|
|
CExtSI *pExtSI = snapParent.AddExtension(spSnapIn);
|
|
sc = ScCheckPointers(pExtSI,E_FAIL);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// upload new extension entry info
|
|
pExtSI->Persist(persistor);
|
|
}
|
|
|
|
const CLSID& CExtSI::GetCLSID()
|
|
{
|
|
ASSERT(m_pSnapIn != NULL);
|
|
return m_pSnapIn ? m_pSnapIn->GetSnapInCLSID() : GUID_NULL;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CSnapIn::PersistLoad
|
|
*
|
|
* PURPOSE: provided instead Persist to maintain reference to cache,
|
|
* required for registering new extensions during loading
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
* CSnapInsCache* pCache : cache to put new (extension) snapin to
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CSnapIn::PersistLoad(CPersistor& persistor,CSnapInsCache* pCache)
|
|
{
|
|
m_ExtPersistor.SetCache(pCache);
|
|
persistor.Persist(*this);
|
|
m_ExtPersistor.SetCache(NULL);
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CSnapIn::CExtPersistor::Persist
|
|
*
|
|
* PURPOSE: persists collection of extensions for snapin
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CSnapIn::CExtPersistor::Persist(CPersistor& persistor)
|
|
{
|
|
if (persistor.IsStoring())
|
|
{
|
|
CExtSI* pExt = GetParent().GetExtensionSnapIn();
|
|
while (pExt)
|
|
{
|
|
pExt->Persist(persistor);
|
|
pExt = pExt->Next();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
XMLListCollectionBase::Persist(persistor);
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CSnapIn::CExtPersistor::OnNewElement
|
|
*
|
|
* PURPOSE: called for each new entry read from XML doc.
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void CSnapIn::CExtPersistor::OnNewElement(CPersistor& persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::CExtPersistor::OnNewElement"));
|
|
|
|
sc = (persistor.IsLoading() && m_pCache != NULL) ? S_OK : E_FAIL;
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CExtSI::PersistNew(persistor, m_Parent, *m_pCache);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapIn::ScDestroyExtensionList
|
|
*
|
|
* PURPOSE: destroys the list of extensions. used to do preliminary snapin cleanup
|
|
* to avoid circular references held by the extension sanpin
|
|
* locking the objects in the memory.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapIn::ScDestroyExtensionList()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::ScDestroyExtensionList"));
|
|
|
|
// check if we have extensions
|
|
if ( m_pExtSI != NULL )
|
|
{
|
|
// assign to auto variable
|
|
// ( 'this' may not be valid if the only reference is from extension )
|
|
CExtSI *pExtension = m_pExtSI;
|
|
|
|
// update member pointer
|
|
m_pExtSI = NULL;
|
|
|
|
delete pExtension;
|
|
// delete the extension (it will delete the next and so on)
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapIn::ScTempState_ResetReferenceCalculationData
|
|
*
|
|
* PURPOSE: resets external reference calculation data
|
|
* Used as the first step for external reference calculation process
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapIn::ScTempState_ResetReferenceCalculationData( )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::ScTempState_ResetReferenceCalculationData"));
|
|
|
|
m_dwTempState_InternalRef = 0;
|
|
m_bTempState_HasStrongRef = 0;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapIn::ScTempState_UpdateInternalReferenceCounts
|
|
*
|
|
* PURPOSE: Informs snapin's extensions about the references kept to them
|
|
* Having this information extension snapin can know if it is
|
|
* referenced externally
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapIn::ScTempState_UpdateInternalReferenceCounts( )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::ScTempState_UpdateInternalReferenceCounts"));
|
|
|
|
for ( CExtSI* pExtension = m_pExtSI; pExtension; pExtension = pExtension->Next() )
|
|
{
|
|
CSnapIn *pExtensionSnapin = pExtension->GetSnapIn();
|
|
sc = ScCheckPointers( pExtensionSnapin, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
pExtensionSnapin->m_dwTempState_InternalRef++;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapIn::ScTempState_SetHasStrongReference
|
|
*
|
|
* PURPOSE: Marks itself as having external strong references (external to snapin cache)
|
|
* Marks own extensions as well.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapIn::ScTempState_SetHasStrongReference()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::ScTempState_SetHasStrongReference"));
|
|
|
|
// do nothing if already marked (else we'll have the infinite loop)
|
|
if ( m_bTempState_HasStrongRef )
|
|
return sc;
|
|
|
|
m_bTempState_HasStrongRef = true;
|
|
|
|
// recurse to all extensions (they inherit the strong reference too)
|
|
for ( CExtSI* pExtension = m_pExtSI; pExtension; pExtension = pExtension->Next() )
|
|
{
|
|
CSnapIn *pExtensionSnapin = pExtension->GetSnapIn();
|
|
sc = ScCheckPointers( pExtensionSnapin, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = pExtensionSnapin->ScTempState_SetHasStrongReference();
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapIn::ScTempState_MarkIfExternallyReferenced
|
|
*
|
|
* PURPOSE: Used as an intermediate step calculating external references
|
|
* compares internal references to total references.
|
|
* If has external references, marks itself as 'Externally referenced'
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapIn::ScTempState_MarkIfExternallyReferenced()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::ScTempState_MarkIfExternallyReferenced"));
|
|
|
|
DWORD dwStrongRef = m_dwRef - m_dwTempState_InternalRef - 1/*chache reference*/;
|
|
|
|
if ( dwStrongRef > 0 )
|
|
{
|
|
// now mark itself and the extensions as having strong reference
|
|
sc = ScTempState_SetHasStrongReference();
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CSnapIn::ScTempState_IsExternallyReferenced
|
|
*
|
|
* PURPOSE: Returns the cached reference status claculated by preceding
|
|
* call to CSnapInsCache::ScMarkExternallyReferencedSnapins.
|
|
*
|
|
* PARAMETERS:
|
|
* bool& bReferenced [out] - true if snapin has external (to snapin cache) strong references
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CSnapIn::ScTempState_IsExternallyReferenced( bool& bReferenced ) const
|
|
{
|
|
DECLARE_SC(sc, TEXT("CSnapIn::ScTempState_IsExternallyReferenced"));
|
|
|
|
bReferenced = m_bTempState_HasStrongRef;
|
|
|
|
return sc;
|
|
}
|
|
|