/*===================================================================
Microsoft IIS

Microsoft Confidential.
Copyright 1997 Microsoft Corporation. All Rights Reserved.

Component: WAMREG

File: Auxfunc.cpp

    implementation of supporting functions for WAMREG, including

    interface to Register and Unregister WAM CLSID in registry,
    interface to Create Package with MTS,

Owner: LeiJin

Note:

===================================================================*/
#include "common.h"
#include "auxfunc.h"
#include "iiscnfgp.h"
#include "dbgutil.h"
#include "iwamreg.h"
#include <inetinfo.h>

//==================================================================
//    Global data definitions
//
//==================================================================

//
// string contains the physical path of wamreg.dll, ie. c:\winnt\system32\inetsrv\wam.dll
//
CHAR    WamRegRegistryConfig::m_szWamDllPath[MAX_PATH];

//
// the permission to access the registry
//
const REGSAM    WamRegRegistryConfig::samDesired =    KEY_READ | KEY_WRITE;

//
//  A thread will first grab a token, and wait until the token matches the m_dwServiceNum,
//  In such way, the order of threads making the requests are perserved.
//  m_hWriteLock (Event) is used for the blocking other threads
//  m_csWAMREGLock is used for access the m_dwServiceToken and m_dwServiceNum
//
//  private global, static variables for WamAdmLock

WamAdmLock              WamRegGlobal::m_WamAdmLock;
WamRegGlobal            g_WamRegGlobal;

WamRegRegistryConfig    g_RegistryConfig;

// 
// Defined at /LM/W3SVC/
// Default package ID(IIS In-Process Application) and the default WAMCLSID(IISWAM.W3SVC).
//
const WCHAR   WamRegGlobal::g_szIISInProcPackageID[] =
                 W3_INPROC_PACKAGE_ID;
const WCHAR   WamRegGlobal::g_szInProcWAMCLSID[] = 
                 W3_INPROC_WAM_CLSID;
const WCHAR   WamRegGlobal::g_szInProcWAMProgID[] = L"IISWAM.W3SVC";

const WCHAR   WamRegGlobal::g_szIISOOPPoolPackageID[] =
                 W3_OOP_POOL_PACKAGE_ID;
const WCHAR   WamRegGlobal::g_szOOPPoolWAMCLSID[] =
                 W3_OOP_POOL_WAM_CLSID;
const WCHAR   WamRegGlobal::g_szOOPPoolWAMProgID[] = L"IISWAM.OutofProcessPool";

const WCHAR   WamRegGlobal::g_szIISInProcPackageName[] = DEFAULT_PACKAGENAME;
const WCHAR   WamRegGlobal::g_szIISOOPPoolPackageName[] = L"IIS Out-Of-Process Pooled Applications";
const WCHAR   WamRegGlobal::g_szMDAppPathPrefix[] = L"/LM/W3SVC/";
const DWORD   WamRegGlobal::g_cchMDAppPathPrefix = 
                 (sizeof(L"/LM/W3SVC/")/sizeof(WCHAR)) - 1;
const WCHAR   WamRegGlobal::g_szMDW3SVCRoot[] = L"/LM/W3SVC";
const DWORD   WamRegGlobal::g_cchMDW3SVCRoot = (sizeof(L"/LM/W3SVC")/sizeof(WCHAR)) - 1;

#ifndef DBGERROR
#define DBGERROR(args) ((void)0) /* Do Nothing */
#endif
//==================================================================
//  Local functions
//
//==================================================================

//===============================================================
// in line functions
//
//===============================================================
BOOL WamRegGlobal::Init(VOID)
{
    return m_WamAdmLock.Init();
}

BOOL WamRegGlobal::UnInit(VOID)
{
    return m_WamAdmLock.UnInit();
}

//===============================================================
// local functions
//
//===============================================================
WamAdmLock::WamAdmLock()
:   m_dwServiceToken(0),
    m_dwServiceNum(0),
    m_hWriteLock((HANDLE)NULL)
{

}
/*===================================================================
Init

Init certain variables for this supporting module of WAMREG.

Return:        NONE
===================================================================*/
BOOL WamAdmLock::Init()
{        
    BOOL fReturn = TRUE;
    
    INITIALIZE_CRITICAL_SECTION(&m_csLock);

    m_hWriteLock = IIS_CREATE_EVENT(
                       "WamAdmLock::m_hWriteLock",
                       &m_hWriteLock,
                       TRUE,
                       TRUE
                       );

    if (m_hWriteLock == NULL)
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to create m_hWriteLock(Event). error = %08x",
            GetLastError()));
        fReturn = FALSE;
        }
    return fReturn;

}

/*===================================================================
UnInit

Uninit certain variables for this supporting module of WAMREG.

Return:        NONE
===================================================================*/
BOOL WamAdmLock::UnInit()
{
    BOOL fReturn = TRUE;
    DeleteCriticalSection(&m_csLock);
    if (m_hWriteLock)
        {
        BOOL fTemp = CloseHandle(m_hWriteLock);
        if ( fTemp == FALSE)
            {
            DBGPRINTF((DBG_CONTEXT, "error in CloseHandle. errno = %d\n", GetLastError()));
            fReturn = FALSE;
            }
        m_hWriteLock = (HANDLE)0;
        }
    return fReturn;
}

/*===================================================================
GetNextServiceToken

Obtain the next service token when a thread enters WAMREG, if the token
obtained by the thread is not the same as m_dwServiceNum, the thread has
to wait until the token it owns is same as m_dwServiceNum.

The function returns a token value.

Return:        DWORD, Next Service Token
===================================================================*/
DWORD WamAdmLock::GetNextServiceToken( )
{
    DWORD dwToken;

    Lock();
    dwToken = m_dwServiceToken;
    m_dwServiceToken++;
    UnLock();

    return dwToken;
}

/*===================================================================
FAppPathAllowConfig

Test to see if we can make configuration changes(Delete/Create) on a path.  Currently,
this function is used to block changes to the default application/package.  The default
in proc package should not be deleted/altered at runtime.

Parameter:
pwszMetabasePath

Return:         BOOL

Side affect:    TRUE if we can configure the app on the app path.
===================================================================*/
BOOL WamRegGlobal::FAppPathAllowConfig
(
IN LPCWSTR pwszMetabasePath
)
{
    BOOL fReturn = TRUE;
    DWORD cchMDPath = 0;

    DBG_ASSERT(pwszMetabasePath);
    // Since szMDPath has a path that always starts with "/LM/W3SVC/", the input size must be
    // greater that length of "/LM/W3SVC/", This check is necessary to protect that the default
    // IIS (inproc) package being deleted by accident. 
    cchMDPath = wcslen(pwszMetabasePath);
    if (cchMDPath <= WamRegGlobal::g_cchMDAppPathPrefix)
        {
        fReturn = FALSE;
        }

    return fReturn;
}

