/*++

   Copyright    (c)    1994-1998    Microsoft Corporation

   Module  Name :

        minetmgr.cpp

   Abstract:

        Snapin object

   Author:

        Ronald Meijer (ronaldm)

   Project:

        Internet Services Manager

   Revision History:

--*/

//
// Include Files
//
#include "stdafx.h"
#include "inetmgr.h"
#include "initguid.h"
#include "cinetmgr.h"
#include "constr.h"
#include "mycomput.h"

//
// Registry Definitions
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


const LPCTSTR g_cszCLSID           = _T("CLSID");
const LPCTSTR g_cszLS32            = _T("LocalServer32");
const LPCTSTR g_cszIPS32           = _T("InprocServer32");
const LPCTSTR g_cszMMCBasePath     = _T("Software\\Microsoft\\MMC");
const LPCTSTR g_cszSnapins         = _T("Snapins");
const LPCTSTR g_cszNameString      = _T("NameString");
const LPCTSTR g_cszNameStringInd   = _T("NameStringIndirect");
const LPCTSTR g_cszProvider        = _T("Provider");
const LPCTSTR g_cszVersion         = _T("Version");
const LPCTSTR g_cszStandAlone      = _T("StandAlone");
const LPCTSTR g_cszNodeTypes       = _T("NodeTypes");
const LPCTSTR g_cszAbout           = _T("About");
const LPCTSTR g_cszExtensions      = _T("Extensions");
const LPCTSTR g_cszNameSpace       = _T("NameSpace");
const LPCTSTR g_cszDynamicExt      = _T("Dynamic Extensions");
const LPCTSTR g_cszValProvider     = _T("Microsoft");
const LPCTSTR g_cszValVersion      = _T("5.0");
const LPCTSTR g_cszMyCompMsc       = _T("%SystemRoot%\\system32\\compmgmt.msc");
const LPCTSTR g_cszServerAppsLoc   = _T("System\\CurrentControlSet\\Control\\Server Applications");



CFlexComModule _Module;




HRESULT WINAPI 
CFlexComModule::UpdateRegistryClass(
    IN const CLSID & clsid,
    IN LPCTSTR lpszProgID,
    IN LPCTSTR lpszVerIndProgID,
    IN UINT nDescID,
    IN DWORD dwFlags,
    IN BOOL bRegister
    )
/*++

Routine Description:

    Override of UpdateRegistry to change the path to a path that uses
    a %systemroot%\... EXPAND_SZ path

Arguments:

    const CLSID& clsid          : GUID
    LPCTSTR lpszProgID          : Program ID
    LPCTSTR lpszVerIndProgID    : Version independent program ID
    UINT nDescID                : Description resource ID
    DWORD dwFlags               : Flags
    BOOL bRegister              : TRUE for register, FALSE for unregister

Return Value:

    HRESULT

--*/
{
    //
    // Call base class as normal
    //
    CError err = CComModule::UpdateRegistryClass(
        clsid, 
        lpszProgID,
        lpszVerIndProgID, 
        nDescID, 
        dwFlags, 
        bRegister
        );

    if (bRegister && err.Succeeded())
    {
        CString str, strKey, strCLSID;
        GUIDToCString(clsid, strCLSID);

        //
        // Change the path in InProcServer32/LocalServer32 to a 
        // REG_EXPAND_SZ path
        //
        strKey.Format(_T("%s\\%s"), 
            g_cszCLSID, 
            strCLSID
            );

        CRMCRegKey rkCLSID(HKEY_CLASSES_ROOT, strKey, KEY_ENUMERATE_SUB_KEYS);
        if (!rkCLSID.Ok())
        {
            //
            // This should have been created by 
            // _Module.RegisterServer
            //
            err.GetLastWinError();
            return err;
        }

        //
        // Look for inproc32 or local32
        //
        CRMCRegKey rkInProc(rkCLSID, g_cszIPS32);
        CRMCRegKey rkLocProc(rkCLSID,  g_cszLS32);
        CRMCRegKey * prk = rkInProc.Ok()
            ? &rkInProc
            : rkLocProc.Ok()
                ? &rkLocProc
                : NULL;

        if (prk == NULL)
        {
            err.GetLastWinError();
            return err;
        }

        //
        // Rewrite entry as an REG_EXPAND_SZ
        //
        err = prk->QueryValue(NULL, str);
        if (err.Succeeded())
        {
            err = prk->SetValue(NULL, str, TRUE);        
        }
    }

    return err;
}



BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_Snapin, CComponentDataImpl)
    OBJECT_ENTRY(CLSID_About,  CSnapinAboutImpl)
END_OBJECT_MAP()



#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


//
// Message Map
//
BEGIN_MESSAGE_MAP(CSnapinApp, CWinApp)
    //{{AFX_MSG_MAP(CConfigDll)
    //}}AFX_MSG_MAP
    //
    // Global help commands
    //
    ON_COMMAND(ID_HELP, CWinApp::OnHelp)
    ON_COMMAND(ID_CONTEXT_HELP, CWinApp::OnContextHelp)
END_MESSAGE_MAP()


#ifdef _DEBUG

//
// Allocation tracker
//
BOOL 
TrackAllocHook( 
    IN size_t nSize, 
    IN BOOL bObject, 
    IN LONG lRequestNumber 
    )
{
    //
    // Track lRequestNumber here
    //
    //TRACEEOLID("allocation # " << lRequestNumber);

    return TRUE;
}

#endif // _DEBUG



CSnapinApp::CSnapinApp()
/*++

Routine Description:

    Constructor

Arguments:

    None

Return Value:

    N/A

--*/
    : CWinApp()
{

#ifdef _DEBUG

    afxMemDF |= checkAlwaysMemDF;

    AfxSetAllocHook(TrackAllocHook);

#endif // _DEBUG

}



void
CSnapinApp::SetHelpPath(
    IN CServerInfo * pItem OPTIONAL
    )
/*++

Routine Description:

    Change the default help path

Arguments:

    CServerInfo * pItem : New help path owner or NULL to reset the help path

Return Value:

    None

Notes:

    A call to SetHelpPath() without parameters restores the help path.

    The help handler for the snapin is used to support help for down-level
    inetmgr extensions that do not have their own help handler.  Since many
    of these extensions are MFC-based, help is expected to be provided in the
    CWinApp object.  

--*/
{
    if (pItem == NULL)
    {
        //
        // Restore help path back to the inetmgr app
        //
        ASSERT(m_lpOriginalHelpPath != NULL);
        m_pszHelpFilePath = m_strInetMgrHelpPath;
        return;
    }

    //
    // Set the current help path equal to the one expected
    // from the config dll name
    //
    LPTSTR lpPath = m_strHelpPath.GetBuffer(MAX_PATH + 1);
    ::GetModuleFileName(pItem->QueryInstanceHandle(), lpPath, MAX_PATH);
    LPTSTR lp2 = _tcsrchr(lpPath, _T('.'));
    ASSERT(lp2 != NULL);
    if (lp2 != NULL)
      *lp2 = '\0';
    m_strHelpPath.ReleaseBuffer();
    m_strHelpPath += _T(".HLP");
    m_pszHelpFilePath = m_strHelpPath;
}



BOOL 
CSnapinApp::InitInstance()
/*++

Routine Description

    Instance initiation handler

Arguments:

    None

Return Value:

    TRUE for success, FALSE for failure

--*/
{
    _Module.Init(ObjectMap, m_hInstance);

    //
    // Save a pointer to the old help file
    //
    m_lpOriginalHelpPath = m_pszHelpFilePath;

    //
    // Build up inetmgr help path, expanding 
    // the help path if necessary.
    //
    CRMCRegKey rk(REG_KEY, SZ_PARAMETERS, KEY_READ);
    rk.QueryValue(SZ_HELPPATH, m_strInetMgrHelpPath, EXPANSION_ON);
    m_strInetMgrHelpPath += _T("\\inetmgr.hlp");
    TRACEEOLID("Initialized help file " << m_strInetMgrHelpPath);

    m_pszHelpFilePath = m_strInetMgrHelpPath;
    
    return CWinApp::InitInstance();
}



int 
CSnapinApp::ExitInstance()
/*++

Routine Description:

    Exit instance handler

Arguments:

    None

Return Value:

    0 for success

--*/
{
    _Module.Term();

    //
    // Restore original help file path so it can be deleted
    //
    ASSERT(m_lpOriginalHelpPath != NULL);
    m_pszHelpFilePath = m_lpOriginalHelpPath;

    return CWinApp::ExitInstance();
}



//
// Instantiate the app object
//
CSnapinApp theApp;



