/*++

   Copyright    (c)    1994-2001    Microsoft Corporation

   Module  Name :
        cacheopt.cpp

   Abstract:
        ASP Cache options page

   Author:
        Sergei Antonov (sergeia)

   Project:
        Internet Services Manager

   Revision History:

--*/
#include "stdafx.h"
#include <aclapi.h>
#include "CacheOpt.h"

#define TOTAL_CACHE_DEFAULT      1000
#define IIS5_CACHE_DEFAULT		 250

LRESULT 
CCacheOptPage::OnInitDialog(HWND hDlg, LPARAM lParam)
{
   if (NULL == m_pData)
   {
      ASSERT(FALSE);
      ::EndDialog(hDlg, 0);
      return -1;
   }
   m_bInitDone = FALSE;
   // Set defaults for disabled controls
   if (m_pData->m_NoCache)
   {
	   m_pData->m_TotalCacheSize = TOTAL_CACHE_DEFAULT;
	   m_pData->m_LimCacheInMemorySize = 250;
	   m_pData->m_UnlimCacheInMemorySize = 250;
   }
   else if (m_pData->m_UnlimCache)
   {
	   m_pData->m_TotalCacheSize = TOTAL_CACHE_DEFAULT;
	   m_pData->m_LimCacheInMemorySize = 250;
   }
   else if (m_pData->m_LimCache)
   {
	   m_pData->m_UnlimCacheInMemorySize = 250;
   }
   DoDataExchange();
   m_FileChooser.Init(this, m_pData->IsLocal() ? FC_DIRECTORY_ONLY | FC_FORWRITE : 0, 
       IDC_CACHE_PATH, IDC_BROWSE);
   CString title;
   if (title.LoadString(_Module.GetResourceInstance(), IDS_SELECT_CACHE_PATH))
      m_FileChooser.SetDialogTitle(title);
   m_FileChooser.SetPath(m_pData->m_DiskCacheDir);

   ::EnableWindow(GetDlgItem(IDC_BROWSE), m_pData->IsLocal());

   UDACCEL toAcc[3] = {{1, 1}, {3, 5}, {6, 10}};

   m_eng_cache.SetRange32(SCRIPT_ENG_MIN, SCRIPT_ENG_MAX);
   m_eng_cache.SetPos32(m_pData->m_ScriptEngCacheMax);
   m_eng_cache.SetAccel(3, toAcc);
   
   m_cache_size.SetRange32(CACHE_SIZE_MIN, CACHE_SIZE_MAX);
   m_cache_size.SetPos32(m_pData->m_TotalCacheSize);
   m_cache_size.SetAccel(3, toAcc);
   
   m_inmem_unlim.SetRange32(0, m_pData->m_TotalCacheSize);
   m_inmem_unlim.SetPos32(m_pData->m_UnlimCacheInMemorySize);
   m_inmem_unlim.SetAccel(3, toAcc);
   
   m_inmem_lim.SetRange32(0, m_pData->m_TotalCacheSize);
   m_inmem_lim.SetPos32(m_pData->m_LimCacheInMemorySize);
   m_inmem_lim.SetAccel(3, toAcc);

   UINT id = IDC_UNLIMITED_CACHE;
   if (m_pData->m_NoCache) 
      id = IDC_NO_CACHE;
   else if (m_pData->m_LimCache) 
      id = IDC_LIMITED_CACHE;
   OnCacheSwitch(0, id, NULL);

   AdjustTracker();

   DoDataExchange();

   m_bInitDone = TRUE;

   return FALSE;
};

