// MSInfo.cpp : Implementation of DLL Exports, main application object
//		and Registry Object Map.  All registry information (apart from
//		MIDL) lives here.
//
// Copyright (c) 1998-1999 Microsoft Corporation

#include "stdafx.h"
#include "resource.h"
#include "initguid.h"

//	This hack is required because we may be building in an environment
//	which doesn't have a late enough version of rpcndr.h
#if		__RPCNDR_H_VERSION__ < 440
#define __RPCNDR_H_VERSION__ 440
#define MIDL_INTERFACE(x)	interface
#endif

#include "MSInfo.h"
#include "DataObj.h"
#include "CompData.h"
#include "About.h"
#include "Toolset.h"
#include "Dispatch.h"

#include "MSInfo_i.c"

static LPCTSTR		cszBasePath			= _T("Software\\Microsoft\\MMC");
static LPCTSTR		cszBaseSnapinPath	= _T("Software\\Microsoft\\MMC\\Snapins");
static LPCTSTR		cszBaseNodeTypePath = _T("Software\\Microsoft\\MMC\\NodeTypes");
static LPCTSTR		cszNameString		= _T("NameString");
static LPCTSTR		cszProvider			= _T("Provider");
static LPCTSTR		cszVersion			= _T("Version");
static LPCTSTR		cszAbout			= _T("About");
static LPCTSTR		cszStandAlone		= _T("StandAlone");
static LPCTSTR		cszNodeTypes		= _T("NodeTypes");
static LPCTSTR		cszExtensions		= _T("Extensions");
static LPCTSTR		cszNameSpace		= _T("NameSpace");
static LPCTSTR		cszTask				= _T("Task");

static LPCTSTR		cszMSInfoBaseKey	= _T("Software\\Microsoft\\Shared Tools");
static LPCTSTR		cszMSInfoSubKey		= _T("MSInfo");
static LPCTSTR		cszMSInfoKey		= _T("Software\\Microsoft\\Shared Tools\\MSInfo");
static LPCTSTR		cszPathValue		= _T("Path");
static LPCTSTR		cszRunKey			= _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msinfo32.exe");
static LPCTSTR		cszRunRootKey		= _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths");
static LPCTSTR		cszRunSubKey		= _T("msinfo32.exe");

static LPCTSTR		cszNFOExtension		= _T(".nfo");
static LPCTSTR		cszOLERegistration	= _T("MSInfo.Document");
static LPCTSTR		cszDefaultIconKey	= _T("DefaultIcon");

static LPCTSTR		cszCLSIDKey			= _T("CLSID");
static LPCTSTR		cszShellKey			= _T("shell");
static LPCTSTR		cszOpenCommandKey	= _T("open\\command");
static LPCTSTR		cszPrintCommandKey	= _T("print\\command");
static LPCTSTR		cszPrintToKey		= _T("printto\\command");

// note trailing quotes on file path
static LPCTSTR		cszMSInfoPath		= _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\"");	
static LPCTSTR		cszDefaultIconValue	= _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\",0"); 
static LPCTSTR		cszOpenCommand		= _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\" /msinfo_file \"%1\"");

// these are never referenced 
static LPCTSTR		cszMSInfoDir	    = _T("Common Files\\Microsoft Shared\\MSInfo\\");
static LPCTSTR		cszPrintToCommand	= _T("Common Files\\Microsoft Shared\\MSInfo\\MSInfo32.exe /pt \"%1\" \"%2\" \"%3\" \"%4\"");
//static LPCTSTR		cszPrintCommand		= _T("Common Files\\Microsoft Shared\\MSInfo\\MSInfo32.exe /p \"%1\"");
//a-kjaw
static LPCTSTR		cszPrintCommand		= _T("Microsoft Shared\\MSInfo\\MSInfo32.exe\" /p \"%1\"");
//a-kjaw

//	Nodes we extend
static LPCTSTR		cszCompMgrNode		= _T("{476E6448-AAFF-11D0-B944-00C04FD8D5B0}");

CComModule _Module;

/*
 * Object Map of Registered objects, allowing the Active Template Library to
 *		register our CLSIDs.
 */
BEGIN_OBJECT_MAP(ObjectMap)
	OBJECT_ENTRY(CLSID_MSInfo, CSystemInfoScopePrimary)
	OBJECT_ENTRY(CLSID_Extension, CSystemInfoScopeExtension)
	OBJECT_ENTRY(CLSID_About, CAboutImpl)
	OBJECT_ENTRY(CLSID_SystemInfo, CMSInfo)
END_OBJECT_MAP()

/*
 * The MSInfo application object.
 *
 * History:	a-jsari		10/1/97		Initial version.
 */
class CMSInfoApp : public CWinApp
{
public:
	virtual BOOL InitInstance();
	virtual int ExitInstance();
};

CMSInfoApp theApp;

/*
 * InitInstance - Initialize an instance of the application.
 *
 * History:	a-jsari		10/1/97		Initial version.
 */

extern void LoadDialogResources();
BOOL CMSInfoApp::InitInstance()
{
	_Module.Init(ObjectMap, m_hInstance);
	LoadDialogResources(); // loads dialog strings from resources
	return CWinApp::InitInstance();
}