STDAPI 
DllCanUnloadNow()
/*++

Routine Description:

    Used to determine whether the DLL can be unloaded by OLE

Arguments:

    None
    
Return Value:    
    
    HRESULT    

--*/
{
    AFX_MANAGE_STATE(::AfxGetStaticModuleState());

#ifdef _DEBUG

    HRESULT hr = ::AfxDllCanUnloadNow();
    LONG l = _Module.GetLockCount();
    TRACEEOLID("Module lock count " << l);
    BOOL fCanUnLoad = (l == 0 && hr == S_OK);

#endif // _DEBUG

    return (::AfxDllCanUnloadNow() == S_OK 
        && _Module.GetLockCount() == 0) ? S_OK : S_FALSE;
}



STDAPI 
DllGetClassObject(
    IN REFCLSID rclsid, 
    IN REFIID riid, 
    IN LPVOID * ppv
    )
/*++

Routine Description:

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

Arguments:

    REFCLSID rclsid
    REFIID riid
    LPVOID * ppv
    
Return Value:    
    
    HRESULT    

--*/
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}



STDAPI 
DllRegisterServer()
/*++

Routine Description:

    DllRegisterServer - Adds entries to the system registry

Arguments:

    None.

Return Value:

    HRESULT

--*/
{
    //
    // Registers object, typelib and all interfaces in typelib
    //
    CError err(_Module.RegisterServer(FALSE));

    if (err.Failed())
    {
        return err;
    }

    CString str, strKey, strExtKey;

    try
    {
        AFX_MANAGE_STATE(::AfxGetStaticModuleState());

        //
        // Create the primary snapin nodes
        //
        CString strNameString((LPCTSTR)IDS_NODENAME);
        CString strNameStringInd;
        // Fix for bug 96801: moving strings from the Registry to resources (MUI requirement)
        TCHAR path[MAX_PATH];
        GetModuleFileName(_Module.GetResourceInstance(), path, MAX_PATH - 1);
        strNameStringInd.Format(_T("@%s,-%d"), path, IDS_NODENAME);
        TRACEEOLID("MUI-lized snapin name: " << strNameStringInd);

        CString strProvider(g_cszValProvider);
        CString strVersion(g_cszValVersion);
        CString str;
    
        strKey.Format(
            _T("%s\\%s\\%s"), 
            g_cszMMCBasePath, 
            g_cszSnapins,
            GUIDToCString(CLSID_Snapin, str)
            );
        TRACEEOLID(strKey);

        CString strAbout;
        GUIDToCString(CLSID_About, strAbout);

        CRMCRegKey rkSnapins(NULL, HKEY_LOCAL_MACHINE, strKey);
        rkSnapins.SetValue(g_cszAbout,      strAbout);
        rkSnapins.SetValue(g_cszNameString, strNameString);
        rkSnapins.SetValue(g_cszNameStringInd, strNameStringInd);
        rkSnapins.SetValue(g_cszProvider,   strProvider);
        rkSnapins.SetValue(g_cszVersion,    strVersion);

        CRMCRegKey rkStandAlone(NULL, rkSnapins, g_cszStandAlone);
        CRMCRegKey rkNodeTypes (NULL, rkSnapins, g_cszNodeTypes);

        //
        // Create the nodetype GUIDS
        //
        CRMCRegKey rkN1(NULL, rkNodeTypes, GUIDToCString(cInternetRootNode, str));
        CRMCRegKey rkN2(NULL, rkNodeTypes, GUIDToCString(cMachineNode, str));
        CRMCRegKey rkN3(NULL, rkNodeTypes, GUIDToCString(cInstanceNode, str));
        CRMCRegKey rkN4(NULL, rkNodeTypes, GUIDToCString(cChildNode, str));
        CRMCRegKey rkN5(NULL, rkNodeTypes, GUIDToCString(cFileNode, str));

        {
            //
            // Register as a dynamic extension to computer management
            //
            strExtKey.Format(
                _T("%s\\%s\\%s\\%s"), 
                g_cszMMCBasePath, 
                g_cszNodeTypes,
                lstruuidNodetypeServerApps,
                g_cszDynamicExt
                );

            TRACEEOLID(strExtKey);

            CRMCRegKey rkMMCNodeTypes(NULL, HKEY_LOCAL_MACHINE, strExtKey);
            rkMMCNodeTypes.SetValue(            
                GUIDToCString(CLSID_Snapin, str),
                strNameString
                );
        }

        {
            //
            // Register as a namespace extension to computer management
            //
            strExtKey.Format(
                _T("%s\\%s\\%s\\%s\\%s"), 
                g_cszMMCBasePath, 
                g_cszNodeTypes,
                lstruuidNodetypeServerApps,
                g_cszExtensions,
                g_cszNameSpace
                );

            TRACEEOLID(strExtKey);

            CRMCRegKey rkMMCNodeTypes(NULL, HKEY_LOCAL_MACHINE, strExtKey);
            rkMMCNodeTypes.SetValue(            
                GUIDToCString(CLSID_Snapin, str),
                strNameString
                );
        }

        //
        // This key indicates that the service in question is available
        // on the local machine
        //
        CRMCRegKey rkCompMgmt(
            NULL, 
            HKEY_LOCAL_MACHINE, 
            g_cszServerAppsLoc
            );

        GUIDToCString(CLSID_Snapin, str);
        rkCompMgmt.SetValue(
            GUIDToCString(CLSID_Snapin, str),
            strNameString
            );
    }
    catch(CMemoryException * e)
    {
        e->Delete();
        err = ERROR_NOT_ENOUGH_MEMORY;
    }
    catch(COleException * e)
    {
        e->Delete();
        err = SELFREG_E_CLASS;
    }

    return err;
}