BOOL
CCacheOptPage::OnKillActive()
{
   HRESULT hr = S_OK;

   if (m_bInitDone)
   {
      if (!DoDataExchange(TRUE))
		  return FALSE;
	  hr = m_pData->Save();
      if (m_pData->m_LimCache)
      {
         if (m_pData->m_LimCacheInMemorySize > m_pData->m_TotalCacheSize)
         {
            ::SetFocus(GetDlgItem(IDC_INMEM_LIM_EDIT));
            return FALSE;
         }
      }
      CString buf;
	  DWORD rc;
      if (FC_SUCCESS != (rc = m_FileChooser.GetFileName(buf)))
	  {
		  DWORD id;
          if (rc == FC_TEXT_IS_INVALID)
          {
			  id = ERROR_DIRECTORY;
          }
		  else if (m_pData->IsLocal() && FC_FILE_DOES_NOT_EXIST == rc)
		  {
			  id = ERROR_PATH_NOT_FOUND;
		  }
		  CError err(id);
		  err.MessageBox(MB_OK);
          ::SetFocus(GetDlgItem(IDC_CACHE_PATH));
          SendDlgItemMessage(IDC_CACHE_PATH, EM_SETSEL, 0, -1);
          return FALSE;
	  }
	  if (m_pData->IsLocal())
	  {
		 TCHAR expanded[MAX_PATH];
	     ExpandEnvironmentStrings(buf, expanded, MAX_PATH);
	     buf = expanded;
         DWORD attr = ::GetFileAttributes(buf);
         if (-1 == (int)attr)
		 {
            CError err(GetLastError());
            err.MessageBox(MB_OK);
            ::SetFocus(GetDlgItem(IDC_CACHE_PATH));
            SendDlgItemMessage(IDC_CACHE_PATH, EM_SETSEL, 0, -1);
            return FALSE;
		 }
	     if (  (attr & FILE_ATTRIBUTE_READONLY) != 0
		    || (attr & FILE_ATTRIBUTE_DIRECTORY) == 0
			|| PathIsNetworkPath(buf)
			)
		 {
		    CString cap, msg;
			cap.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
			msg.LoadString(_Module.GetResourceInstance(), IDS_READ_ONLY_DIRECTORY);
			SendDlgItemMessage(IDC_CACHE_PATH, EM_SETSEL, 0, -1);
			MessageBox(msg, cap);
			::SetFocus(GetDlgItem(IDC_CACHE_PATH));
			return FALSE;
		 }
	  }
#if 0
	  // Andy will do this during startup. It is impossible to do in remote case.
      // Setup access permissions for the directory
      CWaitCursor clock;
      EXPLICIT_ACCESS ea[3];
      PACL pNewDACL = NULL;
      CString strWamIdentity;
      SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;

      // go get the IWAM account name from the metabase
      CMetabasePath w3svc(TRUE, SZ_MBN_WEB);
      CComAuthInfo auth(m_pData->m_ServerName, 
         m_pData->m_UserName, m_pData->m_UserPassword); 
      CMetaKey mk(&auth, w3svc, METADATA_PERMISSION_READ);
      if (!mk.Succeeded() || FAILED(hr = mk.QueryValue(MD_WAM_USER_NAME, strWamIdentity)))
      {
         return FALSE;
      }

      ZeroMemory(ea, sizeof(EXPLICIT_ACCESSA) * 3);
    
      ea[0].grfAccessPermissions = SYNCHRONIZE | GENERIC_ALL;
      ea[0].grfAccessMode = GRANT_ACCESS;
      ea[0].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
      ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;

      ea[1].grfAccessPermissions = SYNCHRONIZE | GENERIC_ALL;
      ea[1].grfAccessMode = GRANT_ACCESS;
      ea[1].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
      ea[1].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
      ea[1].Trustee.ptstrName = (LPTSTR)(LPCTSTR)strWamIdentity;

      ea[2].grfAccessPermissions = SYNCHRONIZE | GENERIC_ALL;
      ea[2].grfAccessMode = GRANT_ACCESS;
      ea[2].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
      ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;

      // build the new DACL with just these ACEs
      if (!AllocateAndInitializeSid(&NtAuthority,
                                       1,
                                       SECURITY_LOCAL_SYSTEM_RID,
                                       0,0,0,0,0,0,0,
                                       (PSID *)(&ea[0].Trustee.ptstrName)))
         hr = HRESULT_FROM_WIN32(GetLastError());
      else if (!AllocateAndInitializeSid(&NtAuthority,
                                       2,            // 2 sub-authorities
                                       SECURITY_BUILTIN_DOMAIN_RID,
                                       DOMAIN_ALIAS_RID_ADMINS,
                                       0,0,0,0,0,0,
                                       (PSID *)(&ea[2].Trustee.ptstrName)))

         hr = HRESULT_FROM_WIN32(GetLastError());

      else if ((hr = SetEntriesInAcl(3, ea, NULL, &pNewDACL)) != ERROR_SUCCESS);

      // set the ACL on the directory

      else hr = SetNamedSecurityInfo((LPTSTR)(LPCTSTR)buf,
                                    SE_FILE_OBJECT,
                                    DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
                                    NULL,
                                    NULL,
                                    pNewDACL,
                                    NULL);
      if (pNewDACL)
         LocalFree(pNewDACL);

      if (ea[0].Trustee.ptstrName)
         FreeSid(ea[0].Trustee.ptstrName);

      if (ea[2].Trustee.ptstrName)
         FreeSid(ea[2].Trustee.ptstrName);
#endif
      if (SUCCEEDED(hr))
      {
         StrCpy(m_pData->m_DiskCacheDir, buf);
      }
   }
   return SUCCEEDED(hr);
}