/*
 * ExitInstance - Deconstruct an instance of the application.
 *
 * History:	a-jsari		10/1/97		Initial version.
 */
int CMSInfoApp::ExitInstance()
{
	_Module.Term();
	return CWinApp::ExitInstance();
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
	return _Module.GetClassObject(rclsid, riid, ppv);
}

/*
 * RegOpenMMCRoot - Open CRegKey
 *
 * History:	a-jsari		9/9/97		Initial version
 */
static inline long RegOpenMMCRoot(CRegKey *pcrkMMCRoot)
{
	return pcrkMMCRoot->Open(HKEY_LOCAL_MACHINE, cszBasePath);
}

/*
 * RegOpenMMCSnapinRoot - Return MMC's registry Snapin root
 *
 * History:	a-jsari		9/9/97		Initial version
 */
static inline long RegOpenMMCSnapinRoot(CRegKey *pcrkSnapinRoot)
{
	return pcrkSnapinRoot->Open(HKEY_LOCAL_MACHINE, cszBaseSnapinPath);
}

/*
 * RegOpenMMCNodeTypeRoot - Return MMC's registry NodeType root
 *
 * History:	a-jsari		9/9/97		Initial version
 */
static inline long RegOpenMMCNodeTypeRoot(CRegKey *pcrkNodeTypesRoot)
{
	return pcrkNodeTypesRoot->Open(HKEY_LOCAL_MACHINE, cszBaseNodeTypePath);
}

/*
 * RegisterStandaloneSnapin() - Do all registration for the Standalone
 *		portion of the snapin.
 *
 * History:	a-jsari		9/9/97		Initial version
 *
 * Note: Would require AFX_MANAGE_STATE, except that the calling function
 *		handles it.
 */
static inline HRESULT RegisterStandaloneSnapin()
{
	CRegKey			crkRoot;
	CRegKey			crkClsid;
	CRegKey			crkIterator;
	CString			szResourceLoader;

	HRESULT			hr = E_FAIL;

	do {
		//	HKEY_CLASSES_ROOT\.nfo
		long	lRegOpenResult = crkRoot.Create(HKEY_CLASSES_ROOT, cszNFOExtension);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		lRegOpenResult = crkRoot.SetValue(cszOLERegistration);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		crkRoot.Close();

		//	HKEY_CLASSES_ROOT\MSInfo.Document
		lRegOpenResult = crkRoot.Create(HKEY_CLASSES_ROOT, cszOLERegistration);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		
		// This originally set the default value for the key to "msinfo.document", which
		// is what would show up in the UI for a description of the NFO filetype. Fixing
		// this to load the string from a resource (bug 10442).
		//
		//	lRegOpenResult = crkRoot.SetValue(cszOLERegistration);
		//	if (lRegOpenResult != ERROR_SUCCESS) break;

		CString strDescription;
		strDescription.LoadString(IDS_NFODESCRIPTION);
		crkRoot.SetValue((LPCTSTR)strDescription);
		
		lRegOpenResult = crkIterator.Create(crkRoot, cszCLSIDKey);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		lRegOpenResult = crkIterator.SetValue(cszClsidMSInfoSnapin);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		crkIterator.Close();

		TCHAR	szWindowsPath[MAX_PATH];
		DWORD	dwSize;
		CString	szPathValue;
		dwSize = sizeof(szWindowsPath);
		lRegOpenResult = crkIterator.Open(HKEY_LOCAL_MACHINE, cszWindowsCurrentKey);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		lRegOpenResult = crkIterator.QueryValue(szWindowsPath, cszCommonFilesValue, &dwSize);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		lRegOpenResult = crkIterator.Create(crkRoot, cszDefaultIconKey);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		szPathValue  = _T("\"");
		szPathValue += szWindowsPath;
		szPathValue += _T("\\");
		szPathValue += cszDefaultIconValue;
		lRegOpenResult = crkIterator.SetValue(szPathValue);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		crkIterator.Close();
		lRegOpenResult = crkRoot.Create(crkRoot, cszShellKey);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		lRegOpenResult = crkIterator.Create(crkRoot, cszOpenCommandKey);
		if (lRegOpenResult != ERROR_SUCCESS) break;

		szPathValue  = _T("\"");
		szPathValue += szWindowsPath;
		szPathValue += _T("\\");
		szPathValue += cszOpenCommand;

		lRegOpenResult = crkIterator.SetValue(szPathValue);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		crkIterator.Close();
		//crkRoot.Close();
//a-kjaw

		lRegOpenResult = crkIterator.Create(crkRoot, cszPrintCommandKey);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		
		szPathValue  = _T("\"");
		szPathValue += szWindowsPath;
		szPathValue += _T("\\");
		szPathValue += cszPrintCommand;		

		lRegOpenResult = crkIterator.SetValue(szPathValue);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		crkIterator.Close();
		crkRoot.Close();
//a-kjaw

		//	HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\Snapins
		lRegOpenResult =	RegOpenMMCSnapinRoot(&crkRoot);

		//	FIX: This fail should behave differently (registering the DLL
		//		w/o MMC registered).
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//		{45ac8c63-23e2-11e1-a696-00c04fd58bc3}
		lRegOpenResult =			crkClsid.Create(crkRoot, cszClsidMSInfoSnapin);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//			NameString = REG_SZ "Microsoft System Information"
		VERIFY(szResourceLoader.LoadString(IDS_DESCRIPTION));
		lRegOpenResult =			crkClsid.SetValue(szResourceLoader, cszNameString);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//			About = REG_SZ "{45ac8c65-23e2-11e1-a696-00c04fd58bc3}"
		lRegOpenResult =			crkClsid.SetValue(cszClsidAboutMSInfo, cszAbout);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//			Provider = REG_SZ "Microsoft Corporation"
		VERIFY(szResourceLoader.LoadString(IDS_COMPANY));
		lRegOpenResult =			crkClsid.SetValue(szResourceLoader, cszProvider);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//			Version = REG_SZ "5.0"
		VERIFY(szResourceLoader.LoadString(IDS_VERSION));
		lRegOpenResult =			crkClsid.SetValue(szResourceLoader, cszVersion);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//			StandAlone
		lRegOpenResult =			crkIterator.Create(crkClsid, cszStandAlone);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//			NodeTypes
		lRegOpenResult =			crkIterator.Create(crkClsid, cszNodeTypes);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//				{45ac8c66-23e2-11e1-a696-00c04fd58bc3}
		CRegKey		crkNodeType;
		lRegOpenResult =			crkNodeType.Create(crkIterator, cszNodeTypeStatic);
		if (lRegOpenResult != ERROR_SUCCESS) break;

		//	Replace the SnapinRoot with the NodeType root;
		//	work from the new base.
		//	HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\NodeTypes
		lRegOpenResult = RegOpenMMCNodeTypeRoot(&crkRoot);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		//		{45ac8c66-23e2-11e1-a696-00c04fd58bc3}
		//			= REG_SZ "Microsoft System Information Root"
		VERIFY(szResourceLoader.LoadString(IDS_NODEDESCRIPTION));
		lRegOpenResult = crkRoot.SetKeyValue(cszNodeTypeStatic, szResourceLoader);
		if (lRegOpenResult != ERROR_SUCCESS) break;
		hr = S_OK;
	} while (0);
	return hr;
}