STDAPI 
DllUnregisterServer()
/*++

Routine Description:

    DllUnregisterServer - Removes entries from the system registry

Arguments:

    None.

Return Value:

    HRESULT

--*/
{
    CError err;

    try
    {
        CString strKey(g_cszMMCBasePath);
        strKey += _T("\\");
        strKey += g_cszSnapins;

        TRACEEOLID(strKey);

        CRMCRegKey rkBase(HKEY_LOCAL_MACHINE, strKey);
        CString str, strExtKey;
        {
            CRMCRegKey rkCLSID(rkBase, GUIDToCString(CLSID_Snapin, str));
            ::RegDeleteKey(rkCLSID, g_cszStandAlone);
            {
                CRMCRegKey rkNodeTypes(rkCLSID, g_cszNodeTypes);
                ::RegDeleteKey(rkNodeTypes, GUIDToCString(cInternetRootNode, str));
                ::RegDeleteKey(rkNodeTypes, GUIDToCString(cMachineNode, str));
                ::RegDeleteKey(rkNodeTypes, GUIDToCString(cInstanceNode, str));
                ::RegDeleteKey(rkNodeTypes, GUIDToCString(cChildNode, str));
                ::RegDeleteKey(rkNodeTypes, GUIDToCString(cFileNode, str));
            }

            ::RegDeleteKey(rkCLSID, g_cszNodeTypes);
        }

        ::RegDeleteKey(rkBase, GUIDToCString(CLSID_Snapin, str));

        {
            //
            // Delete a dynamic extension to computer management
            //
            strExtKey.Format(
                _T("%s\\%s\\%s\\%s"), 
                g_cszMMCBasePath, 
                g_cszNodeTypes,
                lstruuidNodetypeServerApps,
                g_cszDynamicExt
                );

            CRMCRegKey rkMMCNodeTypes(HKEY_LOCAL_MACHINE, strExtKey);
            ::RegDeleteValue(rkMMCNodeTypes, GUIDToCString(CLSID_Snapin, str));
        }

        {
            //
            // Delete the namespace extension to computer management
            //
            strExtKey.Format(
                _T("%s\\%s\\%s\\%s\\%s"), 
                g_cszMMCBasePath, 
                g_cszNodeTypes,
                lstruuidNodetypeServerApps,
                g_cszExtensions,
                g_cszNameSpace
                );

            CRMCRegKey rkMMCNodeTypes(HKEY_LOCAL_MACHINE, strExtKey);
            ::RegDeleteValue(rkMMCNodeTypes, GUIDToCString(CLSID_Snapin, str));
        }

        //
        // And the service itself no longer available on the local 
        // computer
        //
        CRMCRegKey rkCompMgmt(
            HKEY_LOCAL_MACHINE, 
            g_cszServerAppsLoc
            );
        ::RegDeleteValue(rkCompMgmt, GUIDToCString(CLSID_Snapin, str));
    }
    catch(CException * e)
    {
        err.GetLastWinError();
        e->Delete();
    }

    if (err.Failed())
    {
        return err.Failed();
    }

    return _Module.UnregisterServer();
}