Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2440 lines
54 KiB

/*++
Copyright (c) 1994-2000 Microsoft Corporation
Module Name :
iismachine.cpp
Abstract:
IIS Machine node
Author:
Ronald Meijer (ronaldm)
Sergei Antonov (sergeia)
Project:
Internet Services Manager
Revision History:
10/28/2000 sergeia Split from iisobj.cpp
--*/
#include "stdafx.h"
#include "common.h"
#include "inetprop.h"
#include "InetMgrApp.h"
#include "supdlgs.h"
#include "connects.h"
#include "metaback.h"
#include "iisobj.h"
#include "shutdown.h"
#include "machsht.h"
#include "w3sht.h"
#include "fltdlg.h"
#include "savedata.h"
#include "util.h"
#include "tracker.h"
#include "iishelp.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
extern CPropertySheetTracker g_OpenPropertySheetTracker;
extern CWNetConnectionTrackerGlobal g_GlobalConnections;
extern CInetmgrApp theApp;
#if defined(_DEBUG) || DBG
extern CDebug_IISObject g_Debug_IISObject;
#endif
extern DWORD g_dwInetmgrParamFlags;
DWORD WINAPI CIISMachine::GetProcessModeThread(LPVOID pInfo)
{
CError err(ERROR_NOT_FOUND);
GET_PROCESS_MODE_STRUCT * pMyStructOfInfo = (GET_PROCESS_MODE_STRUCT *) pInfo;
//
// This thread needs its own CoInitialize
//
CoInitialize(NULL);
// Do the work
CIISAppPool pool(pMyStructOfInfo->pComAuthInfo, (LPCTSTR) _T("LM/W3SVC"));
DWORD dwProcessMode = -1;
pMyStructOfInfo->dwProcessMode = dwProcessMode;
err = pool.GetProcessMode(&dwProcessMode);
if (err.Succeeded())
{
pMyStructOfInfo->dwProcessMode = dwProcessMode;
}
pMyStructOfInfo->dwReturnStatus = err;
return err;
}
BOOL CIISMachine::GetProcessMode(GET_PROCESS_MODE_STRUCT * pMyStructOfInfo)
{
BOOL bReturn = FALSE;
DWORD ThreadID = 0;
DWORD status = 0;
HANDLE hMyThread = ::CreateThread(NULL,0,GetProcessModeThread,pMyStructOfInfo,0,&ThreadID);
if (hMyThread)
{
// wait for 10 secs only
DWORD res = WaitForSingleObject(hMyThread,10*1000);
if (res == WAIT_TIMEOUT)
{
GetExitCodeThread(hMyThread, &status);
if (status == STILL_ACTIVE)
{
if (hMyThread != NULL)
{TerminateThread(hMyThread, 0);}
}
}
else
{
GetExitCodeThread(hMyThread, &status);
if (status == STILL_ACTIVE)
{
if (hMyThread != NULL)
{TerminateThread(hMyThread, 0);}
}
else
{
if (ERROR_SUCCESS == status)
{
bReturn = TRUE;
}
}
if (hMyThread != NULL)
{CloseHandle(hMyThread);}
}
}
return bReturn;
}
/* static */ LPOLESTR CIISMachine::_cszNodeName = _T("LM");
/* static */ CComBSTR CIISMachine::_bstrYes;
/* static */ CComBSTR CIISMachine::_bstrNo;
/* static */ CComBSTR CIISMachine::_bstrVersionFmt;
/* static */ BOOL CIISMachine::_fStaticsLoaded = FALSE;
//
// Define result view for machine objects
//
/* static */ int CIISMachine::_rgnLabels[COL_TOTAL] =
{
IDS_RESULT_COMPUTER_NAME,
IDS_RESULT_COMPUTER_LOCAL,
IDS_RESULT_COMPUTER_VERSION,
IDS_RESULT_STATUS,
};
/* static */ int CIISMachine::_rgnWidths[COL_TOTAL] =
{
200,
50,
//100,
150,
200,
};
/* static */
void
CIISMachine::InitializeHeaders(
LPHEADERCTRL lpHeader
)
{
BuildResultView(lpHeader, COL_TOTAL, _rgnLabels, _rgnWidths);
if (!_fStaticsLoaded)
{
_fStaticsLoaded =
_bstrYes.LoadString(IDS_YES) &&
_bstrNo.LoadString(IDS_NO) &&
_bstrVersionFmt.LoadString(IDS_VERSION_FMT);
}
}
/* virtual */
void
CIISMachine::InitializeChildHeaders(
LPHEADERCTRL lpHeader
)
{
CIISService::InitializeHeaders(lpHeader);
}
/* static */
HRESULT
CIISMachine::VerifyMachine(
CIISMachine *& pMachine
)
/*++
Routine Description:
Create the interface on the given machine object.
Arguments:
CIISMachine *& pMachine : Machine object
BOOL fAskBeforeRedirecting
Return Value:
HRESULT
Notes:
THe CIISMachine object pass in may refer to the cluster master
on return.
--*/
{
CError err;
if (pMachine)
{
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
CWaitCursor wait;
//
// Attempt to create the interface to ensure the machine
// contains a metabase.
//
err = pMachine->CreateInterface(FALSE);
}
return err;
}
CIISMachine::CIISMachine(
IConsoleNameSpace * pConsoleNameSpace,
IConsole * pConsole,
CComAuthInfo * pAuthInfo,
CIISRoot * pRoot
)
: m_pInterface(NULL),
m_bstrDisplayName(NULL),
m_auth(pAuthInfo),
m_pRootExt(pRoot),
m_err(),
//
// By default we assume the password is entered.
// If this machine object is constructed from the
// cache, it will get reset by InitializeFromStream()
//
m_fPasswordEntered(TRUE),
m_dwVersion(MAKELONG(5, 0)), // Assume as a default
m_pAppPoolsContainer(NULL),
m_pWebServiceExtensionContainer(NULL),
CIISMBNode(this, _cszNodeName),
m_MachineWNetConnections(&g_GlobalConnections)
{
//
// Load one-liner error messages
//
SetErrorOverrides(m_err, TRUE);
SetDisplayName();
SetConsoleData(pConsoleNameSpace,pConsole);
m_fIsLocalHostIP = FALSE;
m_fLocalHostIPChecked = FALSE;
}
CIISMachine::~CIISMachine()
/*++
Routine Description:
Destructor
Arguments:
N/A
Return Value:
N/A
--*/
{
if (m_bstrDisplayName)
{
::SysFreeString(m_bstrDisplayName);
}
// Disconnect all connections made from this iismachine.
m_MachineWNetConnections.Clear();
SAFE_DELETE(m_pInterface);
}
/* static */
HRESULT
CIISMachine::ReadFromStream(
IStream * pStream,
CIISMachine ** ppMachine,
IConsoleNameSpace * pConsoleNameSpace,
IConsole * pConsole
)
/*++
Routine Description:
Static helper function to allocate a new CIISMachine object read
from the storage stream.
Arguments:
IStream * pStream : Stream to read from
CIISMachine ** ppMachine : Returns CIISMachine object
Return Value:
HRESULT
--*/
{
CComBSTR strMachine, strUser;
ASSERT_WRITE_PTR(ppMachine);
ASSERT_READ_WRITE_PTR(pStream);
CError err;
*ppMachine = NULL;
do
{
err = strMachine.ReadFromStream(pStream);
BREAK_ON_ERR_FAILURE(err);
err = strUser.ReadFromStream(pStream);
BREAK_ON_ERR_FAILURE(err);
*ppMachine = new CIISMachine(pConsoleNameSpace,pConsole,CComAuthInfo(strMachine, strUser));
if (!*ppMachine)
{
err = ERROR_NOT_ENOUGH_MEMORY;
break;
}
err = (*ppMachine)->InitializeFromStream(pStream);
}
while(FALSE);
return err;
}
HRESULT
CIISMachine::WriteToStream(
IStream * pStgSave
)
/*++
Routine Description:
Write machine information to stream.
Arguments:
IStream * pStgSave : Open stream
Return Value:
HRESULT
Notes:
Be sure to keep this information in sync with CIISMachine::InitializeFromStream()
--*/
{
ASSERT_READ_WRITE_PTR(pStgSave);
CComBSTR bstrServerName(m_auth.QueryServerName());
CComBSTR bstrUserName(m_auth.QueryUserName());
CError err;
ULONG cb;
do
{
err = bstrServerName.WriteToStream(pStgSave);
BREAK_ON_ERR_FAILURE(err);
err = bstrUserName.WriteToStream(pStgSave);
BREAK_ON_ERR_FAILURE(err);
//
// Now cache the dynamically-generated information, such
// as version number, snapin status etc. This will be
// displayed in the result view before the interface is
// created.
//
err = pStgSave->Write(&m_dwVersion, sizeof(m_dwVersion), &cb);
BREAK_ON_ERR_FAILURE(err);
}
while(FALSE);
return err;
}
HRESULT
CIISMachine::InitializeFromStream(
IStream * pStream
)
/*++
Routine Description:
Read version number and other cached parameters that will
be overridden at runtime when the interface is created.
This is cached, because it's required before the interface
is created.
Arguments:
IStream * pStream : Open stream
Return Value:
HRESULT
Notes:
Be sure to keep this information in sync with CIISMachine::WriteToStream()
--*/
{
ASSERT_READ_PTR(pStream);
CError err;
ULONG cb;
//
// Passwords are never cached. IIS status will
// always be verified when the actual interface
// is created.
//
m_fPasswordEntered = FALSE;
//
// Version number
//
err = pStream->Read(&m_dwVersion, sizeof(m_dwVersion), &cb);
return err;
}
void
CIISMachine::SetDisplayName()
/*++
Routine Description:
Create a special display name for this machine object if it's
either the local machine, or
--*/
{
CString fmt;
if (IsLocal())
{
//
// Use the local computer name, and not the name
// that's on the server object, because that could
// be and ip address or "localhost".
//
TCHAR szLocalServer[MAX_PATH + 1];
DWORD dwSize = MAX_PATH;
VERIFY(::GetComputerName(szLocalServer, &dwSize));
fmt.Format(IDS_LOCAL_COMPUTER, szLocalServer);
}
else
{
//
// No special display name necessary
//
m_bstrDisplayName = NULL;
return;
}
m_bstrDisplayName = ::SysAllocStringLen(fmt, fmt.GetLength());
TRACEEOLID("Machine display name: " << m_bstrDisplayName);
}
LPOLESTR
CIISMachine::QueryDisplayName()
/*++
Routine Description:
Get the display name for the machine/cluster object
Arguments:
None
Return Value:
Display Name
--*/
{
if (m_pRootExt != NULL)
return m_pRootExt->QueryDisplayName();
else
return m_bstrDisplayName ? m_bstrDisplayName : QueryServerName();
}
int
CIISMachine::QueryImage() const
/*++
Routine Description:
Return machine bitmap index appropriate for the current
state of this machine object.
Arguments:
None
Return Value:
Bitmap index
--*/
{
if (m_pRootExt != NULL)
{
return m_pRootExt->QueryImage();
}
else
{
if (m_err.Failed())
{
return IsLocal() ? iLocalMachineErr : iMachineErr;
}
else
{
return IsLocal() ? iLocalMachine : iMachine;
}
}
}
HRESULT
CIISMachine::CreateInterface(
BOOL fShowError
)
/*++
Routine Description:
Create the interface. If the interface is already created, recreate it.
Arguments:
BOOL fShowError : TRUE to display error messages
Return Value:
HRESULT
Notes:
This function is deliberately NOT called from the constructor for performance
reasons.
--*/
{
CError err;
BOOL bHasInterface = FALSE;
bHasInterface = HasInterface();
if (bHasInterface)
{
//
// Recreate the interface (this should re-use the impersonation)
//
TRACEEOLID("Warning: Rebinding existing interface.");
err = m_pInterface->Regenerate();
}
else
{
//
// Create new interface
//
m_pInterface = new CMetaKey(&m_auth);
err = m_pInterface
? m_pInterface->QueryResult()
: ERROR_NOT_ENOUGH_MEMORY;
}
if (err.Succeeded())
{
//
// Load its display parameters
//
err = RefreshData();
if (bHasInterface)
{
// Do extra stuff if we regenerated an existing interface...
}
CMetabasePath path;
err = DetermineIfAdministrator(
m_pInterface,
path,
&m_fIsAdministrator,
&m_dwMetabaseSystemChangeNumber
);
// Set the latest system change number.
RefreshMetabaseSystemChangeNumber();
}
if (err.Failed())
{
if (fShowError)
{
CWnd * pWnd = GetMainWindow(GetConsole());
DisplayError(err,pWnd ? pWnd->m_hWnd : NULL);
}
//
// Kill bogus interface
//
SAFE_DELETE(m_pInterface);
}
return err;
}
/* virtual */
int
CIISMachine::CompareScopeItem(
CIISObject * pObject
)
/*++
Routine Description:
Compare against another CIISMachine object.
Arguments:
CIISObject * pObject : Object to compare against
Return Value:
0 if the two objects are identical
<0 if this object is less than pObject
>0 if this object is greater than pObject
--*/
{
ASSERT_READ_PTR(pObject);
//
// First criteria is object type
//
int n1 = QuerySortWeight();
int n2 = pObject->QuerySortWeight();
if (n1 != n2)
{
return n1 - n2;
}
//
// pObject is a CIISMachine object (same sortweight)
//
CIISMachine * pMachine = (CIISMachine *)pObject;
//
// Next sort on local key (local sorts before non-local)
//
n1 = IsLocal() ? 0 : 1;
n2 = pMachine->IsLocal() ? 0 : 1;
if (n1 != n2)
{
return n1 - n2;
}
if (!n1 && !n2)
{
//
// This is the local machine, even if the name is different
//
return 0;
}
//
// Else sort on name.
//
return _tcsicmp(QueryServerName(), pMachine->QueryServerName());
}
BOOL
CIISMachine::SetCacheDirty()
/*++
Routine Description:
Set the cache as dirty
Arguments:
None
Return Value:
TRUE for success, FALSE if the cache was not found
--*/
{
ASSERT(m_pRootExt == NULL);
//
// Cache is stored at the root object
//
CIISRoot * pRoot = GetRoot();
ASSERT_PTR(pRoot);
if (pRoot)
{
pRoot->m_scServers.SetDirty();
return TRUE;
}
return FALSE;
}
int
CIISMachine::ResolvePasswordFromCache()
/*++
Routine Description:
Look through the machine cache for machines with the same username
as this object. If they have a password entered, grab it.
Arguments:
None
Return Value:
TRUE if a machine with the same username was found whose password
we stole. FALSE otherwise.
--*/
{
BOOL fUpdated = FALSE;
//
// Doesn't make sense if this machine object doesn't use impersonation
// or already has a password.
//
ASSERT(UsesImpersonation() && !PasswordEntered());
CIISRoot * pRoot = GetRoot();
ASSERT_PTR(pRoot);
if (pRoot)
{
CIISMachine * pMachine = pRoot->m_scServers.GetFirst();
while(pMachine)
{
if (pMachine->UsesImpersonation() && pMachine->PasswordEntered())
{
if (!_tcsicmp(QueryUserName(), pMachine->QueryUserName()))
{
TRACEEOLID("Swiping cached password from " << pMachine->QueryServerName());
StorePassword(pMachine->QueryPassword());
++fUpdated;
break;
}
}
pMachine = pRoot->m_scServers.GetNext();
}
}
return fUpdated;
}
HRESULT
CIISMachine::Impersonate(
LPCTSTR szUserName,
LPCTSTR szPassword
)
/*++
Routine Description:
Set and store proxy blanket security information. Store username/password
for use by metaback and other interfaces.
Arguments:
LPCTSTR szUserName : Username (domain\username)
LPCTSTR szPassword : Password
Return Value:
None
--*/
{
ASSERT_READ_PTR(szUserName);
CError err;
if (m_pInterface)
{
//
// Already have an interface created; Change the
// the security blanket.
//
err = m_pInterface->ChangeProxyBlanket(szUserName, szPassword);
}
if (err.Succeeded())
{
//
// Store new username/password
//
m_auth.SetImpersonation(szUserName, szPassword);
m_fPasswordEntered = TRUE;
}
return err;
}
void
CIISMachine::RemoveImpersonation()
/*++
Routine Description:
Remove impersonation parameters. Destroy any existing interface.
Arguments:
None
Return Value:
N/A
--*/
{
m_auth.RemoveImpersonation();
m_fPasswordEntered = FALSE;
SAFE_DELETE(m_pInterface);
}
void
CIISMachine::StorePassword(
LPCTSTR szPassword
)
/*++
Routine Description:
Store password.
Arguments:
LPCTSTR szPassword : Password
Return Value:
None
--*/
{
ASSERT_READ_PTR(szPassword);
m_auth.StorePassword(szPassword);
m_fPasswordEntered = TRUE;
}
BOOL
CIISMachine::ResolveCredentials()
/*++
Routine Description:
If this machine object uses impersonation, but hasn't entered a password
yet, check to see if there are any other machines in the cache with the
same username and grab its password. If not, prompt the user for it.
Arguments:
None
Return Value:
TRUE if a password was entered. FALSE otherwise.
--*/
{
BOOL fPasswordEntered = FALSE;
if (UsesImpersonation() && !PasswordEntered())
{
//
// Attempt to find the password from the cache
//
if (!ResolvePasswordFromCache())
{
//
// Didn't find the password in the cache. Prompt
// the user for it.
//
CLoginDlg dlg(LDLG_ENTER_PASS, this, GetMainWindow(GetConsole()));
if (dlg.DoModal() == IDOK)
{
fPasswordEntered = TRUE;
if (dlg.UserNameChanged())
{
//
// User name has changed -- remember to
// save the machine cache later.
//
SetCacheDirty();
}
}
else
{
//
// Pressing cancel on this dialog means the user
// wants to stop using impersonation.
//
RemoveImpersonation();
SetCacheDirty();
}
}
}
return fPasswordEntered;
}
BOOL
CIISMachine::HandleAccessDenied(
CError & err
)
/*++
Routine Description:
After calling interface method, pass the error object to this function
to handle the access denied case. If the error is access denied,
give the user a chance to change credentials. Since we assume an
attempt has been made to create an interface at least -- the interface
will be recreated with the new credentials.
Arguments:
CError & err : Error object. Checked for ACCESS_DENIED on entry,
will contain new error code on exit if the interface
was recreated.
Return Value:
TRUE if new credentials were applied
--*/
{
BOOL fPasswordEntered = FALSE;
//
// If access denied occurs here -- give another chance
// at entering the password.
//
if (err.Win32Error() == ERROR_ACCESS_DENIED)
{
CLoginDlg dlg(LDLG_ACCESS_DENIED, this, GetMainWindow(GetConsole()));
if (dlg.DoModal() == IDOK)
{
fPasswordEntered = TRUE;
err.Reset();
if (!HasInterface())
{
//
// If we already had an interface, the login dialog
// will have applied the new security blanket.
// If we didn't have an interface, it needs to be
// recreated with the new security blanket.
//
CWaitCursor wait;
err = CreateInterface(FALSE);
}
}
}
return fPasswordEntered;
}
HRESULT
CIISMachine::CheckCapabilities()
/*++
Routine Description:
Load the capabilities information for this server.
Arguments:
None
Return Value:
HRESULT
--*/
{
CError err = AssureInterfaceCreated(TRUE);
if (err.Succeeded())
{
//
// Fetch capability bits and version numbers.
//
CString strMDInfo;
CMetabasePath::GetServiceInfoPath(_T(""), strMDInfo,SZ_MBN_WEB);
//
// Reuse existing interface we have lying around.
//
CMetaKey mk(m_pInterface);
err = mk.QueryResult();
if (err.Succeeded())
{
if (FAILED(mk.DoesPathExist(strMDInfo)))
{
CMetabasePath::GetServiceInfoPath(_T(""),strMDInfo,SZ_MBN_FTP);
if (FAILED(mk.DoesPathExist(strMDInfo)))
{
CMetabasePath::GetServiceInfoPath(_T(""),strMDInfo,SZ_MBN_SMTP);
if (FAILED(mk.DoesPathExist(strMDInfo)))
{
CMetabasePath::GetServiceInfoPath(_T(""),strMDInfo,SZ_MBN_NNTP);
if (FAILED(mk.DoesPathExist(strMDInfo)))
{
TRACEEOLID("No services exist:W3SVC,MSFTPSVC,SMTPSVC,NNTPSVC");
}
}
}
}
}
//if (m_pInterface)
{
CServerCapabilities sc(m_pInterface, strMDInfo);
err = sc.LoadData();
if (err.Succeeded())
{
DWORD dwVersion = sc.QueryMajorVersion();
if (dwVersion)
{
m_dwVersion = dwVersion | (sc.QueryMinorVersion() << SIZE_IN_BITS(WORD));
}
m_fCanAddInstance = sc.HasMultipleSites();
m_fHas10ConnectionsLimit = sc.Has10ConnectionLimit();
m_fIsWorkstation = sc.IsWorkstation();
m_fIsPerformanceConfigurable = sc.IsPerformanceConfigurable();
m_fIsServiceLevelConfigurable = sc.IsServiceLevelConfigurable();
}
}
}
return err;
}
/* virtual */
HRESULT
CIISMachine::RefreshData()
/*++
Routine Description:
Refresh relevant configuration data required for display.
Arguments:
None
Return Value:
HRESULT
--*/
{
CError err;
IConsoleNameSpace2 * pConsoleNameSpace = (IConsoleNameSpace2 *)GetConsoleNameSpace();
// Check if we have a valid connection to the metabase
err = CheckForMetabaseAccess(METADATA_PERMISSION_READ,this,TRUE,METADATA_MASTER_ROOT_HANDLE);
if (err.Succeeded())
{
//
// Check capability and version information.
//
err = CheckCapabilities();
SetDisplayName();
if (err.Succeeded())
{
// check if we should be showing the App Pools node...
if (QueryMajorVersion() >= 6)
{
BOOL fCompatMode = FALSE;
CMetabasePath path(TRUE, SZ_MBN_WEB);
CMetaKey mk(QueryAuthInfo(), path, METADATA_PERMISSION_READ);
err = mk.QueryResult();
if (err.Succeeded())
{
err = mk.QueryValue(MD_GLOBAL_STANDARD_APP_MODE_ENABLED, fCompatMode);
}
// If we fail here, then we have no Web service or no standard mode property defined
// That means that we are either not in standard mode or it doesn't matter
err.Reset();
// Loop thru the scope items to see what we actually have displayed...
CIISMBNode * pBadGuy = NULL;
CIISService * pWebService = NULL;
HSCOPEITEM hChild = NULL, hCurrent;
LONG_PTR cookie = 0;
BOOL bAppPoolsNodeExists = FALSE;
HSCOPEITEM hScope = QueryScopeItem();
if (hScope)
{
HRESULT hr = pConsoleNameSpace->GetChildItem(hScope, &hChild, &cookie);
while (SUCCEEDED(hr) && hChild != NULL)
{
CIISMBNode * pNode = (CIISMBNode *)cookie;
if (IsEqualGUID(* (GUID *)pNode->GetNodeType(),cAppPoolsNode))
{
pBadGuy = pNode;
bAppPoolsNodeExists = TRUE;
// Check if the Application Node container exists...
// if it does, then make sure we're in the right mode.
}
else
{
if (0 == _tcsicmp(pNode->GetNodeName(), SZ_MBN_WEB))
{
pWebService = (CIISService *) pNode;
}
}
hCurrent = hChild;
hr = pConsoleNameSpace->GetNextItem(hCurrent, &hChild, &cookie);
}
if (pWebService)
{
// do this check only if we have a W3SVC service
// that's because if the user only installed FTP
// we won't have the interface required for AppPool (WAM interface)
// and this may potential AV!!!!
//
// Find out what mode IIS is R-E-A-L-L-Y running as...
GET_PROCESS_MODE_STRUCT MyStructOfInfo;
MyStructOfInfo.pComAuthInfo = QueryAuthInfo();
MyStructOfInfo.dwReturnStatus = 0;
MyStructOfInfo.dwProcessMode = -1;
if (GetProcessMode(&MyStructOfInfo))
{
// We got it back in time (no timeout)
if (-1 != MyStructOfInfo.dwProcessMode)
{
fCompatMode = FALSE;
if (0 == MyStructOfInfo.dwProcessMode)
{
fCompatMode = TRUE;
}
TRACEEOLID("GetProcessMode:" << MyStructOfInfo.dwProcessMode);
}
}
else
{
// Leave it at whatever we got from the metabase...
}
}
// fCompatMode = 1 means there should be No App Pools (bAppPoolsNodeExists should be 0)
// fCompatMode = 0 means there should be App Pools (bAppPoolsNodeExists should be 1)
TRACEEOLID("fCompatMode:" << fCompatMode);
if (fCompatMode == bAppPoolsNodeExists)
{
if (fCompatMode && bAppPoolsNodeExists)
{
// find it and delete it.
if (pBadGuy->IsMyPropertySheetOpen())
{
// don't remove it if the property sheet is open on it.
}
else
{
pBadGuy->RemoveScopeItem();
}
}
else if (!fCompatMode && !bAppPoolsNodeExists)
{
if (pWebService)
{
m_pAppPoolsContainer = NULL;
CAppPoolsContainer * pPools = new CAppPoolsContainer(this, pWebService);
if (pPools)
{
// Insert pools container before Web Services node
pPools->AddRef();
pPools->AddToScopePane(pWebService->QueryScopeItem(), FALSE, TRUE);
m_pAppPoolsContainer = pPools;
}
}
}
}
}
}
}
}
#if defined(_DEBUG) || DBG
//DumpAllScopeItems(pConsoleNameSpace,m_hScopeItem,0);
#endif
return err;
}
/* virtual */
void
CIISMachine::SetInterfaceError(
HRESULT hr
)
/*++
Routine Description:
Set the interface error. If different from current error,
change the display icon
Arguments:
HRESULT hr : Error code (S_OK is acceptable)
Return Value:
None
--*/
{
if (m_err.HResult() != hr)
{
//
// Change to error/machine icon for the parent machine.
//
m_err = hr;
RefreshDisplay();
}
}
/* virtual */
HRESULT
CIISMachine::BuildMetaPath(
CComBSTR & bstrPath
) const
/*++
Routine Description:
Recursively build up the metabase path from the current node
and its parents
Arguments:
CComBSTR & bstrPath : Returns metabase path
Return Value:
HRESULT
--*/
{
//
// This starts off the path
//
bstrPath.Append(_cszSeparator);
bstrPath.Append(QueryNodeName());
return S_OK;
}
/* virtual */
HRESULT
CIISMachine::BuildURL(
CComBSTR & bstrURL
) const
/*++
Routine Description:
Recursively build up the URL from the current node
and its parents. The URL built up from a machine node
doesn't make a lot of sense, but for want of anything better,
this will bring up the default web site.
Arguments:
CComBSTR & bstrURL : Returns URL
Return Value:
HRESULT
--*/
{
CString strOwner;
if (IsLocal())
{
//
// Security reasons restrict this to "localhost" oftentimes
//
strOwner = _bstrLocalHost;
}
else
{
LPOLESTR lpOwner = QueryMachineName();
strOwner = PURE_COMPUTER_NAME(lpOwner);
}
//
// An URL on the machine node is built in isolation.
//
// ISSUE: Is this really a desirable URL? Maybe we should
// use something else.
//
bstrURL = _T("http://");
bstrURL.Append(strOwner);
return S_OK;
}
/* virtual */
HRESULT
CIISMachine::CreatePropertyPages(
LPPROPERTYSHEETCALLBACK lpProvider,
LONG_PTR handle,
IUnknown * pUnk,
DATA_OBJECT_TYPES type
)
/*++
Routine Description:
Create the property pages for the given object
Arguments:
LPPROPERTYSHEETCALLBACK lpProvider : Provider
LONG_PTR handle : Handle.
IUnknown * pUnk,
DATA_OBJECT_TYPES type
Return Value:
HRESULT
--*/
{
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
CError err;
if (S_FALSE == (HRESULT)(err = CIISMBNode::CreatePropertyPages(lpProvider, handle, pUnk, type)))
{
return S_OK;
}
if (ERROR_ALREADY_EXISTS == err.Win32Error())
{
return S_FALSE;
}
if (err.Succeeded())
{
CComBSTR bstrPath;
//
// ISSUE: What to do with m_err? This might be
// a bad machine object in the first place. Aborting
// when the machine object has an error code isn't
// such a bad solution here. If the error condition
// no longer exists, a refresh will cure.
//
if (m_err.Failed())
{
m_err.MessageBox();
return m_err;
}
err = BuildMetaPath(bstrPath);
if (err.Succeeded())
{
err = CheckForMetabaseAccess(METADATA_PERMISSION_READ,this,TRUE,bstrPath);
if (err.Succeeded())
{
CIISMachineSheet * pSheet = new CIISMachineSheet(
QueryAuthInfo(), bstrPath, GetMainWindow(GetConsole()),
(LPARAM)this,(LPARAM) NULL
);
if (pSheet)
{
// cache handle for user in MMCPropertyChangeNotify
m_ppHandle = handle;
pSheet->SetModeless();
err = AddMMCPage(lpProvider, new CIISMachinePage(pSheet));
}
else
{
err = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
}
err.MessageBoxOnFailure();
return err;
}
/* virtual */
HRESULT
CIISMachine::EnumerateScopePane(
HSCOPEITEM hParent
)
/*++
Routine Description:
Enumerate scope child items.
Arguments:
HSCOPEITEM hParent : Parent console handle
Return Value:
HRESULT
--*/
{
ASSERT(m_hScopeItem == hParent);
CError err;
CString str;
CIISService * pService, * pWebService = NULL;
CWaitCursor wait;
CMetaEnumerator * pme = NULL;
if (IsExpanded())
{
//
// Verify user credentials are satisfactorily resolved.
// Machines objects are loaded from the cache without a
// password, so the function below will ask for it.
//
ResolveCredentials();
wait.Restore();
BOOL fShouldRefresh = !HasInterface();
BOOL fCompatMode = FALSE;
err = AssureInterfaceCreated(FALSE);
if (err.Succeeded() && QueryMajorVersion() >= 6)
{
CMetabasePath path(TRUE, SZ_MBN_WEB);
CMetaKey mk(QueryAuthInfo(), path, METADATA_PERMISSION_READ);
err = mk.QueryResult();
if (err.Succeeded())
{
err = mk.QueryValue(MD_GLOBAL_STANDARD_APP_MODE_ENABLED, fCompatMode);
}
// If we fail here, then we have no Web service or no standard mode property defined
// That means that we are either not in standard mode or it doesn't matter
err.Reset();
}
if (err.Succeeded())
{
//
// Creation of the interface will have loaded display parameters, which
// may differ from the cached parameters.
//
if (fShouldRefresh)
{
RefreshDisplay();
}
err = CreateEnumerator(pme);
}
//
// Only check for acces denied now, because virtually any idiot
// is allowed to create a metabase interface, but will get the
// access denied when calling a method, such as enumeration.
//
if (HandleAccessDenied(err))
{
wait.Restore();
//
// Credentials were changed. Try again (interface should be
// created already)
//
SAFE_DELETE(pme);
if (err.Succeeded())
{
err = RefreshData();
CMetabasePath path;
err = DetermineIfAdministrator(
m_pInterface,
path,
&m_fIsAdministrator,
&m_dwMetabaseSystemChangeNumber
);
// Set the latest system change number.
RefreshMetabaseSystemChangeNumber();
err = CreateEnumerator(pme);
}
}
//
// Enumerate administerable services from the metabase
//
while (err.Succeeded())
{
err = pme->Next(str);
if (err.Succeeded())
{
TRACEEOLID("Enumerating node: " << str);
pService = new CIISService(this, str);
if (!pService)
{
err = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//
// See if we care
//
if (pService->IsManagedService())
{
pService->AddRef();
// update the service state
pService->GetServiceState();
err = pService->AddToScopePane(hParent);
if (err.Succeeded())
{
if (0 == _tcsicmp(pService->GetNodeName(), SZ_MBN_WEB))
{
pWebService = pService;
}
}
else
{
pService->Release();
}
}
else
{
//
// Node is not a managed service, or we're managing the
// cluster and the service is not clustered.
//
pService->Release();
}
}
}
if (err.Win32Error() == ERROR_NO_MORE_ITEMS)
{
err.Reset();
}
if (pWebService)
{
// do this check only if we have a W3SVC service
// that's because if the user only installed FTP
// we won't have the interface required for AppPool (WAM interface)
// and this may potential AV!!!!
//
// Find out what mode IIS is R-E-A-L-L-Y running as...
GET_PROCESS_MODE_STRUCT MyStructOfInfo;
MyStructOfInfo.pComAuthInfo = QueryAuthInfo();
MyStructOfInfo.dwReturnStatus = 0;
MyStructOfInfo.dwProcessMode = -1;
if (GetProcessMode(&MyStructOfInfo))
{
// We got it back in time (no timeout)
if (-1 != MyStructOfInfo.dwProcessMode)
{
fCompatMode = FALSE;
if (0 == MyStructOfInfo.dwProcessMode)
{
fCompatMode = TRUE;
}
TRACEEOLID("GetProcessMode:" << MyStructOfInfo.dwProcessMode);
}
}
else
{
// Leave it at whatever we got from the metabase...
}
}
// If we are encountered web service, we should add
// Application Pools container before this service
//
m_pAppPoolsContainer = NULL;
if (err.Succeeded() && pWebService != NULL && !fCompatMode)
{
// We could have iis5 machine which doesn't have any pools
//
CMetabasePath path(TRUE, SZ_MBN_WEB, SZ_MBN_APP_POOLS);
CMetaKey mk(pme, path);
if (mk.Succeeded())
{
CAppPoolsContainer * pPools = new CAppPoolsContainer(
this, pWebService);
if (!pPools)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto Fail;
}
// Insert pools container before Web Services node
pPools->AddRef();
err = pPools->AddToScopePane(pWebService->QueryScopeItem(), FALSE, TRUE);
m_pAppPoolsContainer = pPools;
}
}
// If we are encountered web service, we should add
// this container before after the web service
//
m_pWebServiceExtensionContainer = NULL;
if (err.Succeeded() && pWebService != NULL)
{
if (QueryMajorVersion() >= 6)
{
// We could have iis5 machine which doesn't have any extensions
CWebServiceExtensionContainer * pNode = new CWebServiceExtensionContainer(this, pWebService);
if (!pNode)
{
err = ERROR_NOT_ENOUGH_MEMORY;
goto Fail;
}
// Insert container AFTER Web Services node
pNode->AddRef();
err = pNode->AddToScopePane(pWebService->QueryScopeItem(), FALSE, FALSE);
m_pWebServiceExtensionContainer = pNode;
}
}
Fail:
if (err.Failed())
{
CWnd * pWnd = GetMainWindow(GetConsole());
DisplayError(err,pWnd ? pWnd->m_hWnd : NULL);
}
SetInterfaceError(err);
//
// Clean up
//
SAFE_DELETE(pme);
}
return err;
}
/* virtual */
HRESULT
CIISMachine::RemoveScopeItem()
/*++
Routine Description:
Remove the machine from the scope view and the cache.
Arguments:
None
Return Value:
HRESULT
--*/
{
ASSERT(m_pRootExt == NULL);
//
// Find out root before deleting scope node
//
CIISRoot * pRoot = GetRoot();
ASSERT_PTR(pRoot);
//
// Remove from the tree
//
#if defined(_DEBUG) || DBG
// check if we have open property pages...
g_OpenPropertySheetTracker.Dump();
// dump out any open connections
m_MachineWNetConnections.Dump();
#endif
HRESULT hr = CIISMBNode::RemoveScopeItem();
if (SUCCEEDED(hr) && pRoot)
{
// Disconnect all connections made from this iismachine.
m_MachineWNetConnections.Clear();
pRoot->m_scServers.Remove(this);
#if defined(_DEBUG) || DBG
// check if we leaked anything.
g_Debug_IISObject.Dump(2);
#endif
}
return hr;
}
HRESULT
CIISMachine::DeleteChildObjects(HSCOPEITEM hItem)
{
CMetaInterface * pInterface = QueryInterface();
if (pInterface)
{
pInterface->SaveData();
}
return CIISMBNode::DeleteChildObjects(hItem);
}
BOOL
CIISMachine::IsLocalHost()
{
if (FALSE == m_fLocalHostIPChecked)
{
BOOL bIsLocalHost = FALSE;
LPOLESTR lpOwner = QueryMachineName();
CString strCleanName;
strCleanName = PURE_COMPUTER_NAME(lpOwner);
// Check if MachineName is mapped to 127.0.0.1 or localhost...
// if the machine specified
// is not the local machine...
if (!::IsLocalHost(strCleanName,&bIsLocalHost))
{
// WinSockFailure
}
m_fIsLocalHostIP = bIsLocalHost;
m_fLocalHostIPChecked = TRUE;
}
return m_fIsLocalHostIP;
}
/* virtual */
LPOLESTR
CIISMachine::GetResultPaneColInfo(int nCol)
/*++
Routine Description:
Return result pane string for the given column number
Arguments:
int nCol : Column number
Return Value:
String
--*/
{
if (m_pRootExt != NULL)
{
return m_pRootExt->GetResultPaneColInfo(nCol);
}
ASSERT(_fStaticsLoaded);
switch(nCol)
{
case COL_NAME:
return QueryDisplayName();
case COL_LOCAL:
return IsLocalHost() ? _bstrYes : _bstrNo;
case COL_VERSION:
{
CString str;
str.Format(_bstrVersionFmt, QueryMajorVersion(), QueryMinorVersion());
_bstrResult = str;
}
return _bstrResult;
case COL_STATUS:
{
if (m_err.Succeeded())
{
return OLESTR("");
}
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
return m_err;
}
}
ASSERT_MSG("Bad column number");
return OLESTR("");
}
/* virtual */
HRESULT
CIISMachine::GetResultViewType(
LPOLESTR * lplpViewType,
long * lpViewOptions
)
/*++
Routine Description:
If we have an URL built up, display our result view as that URL,
and destroy it. This is done when 'browsing' a metabase node.
The derived class will build the URL, and reselect the node.
Arguments:
BSTR * lplpViewType : Return view type here
long * lpViewOptions : View options
Return Value:
S_FALSE to use default view type, S_OK indicates the
view type is returned in *ppViewType
--*/
{
if (m_bstrURL.Length())
{
*lpViewOptions = MMC_VIEW_OPTIONS_NONE;
*lplpViewType = (LPOLESTR)::CoTaskMemAlloc(
(m_bstrURL.Length() + 1) * sizeof(WCHAR)
);
if (*lplpViewType)
{
lstrcpy(*lplpViewType, m_bstrURL);
//
// Destroy URL so we get a normal result view next time
//
m_bstrURL.Empty();
m_fSkipEnumResult = TRUE;
return S_OK;
}
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
}
//
// No URL waiting -- use standard result view
//
return CIISObject::GetResultViewType(lplpViewType, lpViewOptions);
}
/* virtual */
HRESULT
CIISMachine::AddMenuItems(
LPCONTEXTMENUCALLBACK lpContextMenuCallback,
long * pInsertionAllowed,
DATA_OBJECT_TYPES type
)
/*++
Routine Description:
Add menu items to the context menu
Arguments:
LPCONTEXTMENUCALLBACK lpContextMenuCallback : Context menu callback
long * pInsertionAllowed : Insertion allowed
DATA_OBJECT_TYPES type : Object type
Return Value:
HRESULT
--*/
{
ASSERT_READ_PTR(lpContextMenuCallback);
IConsoleNameSpace2 * pConsoleNameSpace = (IConsoleNameSpace2 *)GetConsoleNameSpace();
//
// Add base menu items
//
HRESULT hr = CIISObject::AddMenuItems(
lpContextMenuCallback,
pInsertionAllowed,
type
);
if (SUCCEEDED(hr))
{
if (IsAdministrator() && (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) != 0)
{
AddMenuItemByCommand(lpContextMenuCallback, IDM_METABACKREST);
AddMenuItemByCommand(lpContextMenuCallback, IDM_SHUTDOWN);
}
// Check if we can do save data on this version of iis...
if (IsConfigFlushable() && (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) != 0)
{
AddMenuItemByCommand(lpContextMenuCallback, IDM_SAVE_DATA);
}
#if 0
if (CanAddInstance())
{
ASSERT(pInsertionAllowed != NULL);
if ((*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) != 0)
{
#define ADD_SERVICE_MENU(x)\
if (!bSepAdded)\
{\
AddMenuSeparator(lpContextMenuCallback);\
bSepAdded = TRUE;\
}\
AddMenuItemByCommand(lpContextMenuCallback, (x))
HSCOPEITEM hChild = NULL, hCurrent;
LONG_PTR cookie;
BOOL bSepAdded = FALSE;
hr = pConsoleNameSpace->GetChildItem(QueryScopeItem(), &hChild, &cookie);
while (SUCCEEDED(hr) && hChild != NULL)
{
CIISMBNode * pNode = (CIISMBNode *)cookie;
ASSERT(pNode != NULL);
if (_tcsicmp(pNode->GetNodeName(), SZ_MBN_FTP) == 0)
{
ADD_SERVICE_MENU(IDM_NEW_FTP_SITE);
}
else if (_tcsicmp(pNode->GetNodeName(), SZ_MBN_WEB) == 0)
{
ADD_SERVICE_MENU(IDM_NEW_WEB_SITE);
}
else if (_tcsicmp(pNode->GetNodeName(), SZ_MBN_APP_POOLS) == 0)
{
ADD_SERVICE_MENU(IDM_NEW_APP_POOL);
}
hCurrent = hChild;
hr = pConsoleNameSpace->GetNextItem(hCurrent, &hChild, &cookie);
}
}
}
#endif
//
// CODEWORK: Add new instance commands for each of the services
// keeping in mind which ones are installed and all.
// add that info to the table, remembering that this
// is per service.
//
}
return hr;
}
#if 0
// BUGBUG: It should be quite different -> we don't know in advance
// which service is this site for
HRESULT
CIISMachine::InsertNewInstance(DWORD inst)
{
CError err;
// Now we should insert and select this new site
TCHAR buf[16];
CIISSite * pSite = new CIISSite(m_pOwner, this, _itot(inst, buf, 10));
if (pSite != NULL)
{
// If machine is not expanded we will get error and no effect
if (!IsExpanded())
{
SelectScopeItem();
IConsoleNameSpace2 * pConsole
= (IConsoleNameSpace2 *)GetConsoleNameSpace();
pConsole->Expand(QueryScopeItem());
}
// Now we should find the relevant service node, and inset this one under
// this node
pSite->AddRef();
err = pSite->AddToScopePaneSorted(QueryScopeItem(), FALSE);
if (err.Succeeded())
{
VERIFY(SUCCEEDED(pSite->SelectScopeItem()));
}
else
{
pSite->Release();
}
}
else
{
err = ERROR_NOT_ENOUGH_MEMORY;
}
return err;
}
#endif
HRESULT
CIISMachine::Command(
long lCommandID,
CSnapInObjectRootBase * pObj,
DATA_OBJECT_TYPES type
)
/*++
Routine Description:
Handle command from context menu.
Arguments:
long lCommandID : Command ID
CSnapInObjectRootBase * pObj : Base object
DATA_OBJECT_TYPES type : Data object type
Return Value:
HRESULT
--*/
{
HRESULT hr = S_OK;
switch (lCommandID)
{
case IDM_DISCONNECT:
hr = OnDisconnect();
break;
case IDM_METABACKREST:
hr = OnMetaBackRest();
break;
case IDM_SHUTDOWN:
hr = OnShutDown();
break;
case IDM_SAVE_DATA:
hr = OnSaveData();
break;
#if 0
case IDM_NEW_FTP_SITE:
CError err;
CComBSTR bstrMetaPath;
BuildMetaPath(bstrMetaPath);
err = CheckForMetabaseAccess(METADATA_PERMISSION_READ,this,TRUE,bstrMetaPath);
if (!IsLostInterface(err))
{
// reset error if an other error other than No interface
err.Reset();
}
if (err.Succeeded())
{
if (SUCCEEDED(hr = AddFTPSite(pObj, type, &inst)))
{
hr = InsertNewInstance(inst);
}
}
break;
case IDM_NEW_WEB_SITE:
CError err;
CComBSTR bstrMetaPath;
BuildMetaPath(bstrMetaPath);
err = CheckForMetabaseAccess(METADATA_PERMISSION_READ,this,TRUE,bstrMetaPath);
if (!IsLostInterface(err))
{
// reset error if an other error other than No interface
err.Reset();
}
if (err.Succeeded())
{
if (SUCCEEDED(hr = AddWebSite(pObj, type, &inst)))
{
hr = InsertNewInstance(inst);
}
}
break;
case IDM_NEW_APP_POOL:
CError err;
CComBSTR bstrMetaPath;
BuildMetaPath(bstrMetaPath);
err = CheckForMetabaseAccess(METADATA_PERMISSION_READ,this,TRUE,bstrMetaPath);
if (!IsLostInterface(err))
{
// reset error if an other error other than No interface
err.Reset();
}
if (err.Succeeded())
{
hr = AddAppPool(pObj, type);
}
break;
#endif
//
// Pass on to base class
//
default:
hr = CIISMBNode::Command(lCommandID, pObj, type);
}
return hr;
}
HRESULT
CIISMachine::OnDisconnect()
/*++
Routine Description:
Disconnect this machine. Confirm user choice.
Arguments:
None
Return Value:
HRESULT
--*/
{
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
CString str;
str.Format(IDS_CONFIRM_DISCONNECT, QueryDisplayName());
BOOL bOpenPropertySheets = FALSE;
CIISObject * pOpenItem = NULL;
if (g_OpenPropertySheetTracker.IsPropertySheetOpenComputer(this,TRUE,&pOpenItem))
{
g_OpenPropertySheetTracker.Dump();
if (pOpenItem)
{
HWND hHwnd = pOpenItem->IsMyPropertySheetOpen();
// a property sheet is open somewhere..
// make sure they close it before proceeding with refresh...
// Highlight the property sheet.
if (hHwnd && (hHwnd != (HWND) 1))
{
DoHelpMessageBox(NULL,IDS_CLOSE_ALL_PROPERTY_SHEET_DISCONNECT, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
if (!SetForegroundWindow(hHwnd))
{
// wasn't able to bring this property sheet to
// the foreground, the propertysheet must not
// exist anymore. let's just clean the hwnd
// so that the user will be able to open propertysheet
pOpenItem->SetMyPropertySheetOpen(0);
}
bOpenPropertySheets = TRUE;
}
}
}
if (!bOpenPropertySheets)
{
if (NoYesMessageBox(str))
{
return RemoveScopeItem();
}
}
return S_OK;
}
HRESULT
CIISMachine::OnMetaBackRest()
/*++
Routine Description:
Backup/Restore the metabase
Arguments:
None
Return Value:
HRESULT
--*/
{
CError err;
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
//
// Verify user credentials are satisfactorily resolved.
// Machines objects are loaded from the cache without a
// password, so the function below will ask for it.
//
ResolveCredentials();
// ensure the dialog gets themed
CThemeContextActivator activator(theApp.GetFusionInitHandle());
CBackupDlg dlg(this, QueryServerName(), GetMainWindow(GetConsole()));
dlg.DoModal();
if (dlg.ServicesWereRestarted())
{
//
// Rebind all metabase handles on this server
//
err = CreateInterface(TRUE);
//
// Now do a refresh on the computer node. Since we've forced
// the rebinding already, we should not get the disconnect warning.
//
if (err.Succeeded())
{
err = Refresh(TRUE);
}
}
else
{
if (dlg.HasChangedMetabase())
{
//
// Refresh and re-enumerate child objects
//
err = Refresh(TRUE);
}
}
return err;
}
HRESULT
CIISMachine::OnShutDown()
/*++
Routine Description:
Bring up the IIS shutdown dialog. If the services on the remote
machine are restarted, the metabase interface should be recreated.
Arguments:
None
Return Value:
HRESULT
--*/
{
CError err;
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
//
// Verify user credentials are satisfactorily resolved.
// Machines objects are loaded from the cache without a
// password, so the function below will ask for it.
//
ResolveCredentials();
// ensure the dialog gets themed
CThemeContextActivator activator(theApp.GetFusionInitHandle());
CIISShutdownDlg dlg(this, GetMainWindow(GetConsole()));
dlg.DoModal();
if (dlg.ServicesWereRestarted())
{
//
// Rebind all metabase handles on this server
//
err = CreateInterface(TRUE);
//
// Now do a refresh on the computer node. Since we've forced
// the rebinding already, we should not get the disconnect warning.
//
if (err.Succeeded())
{
err = Refresh(TRUE);
}
}
return err;
}
HRESULT
CIISMachine::OnSaveData()
/*++
Routine Description:
Flush the metabase to disk and display saved config file
Arguments:
None
Return Value:
HRESULT
--*/
{
CError err;
CComBSTR bstrPath;
err = BuildMetaPath(bstrPath);
err = CheckForMetabaseAccess(METADATA_PERMISSION_READ,this,TRUE,bstrPath);
if (err.Succeeded())
{
err = DoOnSaveData(::GetActiveWindow(), QueryServerName(),QueryInterface(),TRUE,GetMetabaseSystemChangeNumber());
RefreshMetabaseSystemChangeNumber();
}
return err;
}
HRESULT
CIISMachine::RefreshMetabaseSystemChangeNumber()
/*++
Routine Description:
Arguments:
None
Return Value:
HRESULT
--*/
{
return QueryInterface()->GetSystemChangeNumber(&m_dwMetabaseSystemChangeNumber);
}