/*
 * HRESULT UnregisterStandaloneSnapin - Remove all registry entries for
 *		the standalone portion of the snapin.
 *
 * History:	a-jsari		9/9/97		Initial version.
 */
static inline HRESULT UnregisterStandaloneSnapin()
{
	CRegKey			crkSnapinRoot;

	//	Remove HKEY_CLASSES_ROOT\.nfo
	long lRegOpenResult = crkSnapinRoot.Open(HKEY_CLASSES_ROOT, NULL);
	if (lRegOpenResult != ERROR_SUCCESS) {
		if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
	} else {
		lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszNFOExtension);
		//	It's not really an error to not find a key we were deleting anyhow.
		if (lRegOpenResult != ERROR_SUCCESS
			&& lRegOpenResult != ERROR_FILE_NOT_FOUND) {
			return E_FAIL;
		}
		crkSnapinRoot.Close();
	}

	//	Remove HKEY_CLASSES_ROOT\MSInfo.Document
	lRegOpenResult = crkSnapinRoot.Open(HKEY_CLASSES_ROOT, NULL);
	if (lRegOpenResult != ERROR_SUCCESS) {
		if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
	} else {
		lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszOLERegistration);
		//	It's not really an error to not find a key we were deleting anyhow.
		if (lRegOpenResult != ERROR_SUCCESS
			&& lRegOpenResult != ERROR_FILE_NOT_FOUND) {
			return E_FAIL;
		}
		crkSnapinRoot.Close();
	}

	lRegOpenResult = RegOpenMMCSnapinRoot(&crkSnapinRoot);
	if (lRegOpenResult != ERROR_SUCCESS) {
		if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
	} else {
		//	Just recursively delete our root.  Extensions will be automatically
		//	deleted as well.
		lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszClsidMSInfoSnapin);
		//	It's not really an error to not find a key we were deleting anyhow.
		if (lRegOpenResult != ERROR_SUCCESS
			&& lRegOpenResult != ERROR_FILE_NOT_FOUND) {
			return E_FAIL;
		}
	}

	lRegOpenResult = RegOpenMMCNodeTypeRoot(&crkSnapinRoot);
	if (lRegOpenResult != ERROR_SUCCESS) {
		if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
	} else {
		lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszNodeTypeStatic);

		ASSERT(lRegOpenResult == ERROR_SUCCESS);
		//	It's not really an error to not find a key we were deleting anyhow.
		if (lRegOpenResult != ERROR_SUCCESS
			&& lRegOpenResult != ERROR_FILE_NOT_FOUND) {
			return E_FAIL;
		}
	}
	return S_OK;
}

/*
 * OpenExtendKeyForNodeType - Return in pcrkExtension the Registry Key for
 *		HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\NodeTypes\
 *		cszNodeTypeGuid (GUID string) \Extensions
 * If fCreateIfNonextistent is TRUE, the Extensions key (only) is created
 *		if it doesn't exist.
 *
 * Return Codes:
 *		S_OK - All operations succeeded.
 *		E_FAIL - A critical registry operation failed.
 *		E_ABORT - The GUID string could not be opened, or
 *			the Extensions key could not be opened (and
 *			fCreateIfNonexistent is FALSE).
 *
 * History:	a-jsari		9/18/97		Initial version
 */