/*===================================================================
FIsW3SVCRoot

Test to see the MetabasePath is same as L"/LM/W3SVC".

Parameter:
pwszMetabasePath

Return:         BOOL

Side affect:    TRUE if we can configure the app on the app path.
===================================================================*/
BOOL WamRegGlobal::FIsW3SVCRoot
(
IN LPCWSTR	wszMetabasePath
)
{
    INT iReturn;
    DBG_ASSERT(wszMetabasePath != NULL);
    
    iReturn = _wcsnicmp(wszMetabasePath, WamRegGlobal::g_szMDW3SVCRoot, WamRegGlobal::g_cchMDW3SVCRoot+1);
    return (iReturn == 0 ? TRUE : FALSE);
}

/*===================================================================
AcquireLock

Get a write lock, there can only be one thread doing work via DCOM interface, (i.e. has the write lock).
All other threads calling WamAdmin interfaces are blocked in this function.  After returning from
this call, the thread is guaranteed to be a "Critical Section".

A simple CriticalSection only solve half of the problem.  It guarantees the mutual exclusive condition.
But once a thread leaves the CS, the CS can not control which blocking threads can access CS next.

Parameter:
NONE.

Return:         HRESULT

Side affect:    Once returned, thread is in a "Critical Section".
===================================================================*/
VOID WamAdmLock::AcquireWriteLock( )
{
    DWORD     dwWaitReturn = WAIT_OBJECT_0;
    DWORD     dwMyToken = GetNextServiceToken();
    BOOL    fIsMyTurn = FALSE;  // Assume it is not my turn before we try to acquire the lock.
    
    DBG_ASSERT(m_hWriteLock);
    do    {
        dwWaitReturn = WaitForSingleObject(m_hWriteLock, INFINITE);

        //
        // Check for successful return.
        //
        if (dwWaitReturn == WAIT_OBJECT_0)        
            {
            Lock();
            if (dwMyToken == m_dwServiceNum)
                {
                fIsMyTurn = TRUE;
                }
            UnLock();
                
            }
        else
            {
            //
            // A failure down this code path might cause a busy loop...
            // However, such failure is very unlikely.  Attach a debugger can tell the story immediately.
            //
            DBGPRINTF((DBG_CONTEXT, "WaitForSingleObject failed, function returns %08x, errno = %08x\n",
                        dwWaitReturn,
                        GetLastError()));
            DBG_ASSERT(FALSE);
            }
    } while (FALSE == fIsMyTurn);
    
    ResetEvent(m_hWriteLock);
    IF_DEBUG(WAMREG_MTS)
        {
        DBGPRINTF((DBG_CONTEXT, "Thread %08x acquired the WriteLock of WAMREG, ServiceNum is %d.\n",
                    GetCurrentThreadId(),
                    dwMyToken));
        }
}

/*===================================================================
ReleaseLock

Release a write lock.  See comments in CWmRgSrv::AcquireLock.

Parameter:
NONE.

Return:         HRESULT

Side affect:    Leave "Critical Section".
===================================================================*/
VOID WamAdmLock::ReleaseWriteLock( )
{
    //CONSIDER: m_dwServerNum out-of-bound
    Lock();
    IF_DEBUG(WAMREG_MTS)
        {
        DBGPRINTF((DBG_CONTEXT, "Thread %08x released the WriteLock of WAMREG, ServiceNum is %d.\n",
            GetCurrentThreadId(),
            m_dwServiceNum));
        }
        
    m_dwServiceNum++;
    SetEvent(m_hWriteLock);
    UnLock();

}


/*===================================================================
RegisterCLSID

Register a WAM CLSID and a ProgID..After a successful registerCLSID call, you
should have

My Computer\HKEY_CLASSES_ROOT\CLSID\{szCLSIDEntryIn}
                                        \InProcServer32     "...physical location of wam.dll"
                                        \ProgID             szProgIDIn
                                        \VersionIndependentProgID   "IISWAM.W3SVC"

Parameter:
szCLSIDEntryIn:     CLSID for a WAM.
szProgIDIn:         ProgID for the WAM.
fSetVIProgID:       TRUE if this function needs to set the Version Independent ProgID.
                    FALSE, Otherwise.

Return:     HRESULT

NOTE: Registry API should use ANSI version, otherwise, it will cause trouble on Win95.

===================================================================*/
HRESULT WamRegRegistryConfig::RegisterCLSID
(
IN LPCWSTR szCLSIDEntryIn,
IN LPCWSTR szProgIDIn,
IN BOOL fSetVIProgID
)
{
    static const CHAR szWAMDLL[]            = "wam.dll";
    static const CHAR szClassDesc[]         = "Web Application Manager Object";
    static const CHAR szThreadingModel[]    = "ThreadingModel";
    static const CHAR szInprocServer32[]    = "InprocServer32";
    static const CHAR szTEXT_VIProgID[]     = "VersionIndependentProgID";
    static const CHAR szTEXT_ProgID[]       = "ProgID";
    static const CHAR szTEXT_Clsid[]        = "Clsid";
    static const CHAR szFreeThreaded[]      = "Free";
    static const CHAR szVIProgID[]          = "IISWAM.Application";

    HRESULT     hr = E_FAIL;
    HKEY        hkeyT = NULL, hkey2 = NULL;
    CHAR         szCLSIDPath[100] = "CLSID\\";   // CLSID\\{....} , less that 100.
    CHAR        szCLSIDEntry[uSizeCLSID];       // ANSI version of CLSID.
    CHAR*        szProgID = NULL;                // a pointer to ANSI version of ProgID.
    DWORD        dwSizeofProgID = 0;             // # of char of ProgID.
    DBG_ASSERT(szProgIDIn);
    DBG_ASSERT(szCLSIDEntryIn);
    
    //
    //    Make a clsid ID.
    //
    WideCharToMultiByte(CP_ACP, 0, szCLSIDEntryIn, -1, szCLSIDEntry, uSizeCLSID, NULL, NULL);
    strncat(szCLSIDPath, szCLSIDEntry, uSizeCLSID);
    
    //
    //  Make a Prog ID.
    //
    // *2 for DBCS enabling for App MD path
    dwSizeofProgID = wcslen(szProgIDIn)*2 + 1;
    szProgID = new CHAR[dwSizeofProgID];
    
    if (NULL == szProgID)
        {
        hr = E_OUTOFMEMORY;
        goto LErrExit;
        }
    WideCharToMultiByte(CP_ACP, 0, szProgIDIn, -1, szProgID, dwSizeofProgID, NULL, NULL);
    
    // install the CLSID key
    // Setting the value of the description creates the key for the clsid
    //
    if ((RegSetValueA(HKEY_CLASSES_ROOT, szCLSIDPath, REG_SZ, szClassDesc,
        strlen(szClassDesc)) != ERROR_SUCCESS))
        goto LErrExit;
    //
    // Open the CLSID key so we can set values on it
    //
    if    (RegOpenKeyExA(HKEY_CLASSES_ROOT, szCLSIDPath, 0, samDesired, &hkeyT) != ERROR_SUCCESS)
            goto LErrExit;
    //
    // install the InprocServer32 key and open the sub-key to set the named value
    //
    if ((RegSetValueA(hkeyT, szInprocServer32, REG_SZ, m_szWamDllPath, strlen(m_szWamDllPath)) != ERROR_SUCCESS) ||
        (RegOpenKeyExA(hkeyT, szInprocServer32, 0, samDesired, &hkey2) != ERROR_SUCCESS))
        goto LErrExit;
    //
    // install the ProgID key and version independent ProgID key
    //
    if ((RegSetValueA(hkeyT, szTEXT_ProgID, REG_SZ, szProgID, strlen(szProgID)) != ERROR_SUCCESS) ||
        (RegSetValueA(hkeyT, szTEXT_VIProgID, REG_SZ, szVIProgID, strlen(szVIProgID)) != ERROR_SUCCESS))
        goto LErrExit;

    if (RegCloseKey(hkeyT) != ERROR_SUCCESS)
            goto LErrExit;

    hkeyT = hkey2;
    hkey2 = NULL;
    //
    // install the ThreadingModel named value
    //
    if (RegSetValueExA(hkeyT, szThreadingModel, 0, REG_SZ, (const BYTE *)szFreeThreaded,
        strlen(szFreeThreaded)) != ERROR_SUCCESS)
        goto LErrExit;
    if (RegCloseKey(hkeyT) != ERROR_SUCCESS)
        goto LErrExit;
    else
        hkeyT = NULL;

   // Set up ProgID key
    if ((RegSetValueA(HKEY_CLASSES_ROOT, szProgID, REG_SZ, szClassDesc,
        strlen(szClassDesc)) != ERROR_SUCCESS))
        goto LErrExit;

    if  (RegOpenKeyExA(HKEY_CLASSES_ROOT, szProgID, 0, samDesired, &hkeyT) != ERROR_SUCCESS)
        goto LErrExit;

    if ((RegSetValueA(hkeyT, szTEXT_Clsid, REG_SZ, szCLSIDEntry, strlen(szCLSIDEntry)) != ERROR_SUCCESS))
        goto LErrExit;

    if (RegCloseKey(hkeyT) != ERROR_SUCCESS)
        goto LErrExit;
    else
        hkeyT = NULL;

    // Set up Version Independent key only at setup IIS default time
    if (fSetVIProgID)
        {
        if ((RegSetValueA(HKEY_CLASSES_ROOT, szVIProgID, REG_SZ, szClassDesc,
            strlen(szClassDesc)) != ERROR_SUCCESS))
            goto LErrExit;

        if  (RegOpenKeyExA(HKEY_CLASSES_ROOT, szVIProgID, 0, samDesired, &hkeyT) != ERROR_SUCCESS)
            goto LErrExit;

        if ((RegSetValueA(hkeyT, szTEXT_Clsid, REG_SZ, szCLSIDEntry, strlen(szCLSIDEntry)) != ERROR_SUCCESS))
            goto LErrExit;

        if (RegCloseKey(hkeyT) != ERROR_SUCCESS)
            goto LErrExit;
        else
            hkeyT = NULL;
        }

    hr = NOERROR;


LErrExit:
    if (szProgID)
        {
        delete [] szProgID;
        szProgID = NULL;
        }

    if (hkeyT)
        {
        RegCloseKey(hkeyT);
        }
    if (hkey2)
        {
        RegCloseKey(hkey2);
        }

    return hr;
}