void
CCacheOptPage::OnCacheSwitch(UINT, UINT nID, HWND)
{
   switch (nID)
   {
   case IDC_NO_CACHE:
      m_pData->m_NoCache = TRUE;
      m_pData->m_UnlimCache = FALSE;
      m_pData->m_LimCache = FALSE;
      break;
   case IDC_UNLIMITED_CACHE:
      m_pData->m_NoCache = FALSE;
      m_pData->m_UnlimCache = TRUE;
      m_pData->m_LimCache = FALSE;
      break;
   case IDC_LIMITED_CACHE:
      // When cache is unlimited or disabled, total size is set to -1,
      // reset it to reasonable default here
//      if (m_pData->m_TotalCacheSize >= CACHE_UNLIM_MAX)
//      {
      {
//         m_pData->m_TotalCacheSize = 
//            __max(m_pData->m_LimCacheInMemorySize, TOTAL_CACHE_DEFAULT);
//         DoDataExchange(FALSE, IDC_CACHE_SIZE_EDIT);
//      }
      }
      m_pData->m_NoCache = FALSE;
      m_pData->m_UnlimCache = FALSE;
      m_pData->m_LimCache = TRUE;
      break;
   }
   m_NoCacheBtn.SetCheck(m_pData->m_NoCache);
   m_UnlimCacheBtn.SetCheck(m_pData->m_UnlimCache);
   m_LimCacheBtn.SetCheck(m_pData->m_LimCache);

   m_inmem_unlim.EnableWindow(m_pData->m_UnlimCache);
   ::EnableWindow(GetDlgItem(IDC_INMEM_UNLIM_EDIT), m_pData->m_UnlimCache);

   m_inmem_lim.EnableWindow(m_pData->m_LimCache);
   ::EnableWindow(GetDlgItem(IDC_INMEM_LIM_EDIT), m_pData->m_LimCache);
   m_cache_size.EnableWindow(m_pData->m_LimCache);
   ::EnableWindow(GetDlgItem(IDC_CACHE_SIZE_EDIT), m_pData->m_LimCache);
   m_cache_dist.EnableWindow(m_pData->m_LimCache);

   SET_MODIFIED(TRUE);
}

void
CCacheOptPage::OnChangeCacheSize(UINT nCode, UINT nID, HWND)
{
   if (::IsWindow(m_cache_dist.m_hWnd) && m_bInitDone)
   {
//      DoDataExchange(TRUE, IDC_CACHE_SIZE_EDIT);
//      DoDataExchange(TRUE, IDC_INMEM_LIM_EDIT);
// DoDataExchange is not suitable here -- it will call OnDataExchange error internally
// and will immediately produce annoying popup
      BOOL translated;
      m_pData->m_TotalCacheSize = GetDlgItemInt(IDC_CACHE_SIZE_EDIT, &translated, FALSE);
      m_pData->m_LimCacheInMemorySize = GetDlgItemInt(IDC_INMEM_LIM_EDIT, &translated, FALSE);
      m_inmem_lim.SetRange32(0, m_pData->m_TotalCacheSize);
	  if (m_pData->m_LimCacheInMemorySize > m_pData->m_TotalCacheSize)
	  {
		  // If in-memory limit is currently higher than total, set it to total
		  m_pData->m_LimCacheInMemorySize = m_pData->m_TotalCacheSize;
		  DoDataExchange(FALSE, IDC_INMEM_LIM_EDIT);
		  m_inmem_lim.SetPos32(m_pData->m_LimCacheInMemorySize);
	  }
      AdjustTracker();
      // Here we could adjust in memory size to be less or equal to the total size,
      // but I decided that it could be annoying: what if user deleted last zero
      // in total size control, and in memory size turned to be larger than total size.
      // We immediately will cut it to total, and to fix this mistake user will need 
      // to touch two places. It will be too bad.
      SET_MODIFIED(TRUE);
   }
}