static inline HRESULT OpenExtendKeyForNodeType(LPCTSTR cszNodeTypeGuid,
			CRegKey *pcrkExtension, BOOL fCreateIfNonexistent = TRUE)
{	
	CRegKey			crkRoot;
	CRegKey			crkNodeToExtend;

	ASSERT(pcrkExtension != NULL);
	long lRegOpenResult = RegOpenMMCNodeTypeRoot(&crkRoot);
	if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
	//	Not finding the proper nodetype is a different kind of error
	//		than not finding MMC, or not creating a new one.
	if (fCreateIfNonexistent) {
		lRegOpenResult = crkNodeToExtend.Create(crkRoot, cszNodeTypeGuid);
		if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
		lRegOpenResult = pcrkExtension->Create(crkNodeToExtend, cszExtensions);
	} else {
		lRegOpenResult = crkNodeToExtend.Open(crkRoot, cszNodeTypeGuid);
		if (lRegOpenResult != ERROR_SUCCESS) return E_ABORT;
		lRegOpenResult = pcrkExtension->Open(crkNodeToExtend, cszExtensions);
		if (lRegOpenResult == ERROR_FILE_NOT_FOUND) return E_ABORT;
	}
	if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
	return S_OK;
}

/*
 * ExtendNode - Does all registry extension required for the NodeType
 *		cszNodeTypeGuid.
 *
 * Return Codes:
 *		S_OK - Upon successful completion, or if cszNodeTypeGuid can't
 *				be found.
 *		E_FAIL - If MMC cannot be found or NodeTypeGuid can't be opened.
 *
 * History:	a-jsari		9/9/97	Initial version
 */
static HRESULT ExtendNode(LPCTSTR cszNodeTypeGuid)
{
	CRegKey			crkExtension;
	CRegKey			crkIterator;
	CString			szResourceLoader;

	HRESULT			hrOpenExtension = OpenExtendKeyForNodeType(cszNodeTypeGuid,
				&crkExtension, TRUE);
	// Don't return an error if we can't open the cszNodeTypeGuid
	if (hrOpenExtension != S_OK)
		return hrOpenExtension == E_ABORT ? S_OK : hrOpenExtension;

	//	NameSpace
	//		{GUID} = "System Information Extension"
	long lRegOpenResult = crkIterator.Create(crkExtension, cszNameSpace);
	if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
	VERIFY(szResourceLoader.LoadString(IDS_EXTENSIONDESCRIPTION));
	lRegOpenResult = crkIterator.SetValue(szResourceLoader, cszClsidMSInfoExtension);
	if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
	//	Task
	//		{GUID} = "System Information Extension"
	lRegOpenResult = crkIterator.Create(crkExtension, cszTask);
	if (lRegOpenResult != ERROR_SUCCESS) return E_FAIL;
	lRegOpenResult = crkIterator.SetValue(szResourceLoader, cszClsidMSInfoExtension);
	return S_OK;
}

/*
 * HRESULT UnregisterNode - Remove all registry entries for a specific
 *		Nodetype.
 *
 * Return Codes:
 *		S_OK - If all of the essential nodes are found.
 *		E_FAIL - If any of the path to the keys to remove fail to be found.
 *
 * History:	a-jsari		9/9/97		Initial version.
 */
static HRESULT UnregisterNode(LPCTSTR cszNodeTypeGuid)
{
		CRegKey		crkExtension;
		CRegKey		crkIterator;

		HRESULT		hrOpenExtension = OpenExtendKeyForNodeType(cszNodeTypeGuid,
					&crkExtension);
		ASSERT(hrOpenExtension == S_OK);
		//	Don't return an error if we can't open the cszNodeTypeGuid
		if (hrOpenExtension != S_OK)
			return (hrOpenExtension == E_ABORT) ? S_OK : hrOpenExtension;
		long lRegOpenResult = crkIterator.Open(crkExtension, cszNameSpace);
		if (lRegOpenResult != ERROR_SUCCESS)
			return (lRegOpenResult == ERROR_FILE_NOT_FOUND) ? S_OK : E_FAIL;
		lRegOpenResult = crkIterator.DeleteValue(cszClsidMSInfoExtension);

		//	It's not really an error to not find a key we were deleting anyhow.
		if (lRegOpenResult != ERROR_SUCCESS
			&& lRegOpenResult != ERROR_FILE_NOT_FOUND) {
			return E_FAIL;
		}
	return S_OK;
}

/*
 * RegisterExtensionSnapin - Do all registration required for the extension side
 *		of the snapin.
 *
 * History:	a-jsari		9/9/97		Initial version
 */