/*===================================================================
UnRegisterCLSID    

UnRegister a WAM CLSID & a corresponding WAM PROG ID.

Parameter:
szCLSIDEntryIn:    [in]     CLSID for a WAM.
fDeleteVIProgID:        TRUE, to delete the version independent prog id, FALSE, not touch VI progID.

Return:        HRESULT
===================================================================*/
HRESULT WamRegRegistryConfig::UnRegisterCLSID
(
IN LPCWSTR wszCLSIDEntryIn, 
IN BOOL fDeleteVIProgID
)
{
    HRESULT        hr = E_FAIL;
    HKEY        hkey = NULL;
    CHAR        szCLSIDEntry[uSizeCLSID];
    CHAR        szCLSIDPath[100] = "CLSID\\";
    WCHAR        *szProgID = NULL;
    CLSID       Clsid_WAM;
    static      const WCHAR szVIProgID[]    = L"IISWAM.Application";

    DBG_ASSERT(wszCLSIDEntryIn);
    //
    //    Make a clsid ID.
    //
    WideCharToMultiByte(CP_ACP, 0, wszCLSIDEntryIn, -1, szCLSIDEntry, uSizeCLSID, NULL, NULL);
    strncat(szCLSIDPath, szCLSIDEntry, uSizeCLSID);

    //
    // UnRegister ProgID and Version Independent Prog ID.
    //
    hr = CLSIDFromString((WCHAR *)wszCLSIDEntryIn, &Clsid_WAM);
    if (SUCCEEDED(hr))
        {
        hr = ProgIDFromCLSID(Clsid_WAM, &szProgID);    
        if (SUCCEEDED(hr))
            {
            hr = UnRegisterProgID(szProgID);
            CoTaskMemFree(szProgID);
            szProgID = NULL;
            }
        else
            {
            DBGPRINTF((DBG_CONTEXT, "error = %08x\n", hr));
            }
        }
    else
        {
        DBGPRINTF((DBG_CONTEXT, "error = %08x\n", hr));
        }


    if (fDeleteVIProgID)
        {
        hr = UnRegisterProgID(szVIProgID);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "error = %08x\n", hr));
            }
        }

    DWORD dwReg;
    //
    // Open the HKEY_CLASSES_ROOT\CLSID\{...} key so we can delete its subkeys
    //
    dwReg = RegOpenKeyExA(HKEY_CLASSES_ROOT, szCLSIDPath, 0, samDesired, &hkey);
    if    (dwReg == ERROR_SUCCESS)
        {    
        DWORD        iKey = 0;
        CHAR        szKeyName[MAX_PATH];  
        DWORD        cbKeyName;
        //
        // Enumerate all its subkeys, and delete them
        //    for (iKey=0;;iKey++) might not work with multiple sub keys, the last interation has iKey >
        // the actually number of subkeys left.  Set iKey = 0, so that we can always delete them all.
        //
        while(TRUE)
            {
            cbKeyName = sizeof(szKeyName);
            if (RegEnumKeyExA(hkey, iKey, szKeyName, &cbKeyName, 0, NULL, 0, NULL) != ERROR_SUCCESS)
                break;

            if (RegDeleteKeyA(hkey, szKeyName) != ERROR_SUCCESS)
                break;
            }

        // Close the key, and then delete it
        dwReg = RegCloseKey(hkey);
        if ( dwReg != ERROR_SUCCESS)
            {
            DBGPRINTF((DBG_CONTEXT, "error = %08x\n", HRESULT_FROM_WIN32(dwReg)));
            }
        }

    dwReg = RegDeleteKeyA(HKEY_CLASSES_ROOT, szCLSIDPath);
    if ( dwReg != ERROR_SUCCESS)
        {
        DBGPRINTF((DBG_CONTEXT, "error = %08x\n", HRESULT_FROM_WIN32(dwReg)));
        }

    //
    // Return hr Result
    //
    if (SUCCEEDED(hr))
        {
        if (dwReg != ERROR_SUCCESS)
            {
            hr = HRESULT_FROM_WIN32(dwReg);
            }
        }
    else
        {
        DBG_ASSERT((DBG_CONTEXT, "Failed to UnRegisterCLSID, %S, fDeleteVIProgID = %s\n",
            wszCLSIDEntryIn,
            (fDeleteVIProgID ? "TRUE" : "FALSE")));
        }
        
    return hr;
}

