mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1860 lines
53 KiB
1860 lines
53 KiB
/*===================================================================
|
|
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;
|
|
}
|
|
|
|
|