static inline HRESULT RegisterExtensionSnapin()
{
	const HRESULT	hrErrorReturn = E_FAIL;

	CRegKey			crkRoot;
	CRegKey			crkClsid;
	CRegKey			crkIterator;
	CString			szResourceLoader;

	//	HKEY_LOCAL_MACHINE\Software\Microsoft\MMC\Snapins
	long	lRegOpenResult =	RegOpenMMCSnapinRoot(&crkRoot);

	//	FIX: This fail should behave differently (registering the DLL
	//		w/o MMC registered).
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
	//		{45ac8c63-23e2-11e1-a696-00c04fd58bc3}
	lRegOpenResult =			crkClsid.Create(crkRoot, cszClsidMSInfoExtension);
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
	//			NameString = REG_SZ "System Information Extension"
	VERIFY(szResourceLoader.LoadString(IDS_EXTENSIONDESCRIPTION));
	lRegOpenResult =			crkClsid.SetValue(szResourceLoader, cszNameString);
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
	//			Provider = REG_SZ "Microsoft Corporation"
	VERIFY(szResourceLoader.LoadString(IDS_COMPANY));
	lRegOpenResult =			crkClsid.SetValue(szResourceLoader, cszProvider);
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
	//			Version = REG_SZ "5.0"
	VERIFY(szResourceLoader.LoadString(IDS_VERSION));
	lRegOpenResult =			crkClsid.SetValue(szResourceLoader, cszVersion);
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;

	// Register our about interface CLSID under the "About" value, so when
	// we're being added as an extension, our information shows up.

	lRegOpenResult = crkClsid.SetValue(cszClsidAboutMSInfo, cszAbout);
	if (lRegOpenResult != ERROR_SUCCESS) 
		return hrErrorReturn;

	//			NodeTypes
	lRegOpenResult =			crkIterator.Create(crkClsid, cszNodeTypes);
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;
	//				{45ac8c66-23e2-11e1-a696-00c04fd58bc3}
	CRegKey		crkNodeType;
	lRegOpenResult =			crkNodeType.Create(crkIterator, cszNodeTypeStatic);
	if (lRegOpenResult != ERROR_SUCCESS) return hrErrorReturn;

	// We no longer want to extend the computer management node (138503).
	// So we won't make this call to create the extension key. Also, we'll
	// delete it if it exists.
	//
	//	HRESULT		hrExtend = ExtendNode(cszCompMgrNode);
	//	ASSERT(hrExtend == S_OK);
	//	return hrExtend;

	CRegKey crkCompmgmtExtension;
	if (SUCCEEDED(OpenExtendKeyForNodeType(cszCompMgrNode, &crkCompmgmtExtension, TRUE)))
	{
		CRegKey crkNameSpace, crkTaskPad;

		if (ERROR_SUCCESS == crkNameSpace.Open((HKEY)crkCompmgmtExtension, cszNameSpace))
			crkNameSpace.DeleteValue(cszClsidMSInfoExtension);

		if (ERROR_SUCCESS == crkTaskPad.Open((HKEY)crkCompmgmtExtension, cszTask))
			crkTaskPad.DeleteValue(cszClsidMSInfoExtension);
	}

	return S_OK;
}

/*
 * UnregisterExtensionSnapin - Unregister the extension part of the snapin.
 *
 * History:	a-jsari		9/9/97		Initial version
 */
static inline HRESULT UnregisterExtensionSnapin()
{
	CRegKey			crkSnapinRoot;

	long lRegOpenResult = RegOpenMMCSnapinRoot(&crkSnapinRoot);
	if (lRegOpenResult != ERROR_SUCCESS) {
		if (lRegOpenResult != ERROR_FILE_NOT_FOUND) return E_FAIL;
	} else {
		// CHECK: Is this appropriate for potential extensions (probably)
		lRegOpenResult = crkSnapinRoot.RecurseDeleteKey(cszClsidMSInfoExtension);

		ASSERT(lRegOpenResult == ERROR_SUCCESS);
		//	It's not really an error to not find a key we were deleting anyhow.
		if (lRegOpenResult != ERROR_SUCCESS
			&& lRegOpenResult != ERROR_FILE_NOT_FOUND) {
			return E_FAIL;
		}
	}

	HRESULT		hrUnregister = UnregisterNode(cszCompMgrNode);
	return hrUnregister;
}

/*
 * RegisterPaths - Perform registration for path items
 *
 * History:	a-jsari		12/4/97		Initial version.
 */