void
CCacheOptPage::OnChangeInmemCacheSize(UINT nCode, UINT nID, HWND)
{
   if (::IsWindow(m_cache_dist.m_hWnd) && m_bInitDone)
   {
//      DoDataExchange(TRUE, nID);
// DoDataExchange is not suitable here -- it will call OnDataExchange error internally
// and will immediately produce annoying popup
      BOOL translated;
      m_pData->m_LimCacheInMemorySize = GetDlgItemInt(IDC_INMEM_LIM_EDIT, &translated, FALSE);
      AdjustTracker();
      SET_MODIFIED(TRUE);
   }
}

void
CCacheOptPage::OnTrackBarScroll(UINT nSBCode, UINT nPos, HWND hwnd)
{
   BOOL bChange = FALSE;
   int endval;
   switch (nSBCode)
   {
   case SB_THUMBPOSITION:
       if (nPos > m_pData->m_LimCacheInMemorySize)
       {
           m_pData->m_LimCacheInMemorySize = min(nPos, m_pData->m_TotalCacheSize);
       }
       else if (nPos < m_pData->m_LimCacheInMemorySize)
       {
           m_pData->m_LimCacheInMemorySize = max(nPos, 0);
       }
       else
       {
           m_pData->m_LimCacheInMemorySize = nPos;
       }
      bChange = TRUE;
      break;
   case SB_LINELEFT:
      endval = m_pData->m_LimCacheInMemorySize - m_cache_dist.GetLineSize();
      if (endval >= 0)
      {
         m_pData->m_LimCacheInMemorySize = endval;
         bChange = TRUE;
      }
      else
      {
         bChange = m_pData->m_LimCacheInMemorySize != 0;
         m_pData->m_LimCacheInMemorySize = 0;
      }
      break;
   case SB_LINERIGHT:
      endval = m_pData->m_LimCacheInMemorySize + m_cache_dist.GetLineSize();
      if (endval <= m_pData->m_TotalCacheSize)
      {
         m_pData->m_LimCacheInMemorySize = endval;
         bChange = TRUE;
      }
      else
      {
         bChange = m_pData->m_LimCacheInMemorySize != m_pData->m_TotalCacheSize;
         m_pData->m_LimCacheInMemorySize = m_pData->m_TotalCacheSize;
      }
      break;
   case SB_PAGELEFT:
      endval = m_pData->m_LimCacheInMemorySize - m_cache_dist.GetPageSize();
      if (endval >= 0)
      {
         m_pData->m_LimCacheInMemorySize = endval;
         bChange = TRUE;
      }
      else
      {
         bChange = m_pData->m_LimCacheInMemorySize != 0;
         m_pData->m_LimCacheInMemorySize = 0;
      }
      break;
   case SB_PAGERIGHT:
      endval = m_pData->m_LimCacheInMemorySize + m_cache_dist.GetPageSize();
      if (endval <= m_pData->m_TotalCacheSize)
      {
         m_pData->m_LimCacheInMemorySize = endval;
         bChange = TRUE;
      }
      else
      {
         bChange = m_pData->m_LimCacheInMemorySize != m_pData->m_TotalCacheSize;
         m_pData->m_LimCacheInMemorySize = m_pData->m_TotalCacheSize;
      }
      break;
   case SB_LEFT:
      bChange = m_pData->m_LimCacheInMemorySize != 0;
      m_pData->m_LimCacheInMemorySize = 0;
      break;
   case SB_RIGHT:
      bChange = m_pData->m_LimCacheInMemorySize != m_pData->m_TotalCacheSize;
      m_pData->m_LimCacheInMemorySize = m_pData->m_TotalCacheSize;
      break;
   case SB_THUMBTRACK:
   case SB_ENDSCROLL:
      break;
   }
   if (bChange)
   {
      DoDataExchange(FALSE, IDC_INMEM_LIM_EDIT);
      SET_MODIFIED(TRUE);
   }
}