/*===================================================================
UnRegisterProgID

UnRegister a Prog ID.

Parameter:
szProgID:    [in]     Prog ID can be a version independent Prog ID.

Return:        HRESULT
===================================================================*/
HRESULT WamRegRegistryConfig::UnRegisterProgID
(
IN LPCWSTR szProgIDIn
)
{
    HKEY        hkey = NULL;
    DWORD        iKey;
    DWORD        cbKeyName;
    DWORD        dwSizeofProgID;
    CHAR        szKeyName[255];
    CHAR*        szProgID = NULL;
    HRESULT        hr = E_FAIL;

    DBG_ASSERT(szProgIDIn);
    //
    //  Make a Prog ID.
    //
    // DBCS enabling *2
    dwSizeofProgID = wcslen(szProgIDIn)*2 + 1;
    szProgID = new CHAR[dwSizeofProgID];
    
    if (NULL == szProgID)
        {
        hr = E_OUTOFMEMORY;
        goto LErrExit;
        }
    WideCharToMultiByte(CP_ACP, 0, szProgIDIn, -1, szProgID, dwSizeofProgID, NULL, NULL);
    
    // Open the HKEY_CLASSES_ROOT\szProgID key so we can delete its subkeys
    if    (RegOpenKeyExA(HKEY_CLASSES_ROOT, szProgID, 0, samDesired, &hkey) != ERROR_SUCCESS)
        goto LErrExit;

    // Enumerate all its subkeys, and delete them
    for (iKey=0;;iKey++)
        {
        cbKeyName = sizeof(szKeyName);
        if (RegEnumKeyExA(hkey, iKey, szKeyName, &cbKeyName, 0, NULL, 0, NULL) != ERROR_SUCCESS)
            break;

        if (RegDeleteKeyA(hkey, szKeyName) != ERROR_SUCCESS)
            goto LErrExit;
        }

    // Close the key, and then delete it
    if (RegCloseKey(hkey) != ERROR_SUCCESS)
        goto LErrExit;
    else
        hkey = NULL;
        
    if (RegDeleteKeyA(HKEY_CLASSES_ROOT, szProgID) != ERROR_SUCCESS)
        goto LErrExit;

    hr = NOERROR;

LErrExit:
    if (szProgID)
        delete [] szProgID;

    if (hkey)
        RegCloseKey(hkey);
        
    return hr;
}


/*===================================================================
LoadWamDllPath    

Read Wam Dll Path from Registry.  This function is implemented in ANSI version
of Registry API(Win95 compatibility).

Parameter:

Return:     HRESULT
Side Affect:
    NONE.
===================================================================*/
HRESULT WamRegRegistryConfig::LoadWamDllPath
(
void
)
{
    LONG    lReg = 0;
    HKEY    hKey = NULL;
    HRESULT hr = NOERROR;

    m_szWamDllPath[0] = '\0';
    
    lReg = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
                    "Software\\Microsoft\\InetStp",
                    0,
                    KEY_READ,
                    &hKey);

    if (lReg == ERROR_SUCCESS) 
        {
        LONG    lErr = 0;
        DWORD    dwType;
        CHAR    szWamDllName[] = "\\wam.dll";
        DWORD    cbData = (sizeof(m_szWamDllPath) - sizeof(szWamDllName));

        lErr = RegQueryValueExA(hKey,
                    "InstallPath",
                    0,
                    &dwType,
                    (LPBYTE)m_szWamDllPath,
                    &cbData);

        if (lErr == ERROR_SUCCESS)
            {
            if (dwType == REG_SZ) 
                {    
                strncat(m_szWamDllPath, szWamDllName, sizeof(szWamDllName));
                hr = NOERROR;
                }
            else
                {
                hr = E_UNEXPECTED;
                DBGPRINTF((DBG_CONTEXT, "Wrong Type for InstallPath registry key.dwType = %d\n",
                        dwType));
                }
            }
        else
            {
            hr = HRESULT_FROM_WIN32(lErr);
            }
        RegCloseKey(hKey);
        }
    else
        {
        hr = HRESULT_FROM_WIN32(lReg);
        }

    return hr;
}

/*===================================================================
SzWamProgID    

Make a WAM Prog ID, if the MetabasePath is null, assume it is the default,
that is WAM de
fault, so, it will be IISWAM.W3SVC.  Otherwise, the format is
IISWAM.__1__Application__Path where application path is \\LM\w3svc\1\
Application_path.

Parameter:
szMetabasePath:            [in] MetabasePath.

Return:        
TYPE:    LPWSTR, a string contains ProgID
        NULL, if failed.
        
Side Affect:
    Allocate memory for return result use new.  Caller needs to free szWamProgID 
use delete[].
===================================================================*/
HRESULT WamRegGlobal::SzWamProgID    
(
IN LPCWSTR pwszMetabasePath,
OUT LPWSTR* ppszWamProgID
)
{
    HRESULT            hr = NOERROR;
    static WCHAR    wszIISProgIDPreFix[]    = L"IISWAM.";   
    WCHAR            *pwszResult = NULL;
    WCHAR            *pwszApplicationPath = NULL;
    UINT             uPrefixLen = (sizeof(wszIISProgIDPreFix) / sizeof(WCHAR));


    DBG_ASSERT(pwszMetabasePath);
    *ppszWamProgID = NULL;

    //
    // Make a new WAM Prog ID based on pwszMetabasepath
    //
    WCHAR     *pStr, *pResult;
    UINT    uSize = 0;

    //
    // CONSIDER: use (sizeof(L"/LM/W3SVC/")/sizeof(WCHAR) - 1) for 10
    // CONSIDER: use global const for L"/LM/W3SVC/"
    // Since all paths start with /LM/W3SVC/, omit the prefix.
    //
    if (_wcsnicmp(pwszMetabasePath, L"\\LM\\W3SVC\\", 10) == 0 ||
        _wcsnicmp(pwszMetabasePath, L"/LM/W3SVC/", 10) == 0)
        {
        pwszApplicationPath = (WCHAR *)(pwszMetabasePath + 10);
        }
    else
        {
        *ppszWamProgID = NULL;
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
        }

    if (SUCCEEDED(hr))
        {
        pStr = pwszApplicationPath;
        //
        // Calculate uSize for allocation
        //
        while(*pStr != NULL)
            {
            //
            // '/' or '\\' will be converted to '__', from 1 char to 2 chars.
            //
            if (*pStr == '\\' || *pStr == '/')
                uSize ++;
            pStr++;
            uSize++;
            }

        DBG_ASSERT(uSize > 0);
        uSize += uPrefixLen;
        
        // uSize takes the null terminator into count.
        pwszResult = new WCHAR[uSize];
        if (pwszResult != NULL)
            {
            wcsncpy(pwszResult, wszIISProgIDPreFix, uPrefixLen);
            pStr = pwszApplicationPath;
            pResult = pwszResult + uPrefixLen - 1;
            
            while(*pStr != NULL)
                {
                if (*pStr == '\\' || *pStr == '/')
                    {
                    *pResult++ = '_';
                    *pResult++ = '_';
                    pStr++;
                    }
                else
                    {
                    *pResult++ = *pStr++;
                    }
                }

            // NULL Terminating the result
            pwszResult[uSize-1] = '\0';
            }
        else
            {
            hr = E_OUTOFMEMORY;
            }
        }

    if (SUCCEEDED(hr))
        {
        *ppszWamProgID = pwszResult;
        }
        
    return hr;
}