static inline HRESULT RegisterPaths()
{
	CRegKey		crkSnapinRoot;
	CRegKey		crkProgramFilesKey;
	TCHAR		szBuffer[MAX_PATH];
	CString		strPath;
	DWORD		dwSize;
	long		lResult;

	do {
		//	Register MSInfo's Windows App Path.
		lResult = crkSnapinRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoKey);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;
		dwSize = sizeof(szBuffer);
		//	Get the Path registry value into szBuffer
		lResult = crkSnapinRoot.QueryValue(szBuffer, cszPathValue, &dwSize);
		if (lResult != ERROR_SUCCESS) {
			//	The path can't be read from the registry; register it.
			lResult = crkProgramFilesKey.Open(HKEY_LOCAL_MACHINE, cszWindowsCurrentKey);
			ASSERT(lResult == ERROR_SUCCESS);
			if (lResult != ERROR_SUCCESS) break;
			dwSize = sizeof(szBuffer);
			lResult = crkProgramFilesKey.QueryValue(szBuffer, cszCommonFilesValue, &dwSize);
			ASSERT(lResult == ERROR_SUCCESS);
			if (lResult != ERROR_SUCCESS) break;

			// Remove the quotes from around the path. Bug #363834.
			// strPath  = "\"";
			strPath = CString(_T(""));
			strPath += szBuffer;
			strPath += _T("\\");
			strPath += cszMSInfoPath;

			// Remove the quotes from around the path to mimic the behaviour of MSInfo 4.10 
			// (so that apps which used this key to launch MSInfo will continue to work). Bug #363834.

			if (strPath.Right(1) == CString(_T("\"")))
				strPath = strPath.Left(strPath.GetLength() - 1);

			//	Set the path value: Path = <Path to MSInfo32.exe>
			lResult = crkSnapinRoot.SetValue(strPath, cszPathValue);
			ASSERT(lResult == ERROR_SUCCESS);
			if (lResult != ERROR_SUCCESS) break;
		} else {
			//	Read the path from the previously registered location and set the variable
			//	equal to it.
			strPath = szBuffer;

			// Remove the quotes from around the path. Bug #363834.

			if (strPath.Left(1) == CString(_T("\"")))
			{
				strPath = strPath.Right(strPath.GetLength() - 1);
				if (strPath.Right(1) == CString(_T("\"")))
					strPath = strPath.Left(strPath.GetLength() - 1);

				lResult = crkSnapinRoot.SetValue(strPath, cszPathValue);
			}
		}

		// Bug #363834 - we still want quotes around the string when it's written elsewhere in the registry.

		if (strPath.Left(1) != CString(_T("\"")))
			strPath = _T("\"") + strPath;

		if (strPath.Right(1) != CString(_T("\"")))
			strPath += _T("\"");

		lResult = crkSnapinRoot.Create(HKEY_LOCAL_MACHINE, cszRunKey);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;
		lResult = crkSnapinRoot.SetValue(strPath);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;
		int		iValue = strPath.ReverseFind((TCHAR)'\\');
		strPath = strPath.Left(iValue);
		strPath += "\"";

		lResult = crkSnapinRoot.SetValue(strPath, cszPathValue);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;

#if 0
		//	We are now assuming that the path to MSInfo will be registered by some
		//	outside agent, presumably setup.
		//	Register the Path in the MSInfo directory
		lResult = crkSnapinRoot.Open(HKEY_LOCAL_MACHINE, cszMSInfoKey);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;
		//	Reuse the saved szPath value.
		lResult = crkSnapinRoot.SetValue(szPath, cszPathValue);
		ASSERT(lResult == ERROR_SUCCESS);
#endif
	} while (FALSE);
	return HRESULT_FROM_WIN32(lResult);
}

/*
 * UnregisterPaths - Remove registry entries for path items.
 *
 * History:	a-jsari		12/4/97		Initial version
 */
static inline HRESULT UnregisterPaths()
{
	CRegKey		crkRootKey;
	long		lResult;

	do {
		//	Remove the App Path
		lResult = crkRootKey.Open(HKEY_LOCAL_MACHINE, cszRunRootKey);
		if (lResult != ERROR_SUCCESS) break;
		lResult = crkRootKey.DeleteSubKey(cszRunSubKey);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;

		//	Unregister the MSInfo Key
		lResult = crkRootKey.Open(HKEY_LOCAL_MACHINE, cszMSInfoBaseKey);
		ASSERT(lResult == ERROR_SUCCESS);
		if (lResult != ERROR_SUCCESS) break;
		lResult = crkRootKey.DeleteSubKey(cszMSInfoSubKey);
		ASSERT(lResult == ERROR_SUCCESS);
	} while (FALSE);
	return HRESULT_FROM_WIN32(lResult);
}

/*
 * DllRegisterServer - Registers the snapin in
 *		HKEY_LOCAL_MACHINE\Software\Microsoft\MMC
 *		HKEY_LOCAL_MACHINE\Software\Classes\MSInfo.*
 *		HKEY_CLASSES_ROOT\CLSID\{45AC8C6...}
 *
 * History:	a-jsari		9/9/97		Initial version.
 */
STDAPI DllRegisterServer(void)
{
	long	lToolResult;
	if ((lToolResult = CToolList::Register(TRUE)) != ERROR_SUCCESS)
		return HRESULT_FROM_WIN32(lToolResult);
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	HRESULT		hrRegister = RegisterStandaloneSnapin();
	if (hrRegister != S_OK) return hrRegister;
	hrRegister = RegisterExtensionSnapin();
	if (hrRegister != S_OK) return hrRegister;
	hrRegister = RegisterPaths();
	if (hrRegister != S_OK) return hrRegister;
	// Registers object and all interfaces in typelib
#if 0
	//	This version has been failing
	return _Module.RegisterServer(TRUE);
#else
	//	So use this method.
	return _Module.RegisterServer(FALSE);
#endif
}

/*
 * DllUnregisterServer - Removes entries from the system registry
 *
 * History:	a-jsari		9/9/97		Initial version.
 */