void
CCacheOptPage::AdjustTracker()
{
   if (::IsWindow(m_cache_dist.m_hWnd))
   {
      m_cache_dist.SetRange(0, m_pData->m_TotalCacheSize, TRUE);
      m_cache_dist.SetPos(m_pData->m_LimCacheInMemorySize);
      m_cache_dist.SetPageSize(m_pData->m_TotalCacheSize / 10);
      m_cache_dist.SetLineSize(m_pData->m_TotalCacheSize / 25);
      m_cache_dist.SetTicFreq(m_pData->m_TotalCacheSize / 25);
   }
}

void 
CCacheOptPage::OnChangePath(UINT nCode, UINT nID, HWND hWnd)
{
	m_FileChooser.OnEditChange();
    CString buf;
    DWORD rc;
    if (FC_SUCCESS != (rc = m_FileChooser.GetFileName(buf)))
	{
        ::EnableWindow(m_pData->m_pSheet->GetDlgItem(IDOK), FALSE);
        SET_MODIFIED(FALSE);
	}
    else
    {
        ::EnableWindow(m_pData->m_pSheet->GetDlgItem(IDOK), TRUE);
        OnChangeData(nCode, nID, hWnd);
    }
}

void 
CCacheOptPage::OnDataValidateError(UINT id, BOOL bSave,_XData& data)
{
	if (bSave)
	{
		CString str, fmt, caption;
		caption.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
 
		switch (data.nDataType)
		{
		case ddxDataText:
 			break;
		case ddxDataNull:
			break;
		case ddxDataInt:
			fmt.LoadString(_Module.GetResourceInstance(), IDS_ERR_INT_RANGE);
			str.Format(fmt, data.intData.nMin, data.intData.nMax);
			break;
		}
		if (!str.IsEmpty())
		{
			MessageBox(str, caption, MB_OK | MB_ICONEXCLAMATION);
			::SetFocus(GetDlgItem(id));
		}
	}
}

void 
CCacheOptPage::OnDataExchangeError(UINT nCtrlID, BOOL bSave)
{
	if (bSave)
	{
		CString str, fmt, caption;
		int min, max;
		caption.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
		switch (nCtrlID)
		{
		case IDC_CACHE_SIZE_EDIT:
		case IDC_INMEM_UNLIM_EDIT:
			min = CACHE_SIZE_MIN;
			max = CACHE_SIZE_MAX;
			fmt.LoadString(_Module.GetResourceInstance(), IDS_ERR_INT_RANGE);
			break;
		case IDC_INMEM_LIM_EDIT:
			min = CACHE_SIZE_MIN;
			max = m_pData->m_TotalCacheSize;
			fmt.LoadString(_Module.GetResourceInstance(), IDS_ERR_INT_RANGE);
			break;
		case IDC_ENGINES:
			min = SCRIPT_ENG_MIN;
			max = SCRIPT_ENG_MAX;
			fmt.LoadString(_Module.GetResourceInstance(), IDS_ERR_INT_RANGE);
			break;
		default:
			str.LoadString(_Module.GetResourceInstance(), IDS_ERR_INVALID_DATA);
			break;
		}
		if (!fmt.IsEmpty())
		{
			str.Format(fmt, min, max);
		}
		MessageBox(str, caption, MB_OK | MB_ICONEXCLAMATION);
		::SetFocus(GetDlgItem(nCtrlID));
	}
}

void
CCacheOptPage::OnHelp()
{
    WinHelp(m_pData->m_HelpPath, HELP_CONTEXT, CCacheOptPage::IDD + WINHELP_NUMBER_BASE);
}

////////////////////////////////////////////////////////////////////////////

LRESULT 
CCacheOptPage_iis5::OnInitDialog(HWND hDlg, LPARAM lParam)
{
   if (NULL == m_pData)
   {
      ASSERT(FALSE);
      ::EndDialog(hDlg, 0);
      return -1;
   }
   m_bInitDone = FALSE;
   DoDataExchange();

   UDACCEL toAcc[3] = {{1, 1}, {3, 5}, {6, 10}};

   m_eng_cache.SetRange32(SCRIPT_ENG_MIN, SCRIPT_ENG_MAX);
   m_eng_cache.SetPos32(m_pData->m_ScriptEngCacheMax);
   m_eng_cache.SetAccel(3, toAcc);
   
   m_inmem_lim.SetRange32(CACHE_SIZE_MIN, CACHE_SIZE_MAX);
   m_inmem_lim.SetPos32(m_pData->m_LimCacheInMemorySize);
   m_inmem_lim.SetAccel(3, toAcc);

   UINT id = IDC_UNLIMITED_CACHE;
   if (m_pData->m_NoCache) 
      id = IDC_NO_CACHE;
   else if (m_pData->m_LimCache) 
      id = IDC_LIMITED_CACHE;
   OnCacheSwitch(0, id, NULL);

   DoDataExchange();

   m_bInitDone = TRUE;

   return FALSE;
};