/*===================================================================
GetViperPackageName    

Make a Package Name.  Follow the naming conversion, "IIS-{web site name/
application name}"

Parameter:
szMetabasePath:            [in] MetabasePath.

Return:        HRESULT
Side Affect:
    Allocate memory for return result use new.  Caller needs to free 
szPackageName using delete[].
===================================================================*/
HRESULT    WamRegGlobal::GetViperPackageName    
(
IN LPCWSTR    wszMetabasePath,
OUT LPWSTR*    ppszPackageNameOut
)
{
    static WCHAR    wszPackageNamePreFix[]        = L"IIS-{";
    static WCHAR    wszPackageNamePostFix[]        = L"}";
    WCHAR            wszServerName[500];

    WCHAR*            pwszPackageName;
    WCHAR            *wszResult = NULL;
    WCHAR             *pStr, *pResult;
    
    UINT            cPackageName = 0;
    UINT            cServerName = 0;
    UINT            uSize = 0;

    HRESULT         hr = NOERROR;
    WamRegMetabaseConfig    MDConfig;

    if ((_wcsnicmp(wszMetabasePath, WamRegGlobal::g_szMDAppPathPrefix, WamRegGlobal::g_cchMDAppPathPrefix) == 0) ||
        (_wcsnicmp(wszMetabasePath, WamRegGlobal::g_szMDAppPathPrefix, WamRegGlobal::g_cchMDAppPathPrefix) == 0))
        {
        hr = MDConfig.GetWebServerName(wszMetabasePath, wszServerName, sizeof(wszServerName));
        if (SUCCEEDED(hr))
            {
            cServerName = wcslen(wszServerName);
            }
        }
    else
        {
        hr = E_FAIL;
        DBGPRINTF((DBG_CONTEXT, "Unknown metabase path %S\n", wszMetabasePath));
        DBG_ASSERT(FALSE);    // Confused ??? MetabasePath has other format? not start with /LM/W3SVC/ ???
        }

    if (SUCCEEDED(hr))
        {
        pwszPackageName = (WCHAR *)(wszMetabasePath + 10);
        // Explanation: skip the 1 at /LM/W3SVC/1/, 1 is the virtual server, the 
        // naming conversion
        // will replace the number 1 with some nice name(from GetWebServerName call).
        while(*pwszPackageName != NULL)
            {
                if (*pwszPackageName == L'\\' || *pwszPackageName == L'/')
                    break;
                pwszPackageName++;
            }
        
        DBG_ASSERT(pwszPackageName != NULL);    // We must be able find '\\' or '/' before we scan the whole path.
        cPackageName = wcslen(pwszPackageName);

        pStr = pwszPackageName;
        // 8 = wcslen(TEXT("IIS-{")) + wcslen(TEXT("}")) + '/' + null terminator
        uSize = 8 + cPackageName + cServerName;

        wszResult = new WCHAR [uSize];
        if (wszResult != NULL)
            {
            //
            // IIS-{
            //
            pResult = wszResult;
            wcsncpy(wszResult, wszPackageNamePreFix, sizeof(wszPackageNamePreFix) / sizeof(WCHAR));
            pResult += sizeof(wszPackageNamePreFix) / sizeof(WCHAR) - 1;

            //
            // IIS-{ Web Sever Name
            //
            wcsncpy(pResult, wszServerName, cServerName + 1);
            pResult += cServerName;
            
            //
            // IIS-{  Web Server Name /
            //
            wcsncpy(pResult, L"/", sizeof(L"/"));
            pResult += 1;    // sizeof(TEXT("/")) == 2

            //
            // IIS-{  Web Server Name / PackageName(ApplicationName)
            //
            wcsncpy(pResult, pwszPackageName, cPackageName + 1);
            pResult += cPackageName;

            //
            // IIS-{  Web Server Name / PackageName(ApplicationName)  }  \n
            //
            wcsncpy(pResult, wszPackageNamePostFix, sizeof(wszPackageNamePostFix) / sizeof(WCHAR));
            }
        else
            {
            hr = E_OUTOFMEMORY;
            }
        }
        
    if (FAILED(hr))
        {
        if (wszResult)
            {
            free(wszResult);
            }
        *ppszPackageNameOut = NULL;
        }
    else
        {
        DBG_ASSERT(wszResult);
        *ppszPackageNameOut = wszResult;
        }
        
    return hr;
}

/*===================================================================
ConstructFullPath

When use GetDataPaths call, it only returns patial path relative to a metabase path.
(sub node of a metabase path).  This fuction will contruct the complete metabase path to
a sub node.

Parameter:
pwszMetabasePathPrefix:         [in] MetabasePath.
pwszPartialPath
ppwszResult                     result buffer
Return:     HRESULT
Side Affect:
    Allocate memory for return result use new.  Caller needs to free
*ppwszResult using delete[].
===================================================================*/
HRESULT WamRegGlobal::ConstructFullPath
(
IN LPCWSTR pwszMetabasePathPrefix,
IN DWORD dwcPrefix,
IN LPCWSTR pwszPartialPath,
OUT LPWSTR* ppwszResult
)
{
    HRESULT    hr = NOERROR;
    DWORD    cchPrefix = dwcPrefix;
    DWORD    cchPartialPath = 0;
    DWORD    cchFullPath = 0;
    WCHAR    *pResult = NULL;
    BOOL    fHasEndSlash = FALSE;

    DBG_ASSERT(pwszPartialPath != NULL);
    
    if (pwszMetabasePathPrefix[dwcPrefix-1] == L'\\' ||
        pwszMetabasePathPrefix[dwcPrefix-1] == L'/')
        {
        cchPrefix--;    
        }

    cchPartialPath = wcslen(pwszPartialPath);

    // Skip the ending '/' of pwszPartialPath if thereis any.
    
    if (cchPartialPath > 0 && 
       (pwszPartialPath[cchPartialPath-1] == L'/' 
       || pwszPartialPath[cchPartialPath-1] == L'\\'))
         {
         cchPartialPath--;
         fHasEndSlash=TRUE;
         }

    cchFullPath = cchPrefix + cchPartialPath + 1;

    pResult = new WCHAR [cchFullPath];

    if (pResult)
        {
        memcpy(pResult, pwszMetabasePathPrefix, cchPrefix*sizeof(WCHAR));
        memcpy(pResult+cchPrefix, pwszPartialPath, cchPartialPath*sizeof(WCHAR));
        pResult[cchFullPath-1] = L'\0';
        }
    else
        {
        hr = E_OUTOFMEMORY;
        }

    *ppwszResult = pResult;

    return hr;
}