STDAPI DllUnregisterServer(void)
{
	HRESULT		hrReturn = S_OK;
	long		lToolResult;

	if ((lToolResult = CToolList::Register(FALSE)) != ERROR_SUCCESS)
		hrReturn = HRESULT_FROM_WIN32(lToolResult);
	HRESULT		hrUnregister = UnregisterStandaloneSnapin();
	if (hrUnregister != S_OK) hrReturn = hrUnregister;
	hrUnregister = UnregisterExtensionSnapin();
	if (hrUnregister != S_OK) hrReturn = hrUnregister;
	hrUnregister = UnregisterPaths();
	if (hrUnregister != S_OK) hrReturn = hrUnregister;
	_Module.UnregisterServer();
	return hrReturn;
}

//-----------------------------------------------------------------------------
// Implementation for the CMSInfoLog class, used to keep a log of MSInfo
// activities.
//
// This global variable can be used elsewhere in the snap-in to write log
// entries. Only one should be created.
//-----------------------------------------------------------------------------

CMSInfoLog msiLog;

//-----------------------------------------------------------------------------
// The constructor needs to read the logging state information from the
// registry.
//-----------------------------------------------------------------------------

CMSInfoLog::CMSInfoLog()
{
	m_pLogFile = NULL;
	m_strEndMarker = _T("###"); // see note in OpenLogFile
	ReadLoggingStatus();
}

//-----------------------------------------------------------------------------
// The destructor needs to close the file (if it was ever created and opened).
// Also, this is a good place to put the exit MSInfo log entry.
//-----------------------------------------------------------------------------

CMSInfoLog::~CMSInfoLog()
{
	if (this->IsLogging())
		this->WriteLog(CMSInfoLog::BASIC, _T("EXIT MSInfo\r\n"));

	try
	{
		if (m_pLogFile)
		{
			// Advance past the marker we wrote.

			m_pLogFile->Seek(m_strEndMarker.GetLength() * sizeof(TCHAR), CFile::current);

			// If we aren't at the end of the file, then we've at some point wrapped
			// to the beginning and are overwriting entries. To make it so there are
			// no incomplete entries, write spaces until the end of the file or until
			// we find a '\r' character.

			DWORD dwLength = m_pLogFile->GetLength();
			DWORD dwPosition = m_pLogFile->GetPosition();
			if (dwPosition < dwLength)
			{
				DWORD dwBytesRead = 0;
				TCHAR cRead;

				do
				{
					if (m_pLogFile->Read((void *) &cRead, sizeof(TCHAR)) < sizeof(TCHAR))
						break;
					dwBytesRead += sizeof(TCHAR);
				} while (cRead && cRead != _T('\r') && (dwPosition + dwBytesRead) < dwLength);

				if (dwBytesRead)
				{
					m_pLogFile->Seek(dwBytesRead * -1, CFile::current);

					// Write the spaces (but don't overwrite the '\r' character).

					if (cRead == _T('\0') || cRead == _T('\r'))
						dwBytesRead -= sizeof(TCHAR);
					WriteSpaces(dwBytesRead / sizeof(TCHAR));
				}
			}

			m_pLogFile->Close();
			delete m_pLogFile;
			m_pLogFile = NULL;
		}
	}
	catch (CFileException *e)
	{
		// Some sort of file error - turn off logging.

		m_fLoggingEnabled = FALSE;
		m_iLoggingMask = 0;
	}
}

//-----------------------------------------------------------------------------
// These two WriteLog functions are for writing a log entry directly to the
// file. The second (with two string parameters) assumes that the first
// string is a format with a '%s' and the second is the replacement string.
//
// The iType parameter is used to indicate what sort of log entry this is.
// The const int values from the class definition should be used.
//
// If fContinuation is TRUE, then this write is to terminate a log entry which
// spans an operation, and should not have a timestamp added.
//-----------------------------------------------------------------------------

BOOL CMSInfoLog::WriteLog(int iType, const CString & strMessage, BOOL fContinuation)
{
	if (!OpenLogFile())
		return FALSE;

	if ((m_iLoggingMask & iType) == 0)
		return FALSE;

	CString strWorking(strMessage);

	// If this isn't the continuation of a previous log entry, then 
	// possibly add a timestamp.

	if (!fContinuation && m_fTimestamp)
	{
		CTime time = CTime::GetCurrentTime();
		strWorking = time.Format(_T("%Y-%m-%d %H:%M:%S ")) + strWorking;
	}

	return WriteLogInternal(strWorking);
}

BOOL CMSInfoLog::WriteLog(int iType, const CString & strFormat, const CString & strReplace1)
{
	CString strCombined;

	strCombined.Format(strFormat, strReplace1);
	return WriteLog(iType, strCombined);
}
	
//-----------------------------------------------------------------------------
// This function is called each time a log entry is written, to insure that
// the log file is open. If we can't open the file, or shouldn't be logging in
// the first place, this function should return FALSE.
//-----------------------------------------------------------------------------

