Windows2000/private/windows/shell/dskquota/ui/userprop.cpp
2020-09-30 17:12:32 +02:00

1989 lines
53 KiB
C++

/* File: userprop.cpp
Description: Provides implementations for quota user property page.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
06/25/98 Replaced AddUserPropSheet with AddUserDialog. BrianAu
Now that we're getting user info from the DS
object picker, the prop sheet idea doesn't work
so well. A std dialog is better.
*/
#include "pch.h" // PCH
#pragma hdrstop
#include <lm.h>
#include "undo.h"
#include "userprop.h"
#include "userbat.h"
#include "uihelp.h"
#include "progress.h"
#include "uiutils.h"
// Context help IDs.
#pragma data_seg(".text", "CODE")
const static DWORD rgUserPropSheetHelpIDs[] =
{
IDC_ICON_USER, DWORD(-1),
IDC_STATIC2, DWORD(-1),
IDC_TXT_USERNAME, IDH_TXT_USERNAME,
IDC_TXT_SPACEUSED, IDH_TXT_SPACEUSED,
IDC_TXT_SPACEREMAINING, IDH_TXT_SPACEREMAINING,
IDC_LBL_SPACEUSED, DWORD(-1),
IDC_LBL_SPACEREMAINING, DWORD(-1),
IDC_ICON_USERSTATUS, IDH_ICON_USERSTATUS,
IDC_RBN_USER_NOLIMIT, IDH_RBN_USER_NOLIMIT,
IDC_RBN_USER_LIMIT, IDH_RBN_USER_LIMIT,
IDC_TXT_WARN_LEVEL, DWORD(-1),
IDC_EDIT_USER_LIMIT, IDH_EDIT_USER_LIMIT,
IDC_EDIT_USER_THRESHOLD, IDH_EDIT_USER_THRESHOLD,
IDC_CMB_USER_LIMIT, IDH_CMB_USER_LIMIT,
IDC_CMB_USER_THRESHOLD, IDH_CMB_USER_THRESHOLD,
0,0
};
#pragma data_seg()
// Messages for querying property page for icon images.
#define DQM_QUERY_STATUS_ICON (WM_USER + 1)
#define DQM_QUERY_USER_ICON (WM_USER + 2)
#define DQM_ENABLE_APPLY_BUTTON (WM_USER + 3)
/* Function: UserPropSheet::UserPropSheet
Description: Constructor for a user property sheet object.
Initializes the members that hold user quota data.
Arguments: None.
Returns: Nothing.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
UserPropSheet::UserPropSheet(
PDISKQUOTA_CONTROL pQuotaControl,
const CVolumeID& idVolume,
HWND hWndParent,
LVSelection& LVSel,
UndoList& UndoList
) : m_cVolumeMaxBytes(0),
m_pQuotaControl(pQuotaControl),
m_UndoList(UndoList),
m_LVSelection(LVSel),
m_hWndParent(hWndParent),
m_bIsDirty(FALSE),
m_bHomogeneousSelection(TRUE), // Assume selection is homogeneous.
m_pxbQuotaLimit(NULL),
m_pxbQuotaThreshold(NULL),
m_idVolume(idVolume),
m_strPageTitle(g_hInstDll, IDS_TITLE_GENERAL),
m_idCtlNextFocus(-1)
{
DBGASSERT((NULL != m_pQuotaControl));
DBGASSERT((NULL != m_hWndParent));
DBGTRACE((DM_UPROP, DL_HIGH, TEXT("UserPropSheet::UserPropSheet")));
m_llQuotaUsed = 0;
m_llQuotaLimit = 0;
m_llQuotaThreshold = 0;
DBGASSERT((0 == iICON_USER_SINGLE));
DBGASSERT((1 == iICON_USER_MULTIPLE));
m_hIconUser[0] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_SINGLE_USER));
m_hIconUser[1] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_MULTI_USER));
DBGASSERT((0 == iICON_STATUS_OK));
DBGASSERT((1 == iICON_STATUS_OVER_THRESHOLD));
DBGASSERT((2 == iICON_STATUS_OVER_LIMIT));
m_hIconStatus[0] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_OKBUBBLE));
m_hIconStatus[1] = LoadIcon(NULL, IDI_WARNING);
m_hIconStatus[2] = LoadIcon(g_hInstDll, MAKEINTRESOURCE(IDI_WARNERR));
}
UserPropSheet::~UserPropSheet(
VOID
)
{
DBGTRACE((DM_UPROP, DL_HIGH, TEXT("UserPropSheet::~UserPropSheet")));
INT i = 0;
if (NULL != m_pQuotaControl)
m_pQuotaControl->Release();
if (NULL != m_pxbQuotaLimit)
delete m_pxbQuotaLimit;
if (NULL != m_pxbQuotaThreshold)
delete m_pxbQuotaThreshold;
}
/* Function: UserPropSheet::Run
Description: Creates and runs the property sheet dialog.
This is the only method a client needs to call once the object
is created.
Arguments: None.
Returns:
NO_ERROR
E_FAIL - Couldn't create property sheet.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
HRESULT
UserPropSheet::Run(
VOID
)
{
HRESULT hResult = NO_ERROR; // Assume success.
PROPSHEETHEADER psh;
PROPSHEETPAGE psp;
ZeroMemory(&psh, sizeof(psh));
ZeroMemory(&psp, sizeof(psp));
// Define page.
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
psp.hInstance = g_hInstDll;
psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_USERQUOTA);
psp.pszTitle = (LPCTSTR)m_strPageTitle;
psp.pfnDlgProc = (DLGPROC)DlgProc;
psp.lParam = (LPARAM)this;
psp.pcRefParent = (UINT *)& g_cRefThisDll;
// Define sheet.
psh.dwSize = sizeof(PROPSHEETHEADER);
psh.dwFlags = PSH_PROPSHEETPAGE;
psh.hwndParent = m_hWndParent;
psh.hInstance = g_hInstDll;
psh.pszIcon = NULL;
psh.pszCaption = NULL;
psh.nPages = 1;
psh.nStartPage = 0;
psh.ppsp = (LPCPROPSHEETPAGE)&psp;
if (0 <= PropertySheet(&psh))
hResult = E_FAIL;
return hResult;
}
/* Function: UserPropSheet::DlgProc
Description: Static method called by windows to process messages for the
property page dialog. Since it's static, we have to save the "this"
pointer in the window's USERDATA.
Arguments: Standard WndProc-type arguments.
Returns: Standard WndProc-type return values.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR APIENTRY
UserPropSheet::DlgProc(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
INT_PTR bResult = FALSE;
// Retrieve the "this" pointer from the dialog's userdata.
// It was placed there in OnInitDialog().
UserPropSheet *pThis = (UserPropSheet *)GetWindowLongPtr(hDlg, DWLP_USER);
try
{
switch(message)
{
case WM_INITDIALOG:
DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_INITDIALOG")));
bResult = OnInitDialog(hDlg, wParam, lParam);
break;
case WM_NOTIFY:
DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_NOTIFY")));
bResult = pThis->OnNotify(hDlg, wParam, lParam);
break;
case WM_COMMAND:
DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_COMMAND")));
bResult = pThis->OnCommand(hDlg, wParam, lParam);
break;
case WM_HELP:
DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_HELP")));
bResult = pThis->OnHelp(hDlg, wParam, lParam);
break;
case WM_CONTEXTMENU:
bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
break;
case WM_DESTROY:
DBGPRINT((DM_WND, DL_MID, TEXT("DlgProc: WM_DESTROY")));
break;
case WM_CLOSE:
case WM_ENDSESSION:
DestroyWindow(hDlg);
break;
case DQM_ENABLE_APPLY_BUTTON:
pThis->m_bIsDirty = TRUE;
bResult = PropSheet_Changed(GetParent(hDlg), hDlg);
break;
// These two icon query messages are for automated testing
// of the UI.
case DQM_QUERY_USER_ICON:
bResult = pThis->QueryUserIcon(hDlg);
break;
case DQM_QUERY_STATUS_ICON:
bResult = pThis->QueryUserStatusIcon(hDlg);
break;
default:
break;
}
}
catch(CAllocException& e)
{
DiskQuotaMsgBox(GetDesktopWindow(),
IDS_OUTOFMEMORY,
IDS_TITLE_DISK_QUOTA,
MB_ICONERROR | MB_OK);
}
return bResult;
}
/* Function: UserPropSheet::OnInitDialog
Description: Handler for WM_INITDIALOG. Retrieves the "this" pointer from
the PROPSHEETPAGE structure (pointed to by lParam) and saves it in
the window's USERDATA.
Arguments:
hDlg - Dialog window handle.
wParam - Handle of control to receive focus if we return FALSE.
lParam - Pointer to PROPSHEETPAGE structure for the property page.
Returns:
TRUE = Tells windows to assign focus to the control in wParam.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnInitDialog(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
HRESULT hResult = NO_ERROR;
PROPSHEETPAGE *pPage = (PROPSHEETPAGE *)lParam;
UserPropSheet *pThis = (UserPropSheet *)pPage->lParam;
DWORD dwSectorsPerCluster = 0;
DWORD dwBytesPerSector = 0;
DWORD dwFreeClusters = 0;
DWORD dwTotalClusters = 0;
DBGASSERT((NULL != pThis));
// Save "this" in the window's userdata.
SetWindowLongPtr(hDlg, DWLP_USER, (INT_PTR)pThis);
// Read quota info from NTFS.
// For single selection, we cache the selected user's info.
// For multi selection, we cache the defaults for the volume.
// If adding a new user (count == 0), we also use the defaults for the
// volume.
pThis->RefreshCachedQuotaInfo();
// Calculate the volume's size.
// We'll use this to limit user threshold and quota limit entries.
if (GetDiskFreeSpace(pThis->m_idVolume.ForParsing(),
&dwSectorsPerCluster,
&dwBytesPerSector,
&dwFreeClusters,
&dwTotalClusters))
{
pThis->m_cVolumeMaxBytes = (LONGLONG)dwSectorsPerCluster *
(LONGLONG)dwBytesPerSector *
(LONGLONG)dwTotalClusters;
}
pThis->m_pxbQuotaLimit = new XBytes(hDlg,
IDC_EDIT_USER_LIMIT,
IDC_CMB_USER_LIMIT,
pThis->m_llQuotaLimit);
pThis->m_pxbQuotaThreshold = new XBytes(hDlg,
IDC_EDIT_USER_THRESHOLD,
IDC_CMB_USER_THRESHOLD,
pThis->m_llQuotaThreshold);
pThis->InitializeControls(hDlg);
return TRUE; // Set focus to default control.
}
/* Function: UserPropSheet::RefreshCachedQuotaInfo
Description: Reads the quota limit, threshold and used values from the
property sheet's user object. If multiple users are selected,
only the first one is read.
Arguments: None.
Returns: Result of read operation.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
HRESULT
UserPropSheet::RefreshCachedQuotaInfo(
VOID
)
{
HRESULT hResult = NO_ERROR;
PDISKQUOTA_USER pUser = NULL;
INT cSelectedUsers = m_LVSelection.Count();
m_LVSelection.Retrieve(0, &pUser);
// Read quota threshold. Multi-user selections use the volume's default.
if (1 == cSelectedUsers)
{
hResult = pUser->GetQuotaThreshold(&m_llQuotaThreshold);
}
else
{
hResult = m_pQuotaControl->GetDefaultQuotaThreshold(&m_llQuotaThreshold);
}
if (FAILED(hResult))
goto refresh_quota_info_failed;
// Read quota limit. Multi-user selections use the volume's default.
if (1 == cSelectedUsers)
{
hResult = pUser->GetQuotaLimit(&m_llQuotaLimit);
}
else
{
hResult = m_pQuotaControl->GetDefaultQuotaLimit(&m_llQuotaLimit);
}
if (FAILED(hResult))
goto refresh_quota_info_failed;
// Read quota used.
if (1 == cSelectedUsers)
{
hResult = pUser->GetQuotaUsed(&m_llQuotaUsed);
}
else
{
m_llQuotaUsed = 0;
}
refresh_quota_info_failed:
return hResult;
}
/* Function: UserPropSheet::OnCommand
Description: Handler for WM_COMMAND.
Arguments:
hDlg - Dialog window handle.
wParam - ID of selected control and notification code.
lParam - HWND of selected control.
Returns:
TRUE = Message wasn't handled.
FALSE = Message was handled.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnCommand(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
DWORD dwCtlId = LOWORD(wParam);
HWND hWndCtl = (HWND)lParam;
DWORD dwNotifyCode = HIWORD(wParam);
INT_PTR bResult = FALSE;
switch(dwCtlId)
{
case IDC_TXT_USERNAME:
if (EN_SETFOCUS == dwNotifyCode && IDC_EDIT_USER_THRESHOLD == m_idCtlNextFocus)
{
// Focus is being set as a result of an invalid entry
// in the warning level field. Force input focus to the
// field and select the entire contents. User can then just
// enter a new value.
SetFocus(GetDlgItem(hDlg, IDC_EDIT_USER_THRESHOLD));
SendDlgItemMessage(hDlg, IDC_EDIT_USER_THRESHOLD, EM_SETSEL, 0, -1);
m_idCtlNextFocus = -1;
}
break;
case IDC_RBN_USER_NOLIMIT:
if (m_pxbQuotaThreshold->IsEnabled())
{
// This is simple. Just set both the limit and threshold controls
// to "no limit".
m_pxbQuotaThreshold->SetBytes(NOLIMIT);
m_pxbQuotaLimit->SetBytes(NOLIMIT);
m_bIsDirty = TRUE;
}
break;
case IDC_RBN_USER_LIMIT:
{
LONGLONG llValue;
// This handler needs some logic. We have to handle several
// scenarios/rules with this one.
// 1. Single vs. Multiple selection.
// 2. Single selection for Administrator account.
// 3. Multi selection homogeneous/heterogenous with respect to
// quota limit and threshold values.
// 4. Can't display "No Limit" in edit controls when they're active.
// 5. Use volume defaults for new user and hetergenous multi-select.
if (!m_pxbQuotaThreshold->IsEnabled())
{
enum use_types { USE_CACHED, USE_VOLDEF, USE_NOLIMIT };
INT iUseAsValue = USE_CACHED;
INT cSelected = m_LVSelection.Count();
///
// First set the quota limit controls.
///
if (0 == cSelected) // Adding new user...
{
iUseAsValue = USE_VOLDEF;
}
else if (1 == cSelected) // One user selected...
{
PDISKQUOTA_USER pUser = NULL;
m_LVSelection.Retrieve(0, &pUser);
if (UserIsAdministrator(pUser))
{
// If user is administrator, the limit is always "No Limit".
// This will disable the "Limit" controls and prevent
// user from setting a limit on this account.
iUseAsValue = USE_NOLIMIT;
}
else if (NOLIMIT == m_llQuotaLimit)
{
// Account isn't Administrator AND the limit is NOLIMIT.
// Use the volume's default "new user" limit value.
iUseAsValue = USE_VOLDEF;
}
}
else if (!m_bHomogeneousSelection || NOLIMIT == m_llQuotaLimit) // Multiple user.
{
// Multiple non-homogeneous users get the volume's default limit.
// Multiple homogeneous users get their current cached setting unless
// the cached setting is NOLIMIT; in which case, they get the
// volume's defaults.
iUseAsValue = USE_VOLDEF;
}
// Set the proper quota limit value in the edit/combo box control.
llValue = 0;
switch(iUseAsValue)
{
case USE_VOLDEF:
m_pQuotaControl->GetDefaultQuotaLimit(&llValue);
// If default is NOLIMIT, display 0 MB. We can't display an
// "editable" No Limit in the edit control. Only numbers.
if (NOLIMIT == llValue)
llValue = 0;
break;
case USE_NOLIMIT:
llValue = NOLIMIT;
break;
case USE_CACHED:
llValue = m_llQuotaLimit;
break;
}
m_pxbQuotaLimit->SetBytes(llValue);
///
// Now the threshold controls...
///
llValue = 0;
iUseAsValue = USE_CACHED;
if (0 == cSelected)
{
iUseAsValue = USE_VOLDEF;
}
else if (1 == cSelected)
{
if (NOLIMIT == m_llQuotaThreshold)
{
iUseAsValue = USE_VOLDEF;
}
}
else if (!m_bHomogeneousSelection || NOLIMIT == m_llQuotaThreshold)
{
iUseAsValue = USE_VOLDEF;
}
// Set the proper quota threshold value in the edit/combo box control.
switch(iUseAsValue)
{
case USE_VOLDEF:
m_pQuotaControl->GetDefaultQuotaThreshold(&llValue);
// If default is NOLIMIT, display 0 MB. We can't display an
// "editable" No Limit in the edit control. Only numbers.
if (NOLIMIT == llValue)
llValue = 0;
break;
case USE_NOLIMIT:
llValue = NOLIMIT;
break;
case USE_CACHED:
llValue = m_llQuotaThreshold;
break;
}
m_pxbQuotaThreshold->SetBytes(llValue);
m_bIsDirty = TRUE;
}
break;
}
case IDC_EDIT_USER_LIMIT:
case IDC_EDIT_USER_THRESHOLD:
switch(dwNotifyCode)
{
case EN_UPDATE:
DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, EN_CHANGE")));
bResult = OnEditNotifyUpdate(hDlg, wParam, lParam);
m_bIsDirty = TRUE;
break;
case EN_KILLFOCUS:
DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, EN_KILLFOCUS")));
bResult = OnEditNotifyKillFocus(hDlg, wParam, lParam);
break;
default:
break;
}
break;
case IDC_CMB_USER_LIMIT:
case IDC_CMB_USER_THRESHOLD:
switch(dwNotifyCode)
{
case CBN_SELCHANGE:
DBGPRINT((DM_WND, DL_MID, TEXT("OnCommand, CBN_CHANGE")));
bResult = OnComboNotifySelChange(hDlg, wParam, lParam);
m_bIsDirty = TRUE;
break;
default:
break;
}
break;
default:
bResult = TRUE; // Didn't handle message.
break;
}
if (m_bIsDirty)
PropSheet_Changed(GetParent(hDlg), hDlg);
return bResult;
}
/* Function: UserPropSheet::OnNotify
Description: Handler for WM_NOTIFY.
Arguments:
hDlg - Dialog window handle.
wParam - ID of selected control and notification code.
lParam - HWND of selected control.
Returns:
TRUE = Message wasn't handled.
FALSE = Message was handled.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnNotify(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
INT_PTR bResult = FALSE;
switch(((NMHDR *)lParam)->code)
{
case PSN_SETACTIVE:
DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_SETACTIVE")));
bResult = OnSheetNotifySetActive(hDlg, wParam, lParam);
break;
case PSN_APPLY:
DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_APPLY")));
bResult = OnSheetNotifyApply(hDlg, wParam, lParam);
break;
case PSN_KILLACTIVE:
DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_KILLACTIVE")));
bResult = OnSheetNotifyKillActive(hDlg, wParam, lParam);
break;
case PSN_RESET:
DBGPRINT((DM_WND, DL_MID, TEXT("OnNotify, PSN_RESET")));
// Fall through.
default:
break;
}
return bResult;
}
/* Function: UserPropSheet::OnSheetNotifySetActive
Description: Handler for WM_NOTIFY - PSN_SETACTIVE.
Arguments:
hDlg - Dialog window handle.
wParam - ID of control.
lParam - Address of NMHDR structure.
Returns:
FALSE = Accept activation.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnSheetNotifySetActive(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0);
return TRUE;
}
/* Function: UserPropSheet::OnSheetNotifyApply
Description: Handler for WM_NOTIFY - PSN_APPLY.
Arguments:
hDlg - Dialog window handle.
wParam - ID of control.
lParam - Address of NMHDR structure.
Returns:
TRUE = PSN return value set using SetWindowLong.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnSheetNotifyApply(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
HRESULT hResult = NO_ERROR;
LONG dwPSNReturn = PSNRET_NOERROR;
// Only apply settings if the "Apply" button is enabled indicating
// that something has been changed. No need to apply unchanged
// settings when the OK button is pressed.
if (m_bIsDirty)
{
if (PSNRET_NOERROR == dwPSNReturn)
{
// We need to do this because if you activate the apply button
// with Alt-A we receive PSN_APPLY before EN_KILLFOCUS.
m_pxbQuotaThreshold->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_USER_THRESHOLD));
m_pxbQuotaLimit->OnEditKillFocus((LPARAM)GetDlgItem(hDlg, IDC_EDIT_USER_LIMIT));
// Ensure warning threshold is not above limit.
INT64 iThreshold = m_pxbQuotaThreshold->GetBytes();
INT64 iLimit = m_pxbQuotaLimit->GetBytes();
if (NOLIMIT != iLimit && iThreshold > iLimit)
{
TCHAR szLimit[40], szThreshold[40];
XBytes::FormatByteCountForDisplay(iLimit, szLimit, ARRAYSIZE(szLimit));
XBytes::FormatByteCountForDisplay(iThreshold, szThreshold, ARRAYSIZE(szThreshold));
CString s(g_hInstDll, IDS_FMT_ERR_WARNOVERLIMIT, szThreshold, szLimit, szLimit);
switch(DiskQuotaMsgBox(hDlg, s, IDS_TITLE_DISK_QUOTA, MB_ICONWARNING | MB_YESNO))
{
case IDYES:
m_pxbQuotaThreshold->SetBytes(iLimit);
break;
case IDNO:
// This m_idCtlNextFocus hack stuff is here because I can't get
// the @#$%! prop sheet to return focus to the threshold control.
// The only way I've been able to get this to happen is to
// cache this ID value then on the EN_SETFOCUS generated when
// the page is activated, set focus to the control.
// Gross but it works without too much hassle. [brianau]
m_idCtlNextFocus = IDC_EDIT_USER_THRESHOLD;
dwPSNReturn = PSNRET_INVALID;
break;
}
}
}
if (PSNRET_NOERROR == dwPSNReturn)
{
hResult = ApplySettings(hDlg);
if (FAILED(hResult))
{
INT idMsg = IDS_UNKNOWN_ERROR;
UINT uFlags = MB_OK;
switch(hResult)
{
case E_FAIL:
idMsg = IDS_WRITE_ERROR;
uFlags |= MB_ICONERROR;
break;
default:
switch(HRESULT_CODE(hResult))
{
// case ERROR_USER_EXISTS:
// idMsg = IDS_NOADD_EXISTING_USER;
// uFlags |= MB_ICONWARNING;
// break;
// BUGBUG: Still valid? [brianau - 5/27/98]
case ERROR_NO_SUCH_USER:
idMsg = IDS_NOADD_UNKNOWN_USER;
uFlags |= MB_ICONWARNING;
break;
case ERROR_ACCESS_DENIED:
idMsg = IDS_NO_WRITE_ACCESS;
uFlags |= MB_ICONWARNING;
break;
default:
uFlags |= MB_ICONERROR;
break;
}
break;
}
DiskQuotaMsgBox(GetDesktopWindow(),
idMsg,
IDS_TITLE_DISK_QUOTA,
uFlags);
dwPSNReturn = PSNRET_INVALID;
}
else
{
m_bIsDirty = FALSE;
}
}
}
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, dwPSNReturn);
return TRUE;
}
/* Function: UserPropSheet::OnSheetNotifyKillActive
Description: Handler for WM_NOTIFY - PSN_KILLACTIVE.
Arguments:
hDlg - Dialog window handle.
wParam - ID of control.
lParam - Address of NMHDR structure.
Returns:
TRUE = Invalid data entered. Don't kill page.
FALSE = All data is valid. Ok to kill page.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnSheetNotifyKillActive(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
BOOL bAllDataIsValid = TRUE;
// No sheet-level validation performed at this time.
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, !bAllDataIsValid);
return TRUE;
}
/* Function: UserPropSheet::OnHelp
Description: Handler for WM_HELP. Displays context sensitive help.
Arguments:
lParam - Pointer to a HELPINFO structure.
Returns: TRUE;
Revision History:
Date Description Programmer
-------- ----------- ----------
08/17/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnHelp(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE,
HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgUserPropSheetHelpIDs);
return TRUE;
}
INT_PTR
UserPropSheet::OnContextMenu(
HWND hwndItem,
int xPos,
int yPos
)
{
int idCtl = GetDlgCtrlID(hwndItem);
WinHelp(hwndItem,
UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE,
HELP_CONTEXTMENU,
(DWORD_PTR)((LPTSTR)rgUserPropSheetHelpIDs));
return FALSE;
}
/* Function: UserPropPage::OnEditNotifyUpdate
Description: Handler for WM_COMMAND, EN_UPDATE.
Called whenever a character is entered in an edit control.
Arguments:
Returns: FALSE;
Revision History:
Date Description Programmer
-------- ----------- ----------
09/03/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnEditNotifyUpdate(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
XBytes *pxb = NULL;
switch(LOWORD(wParam))
{
case IDC_EDIT_USER_LIMIT:
pxb = m_pxbQuotaLimit;
break;
case IDC_EDIT_USER_THRESHOLD:
pxb = m_pxbQuotaThreshold;
break;
default:
break;
}
if (NULL != pxb)
pxb->OnEditNotifyUpdate(lParam);
return FALSE;
}
/* Function: UserPropSheet::OnEditNotifyKillFocus
Description: Handler for WM_COMMAND, EN_KILLFOCUS.
Called whenever focus leaves an edit control.
Validates the value in the edit control and adjusts it if necessary.
Arguments:
Returns: FALSE;
Revision History:
Date Description Programmer
-------- ----------- ----------
08/17/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnEditNotifyKillFocus(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
XBytes *pxb = NULL;
switch(LOWORD(wParam))
{
case IDC_EDIT_USER_LIMIT:
pxb = m_pxbQuotaLimit;
break;
case IDC_EDIT_USER_THRESHOLD:
pxb = m_pxbQuotaThreshold;
break;
default:
break;
}
if (NULL != pxb)
pxb->OnEditKillFocus(lParam);
return FALSE;
}
/* Function: UserPropPage::OnComboNotifySelChange
Description: Handler for WM_COMMAND, CBN_SELCHANGE.
Called whenever the user selects the combo box.
Arguments: Std DlgProc args.
Returns: FALSE;
Revision History:
Date Description Programmer
-------- ----------- ----------
09/03/96 Initial creation. BrianAu
*/
INT_PTR
UserPropSheet::OnComboNotifySelChange(
HWND hDlg,
WPARAM wParam,
LPARAM lParam
)
{
XBytes *pxb = NULL;
switch(LOWORD(wParam))
{
case IDC_CMB_USER_LIMIT:
pxb = m_pxbQuotaLimit;
break;
case IDC_CMB_USER_THRESHOLD:
pxb = m_pxbQuotaThreshold;
break;
default:
break;
}
if (NULL != pxb)
pxb->OnComboNotifySelChange(lParam);
return FALSE;
}
/* Function: UserPropSheet::ApplySettings
Description: Applies the current settings to the user's quota information
if they have not changed from the original settings.
Arguments:
hDlg - Dialog window handle.
Returns:
NO_ERROR - Success.
E_INVALIDARG - One of the settings was invalid.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
01/24/98 Added bUndo argument. BrianAu
*/
HRESULT
UserPropSheet::ApplySettings(
HWND hDlg,
bool bUndo // Default == true.
)
{
HRESULT hResult = NO_ERROR;
BOOL bTranslated = FALSE;
com_autoptr<DISKQUOTA_USER> ptrUser;
UINT cUsers = m_LVSelection.Count();
UINT i = 0;
LONGLONG llThreshold;
LONGLONG llLimit;
CAutoSetRedraw autoredraw(m_hWndParent);
if (bUndo)
m_UndoList.Clear(); // Clear current undo list.
// Determine what threshold and limit to apply.
if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_USER_NOLIMIT))
{
llThreshold = NOLIMIT;
llLimit = NOLIMIT;
}
else
{
llThreshold = m_pxbQuotaThreshold->GetBytes();
llLimit = m_pxbQuotaLimit->GetBytes();
}
if (cUsers > 1)
{
// Create batch object and do batch update for multiple users.
com_autoptr<DISKQUOTA_USER_BATCH> ptrBatch;
hResult = m_pQuotaControl->CreateUserBatch(ptrBatch.getaddr());
if (SUCCEEDED(hResult))
{
for (i = 0; i < cUsers; i++)
{
m_LVSelection.Retrieve(i, ptrUser.getaddr());
if (bUndo)
{
// Add an entry to the undo list.
LONGLONG LimitUndo;
LONGLONG ThresholdUndo;
ptrUser->GetQuotaThreshold(&ThresholdUndo);
ptrUser->GetQuotaLimit(&LimitUndo);
// Use a local autoptr to ensure proper release of
// iface in case adding to the undo list throws an exception.
// On success, disown the real ptr so that the object
// stays with the undo list.
com_autoptr<DISKQUOTA_USER> ptrQuotaUser(ptrUser);
ptrUser->AddRef();
m_UndoList.Add(new UndoModify(ptrUser, ThresholdUndo, LimitUndo));
ptrQuotaUser.disown();
}
ptrUser->SetQuotaThreshold(llThreshold, FALSE);
if (UserIsAdministrator(ptrUser) && NOLIMIT != llLimit)
{
// User is the Administrator account AND
// We're trying to set the limit to something other than NOLIMIT.
// Can't set a limit on the administrator account.
DiskQuotaMsgBox(GetDesktopWindow(),
IDS_CANT_SET_ADMIN_LIMIT,
IDS_TITLE_DISK_QUOTA,
MB_ICONWARNING | MB_OK);
}
else
{
// OK to set quota limit.
ptrUser->SetQuotaLimit(llLimit, FALSE);
}
ptrBatch->Add(ptrUser);
}
hResult = ptrBatch->FlushToDisk();
}
}
else
{
// Do single user update or add new user.
m_LVSelection.Retrieve(0, ptrUser.getaddr());
DBGASSERT((NULL != ptrUser.get()));
if (bUndo)
{
// Add an entry to the undo list.
LONGLONG LimitUndo;
LONGLONG ThresholdUndo;
ptrUser->GetQuotaThreshold(&ThresholdUndo);
ptrUser->GetQuotaLimit(&LimitUndo);
// Use local autoptr to ensure proper release of iface ptr if
// an exception is thrown. Disown real ptr on success.
com_autoptr<DISKQUOTA_USER> ptrQuotaUser(ptrUser);
ptrUser->AddRef();
m_UndoList.Add(new UndoModify(ptrUser, ThresholdUndo, LimitUndo));
ptrQuotaUser.disown();
}
if (llThreshold != m_llQuotaThreshold)
{
hResult = ptrUser->SetQuotaThreshold(llThreshold, TRUE);
if (FAILED(hResult))
goto apply_failed;
m_llQuotaThreshold = llThreshold;
}
if (llLimit != m_llQuotaLimit)
{
hResult = ptrUser->SetQuotaLimit(llLimit, TRUE);
if (FAILED(hResult))
goto apply_failed;
m_llQuotaLimit = llLimit;
}
// Update the user's status icon and %used to reflect the new settings.
UpdateUserStatusIcon(hDlg,
m_llQuotaUsed,
m_llQuotaThreshold,
m_llQuotaLimit);
UpdateSpaceUsed(hDlg,
m_llQuotaUsed,
m_llQuotaLimit,
1);
}
// Update the listview item(s) so the user sees a visual response to
// pressing the "Apply" button.
autoredraw.Set(false);
for (i = 0; i < cUsers; i++)
{
INT iItem = 0;
m_LVSelection.Retrieve(i, &iItem);
ListView_Update(m_hWndParent, iItem);
}
autoredraw.Set(true);
InvalidateRect(m_hWndParent, NULL, FALSE);
apply_failed:
return hResult;
}
/* Function: UserPropSheet::InitializeControls
Description: Initializes the page controls based on the user's
quota settings.
Arguments:
hDlg - Dialog window handle.
Returns:
NO_ERROR - Always returns NO_ERROR.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
HRESULT
UserPropSheet::InitializeControls(
HWND hDlg
)
{
PDISKQUOTA_USER pUser = NULL;
UINT cUsers = m_LVSelection.Count();
if (1 == cUsers)
{
// Initialize controls for a single user.
m_LVSelection.Retrieve(0, &pUser);
// Configure the Limit/NoLimit radio buttons.
// Must examine the current threshold rather than the limit because of the
// special-case for the Administrator account. That account can have a
// threshold value but quota limit must always be "No Limit".
CheckDlgButton(hDlg, IDC_RBN_USER_LIMIT, NOLIMIT != m_llQuotaThreshold);
CheckDlgButton(hDlg, IDC_RBN_USER_NOLIMIT, NOLIMIT == m_llQuotaThreshold);
if (UserIsAdministrator(pUser))
{
// Override initialization of Quota Limit control with "No Limit".
m_pxbQuotaLimit->SetBytes(NOLIMIT);
}
// Note that the XBytes controls have already been set for single-user.
// See OnInitDialog().
// Configure the remaining dialog controls.
UpdateUserName(hDlg, pUser);
UpdateSpaceUsed(hDlg, m_llQuotaUsed, m_llQuotaLimit, cUsers);
UpdateUserStatusIcon(hDlg,
m_llQuotaUsed,
m_llQuotaThreshold,
m_llQuotaLimit);
}
else
{
// Initialize controls for multiple users.
LONGLONG llLimit = 0;
LONGLONG llLastLimit = 0;
LONGLONG llThreshold = 0;
LONGLONG llLastThreshold = 0;
LONGLONG llUsed = 0;
LONGLONG llTotalUsed = 0;
// Add up the total usage by all users.
for (UINT i = 0; i < cUsers; i++)
{
m_LVSelection.Retrieve(i, &pUser);
pUser->GetQuotaLimit(&llLimit);
pUser->GetQuotaThreshold(&llThreshold);
pUser->GetQuotaUsed(&llUsed);
llTotalUsed += llUsed;
if (m_bHomogeneousSelection)
{
// Determine if at least one user has a different
// threshold or limit. If all are the same, we can display
// the values in the edit controls. Otherwise, we default
// to "No Limit". Radio buttons don't provide an
// indeterminate state like checkboxes.
if (i > 0 &&
(llLimit != llLastLimit ||
llThreshold != llLastThreshold))
{
m_bHomogeneousSelection = FALSE;
}
else
{
llLastLimit = llLimit;
llLastThreshold = llThreshold;
}
}
}
// If all selected objects have the same limit and threshold,
// set the cached data to represent multiple-selection.
// If any one is different, we stick with the volume's default
// values set in RefreshCachedQuotaInfo().
if (m_bHomogeneousSelection)
{
m_llQuotaThreshold = llLastThreshold;
m_llQuotaLimit = llLastLimit;
}
else
{
// Since not all selected users have the same limit/thresold,
// the number we're displaying will be a change for at least
// one user. Activate the "Apply" button.
PostMessage(hDlg, DQM_ENABLE_APPLY_BUTTON, 0, 0);
}
m_pxbQuotaThreshold->SetBytes(m_llQuotaThreshold);
m_pxbQuotaLimit->SetBytes(m_llQuotaLimit);
// Configure the Limit/NoLimit radio buttons.
CheckDlgButton(hDlg,
IDC_RBN_USER_NOLIMIT,
NOLIMIT == m_llQuotaThreshold);
CheckDlgButton(hDlg,
IDC_RBN_USER_LIMIT,
NOLIMIT != m_llQuotaThreshold);
UpdateUserName(hDlg, cUsers);
UpdateSpaceUsed(hDlg, llTotalUsed, NOLIMIT, cUsers);
// Don't display any user status icon for multi-users.
}
// If "No Limit" radio button is checked, set limit and threshold controls
// to the "No Limit" state (disabled and displaying "No Limit" text).
// This may override any setting we made above.
if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_RBN_USER_NOLIMIT))
{
m_pxbQuotaThreshold->SetBytes(NOLIMIT);
m_pxbQuotaLimit->SetBytes(NOLIMIT);
}
// Set user icon.
SendMessage(GetDlgItem(hDlg, IDC_ICON_USER),
STM_SETICON,
(WPARAM)m_hIconUser[1 == cUsers ? iICON_USER_SINGLE :
iICON_USER_MULTIPLE],
0);
// Force the property sheet to disable the "Apply" button.
// The way I have set up the "Apply" enabling logic through OnCommand(),
// merely initializing the edit controls on the page causes the Apply
// button to become enabled. Since the user hasn't changed anything
// yet, it should be disabled.
m_bIsDirty = FALSE;
PropSheet_UnChanged(GetParent(hDlg), hDlg);
return NO_ERROR;
}
/* Function: UserPropSheet::QueryUserStatusIcon
Description: This function is provided for automated testing of the UI.
It is used by test scripts to determine which user status icon is
currently displayed.
Arguments:
hDlg - Dialog handle.
Returns: -1 = No icon displayed.
0 = "Everything OK" icon.
1 = Threshold exceded icon.
2 = Limit exceded icon.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT
UserPropSheet::QueryUserStatusIcon(
HWND hDlg
) const
{
HICON hicon = (HICON)SendMessage(GetDlgItem(hDlg, IDC_ICON_USERSTATUS),
STM_GETICON,
0, 0);
for (UINT i = 0; i < cSTATUS_ICONS; i++)
{
if (hicon == m_hIconStatus[i])
return i;
}
return -1;
}
/* Function: UserPropSheet::QueryUserIcon
Description: This function is provided for automated testing of the UI.
It is used by test scripts to determine which user status icon is
currently displayed.
Arguments:
hDlg - Dialog handle.
Returns: -1 = No icon displayed.
0 = Single-user icon.
1 = Multi-user icon.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
INT
UserPropSheet::QueryUserIcon(
HWND hDlg
) const
{
HICON hicon = (HICON)SendMessage(GetDlgItem(hDlg, IDC_ICON_USER),
STM_GETICON,
0, 0);
for (UINT i = 0; i < cUSER_ICONS; i++)
{
if (hicon == m_hIconUser[i])
return i;
}
return -1;
}
/* Function: UserPropSheet::UpdateUserStatusIcon
Description: Updates the quota status icon in the dialog box. This icon
must match the icon displayed in the listview for the selected user.
Arguments:
hDlg - Dialog handle.
iUsed - Quota bytes charged to user.
iThreshold - Quota warning threshold (bytes).
iLimit - User's quota limit.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
VOID
UserPropSheet::UpdateUserStatusIcon(
HWND hDlg,
LONGLONG iUsed,
LONGLONG iThreshold,
LONGLONG iLimit
)
{
// Set the user status icon if user is exceding the
// quota threshold or the limit. This is the same icon that is
// displayed in the listview status column. This logic must
// mirror that used in DetailsView::GetDispInfo_Image().
INT iIcon = iICON_STATUS_OK;
if (NOLIMIT != iLimit && iUsed > iLimit)
{
iIcon = iICON_STATUS_OVER_LIMIT;
}
else if (NOLIMIT != iThreshold && iUsed > iThreshold)
{
iIcon = iICON_STATUS_OVER_THRESHOLD;
}
SendMessage(GetDlgItem(hDlg, IDC_ICON_USERSTATUS),
STM_SETICON,
(WPARAM)m_hIconStatus[iIcon],
0);
}
/* Function: UserPropSheet::UpdateUserName
Description: Updates the Domain\Name text with the user's domain name
and account name strings. This method is called for a single-user
selection.
Also sets the property sheet title text.
Arguments:
hDlg - Dialog handle.
pUser - Address of user's IDiskQuotaUser interface.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
08/05/97 Added code to set prop sheet title text. BrianAu
*/
VOID
UserPropSheet::UpdateUserName(
HWND hDlg,
PDISKQUOTA_USER pUser
)
{
DBGASSERT((NULL != pUser));
// Display the user name, or some status text
// if the name hasn't been resolved.
CString strLogonName;
DWORD dwAccountStatus = 0;
pUser->GetAccountStatus(&dwAccountStatus);
if (DISKQUOTA_USER_ACCOUNT_RESOLVED == dwAccountStatus)
{
// User account name has been resolved. Display it.
TCHAR szLogonName[MAX_USERNAME];
TCHAR szDisplayName[MAX_FULL_USERNAME];
pUser->GetName(NULL, 0,
szLogonName, ARRAYSIZE(szLogonName),
szDisplayName, ARRAYSIZE(szDisplayName));
if (TEXT('\0') != szLogonName[0])
{
if (TEXT('\0') != szDisplayName[0])
{
strLogonName.Format(g_hInstDll,
IDS_FMT_DISPLAY_LOGON,
szDisplayName,
szLogonName);
}
else
{
strLogonName = szLogonName;
}
}
}
else
{
// User account name has not been resolved or cannot
// be resolved for some reason. Display appropriate
// status text. This is the same text displayed in the
// listview when the user's name has not been resolved.
INT idText = IDS_USER_ACCOUNT_UNKNOWN;
switch(dwAccountStatus)
{
case DISKQUOTA_USER_ACCOUNT_UNAVAILABLE:
idText = IDS_USER_ACCOUNT_UNAVAILABLE;
break;
case DISKQUOTA_USER_ACCOUNT_DELETED:
idText = IDS_USER_ACCOUNT_DELETED;
break;
case DISKQUOTA_USER_ACCOUNT_INVALID:
idText = IDS_USER_ACCOUNT_INVALID;
break;
case DISKQUOTA_USER_ACCOUNT_UNRESOLVED:
idText = IDS_USER_ACCOUNT_UNRESOLVED;
break;
case DISKQUOTA_USER_ACCOUNT_UNKNOWN:
default:
break;
}
strLogonName.Format(g_hInstDll, idText);
}
SetDlgItemText(hDlg, IDC_TXT_USERNAME, strLogonName);
// Format and draw the prop sheet title string.
CString strSheetTitle(g_hInstDll,
IDS_TITLE_EDIT_USER,
(LPCTSTR)strLogonName);
PropSheet_SetTitle(GetParent(hDlg), 0, (LPCTSTR)strSheetTitle);
}
/* Function: UserPropSheet::UpdateUserName
Description: Replaces the user Domain\Name text with a message showing
how many users are selected. This is used for multi-user selections
where no single user name is applicable.
Also sets the property sheet title text.
Arguments:
hDlg - Dialog handle.
cUsers - Number of users represented in the property dialog.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
08/05/97 Added code to set prop sheet title text. BrianAu
*/
VOID
UserPropSheet::UpdateUserName(
HWND hDlg,
INT cUsers
)
{
// Hide name edit control. Can't display names for all users.
// Display "Multiple Quota Users." instead.
CString strText(g_hInstDll, IDS_TITLE_MULTIUSER, cUsers);
SetDlgItemText(hDlg, IDC_TXT_USERNAME, strText);
// Set the title of the property sheet.
CString strSheetTitle(g_hInstDll, IDS_TITLE_EDIT_MULTIUSER);
PropSheet_SetTitle(GetParent(hDlg), 0, (LPCTSTR)strSheetTitle);
}
/* Function: UserPropSheet::UpdateSpaceUsed
Description: Updates the "space used" and "remaining" fields on the user
property sheet.
Arguments:
hDlg - Dialog handle.
iUsed - Quota bytes charged to user(s).
iLimit - User's quota limit.
cUsers - Number of users represented in the property dialog.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- ----------- ----------
08/15/96 Initial creation. BrianAu
*/
VOID
UserPropSheet::UpdateSpaceUsed(
HWND hDlg,
LONGLONG iUsed,
LONGLONG iLimit,
INT cUsers
)
{
TCHAR szText[80];
// Display - Used: 999XB (99%)
XBytes::FormatByteCountForDisplay(iUsed,
szText, ARRAYSIZE(szText));
CString strText(szText);
if (1 == cUsers)
{
// Only single-user page gets (99%) appended.
// Pct quota is meaningless for multiple users.
if (0 != iLimit && NOLIMIT != iLimit)
{
UINT iPct = (INT)((iUsed * 100) / iLimit);
strText.Format(g_hInstDll,
IDS_QUOTA_USED_SINGLEUSER,
szText,
iPct);
}
}
SetDlgItemText(hDlg,
IDC_TXT_SPACEUSED,
strText);
// Display - Remaining: 999XB
strText = szText;
if (NOLIMIT != iLimit)
{
LONGLONG iAmount = 0;
if (iUsed <= iLimit)
iAmount = iLimit - iUsed;
XBytes::FormatByteCountForDisplay(iAmount,
strText.GetBuffer(80), 80);
strText.ReleaseBuffer();
}
else
{
// Display "N/A" if limit is NOLIMIT.
strText.Format(g_hInstDll, IDS_NOT_APPLICABLE);
}
SetDlgItemText(hDlg,
IDC_TXT_SPACEREMAINING,
strText);
}