/*===================================================================
GetNewSzGUID    

Generate a new GUID and put into *ppszGUID.

Parameter:
LPWSTR *ppszGUID    a pointer to an array, allocated in this function
                    and freed by caller.

Return:     HRESULT
===================================================================*/
HRESULT WamRegGlobal::GetNewSzGUID(OUT LPWSTR *ppszGUID)
{
    GUID    GUID_Temp;
    HRESULT hr;

    DBG_ASSERT(ppszGUID);
    
    // Create a new WAM CLSID
    hr = CoCreateGuid(&GUID_Temp);
    if (SUCCEEDED(hr))
        {
        hr = StringFromCLSID(GUID_Temp, ppszGUID);
        if (FAILED(hr))
            {
            *ppszGUID = NULL;
            }
        }
    return hr;
}

/*===================================================================
CreatePooledApp    

Register a WAM CLSID.

Parameter:
szMetabasePath:        [in]     MetabaseKey.

Return:        HRESULT
===================================================================*/
HRESULT WamRegGlobal::CreatePooledApp
( 
IN LPCWSTR szMetabasePath,
IN BOOL    fInProc,
IN BOOL    fRecover 
)
    {
    HRESULT         hr = NOERROR;
    WamRegMetabaseConfig   MDConfig;
    
    DBG_ASSERT(szMetabasePath);        
    if (SUCCEEDED(hr))
        {
        MDPropItem     rgProp[IWMDP_MAX];

        MDConfig.InitPropItemData(&rgProp[0]);

        // Update APPRoot
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_ROOT, szMetabasePath);

        //Update AppIsolated
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_ISOLATED, 
                                (fInProc) ? static_cast<DWORD>(eAppRunInProc) 
                                          : static_cast<DWORD>(eAppRunOutProcInDefaultPool));

        //
        // in case this is an recover operation, we do not remove App Friendly Name.
        //
        if (!fRecover)
            {
            MDConfig.MDSetPropItem(&rgProp[0], IWMDP_FRIENDLY_NAME, L"");
            }

        hr = MDConfig.UpdateMD( rgProp, METADATA_INHERIT, szMetabasePath );
        }
            
    if (FAILED(hr))
        {
        HRESULT hrT = NOERROR;

        DBGPRINTF((DBG_CONTEXT, "Failed to create in proc application. path = %S, error = %08x\n",
            szMetabasePath,
            hr));
        }

    return hr;
    }

/*===================================================================
CreateOutProcApp

Create an out prop application.

Parameter:
szMetabasePath:     [in]    MetabaseKey.
fRecover            [in]    if TRUE, we recover/recreate the application.
fSaveMB             [in]    if TRUE, save metabase immediately
Return:     HRESULT
===================================================================*/

HRESULT WamRegGlobal::CreateOutProcApp(
    IN LPCWSTR szMetabasePath,
    IN BOOL fRecover, /* = FALSE */
    IN BOOL fSaveMB   /* = TRUE */
    )
{
    WCHAR        *szWAMCLSID = NULL;
    WCHAR         *szPackageID = NULL; 
    WCHAR        *szNameForNewPackage = NULL;
    HRESULT        hr;
    HRESULT        hrRegister = E_FAIL;
    HRESULT        hrPackage = E_FAIL;
    HRESULT        hrMetabase = E_FAIL;
    WCHAR        szIdentity[MAX_PATH];
    WCHAR        szPwd[MAX_PATH];

    WamRegMetabaseConfig    MDConfig;
    WamRegPackageConfig     PackageConfig;
    
    DBG_ASSERT(szMetabasePath);        

    hr = GetNewSzGUID(&szWAMCLSID);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to create a new szGUID. error = %08x\n", hr));
        return hr;
        }
    else
        {
        WCHAR    *szProgID = NULL;
        // Do WAM CLSID registration
        //
        hr = SzWamProgID(szMetabasePath, &szProgID);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to Create WAM ProgID, hr = %08x\n",
                hr));
            }
        else
            {
            hr = g_RegistryConfig.RegisterCLSID(szWAMCLSID, szProgID, FALSE);
            hrRegister = hr;
            delete [] szProgID;
            szProgID = NULL;
            if (FAILED(hrRegister)) 
                {
                DBGPRINTF((DBG_CONTEXT, "Failed to registerCLSID. error %08x\n", hr));
                }
            }
        }

    if (SUCCEEDED(hr))
        {
        WCHAR szLastOutProcPackageID[uSizeCLSID];

        // 
        // When an outproc package gets deleted, it might/might not removed from the MTS,
        // the next time, same app path marked as out-proc again, we try to reuse the outproc
        // package.  Therefore, we need to save the OutprogPackageID as LastOutProcPackageID.
        //
        szLastOutProcPackageID[0] = NULL;
            
        MDConfig.MDGetLastOutProcPackageID(szMetabasePath, szLastOutProcPackageID);        
        if (szLastOutProcPackageID[0] == NULL)
            {
            hr = GetNewSzGUID(&szPackageID);
            }
        else
            {
            szPackageID = (WCHAR *)CoTaskMemAlloc(uSizeCLSID*sizeof(WCHAR));
            if (szPackageID == NULL)
                {
                hr = E_OUTOFMEMORY;
                }
            else
                {
                wcsncpy(szPackageID, szLastOutProcPackageID, uSizeCLSID);
                }
            }
        }

    if (SUCCEEDED(hr))
        {
        hr = GetViperPackageName(szMetabasePath, &szNameForNewPackage);
        }

    if (SUCCEEDED(hr))
        {
        hr = MDConfig.MDGetIdentity(szIdentity, sizeof(szIdentity), szPwd, sizeof(szPwd));
        }

    if (SUCCEEDED(hr))
        {
        //
        // Create the catalog object etc for MTS configuration
        //
        hr = PackageConfig.CreateCatalog();

        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to call MTS Admin API. error %08x\n", hr));
            }
        else
            {
            hr = PackageConfig.CreatePackage(
                                szPackageID,
                                szNameForNewPackage,
                                szIdentity,
                                szPwd,
                                FALSE);

            if (SUCCEEDED(hr))
                {
                hr  = PackageConfig.AddComponentToPackage(
                                                szPackageID,
                                                szWAMCLSID);
                if (FAILED(hr))
                    {
                    PackageConfig.RemovePackage(szPackageID);
                    }
                }
            }

        hrPackage = hr;
        }

    
    if (SUCCEEDED(hr))
        {
        MDPropItem     rgProp[IWMDP_MAX];

        MDConfig.InitPropItemData(&rgProp[0]);
        
        // Update WAMCLSID
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_WAMCLSID, szWAMCLSID);
            
        // Update APPRoot
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_ROOT, szMetabasePath);

        //Update AppIsolated
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_ISOLATED, 1);

        //Update AppPackageName
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_PACKAGE_NAME, szNameForNewPackage);

        //Update AppPackageID
        MDConfig.MDSetPropItem(&rgProp[0], IWMDP_PACKAGEID, szPackageID);
        
        //
        // in case this is an recover operation, we do not remove App Friendly Name.
        //
        if (!fRecover)
            {
            // It doesn't really make sense to set this on every isolated application.
            // This will be much easier to administer globally if we allow it to be set
            // at a higher level.

            // MDConfig.MDSetPropItem(&rgProp[0], IWMDP_OOP_RECOVERLIMIT, APP_OOP_RECOVER_LIMIT_DEFAULT);
            
            MDConfig.MDSetPropItem(&rgProp[0], IWMDP_FRIENDLY_NAME, L"");
            }
            
        // Attempt to Save the metabase changes immediately. We want to ensure
        // that the COM+ package is not orphaned.
        hr = MDConfig.UpdateMD(rgProp, METADATA_INHERIT, szMetabasePath, fSaveMB );
            
        if (FAILED(hr))
            {
            // Removed AbortUpdateMD call - we shouldn't wax the MB settings or
            // we will orphan the COM+ package.
            DBGPRINTF((
                DBG_CONTEXT, 
                "Failed to set metabase properties on (%S). error == %08x\n",
                szMetabasePath,
                hr
                ));
            }

        hrMetabase = hr;
        }

    if (FAILED(hr))
        {
        HRESULT hrT = NOERROR;

        DBGPRINTF((DBG_CONTEXT, "Failed to create out proc application. path = %S, error = %08x\n",
            szMetabasePath,
            hr));
            
        if (SUCCEEDED(hrPackage))
            {
            hrT = PackageConfig.RemovePackage( szPackageID);
            }
        if (SUCCEEDED(hrRegister))
            {
            hrT = g_RegistryConfig.UnRegisterCLSID(szWAMCLSID, FALSE);    
            if (FAILED(hrT))
                {
                DBGPRINTF((DBG_CONTEXT, "Rollback: Failed to UnRegisterCLSID. error = %08x\n", hr));
                }
            }
        }
    
    if (szWAMCLSID)
        {
        CoTaskMemFree(szWAMCLSID);
        szWAMCLSID = NULL;
        }

    if (szPackageID)
        {
        CoTaskMemFree(szPackageID);
        szWAMCLSID = NULL;
        }

    if (szNameForNewPackage)
        {
        delete [] szNameForNewPackage;
        szNameForNewPackage = NULL;
        }

    return hr;
}