BOOL CMSInfoLog::OpenLogFile()
{
	if (!m_fLoggingEnabled)
		return FALSE;

	if (m_pLogFile == NULL)
	{
		m_pLogFile = new CFile;
		if (m_pLogFile == NULL)
			return FALSE;

		try
		{
			if (!m_pLogFile->Open(m_strFilename, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite))
			{
				delete m_pLogFile;
				m_pLogFile = NULL;
				m_fLoggingEnabled = FALSE;
				m_iLoggingMask = 0;
				return FALSE;
			}

			// Move to the right place in the file. If the file is empty, we're
			// already there. If not, look for the end marker (from the last time
			// we added log entries). If there is one, we should be positioned over
			// its first character. Otherwise, just move to the end of the file.

			if (m_pLogFile->GetLength() != 0)
			{
				// IMPORTANT NOTE: We assume here (for efficiency) that the end
				// marker is structured so that we don't need to back up when
				// searching the file. For example, no markers of the form "aaab".

				TCHAR	cRead;
				int		iMarker = 0;

				while (iMarker != m_strEndMarker.GetLength())
				{
					if (m_pLogFile->Read((void *) &cRead, sizeof(TCHAR)) < sizeof(TCHAR))
						break;

					if (cRead != m_strEndMarker[iMarker])
						iMarker = 0;

					if (cRead == m_strEndMarker[iMarker])
						iMarker++;
				}

				if (iMarker == m_strEndMarker.GetLength())
					m_pLogFile->Seek(m_strEndMarker.GetLength() * sizeof(TCHAR) * -1, CFile::current);
				else
					m_pLogFile->SeekToEnd();
			}
		}
		catch (CFileException *e)
		{
			// Some sort of file error - turn off logging.

			m_fLoggingEnabled = FALSE;
			m_iLoggingMask = 0;
			return FALSE;
		}
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
// This function reads information about logging (what to log, where to log,
// etc.) out of the registry.
//-----------------------------------------------------------------------------

void CMSInfoLog::ReadLoggingStatus()
{
	m_fLoggingEnabled	= FALSE;
	m_fTimestamp		= TRUE;
	m_strFilename		= _T("");
	m_iLoggingMask		= 0;
	m_dwMaxFileSize		= 32 * 1024;

	CString	strRegkey = CString(cszMSInfoKey) + CString(_T("\\Logging"));
	CRegKey regkey;

	if (ERROR_SUCCESS == regkey.Open(HKEY_LOCAL_MACHINE, strRegkey, KEY_READ))
	{
		DWORD dwTemp;

		dwTemp = 0;
		if (ERROR_SUCCESS == regkey.QueryValue(dwTemp, _T("LogMask")))
			m_iLoggingMask = (int) dwTemp;

		dwTemp = 0;
		if (ERROR_SUCCESS == regkey.QueryValue(dwTemp, _T("LogFileMaxSize")))
			m_dwMaxFileSize = dwTemp;

		dwTemp = 0;
		if (ERROR_SUCCESS == regkey.QueryValue(dwTemp, _T("LogTimestamp")))
			m_fTimestamp = (dwTemp) ? TRUE : FALSE;

		TCHAR szFilename[MAX_PATH];
		dwTemp = MAX_PATH;
		if (ERROR_SUCCESS == regkey.QueryValue(szFilename, _T("LogFilename"), &dwTemp))
			m_strFilename = szFilename;

		regkey.Close();
	}

	m_fLoggingEnabled = ((m_iLoggingMask != 0) && !m_strFilename.IsEmpty());
}

//-----------------------------------------------------------------------------
// Write a string to the log file. We should be positioned before the end
// marker in the file. Overwrite this, and add the end marker onto the end
// of the string we are writing.
//
// If we are too close to the maximum file size, then fill the rest of the
// file (to that point) with spaces, and wrap to the start of the file.
//
// After we've written the output, back up so that we are positioned over the
// start of the end marker.
//-----------------------------------------------------------------------------

BOOL CMSInfoLog::WriteLogInternal(const CString & strMessage)
{
	CString strTerminated = strMessage + m_strEndMarker;
	DWORD	dwLength = strTerminated.GetLength() * sizeof(TCHAR);

	if (m_pLogFile == NULL)
		return FALSE;

	try
	{
		// If we are too close to the max size of the file, write spaces to that
		// size and start from the beginning.

		DWORD dwPosition = m_pLogFile->GetPosition();
		if (m_dwMaxFileSize && ((dwPosition + dwLength) > m_dwMaxFileSize))
		{
			WriteSpaces((m_dwMaxFileSize - dwPosition) / sizeof(TCHAR));
			m_pLogFile->SeekToBegin();
		}

		// Write the string to the file.

		m_pLogFile->Write((const void *) (LPCTSTR) strTerminated, strTerminated.GetLength() * sizeof(TCHAR));

		// Finally, back up over the terminating characters.

		m_pLogFile->Seek(m_strEndMarker.GetLength() * sizeof(TCHAR) * -1, CFile::current);
	}
	catch (CFileException *e)
	{
		// Some sort of file error - turn off logging.

		m_fLoggingEnabled = FALSE;
		m_iLoggingMask = 0;
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
// Write the specified number of spaces to the file. (Note, this will already
// be inside an exeption handling block from the caller.)
//-----------------------------------------------------------------------------

void CMSInfoLog::WriteSpaces(DWORD dwCount)
{
	TCHAR * szSpaceBuffer = NULL;
	szSpaceBuffer = new TCHAR[dwCount];
	if (szSpaceBuffer)
	{
		_tcsnset(szSpaceBuffer, _T(' '), dwCount);
		m_pLogFile->Write((const void *) (LPCTSTR) szSpaceBuffer, dwCount * sizeof(TCHAR));
		delete [] szSpaceBuffer;
	}
}