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.

1270 lines
39 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: xbytes.cpp
  3. Description: This module implements a class that coordinates the operation
  4. between the edit control and combo box used for entering byte values.
  5. The name "XBytes" is used because the control can represent
  6. KBytes, MBytes, GBytes etc.
  7. The cooperation between edit control and combo control is required
  8. so that the user can enter a byte value in the edit control then
  9. indicate it's order (KB, MB, GB...) using a selection from the combo box.
  10. A simple external interface is provided to initially set the
  11. object's byte value then retrieve the byte value when needed. The
  12. object's client is also required to call two member functions when
  13. the parent dialog receives an EN_UPDATE notification and a CBN_SELCHANGE
  14. message. The XBytes object handles all of the value scaling
  15. internally.
  16. NOTE: I experimented with adding a spin control to the edit control.
  17. I found that without some fancy intervention, the spin control
  18. didn't support fractional values (i.e. 2.5MB). Decided to keep
  19. fractional values and ditch the spinner. I think fractional
  20. values will be more useful to disk admins.
  21. Revision History:
  22. Date Description Programmer
  23. -------- --------------------------------------------------- ----------
  24. 08/30/96 Initial creation. BrianAu
  25. 07/23/97 Added default ctor and CommonInit() function. BrianAu
  26. Also added g_ForLoadingStaticStrings instance.
  27. */
  28. ///////////////////////////////////////////////////////////////////////////////
  29. #include "pch.h" // PCH
  30. #pragma hdrstop
  31. #include "resource.h"
  32. #include "xbytes.h"
  33. const TCHAR CH_NUL = TEXT('\0');
  34. const TCHAR CH_ZERO = TEXT('0');
  35. const INT MAX_EDIT_TEXT = 16; // Max chars in edit text.
  36. const INT MAX_CMB_TEXT = 10; // For "KB", "MB", "GB" etc.
  37. const INT64 MAX_VALUE = ((1i64 << 60) * 6i64); // Max is 6EB.
  38. const INT64 MIN_VALUE = 1024i64; // Min value is 1KB.
  39. TCHAR XBytes::m_szNoLimit[]; // "No Limit" edit control text.
  40. ///////////////////////////////////////////////////////////////////////////////
  41. /* Function: XBytes::XBytes
  42. Description: Constructor
  43. Arguments:
  44. hDlg - Handle to parent dialog.
  45. idCtlEdit - Control ID for edit control.
  46. idCtlCombo - Control ID for combo box control.
  47. CurrentBytes - Initial byte value.
  48. Returns: Nothing.
  49. Revision History:
  50. Date Description Programmer
  51. -------- --------------------------------------------------- ----------
  52. 08/30/96 Initial creation. BrianAu
  53. 10/15/96 Added m_MaxBytes member. BrianAu
  54. 05/29/98 Removed m_MaxBytes member. Don't want to limit BrianAu
  55. user's ability to enter a value larger than
  56. max disk space.
  57. */
  58. ///////////////////////////////////////////////////////////////////////////////
  59. XBytes::XBytes(
  60. HWND hDlg,
  61. DWORD idCtlEdit,
  62. DWORD idCtlCombo,
  63. INT64 CurrentBytes
  64. ) : m_hDlg(hDlg),
  65. m_idCtlEdit(idCtlEdit),
  66. m_idCtlCombo(idCtlCombo),
  67. m_ValueBytes(0)
  68. {
  69. CommonInit();
  70. LoadComboItems(MAXLONGLONG); // Load options into combo.
  71. CurrentBytes = MIN(CurrentBytes, MAX_VALUE);
  72. if (NOLIMIT != CurrentBytes)
  73. CurrentBytes = MAX(CurrentBytes, MIN_VALUE);
  74. SetBytes(CurrentBytes); // Set "current bytes".
  75. //
  76. // Note: SetBytes() calls SetBestDisplay().
  77. //
  78. }
  79. //
  80. // This constructor is sort of a hack. Since the m_szNoLimit string
  81. // is static, and since it is initialized in
  82. // the constructor, at least one instance of XBytes must be created.
  83. // There are cases where the static function FormatByteCountForDisplay
  84. // may be useful when there is no need for an XBytes object. The
  85. // DiskQuota watchdog is just such an example. If an XBytes object
  86. // is not created, the two strings are not created and the function
  87. // doesn't work correctly. To fix this, I've defined a single global
  88. // XBytes object constructed using this default constructor. It's sole
  89. // purpose is to load these static strings. [7/23/97 - brianau]
  90. //
  91. XBytes::XBytes(
  92. VOID
  93. ) : m_hDlg(NULL),
  94. m_idCtlEdit((DWORD)-1),
  95. m_idCtlCombo((DWORD)-1),
  96. m_ValueBytes(0)
  97. {
  98. CommonInit();
  99. }
  100. //
  101. // Initialization common to both constructors.
  102. //
  103. VOID
  104. XBytes::CommonInit(
  105. VOID
  106. )
  107. {
  108. if (NULL != m_hDlg)
  109. SendMessage(m_hDlg, m_idCtlEdit, EM_LIMITTEXT, MAX_EDIT_TEXT);
  110. LoadStaticStrings();
  111. }
  112. ///////////////////////////////////////////////////////////////////////////////
  113. /* Function: XBytes::SetBytes
  114. Description: Stores a new byte value and updates the display to the
  115. proper units (order).
  116. Arguments:
  117. ValueBytes - Value in bytes.
  118. If the value is NOLIMIT, the controls are disabled.
  119. Otherwise the controls are enabled.
  120. Returns: Nothing.
  121. Revision History:
  122. Date Description Programmer
  123. -------- --------------------------------------------------- ----------
  124. 08/30/96 Initial creation. BrianAu
  125. */
  126. ///////////////////////////////////////////////////////////////////////////////
  127. VOID
  128. XBytes::SetBytes(INT64 ValueBytes)
  129. {
  130. if (NOLIMIT != ValueBytes)
  131. ValueBytes = MAX(MIN_VALUE, ValueBytes);
  132. ValueBytes = MIN(MAX_VALUE, ValueBytes);
  133. Store(ValueBytes, e_Byte);
  134. SetBestDisplay();
  135. }
  136. ///////////////////////////////////////////////////////////////////////////////
  137. /* Function: XBytes::Enable
  138. Description: Enables/Disables the edit and combo controls.
  139. Arguments:
  140. bEnable - TRUE = Enable, FALSE = Disable.
  141. Returns: Nothing.
  142. Revision History:
  143. Date Description Programmer
  144. -------- --------------------------------------------------- ----------
  145. 10/28/96 Initial creation. BrianAu
  146. */
  147. ///////////////////////////////////////////////////////////////////////////////
  148. VOID
  149. XBytes::Enable(
  150. BOOL bEnable
  151. )
  152. {
  153. EnableWindow(GetDlgItem(m_hDlg, m_idCtlCombo), bEnable);
  154. EnableWindow(GetDlgItem(m_hDlg, m_idCtlEdit), bEnable);
  155. }
  156. ///////////////////////////////////////////////////////////////////////////////
  157. /* Function: XBytes::IsEnabled
  158. Description: Returns the "enabled" state of the edit control. As long
  159. as the client doesn't enable/disable the edit/combo controls
  160. individually, this represents the state of the control pair.
  161. By using only the SetBytes() method to control enabling/disabling,
  162. this is ensured.
  163. Arguments:
  164. bEnable - TRUE = Enable, FALSE = Disable.
  165. Returns: Nothing.
  166. Revision History:
  167. Date Description Programmer
  168. -------- --------------------------------------------------- ----------
  169. 10/28/96 Initial creation. BrianAu
  170. */
  171. ///////////////////////////////////////////////////////////////////////////////
  172. BOOL
  173. XBytes::IsEnabled(
  174. VOID
  175. )
  176. {
  177. return IsWindowEnabled(GetDlgItem(m_hDlg, m_idCtlEdit));
  178. }
  179. bool
  180. XBytes::UndoLastEdit(
  181. void
  182. )
  183. {
  184. if (SendToEditCtl(EM_CANUNDO, 0, 0))
  185. {
  186. SendToEditCtl(EM_UNDO, 0, 0);
  187. SendToEditCtl(EM_EMPTYUNDOBUFFER, 0, 0);
  188. SendToEditCtl(EM_SETSEL, SendToEditCtl(EM_LINELENGTH, 0, 0), -1);
  189. return true;
  190. }
  191. return false;
  192. }
  193. ///////////////////////////////////////////////////////////////////////////////
  194. /* Function: XBytes::OnEditNotifyUpdate
  195. Description: Must be called whenever the parent window receives a
  196. EN_UPDATE notification for the edit control. The function
  197. reads the current string in the edit control and tries to store it
  198. as a byte value. If the store operation fails, the number is invalid
  199. and an alarm is sounded.
  200. Arguments:
  201. lParam - lParam argument to EN_UPDATE notification. It is unused.
  202. Returns:
  203. Always returns FALSE.
  204. Revision History:
  205. Date Description Programmer
  206. -------- --------------------------------------------------- ----------
  207. 08/30/96 Initial creation. BrianAu
  208. 10/15/96 Added check for too-large input. BrianAu
  209. 10/22/96 Re-organized and added ValueInRange() function. BrianAu
  210. This was to support value check/adjustment when
  211. user changes the combo-box setting (bug).
  212. 02/26/97 Added EM_CANUNDO and EM_EMPTYUNDOBUFFER. BrianAu
  213. 05/29/98 Removed ValueInRange() function and replaced with BrianAu
  214. check for negative number.
  215. */
  216. ///////////////////////////////////////////////////////////////////////////////
  217. BOOL
  218. XBytes::OnEditNotifyUpdate(
  219. LPARAM lParam
  220. )
  221. {
  222. TCHAR szEditText[MAX_PATH];
  223. bool bBeep = false;
  224. DBGASSERT((MAX_EDIT_TEXT < MAX_PATH));
  225. GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
  226. if (lstrlen(szEditText) > MAX_EDIT_TEXT)
  227. {
  228. szEditText[MAX_EDIT_TEXT] = TEXT('\0');
  229. SetDlgItemText(m_hDlg, m_idCtlEdit, szEditText);
  230. }
  231. if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
  232. {
  233. //
  234. // If text in edit control is not "No Limit", convert the text to
  235. // a number, verify that it is in range and store it.
  236. //
  237. if (Store(szEditText, (INT)GetOrderFromCombo()))
  238. {
  239. //
  240. // If number is negative, force it to the minimum.
  241. //
  242. if (0 > Fetch(NULL, e_Byte))
  243. {
  244. SetBytes(MIN_VALUE);
  245. bBeep = true;
  246. }
  247. SendToEditCtl(EM_EMPTYUNDOBUFFER, 0, 0);
  248. }
  249. else
  250. {
  251. bBeep = true;
  252. if (!UndoLastEdit())
  253. {
  254. //
  255. // Number must be too large for the selected order.
  256. // Found that this can happen when first opening the disk quota UI
  257. // after someone's set the value out of the range acceptable by
  258. // the UI. Remember, because we allow decimal values in the UI,
  259. // the UI cannot accept values quite as large as the dskquota APIs.
  260. // Beep the user and force the value to the largest acceptable
  261. // value.
  262. //
  263. SetBytes(MAX_VALUE);
  264. }
  265. }
  266. if (bBeep)
  267. {
  268. //
  269. // Sound beep for either an invalid value or an out-of-range value.
  270. //
  271. MessageBeep(MB_OK);
  272. }
  273. }
  274. return FALSE;
  275. }
  276. BOOL
  277. XBytes::OnEditKillFocus(
  278. LPARAM lParam
  279. )
  280. {
  281. TCHAR szEditText[MAX_EDIT_TEXT];
  282. bool bBeep = false;
  283. GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
  284. if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
  285. {
  286. INT64 value = Fetch(NULL, e_Byte);
  287. if (MIN_VALUE > value)
  288. {
  289. SetBytes(MIN_VALUE);
  290. bBeep = true;
  291. }
  292. else if (MAX_VALUE < value)
  293. {
  294. SetBytes(MAX_VALUE);
  295. bBeep = true;
  296. }
  297. if (bBeep)
  298. {
  299. MessageBeep(MB_OK);
  300. }
  301. }
  302. return FALSE;
  303. }
  304. ///////////////////////////////////////////////////////////////////////////////
  305. /* Function: XBytes::OnComboNotifySelChange
  306. Description: Must be called whenever the parent window receives a
  307. CBM_SELCHANGE message for the combo box control. The function
  308. scales the stored byte value to the new units.
  309. Arguments:
  310. lParam - lParam argument to CBM_SELCHANGE message. It is unused.
  311. Returns:
  312. Always returns FALSE.
  313. Revision History:
  314. Date Description Programmer
  315. -------- --------------------------------------------------- ----------
  316. 08/30/96 Initial creation. BrianAu
  317. 10/22/96 Modified to just call OnEditNotifyUpdate(). BrianAu
  318. Combo-box selection should have same value
  319. check/adjust behavior as edit control changes.
  320. */
  321. ///////////////////////////////////////////////////////////////////////////////
  322. BOOL
  323. XBytes::OnComboNotifySelChange(
  324. LPARAM lParam
  325. )
  326. {
  327. TCHAR szEditText[MAX_EDIT_TEXT];
  328. bool bBeep = false;
  329. GetDlgItemText(m_hDlg, m_idCtlEdit, szEditText, ARRAYSIZE(szEditText));
  330. if (0 != lstrcmpi(XBytes::m_szNoLimit, szEditText))
  331. {
  332. //
  333. // If text in edit control is not "No Limit", convert the text to
  334. // a number, verify that it is in range and store it.
  335. //
  336. if (Store(szEditText, (INT)GetOrderFromCombo()))
  337. {
  338. //
  339. // If number is less than the minimum, force it to the minimum.
  340. //
  341. if (MIN_VALUE > Fetch(NULL, e_Byte))
  342. {
  343. SetBytes(MIN_VALUE);
  344. bBeep = true;
  345. }
  346. }
  347. else
  348. {
  349. //
  350. // Number must be too large for the selected order.
  351. // Beep the user and force the value to the largest acceptable
  352. // value.
  353. //
  354. SetBytes(MAX_VALUE);
  355. bBeep = true;
  356. }
  357. if (bBeep)
  358. {
  359. MessageBeep(MB_OK);
  360. }
  361. }
  362. return FALSE;
  363. }
  364. ///////////////////////////////////////////////////////////////////////////////
  365. /* Function: XBytes::LoadComboItems
  366. Description: Initializes the combo box with its selections.
  367. [ "KB", "MB", "GB"... "PB" ]. The function only adds options that are
  368. reasonable for the size of the drive. For example, if the drive
  369. is less than 1 GB in size, only KB and MB are displayed.
  370. Arguments:
  371. MaxBytes - Maximum bytes available on the drive (drive size).
  372. Returns: Nothing.
  373. Revision History:
  374. Date Description Programmer
  375. -------- --------------------------------------------------- ----------
  376. 08/30/96 Initial creation. BrianAu
  377. */
  378. ///////////////////////////////////////////////////////////////////////////////
  379. VOID
  380. XBytes::LoadComboItems(
  381. INT64 MaxBytes
  382. )
  383. {
  384. TCHAR szText[MAX_CMB_TEXT];
  385. INT idMsg = 0;
  386. INT order = e_Kilo;
  387. //
  388. // Find the string resource ID for the largest units possible.
  389. //
  390. // WARNING: This code assumes that the resource IDs for
  391. // IDS_ORDERKB through IDS_ORDEREB are consecutive
  392. // increasing integers. Hence the following assertions.
  393. //
  394. DBGASSERT((IDS_ORDERMB == IDS_ORDERKB + 1));
  395. DBGASSERT((IDS_ORDERGB == IDS_ORDERKB + 2));
  396. DBGASSERT((IDS_ORDERTB == IDS_ORDERKB + 3));
  397. DBGASSERT((IDS_ORDERPB == IDS_ORDERKB + 4));
  398. DBGASSERT((IDS_ORDEREB == IDS_ORDERKB + 5));
  399. for (idMsg = IDS_ORDERKB; idMsg < IDS_ORDEREB; idMsg++)
  400. {
  401. if ((INT64)(1i64 << (10 * order++)) > MaxBytes)
  402. {
  403. idMsg--;
  404. break;
  405. }
  406. }
  407. //
  408. // idMsg is at largest units string we'll use.
  409. // Add strings to combo box.
  410. //
  411. while(idMsg >= IDS_ORDERKB)
  412. {
  413. if (LoadString(g_hInstDll, idMsg, szText, ARRAYSIZE(szText)))
  414. SendToCombo(CB_INSERTSTRING, 0, (LPARAM)szText);
  415. idMsg--;
  416. }
  417. }
  418. ///////////////////////////////////////////////////////////////////////////////
  419. /* Function: XBytes::SetBestDisplay
  420. Description: Displays the byte value in the highest order that will
  421. produce a whole part of 3 digits or less. That way you see
  422. "25.5" MB instead of "25500 KB".
  423. Arguments: None.
  424. Returns: Nothing.
  425. Revision History:
  426. Date Description Programmer
  427. -------- --------------------------------------------------- ----------
  428. 08/30/96 Initial creation. BrianAu
  429. */
  430. ///////////////////////////////////////////////////////////////////////////////
  431. VOID
  432. XBytes::SetBestDisplay(
  433. VOID
  434. )
  435. {
  436. INT iOrder = e_Byte;
  437. TCHAR szValue[MAX_EDIT_TEXT];
  438. //
  439. // Display NOLIMIT as 0. Edit and combo controls will be disabled
  440. // by property page code. NOLIMIT is (-1). Defined by NTFS.
  441. //
  442. if (NOLIMIT != m_ValueBytes)
  443. {
  444. //
  445. // Format the byte count for display. Leave off the KB, MB... extension.
  446. // That part will be displayed in the combo box.
  447. //
  448. FormatByteCountForDisplay(m_ValueBytes, szValue, ARRAYSIZE(szValue), &iOrder);
  449. //
  450. // If value is 0, display MB units. That's our default.
  451. //
  452. if (0 == m_ValueBytes)
  453. iOrder = e_Mega;
  454. //
  455. // Set the value string in the edit control and the order in the combo box.
  456. //
  457. SetOrderInCombo(iOrder);
  458. SetDlgItemText(m_hDlg,
  459. m_idCtlEdit,
  460. szValue);
  461. Enable(TRUE);
  462. }
  463. else
  464. {
  465. //
  466. // Set edit control to display "No Limit".
  467. //
  468. SetOrderInCombo(0); // This will cause the combo to display nothing.
  469. SetDlgItemText(m_hDlg,
  470. m_idCtlEdit,
  471. m_szNoLimit);
  472. Enable(FALSE);
  473. }
  474. }
  475. ///////////////////////////////////////////////////////////////////////////////
  476. /* Function: XBytes::Store
  477. Description: Store a value in a given order as a byte count.
  478. Arguments:
  479. Value - Byte value in order xbOrder.
  480. xbOrder - Order of number in Value.
  481. One of set { e_Byte, e_Kilo, e_Mega ... e_Exa }
  482. Returns:
  483. TRUE - Success. Always returns TRUE.
  484. Event though we're not returning anything useful, I want
  485. the return type for both Store() methods to be the same.
  486. Revision History:
  487. Date Description Programmer
  488. -------- --------------------------------------------------- ----------
  489. 08/30/96 Initial creation. BrianAu
  490. */
  491. ///////////////////////////////////////////////////////////////////////////////
  492. BOOL
  493. XBytes::Store(
  494. INT64 Value,
  495. INT xbOrder
  496. )
  497. {
  498. DBGASSERT((VALID_ORDER(xbOrder)));
  499. m_ValueBytes = INT64(Value) << (10 * (xbOrder - e_Byte));
  500. return TRUE;
  501. }
  502. ///////////////////////////////////////////////////////////////////////////////
  503. /* Function: XBytes::Store
  504. Description: Store a numeric string in a given order as a byte count.
  505. Arguments:
  506. pszSource - Numeric string.
  507. xbOrder - Order of number in pszSource.
  508. One of set { e_Byte, e_Kilo, e_Mega ... e_Exa }
  509. Returns:
  510. TRUE - Success.
  511. FALSE - Invalid number in string.
  512. Revision History:
  513. Date Description Programmer
  514. -------- --------------------------------------------------- ----------
  515. 08/30/96 Initial creation. BrianAu
  516. */
  517. ///////////////////////////////////////////////////////////////////////////////
  518. BOOL
  519. XBytes::Store(
  520. LPCTSTR pszSource,
  521. INT xbOrder
  522. )
  523. {
  524. TCHAR szValue[MAX_EDIT_TEXT]; // Temp buffer.
  525. TCHAR szDecimalSep[MAX_DECIMAL_SEP];
  526. LPTSTR pszValue = szValue; // Pointer into temp buffer.
  527. LPTSTR pszDec = szValue; // Pointer to decimal part of temp buffer.
  528. BOOL bResult = FALSE;
  529. UINT uMult = 1; // Digit multiplier.
  530. INT64 WholePart = 0;
  531. INT64 FracPart = 0;
  532. DWORD xbOrderX10 = xbOrder * 10; // Saves multiple computations.
  533. DBGASSERT((NULL != pszSource));
  534. DBGASSERT((VALID_ORDER(xbOrder)));
  535. GetLocaleInfo(LOCALE_USER_DEFAULT,
  536. LOCALE_SDECIMAL,
  537. szDecimalSep,
  538. ARRAYSIZE(szDecimalSep));
  539. //
  540. // Local copy to party on.
  541. //
  542. if (FAILED(StringCchCopy(szValue, ARRAYSIZE(szValue), pszSource)))
  543. {
  544. return FALSE;
  545. }
  546. //
  547. // Find the start of the decimal separator.
  548. //
  549. while(NULL != *pszDec && szDecimalSep[0] != *pszDec)
  550. pszDec++;
  551. if (CH_NUL != *pszDec)
  552. {
  553. *pszDec = CH_NUL; // Terminate the whole part.
  554. //
  555. // Skip over the decimal separator character(s).
  556. // Remember, separator is localized.
  557. //
  558. LPTSTR pszDecimalSep = &szDecimalSep[1];
  559. pszDec++;
  560. while(*pszDecimalSep && *pszDec && *pszDec == *pszDecimalSep)
  561. {
  562. pszDecimalSep++;
  563. pszDec++;
  564. }
  565. }
  566. else
  567. pszDec = NULL; // No decimal pt found.
  568. //
  569. // Convert whole part to an integer.
  570. //
  571. if (!StrToInt(pszValue, &WholePart))
  572. goto not_a_number;
  573. //
  574. // Check to make sure the number entered will fit into a 64-bit int when
  575. // scaled up.
  576. // With the text entry field and order combo, users can specify numbers
  577. // that will overflow an __int64. Can't let this happen. Treat overflows
  578. // as invalid entry. The (-1) accounts for the largest fractional part
  579. // that the user could enter.
  580. //
  581. if (WholePart > ((MAXLONGLONG >> xbOrderX10) - 1))
  582. goto not_a_number;
  583. //
  584. // Scale whole part according to order.
  585. //
  586. WholePart *= (1i64 << xbOrderX10);
  587. //
  588. // Convert fractional part to an integer.
  589. //
  590. if (NULL != pszDec)
  591. {
  592. //
  593. // Trim any trailing zero's first.
  594. //
  595. LPTSTR pszZero = pszDec + lstrlen(pszDec) - 1;
  596. while(pszZero >= pszDec && CH_ZERO == *pszZero)
  597. *pszZero-- = CH_NUL;
  598. //
  599. // Convert decimal portion of string to an integer.
  600. //
  601. if (!StrToInt(pszDec, &FracPart))
  602. goto not_a_number;
  603. //
  604. // Scale fractional part according to order.
  605. //
  606. FracPart *= (1i64 << xbOrderX10);
  607. DWORD dwDivisor = 1;
  608. while(pszZero-- >= pszDec)
  609. dwDivisor *= 10;
  610. //
  611. // Round up to the nearest muliple of the divisor to prevent
  612. // undesireable truncation during integer division we do below.
  613. //
  614. DWORD dwRemainder = (DWORD)(FracPart % dwDivisor);
  615. if (0 != dwRemainder)
  616. FracPart += dwDivisor - dwRemainder;
  617. FracPart /= dwDivisor;
  618. }
  619. m_ValueBytes = WholePart + FracPart;
  620. bResult = TRUE;
  621. not_a_number:
  622. return bResult;
  623. }
  624. ///////////////////////////////////////////////////////////////////////////////
  625. /* Function: XBytes::Fetch
  626. Description: Retrieve the byte count from the object using a specified
  627. order (magnitude). i.e. For 60.5 MB, the order is e_Mega, the decimal
  628. part is 5 and the returned value is 60.
  629. Arguments:
  630. pDecimal [optional] - Address of DWORD to receive the fractional part
  631. of the byte count. May be NULL.
  632. xbOrder - Order desired for the returned value. Must be from the
  633. enumeration set { e_Byte, e_Kilo, e_Mega ... e_Exa }
  634. Returns:
  635. Returns the whole part of the byte count.
  636. Revision History:
  637. Date Description Programmer
  638. -------- --------------------------------------------------- ----------
  639. 08/30/96 Initial creation. BrianAu
  640. */
  641. ///////////////////////////////////////////////////////////////////////////////
  642. INT64
  643. XBytes::Fetch(
  644. INT64 *pDecimal,
  645. INT xbOrder
  646. )
  647. {
  648. return BytesToParts(m_ValueBytes, pDecimal, xbOrder);
  649. }
  650. ///////////////////////////////////////////////////////////////////////////////
  651. /* Function: XBytes::Fetch
  652. Description: Retrieve the byte count from the object and tell the caller
  653. what the best order is for display. The logic used for "best order"
  654. is to use the first order that results in a 3-digit number.
  655. Arguments:
  656. pDecimal - Address of DWORD to receive the fractional part of the
  657. byte count.
  658. pxbOrder - Address of integer to receive the order of the number
  659. being returned. The returned order is in the enumeration
  660. { e_Byte, e_Kilo, e_Mega ... e_Exa }
  661. Returns:
  662. Returns the whole part of the byte count.
  663. Revision History:
  664. Date Description Programmer
  665. -------- --------------------------------------------------- ----------
  666. 08/30/96 Initial creation. BrianAu
  667. */
  668. ///////////////////////////////////////////////////////////////////////////////
  669. DWORD
  670. XBytes::Fetch(
  671. LPDWORD pDecimal,
  672. INT *pxbOrder
  673. )
  674. {
  675. return BytesToParts(m_ValueBytes, pDecimal, pxbOrder);
  676. }
  677. ///////////////////////////////////////////////////////////////////////////////
  678. /* Function: XBytes::StrToInt
  679. Description: Converts a string to an integer.
  680. Arguments:
  681. pszValue - Address of string to convert.
  682. pIntValue - Address of INT64 variable to receive resulting number.
  683. Returns:
  684. TRUE - Successful conversion.
  685. FALSE - String was not a valid integer.
  686. Revision History:
  687. Date Description Programmer
  688. -------- --------------------------------------------------- ----------
  689. 08/30/96 Initial creation. BrianAu
  690. */
  691. ///////////////////////////////////////////////////////////////////////////////
  692. BOOL
  693. XBytes::StrToInt(
  694. LPCTSTR pszValue,
  695. INT64 *pIntValue
  696. )
  697. {
  698. INT iMult = 1;
  699. INT64 Value = 0;
  700. LPCTSTR pszDigit = pszValue + lstrlen(pszValue) - 1; // Start at right-most
  701. DBGASSERT((NULL != pszValue));
  702. DBGASSERT((NULL != pIntValue));
  703. *pIntValue = 0;
  704. while(pszDigit >= pszValue)
  705. {
  706. //
  707. // Moving left... check each digit.
  708. //
  709. if (IsCharNumeric(*pszDigit))
  710. {
  711. //
  712. // Valid digit. Add it's value to sum.
  713. //
  714. Value += iMult * (*pszDigit - CH_ZERO);
  715. pszDigit--;
  716. iMult *= 10;
  717. }
  718. else
  719. return FALSE; // Invalid character.
  720. }
  721. *pIntValue = Value;
  722. return TRUE;
  723. }
  724. ///////////////////////////////////////////////////////////////////////////////
  725. /* Function: XBytes::BytesToParts
  726. Description: Converts a byte value to it's whole and fractional parts
  727. for a given magnitude (order). This is a static member function
  728. that can be used outside of the context of an XBytes object.
  729. Arguments:
  730. ValueBytes - Value to convert expressed in bytes.
  731. pDecimal [optional] - Address of variable to receive the fractional
  732. part. May be NULL.
  733. xbOrder - Order that the parts are to represent.
  734. { e_Byte, e_Kilo, e_Mega ... e_Exa }
  735. Returns: Returns the whole part of the value.
  736. Revision History:
  737. Date Description Programmer
  738. -------- --------------------------------------------------- ----------
  739. 08/30/96 Initial creation. BrianAu
  740. */
  741. ///////////////////////////////////////////////////////////////////////////////
  742. INT64
  743. XBytes::BytesToParts(
  744. INT64 ValueBytes,
  745. INT64 *pDecimal,
  746. INT xbOrder
  747. )
  748. {
  749. INT64 Value = ValueBytes;
  750. UINT64 DecMask = 0; // Start with a blank mask.
  751. DWORD dwOrderDeltaX10 = 10 * (xbOrder - e_Byte);
  752. DBGASSERT((VALID_ORDER(xbOrder)));
  753. //
  754. // Convert the value from order e_Byte to the order requested.
  755. // Also build a mask that can extract the decimal portion
  756. // from the original byte value. The following 2 statements implement
  757. // this logic.
  758. //
  759. // for (INT i = e_Byte; i < xbOrder; i++)
  760. // {
  761. // ValueBytes >>= 10; // Divide byte value by 1024.
  762. // DecMask <<= 10; // Shift current mask bits 10 left.
  763. // DecMask |= 0x3FF; // OR in another 10 bits.
  764. // }
  765. //
  766. Value >>= dwOrderDeltaX10;
  767. DecMask = (1i64 << dwOrderDeltaX10) - 1;
  768. if (NULL != pDecimal)
  769. {
  770. //
  771. // Caller wants fractional part.
  772. // Extract fractional part from byte value and scale it to the
  773. // specified order.
  774. // Pseudocode:
  775. // x = value & mask
  776. // pct = x / (2**order) // ** = "raise to the power of".
  777. // dec = 100 * pct
  778. //
  779. *pDecimal = (INT64)(100 * (ValueBytes & DecMask)) >> (10 * xbOrder);
  780. }
  781. return Value;
  782. }
  783. double
  784. XBytes::ConvertFromBytes(
  785. INT64 ValueBytes,
  786. INT xbOrder
  787. )
  788. {
  789. return (double)ValueBytes / (double)(10 * xbOrder);
  790. }
  791. ///////////////////////////////////////////////////////////////////////////////
  792. /* Function: XBytes::BytesToParts
  793. Description: Converts a byte value to it's whole and fractional parts.
  794. Determines the maximum magnitude (order) that will display the
  795. whole part in 3 digits or less.
  796. This is a static member function that can be used outside of the
  797. context of an XBytes object.
  798. Arguments:
  799. ValueBytes - Value to convert expressed in bytes.
  800. pDecimal [optional] - Address of variable to receive the fractional
  801. part. May be NULL.
  802. pxbOrder - Address of variable to receive the determined order.
  803. { e_Byte, e_Kilo, e_Mega ... e_Exa }
  804. Returns: Returns the whole part of the value.
  805. Revision History:
  806. Date Description Programmer
  807. -------- --------------------------------------------------- ----------
  808. 08/30/96 Initial creation. BrianAu
  809. */
  810. ///////////////////////////////////////////////////////////////////////////////
  811. DWORD
  812. XBytes::BytesToParts(
  813. INT64 ValueBytes,
  814. LPDWORD pDecimal,
  815. INT *pxbOrder
  816. )
  817. {
  818. INT64 Value = 0;
  819. INT64 Decimal = 0;
  820. INT xbOrder = e_Byte;
  821. DBGASSERT((NULL != pDecimal));
  822. DBGASSERT((NULL != pxbOrder));
  823. //
  824. // Determine the best order for display.
  825. //
  826. while(xbOrder <= MAX_ORDER)
  827. {
  828. Value = BytesToParts(ValueBytes, &Decimal, xbOrder);
  829. if (Value < (INT64)1000)
  830. break;
  831. xbOrder++;
  832. }
  833. //
  834. // Return the results.
  835. //
  836. *pxbOrder = xbOrder;
  837. *pDecimal = (DWORD)Decimal; // Fetch() guarantees this cast is OK.
  838. return (DWORD)Value;
  839. }
  840. ///////////////////////////////////////////////////////////////////////////////
  841. /* Function: XBytes::FormatByteCountForDisplay
  842. Description: Given a byte count, this static method formats a character
  843. string with the 999.99XB number where "XB" is the maximum units
  844. that can display the whole part in 3 digits or less.
  845. Arguments:
  846. Bytes - Number of bytes to format.
  847. pszDest - Address of destination character buffer.
  848. cchDest - Size of destination buffer in characters.
  849. Returns: Nothing.
  850. Revision History:
  851. Date Description Programmer
  852. -------- --------------------------------------------------- ----------
  853. 08/30/96 Initial creation. BrianAu
  854. */
  855. ///////////////////////////////////////////////////////////////////////////////
  856. VOID
  857. XBytes::FormatByteCountForDisplay(
  858. INT64 Bytes,
  859. LPTSTR pszDest,
  860. UINT cchDest
  861. )
  862. {
  863. DWORD dwWholePart = 0;
  864. DWORD dwFracPart = 0;
  865. INT Order = XBytes::e_Byte;
  866. //
  867. // To avoid using a local temp buffer, the caller's buffer must be
  868. // large enough for final string. "999.99 MB" plus NUL and some pad to
  869. // allow for possible multi-char decimal separators (localized).
  870. //
  871. DBGASSERT((NULL != pszDest));
  872. FormatByteCountForDisplay(Bytes, pszDest, cchDest, &Order);
  873. DWORD dwLen = lstrlen(pszDest);
  874. //
  875. // Insert a space between the number and the suffix (i.e. "99 MB").
  876. // dwLen is incremented to allow for the added space.
  877. //
  878. *(pszDest + dwLen++) = TEXT(' ');
  879. //
  880. // Append the suffix.
  881. //
  882. LoadString(g_hInstDll, IDS_ORDERKB + Order - 1, pszDest + dwLen, cchDest - dwLen);
  883. }
  884. ///////////////////////////////////////////////////////////////////////////////
  885. /* Function: XBytes::FormatByteCountForDisplay
  886. Description: Given a byte count, this static method formats a character
  887. string with the 999.99 number and returns the enumerted value
  888. representing the order in *pOrder. This function complements
  889. the one above for those callers not needing the "KB", "MB"...
  890. suffix. In particular, our combo box.
  891. Arguments:
  892. Bytes - Number of bytes to format.
  893. pszDest - Address of destination character buffer.
  894. cchDest - Size of destination buffer in characters.
  895. pOrder - Address of variable to receive the enumerated order value.
  896. Returns: Nothing.
  897. Revision History:
  898. Date Description Programmer
  899. -------- --------------------------------------------------- ----------
  900. 08/30/96 Initial creation. BrianAu
  901. */
  902. ///////////////////////////////////////////////////////////////////////////////
  903. VOID
  904. XBytes::FormatByteCountForDisplay(
  905. INT64 Bytes,
  906. LPTSTR pszDest,
  907. UINT cchDest,
  908. INT *pOrder
  909. )
  910. {
  911. DBGASSERT((NULL != pszDest));
  912. DBGASSERT((NULL != pOrder));
  913. DWORD dwWholePart = 0;
  914. DWORD dwFracPart = 0;
  915. dwWholePart = BytesToParts(Bytes, &dwFracPart, pOrder);
  916. FormatForDisplay(pszDest, cchDest, dwWholePart, dwFracPart);
  917. }
  918. ///////////////////////////////////////////////////////////////////////////////
  919. /* Function: XBytes::FormatByteCountForDisplay
  920. Description: Given a byte count, and a specified order, this static method
  921. formats a character string with the 999.99 number in the specified
  922. order.
  923. Arguments:
  924. Bytes - Number of bytes to format.
  925. pszDest - Address of destination character buffer.
  926. cchDest - Size of destination buffer in characters.
  927. Order - Order of the value in the resultant string.
  928. Returns: Nothing.
  929. Revision History:
  930. Date Description Programmer
  931. -------- --------------------------------------------------- ----------
  932. 09/30/96 Initial creation. BrianAu
  933. */
  934. ///////////////////////////////////////////////////////////////////////////////
  935. VOID
  936. XBytes::FormatByteCountForDisplay(
  937. INT64 Bytes,
  938. LPTSTR pszDest,
  939. UINT cchDest,
  940. INT Order
  941. )
  942. {
  943. LONGLONG llWholePart;
  944. LONGLONG llFracPart;
  945. DBGASSERT((NULL != pszDest));
  946. //
  947. // WARNING: This code assumes that the whole and fractional parts will
  948. // each be less than 2^32. I think a valid assumption for scaled
  949. // quota information.
  950. //
  951. llWholePart = BytesToParts(Bytes, &llFracPart, Order);
  952. FormatForDisplay(pszDest, cchDest, (DWORD)llWholePart, (DWORD)llFracPart);
  953. }
  954. ///////////////////////////////////////////////////////////////////////////////
  955. /* Function: XBytes::FormatForDisplay
  956. Description: Given a whole part and a fractional part, format a decimal
  957. number suitable for display in 999.99 format. If the fractional
  958. part is 0, no decimal part is included.
  959. Arguments:
  960. pszDest - Address of destination character buffer.
  961. cchDest - Size of destination buffer in characters.
  962. dwWholePart - Whole part of the number.
  963. dwFracPart - Fractional part of the number.
  964. Returns: Nothing.
  965. Revision History:
  966. Date Description Programmer
  967. -------- --------------------------------------------------- ----------
  968. 09/30/96 Initial creation. BrianAu
  969. */
  970. ///////////////////////////////////////////////////////////////////////////////
  971. VOID
  972. XBytes::FormatForDisplay(
  973. LPTSTR pszDest,
  974. UINT cchDest,
  975. DWORD dwWholePart,
  976. DWORD dwFracPart
  977. )
  978. {
  979. DBGASSERT((NULL != pszDest));
  980. TCHAR szTemp[80];
  981. if (0 != dwFracPart)
  982. {
  983. TCHAR szFmt[] = TEXT("%d%s%02d");
  984. TCHAR szDecimalSep[MAX_DECIMAL_SEP];
  985. if ((dwFracPart >= 10) && (0 == (dwFracPart % 10)))
  986. {
  987. //
  988. // Whack off the trailing zero for display.
  989. //
  990. dwFracPart /= 10;
  991. szFmt[6] = TEXT('1');
  992. }
  993. GetLocaleInfo(LOCALE_USER_DEFAULT,
  994. LOCALE_SDECIMAL,
  995. szDecimalSep,
  996. ARRAYSIZE(szDecimalSep));
  997. wnsprintf(szTemp, ARRAYSIZE(szTemp), szFmt, dwWholePart, szDecimalSep, dwFracPart);
  998. }
  999. else
  1000. wnsprintf(szTemp, ARRAYSIZE(szTemp), TEXT("%d"), dwWholePart);
  1001. lstrcpyn(pszDest, szTemp, cchDest);
  1002. }
  1003. //
  1004. // Load the static strings if they haven't been loaded.
  1005. //
  1006. VOID
  1007. XBytes::LoadStaticStrings(
  1008. void
  1009. )
  1010. {
  1011. //
  1012. // Initialize the "No Limit" text string for display in the
  1013. // edit control. This is the same string used in the details list
  1014. // view columns.
  1015. //
  1016. if (TEXT('\0') == m_szNoLimit[0])
  1017. {
  1018. INT cchLoaded = LoadString(g_hInstDll,
  1019. IDS_NO_LIMIT,
  1020. m_szNoLimit,
  1021. ARRAYSIZE(m_szNoLimit));
  1022. DBGASSERT((0 < cchLoaded));
  1023. }
  1024. }