HRESULT 
WamRegGlobal::CreateOutProcAppReplica(
    IN LPCWSTR szMetabasePath,
    IN LPCWSTR szAppName,
    IN LPCWSTR szWamClsid,
    IN LPCWSTR szAppId
    )
/*++
Function:

    Called by the DeSerialize replication method to create a
    new oop application.

Arguments:

    szMetabasePath  
    szAppName       
    szWamClsid      
    szAppId         

Return:

--*/
{
    HRESULT hr = NOERROR;

    DBG_ASSERT(szMetabasePath);
    DBG_ASSERT(szWamClsid);
    DBG_ASSERT(szAppId);

    //
    // Register wam.dll as a new component
    //

    WCHAR * szProgID = NULL;
    BOOL    fRegisteredWam = FALSE;

    hr = SzWamProgID(szMetabasePath, &szProgID);
    if( SUCCEEDED(hr) )
    {
        DBG_ASSERT( szProgID != NULL );
        
        hr = g_RegistryConfig.RegisterCLSID( szWamClsid, 
                                             szProgID, 
                                             FALSE
                                             );
        if( SUCCEEDED(hr) )
        {
            fRegisteredWam = TRUE;
        }

        delete [] szProgID;
        szProgID = NULL;
    }

    if( FAILED(hr) )
    {
        DBGERROR(( DBG_CONTEXT,
                   "Failed to register wam.dll. hr=%08x\n",
                    hr
                    ));
    }

    //
    // Get required application info
    //
    BOOL    fGetCOMAppInfo = FALSE;
    WCHAR   szIdentity[MAX_PATH];
    WCHAR   szPwd[MAX_PATH];

    WamRegMetabaseConfig    MDConfig;
    
    if( fRegisteredWam )
    {
        hr = MDConfig.MDGetIdentity( szIdentity, 
                                     sizeof(szIdentity), 
                                     szPwd, 
                                     sizeof(szPwd)
                                     );
        if( SUCCEEDED(hr) )
        {
            fGetCOMAppInfo = TRUE;
        }
        else
        {
            DBGERROR(( DBG_CONTEXT,
                       "Failed get required COM application info. hr=%08x\n",
                        hr
                        ));
        }
    }

    //
    // Create the COM+ application
    //

    if( fGetCOMAppInfo )
    {
        WamRegPackageConfig PackageConfig;
    
        hr = PackageConfig.CreateCatalog();

        if( SUCCEEDED(hr) )
        {
            hr = PackageConfig.CreatePackage(
                                szAppId,
                                szAppName,
                                szIdentity,
                                szPwd,
                                FALSE
                                );
            if( SUCCEEDED(hr) )
            {
                hr  = PackageConfig.AddComponentToPackage(
                            szAppId,
                            szWamClsid
                            );
            }

            // On failure we might want to cleanup, but I'm not sure
            // that really makes sense.
        }

        // At this point, we would normally set the metabase properties
        // but we will let the MB replication handle that for us.
        // Note: if the MB replication fails, we will be left with
        // a bunch of orphaned com applications

        if( FAILED(hr) )
        {
            DBGERROR(( DBG_CONTEXT,
                       "COM+ App creation failed. AppId(%S) Name(%S) "
                       "Clsid(%S) hr=%08x\n",
                       szAppId,
                       szAppName,
                       szWamClsid,
                       hr
                       ));
        }
    }

    return hr;
}