BOOL
CCacheOptPage_iis5::OnKillActive()
{
   HRESULT hr = S_OK;

   if (m_bInitDone)
   {
      if (!DoDataExchange(TRUE))
		  return FALSE;
	  hr = m_pData->Save();
   }
   return SUCCEEDED(hr);
}

void
CCacheOptPage_iis5::OnCacheSwitch(UINT, UINT nID, HWND)
{
   switch (nID)
   {
   case IDC_NO_CACHE:
      m_pData->m_NoCache = TRUE;
      m_pData->m_UnlimCache = FALSE;
      m_pData->m_LimCache = FALSE;
      break;
   case IDC_UNLIMITED_CACHE:
      m_pData->m_NoCache = FALSE;
      m_pData->m_UnlimCache = TRUE;
      m_pData->m_LimCache = FALSE;
      break;
   case IDC_LIMITED_CACHE:
      // When cache is unlimited or disabled, size is set to -1,
      // reset it to reasonable default here
      if (m_pData->m_LimCacheInMemorySize == -1)
      {
         m_pData->m_LimCacheInMemorySize = IIS5_CACHE_DEFAULT;
         DoDataExchange(FALSE, IDC_CACHE_SIZE_EDIT);
      }
      m_pData->m_NoCache = FALSE;
      m_pData->m_UnlimCache = FALSE;
      m_pData->m_LimCache = TRUE;
      break;
   }
   m_NoCacheBtn.SetCheck(m_pData->m_NoCache);
   m_UnlimCacheBtn.SetCheck(m_pData->m_UnlimCache);
   m_LimCacheBtn.SetCheck(m_pData->m_LimCache);

   m_inmem_lim.EnableWindow(m_pData->m_LimCache);
   ::EnableWindow(GetDlgItem(IDC_CACHE_SIZE_EDIT), m_pData->m_LimCache);

   SET_MODIFIED(TRUE);
}

void 
CCacheOptPage_iis5::OnDataValidateError(UINT id, BOOL bSave,_XData& data)
{
	if (bSave)
	{
		CString str, fmt, caption;
		caption.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
 
		switch (data.nDataType)
		{
		case ddxDataText:
 			break;
		case ddxDataNull:
			break;
		case ddxDataInt:
			fmt.LoadString(_Module.GetResourceInstance(), IDS_ERR_INT_RANGE);
			str.Format(fmt, data.intData.nMin, data.intData.nMax);
			break;
		}
		if (!str.IsEmpty())
		{
			MessageBox(str, caption, MB_OK | MB_ICONEXCLAMATION);
			::SetFocus(GetDlgItem(id));
		}
	}
}

void 
CCacheOptPage_iis5::OnDataExchangeError(UINT nCtrlID, BOOL bSave)
{
	if (bSave)
	{
		CString str, fmt, caption;
		int min, max;
		caption.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
		fmt.LoadString(_Module.GetResourceInstance(), IDS_ERR_INT_RANGE);
		switch (nCtrlID)
		{
		case IDC_CACHE_SIZE_EDIT:
			min = CACHE_SIZE_MIN;
			max = CACHE_SIZE_MAX;
			break;
		case IDC_ENGINES:
			min = SCRIPT_ENG_MIN;
			max = SCRIPT_ENG_MAX;
			break;
		default:
			break;
		}
		str.Format(fmt, min, max);
		MessageBox(str, caption, MB_OK | MB_ICONEXCLAMATION);
		::SetFocus(GetDlgItem(nCtrlID));
	}
}

void
CCacheOptPage_iis5::OnHelp()
{
    WinHelp(m_pData->m_HelpPath, HELP_CONTEXT, CCacheOptPage::IDD + WINHELP_NUMBER_BASE);
}