/*===================================================================
DeleteApp

Register a WAM CLSID.

Parameter:
szMetabasePath:     [in]    MetabaseKey.
fDeletePackage:     [in]    when uninstall, this flag is true, we delete all IIS created packages.
fRemoveAppPool      [in]    Should the AppPoolId Property be removed
Return:     HRESULT
===================================================================*/
HRESULT WamRegGlobal::DeleteApp
(
IN LPCWSTR szMetabasePath,
IN BOOL fRecover,
IN BOOL fRemoveAppPool
)
{
    WCHAR   szWAMCLSID[uSizeCLSID];
    WCHAR   szPackageID[uSizeCLSID];
    DWORD   dwAppMode = eAppRunInProc;
    DWORD   dwCallBack;
    HRESULT hr, hrRegistry;
    METADATA_HANDLE hMetabase = NULL;
    WamRegMetabaseConfig    MDConfig;
    
    hr = MDConfig.MDGetDWORD(szMetabasePath, MD_APP_ISOLATED, &dwAppMode);

    // return immediately, no application is defined, nothing to delete.
    if (hr == MD_ERROR_DATA_NOT_FOUND)
        {
        return NOERROR;
        }

    if (FAILED(hr))
        {
        return hr;
        }
        
    //
    //Set App State to be PAUSE/DISABLE, so that after we remove the application from
    //run time WAM_DICTATOR lookup table, new request won't trigger the application to
    //retstart.
    //WAM_DICTATOR has code to check for this state.
    //
    hr = MDConfig.MDSetAppState(szMetabasePath, APPSTATUS_PAUSE);
    if( FAILED(hr) )
    {
        DBGPRINTF(( DBG_CONTEXT,
                    "MDSetAppState Failed hr=%08x\n",
                    hr
                    ));
    }

    hr = W3ServiceUtil(szMetabasePath, APPCMD_DELETE, &dwCallBack);
    if( FAILED(hr) )
    {
        DBGPRINTF(( DBG_CONTEXT,
                    "W3ServiceUtil APPCMD_DELETE Failed on (%S) hr=%08x\n",
                    szMetabasePath,
                    hr
                    ));
    }

    if (dwAppMode == eAppRunOutProcIsolated)
        {
        // Get WAM_CLSID, and PackageID.
        hr = MDConfig.MDGetIDs(szMetabasePath, szWAMCLSID, szPackageID, dwAppMode);
        // Remove the WAM from the package.
        if (SUCCEEDED(hr))
            {
            WamRegPackageConfig     PackageConfig;
            HRESULT hrPackage;
            
            hr = PackageConfig.CreateCatalog();

            if ( FAILED( hr)) 
                {
                DBGPRINTF(( DBG_CONTEXT,
                            "Failed to Create MTS catalog hr=%08x\n",
                            hr));
                } 
            else 
                {            
                hr = PackageConfig.RemoveComponentFromPackage(szPackageID, 
                                               szWAMCLSID, 
                                               dwAppMode);
                if (FAILED(hr))    
                    {
                        DBGPRINTF((DBG_CONTEXT, "Failed to remove component from package, \npackageid = %S, wamclsid = %S, hr = %08x\n",
                                   szPackageID,
                                   szWAMCLSID,
                                   hr));
                    }
                }
            hrPackage = hr;
            }

        // Unregister WAM
        hr = g_RegistryConfig.UnRegisterCLSID(szWAMCLSID, FALSE);    
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to UnRegister WAMCLSID(%S), hr = %08x\n",
                szWAMCLSID,
                hr));
            hrRegistry = hr;
            }
        }
        
    if (SUCCEEDED(hr))
        {
        BOOL fChanged = FALSE;
        MDPropItem     rgProp[IWMDP_MAX];
        MDConfig.InitPropItemData(&rgProp[0]);
        if (dwAppMode == static_cast<DWORD>(eAppRunOutProcIsolated))
            {    
            // Delete AppPackageName.  (Inherited from W3SVC)
            // Delete AppPackageID. (Inherited from W3SVC)
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_PACKAGE_NAME);
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_PACKAGEID);

            // Delete WAMCLSID
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_WAMCLSID);
            
            if (TsIsNtServer() || TsIsNtWksta())
                {
                MDConfig.MDSetPropItem(&rgProp[0], IWMDP_LAST_OUTPROC_PID, szPackageID);
                }
            fChanged = TRUE;
            }
        // If this is DeleteRecoverable mode, we do not delete APP_ROOT, APP_ISOLATED,
        // OOP_RECOVERLIMIT and APP_STATE.
        if (!fRecover)
            {
            // Delete AppFriendlyName
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_FRIENDLY_NAME);
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_ROOT);
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_ISOLATED);
            MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_APPSTATE);
            if (fRemoveAppPool)
                {
                MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_OOP_APP_APPPOOL_ID);
                }

            if (dwAppMode == static_cast<DWORD>(eAppRunOutProcIsolated))
                {
                // This will only be set for older isolated applications.
                // Since we ignore the result of UpdateMD below, it is ok
                // for us to try to delete the property.
                MDConfig.MDDeletePropItem(&rgProp[0], IWMDP_OOP_RECOVERLIMIT);
                }
            fChanged = TRUE;
            }

        // For DeleteRecover operation, and the app is not outproc isolated,
        // No property changes, therefore, no need to update metabase.
        if (fChanged)
            {
            MDConfig.UpdateMD(rgProp, METADATA_NO_ATTRIBUTES, szMetabasePath);
            }
        }
        
    return NOERROR;
}

/*===================================================================
RecoverApp

Recover an application based on MD_APP_ISOLATED property.

Parameter:
szMetabasePath:     [in]    MetabaseKey.

Return:     HRESULT
===================================================================*/
HRESULT WamRegGlobal::RecoverApp
(
IN LPCWSTR szMetabasePath,
IN BOOL fForceRecover
)
{
    HRESULT hr = NOERROR;
    DWORD    dwAppMode = 0;
    WamRegMetabaseConfig    MDConfig;

    hr = MDConfig.MDGetDWORD(szMetabasePath, MD_APP_ISOLATED, &dwAppMode);
    if (hr == MD_ERROR_DATA_NOT_FOUND)
        {
        hr = NOERROR;
        }
    else
        {
        if (SUCCEEDED(hr))
            {
            if (fForceRecover)
                {
    			if (dwAppMode == static_cast<DWORD>(eAppRunOutProcInDefaultPool))
    				{
    				hr = CreatePooledApp(szMetabasePath, FALSE);				
    				}
    			else if (dwAppMode == static_cast<DWORD>(eAppRunInProc))
                    {
                    hr = CreatePooledApp(szMetabasePath, TRUE);				
                    }
    			else
    				{
    				hr = CreateOutProcApp(szMetabasePath);
    				}
                }
                
            if (SUCCEEDED(hr))
                {
                HRESULT hrT = NOERROR;
                hrT = MDConfig.MDRemoveProperty(szMetabasePath, MD_APP_STATE, DWORD_METADATA);
                if (FAILED(hrT))
                    {
                    if (hrT != MD_ERROR_DATA_NOT_FOUND)
                        {
                        DBGPRINTF((DBG_CONTEXT, "Failed to remove MD_APP_STATE from path %S, hr = %08x\n",
                            szMetabasePath,
                            hrT));
                        }
                    }
                }
            }
        }
        
    return hr;
}
/*============================================================================
W3ServiceUtil

sink function that unload/shutdown/querystatus of an application currently in the runtime
table.

Parameter:
szMDPath            the application Path.
dwCommand           The command.
pdwCallBackResult   Contains the HRESULT from w3svc.dll.

==============================================================================*/
HRESULT WamRegGlobal::W3ServiceUtil
(
IN LPCWSTR  szMDPath,
IN DWORD    dwCommand,
OUT DWORD*    pdwCallBackResult
)
{
    HRESULT hr = NOERROR;

    if (g_pfnW3ServiceSink)
        {

#ifndef _IIS_6_0

        // DBCS enabling for IIS 5.1
        INT cSize = wcslen(szMDPath)*2 + 1;

        CHAR *szPathT = new CHAR[cSize];

        if (szPathT)
            {
            WideCharToMultiByte(0, 0, szMDPath, -1, szPathT, cSize, NULL, NULL);
            hr = g_pfnW3ServiceSink(szPathT,
                                    dwCommand,
                                    pdwCallBackResult);
            }
        else
            {
            hr = E_OUTOFMEMORY;
            }
        
        delete [] szPathT;

#else
        //
        // IIS 6's implementation uses UNICODE directly, so
        // we'll avoid the WideCharToMultiByte nonsense and
        // just cast the path to fit the function arguments.
        //
        // We're not changing the function prototype purely
        // because we are minimizing code churn in this module.
        //
        
        
        // IIS 6 gets the unicode directly
        hr = g_pfnW3ServiceSink(reinterpret_cast<LPCSTR>(szMDPath),
                                dwCommand,
                                pdwCallBackResult);
#endif // _IIS_6_0
        }
    else
        {
        *pdwCallBackResult = APPSTATUS_NOW3SVC;
        hr = NOERROR;
        }

    return hr;
}