561 lines
16 KiB
C++
561 lines
16 KiB
C++
// Copyright (c) 1997-1999 Microsoft Corporation
|
|
#include "precomp.h"
|
|
|
|
#ifdef EXT_DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#include "DrivesPage.h"
|
|
#include "resource.h"
|
|
#include "..\Common\util.h"
|
|
#include <stdlib.h>
|
|
#include <TCHAR.h>
|
|
#include "drawpie.h"
|
|
#include <windowsx.h>
|
|
#include "help.h"
|
|
|
|
#define MAXLEN_NTFS_LABEL 32
|
|
|
|
//--------------------------------------------------------------
|
|
DrivesPage::DrivesPage(WbemServiceThread *thread,
|
|
CWbemClassObject &inst,
|
|
UINT iconID,
|
|
HWND *propSheet,
|
|
bstr_t bstrDesc,
|
|
wchar_t *mangled,
|
|
LONG_PTR lNotifyHandle,
|
|
bool bDeleteHandle,
|
|
TCHAR* pTitle) :
|
|
WBEMPageHelper(thread),
|
|
m_propSheet(propSheet),
|
|
m_bstrDesc(bstrDesc),
|
|
CSnapInPropertyPageImpl<DrivesPage> (pTitle),
|
|
m_lNotifyHandle(lNotifyHandle),
|
|
m_bDeleteHandle(bDeleteHandle) // Should be true for only page.
|
|
{
|
|
// I really dont want this one.
|
|
if(m_service)
|
|
{
|
|
m_service->Release();
|
|
m_service = 0;
|
|
}
|
|
|
|
m_WbemServices.DisconnectServer();
|
|
m_WbemServices = (IWbemServices *)0;
|
|
m_qwTot = 0;
|
|
m_qwFree = 0;
|
|
m_iconID = iconID;
|
|
m_inst = inst;
|
|
m_mangled = mangled;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
DrivesPage::~DrivesPage()
|
|
{
|
|
*m_propSheet = 0;
|
|
if (m_bDeleteHandle)
|
|
HRESULT x = MMCFreeNotifyHandle(m_lNotifyHandle);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
LRESULT DrivesPage::OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
DrawItem((DRAWITEMSTRUCT *)lParam);
|
|
return S_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
LRESULT DrivesPage::OnCommand(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
|
|
{
|
|
::SendMessage(GetParent(), PSM_CHANGED, (WPARAM)m_hWnd, 0L);
|
|
// NOTE: original code (drivesx.c) has many other commands if
|
|
// we ever support those buttons.
|
|
return S_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
BOOL DrivesPage::OnApply()
|
|
{
|
|
WCHAR szLabel[MAXLEN_NTFS_LABEL+1];
|
|
HRESULT hr = 0;
|
|
CWindow volHWND(GetDlgItem(IDC_DRV_LABEL));
|
|
|
|
BOOL modified = (BOOL)volHWND.SendMessage(EM_GETMODIFY);
|
|
|
|
if(modified)
|
|
{
|
|
m_WbemServices = g_serviceThread->GetPtr();
|
|
::GetWindowText(volHWND, szLabel, ARRAYSIZE(szLabel));
|
|
hr = m_inst.Put("VolumeName", (bstr_t)szLabel);
|
|
hr = m_WbemServices.PutInstance(m_inst);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
TCHAR caption[50] = {0}, fmt[100] = {0}, text[100] = {0};
|
|
|
|
::LoadString(HINST_THISDLL,
|
|
IDS_DISPLAY_NAME,
|
|
caption, ARRAYSIZE(caption));
|
|
|
|
::LoadString(HINST_THISDLL,
|
|
IDS_WRITE_PROTECTED_MEDIA,
|
|
fmt, ARRAYSIZE(fmt));
|
|
wsprintf(text, fmt, szLabel);
|
|
|
|
::MessageBox(m_hDlg, text, caption,
|
|
MB_OK | MB_ICONHAND);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
const static DWORD aDrvPrshtHelpIDs[] = { // Context Help IDs
|
|
IDC_DRV_ICON, IDH_FCAB_DRV_ICON,
|
|
IDC_DRV_LABEL, IDH_FCAB_DRV_LABEL,
|
|
IDC_DRV_TYPE_TXT, IDH_FCAB_DRV_TYPE,
|
|
IDC_DRV_TYPE, IDH_FCAB_DRV_TYPE,
|
|
IDC_DRV_FS_TXT, IDH_FCAB_DRV_FS,
|
|
IDC_DRV_FS, IDH_FCAB_DRV_FS,
|
|
IDC_DRV_USEDCOLOR, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_USEDBYTES_TXT, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_USEDBYTES, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_USEDMB, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREECOLOR, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREEBYTES_TXT, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREEBYTES, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_FREEMB, IDH_FCAB_DRV_USEDCOLORS,
|
|
IDC_DRV_TOTSEP, NO_HELP,
|
|
IDC_DRV_TOTBYTES_TXT, IDH_FCAB_DRV_TOTSEP,
|
|
IDC_DRV_TOTBYTES, IDH_FCAB_DRV_TOTSEP,
|
|
IDC_DRV_TOTMB, IDH_FCAB_DRV_TOTSEP,
|
|
IDC_DRV_PIE, IDH_FCAB_DRV_PIE,
|
|
IDC_DRV_LETTER, IDH_FCAB_DRV_LETTER,
|
|
IDC_DRV_CLEANUP, IDH_FCAB_DRV_CLEANUP,
|
|
0, 0
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
LRESULT DrivesPage::OnF1Help(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
::WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle,
|
|
NULL,
|
|
HELP_WM_HELP,
|
|
(ULONG_PTR)(LPSTR)aDrvPrshtHelpIDs);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
LRESULT DrivesPage::OnContextHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
::WinHelp((HWND)wParam,
|
|
NULL,
|
|
HELP_CONTEXTMENU,
|
|
(ULONG_PTR)(LPSTR)aDrvPrshtHelpIDs);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*#undef Static_SetIcon
|
|
#define Static_SetIcon(hwndCtl, hIcon) ((HICON)(UINT)(DWORD)::SendMessage((hwndCtl), STM_SETICON, (WPARAM)(HICON)(hIcon), 0L))
|
|
*/
|
|
LRESULT DrivesPage::OnInit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
TCHAR szFormat[80] = {0};
|
|
TCHAR szTemp[30] = {0};
|
|
_bstr_t driveName;
|
|
HDC hDC;
|
|
SIZE size;
|
|
//_bstr_t temp;
|
|
TCHAR driveLtr[3] = {0};
|
|
|
|
HourGlass(true);
|
|
m_hDlg = m_hWnd;
|
|
|
|
*m_propSheet = GetParent();
|
|
|
|
if(g_serviceThread)
|
|
{
|
|
g_serviceThread->SendPtr(m_hWnd);
|
|
}
|
|
|
|
driveName = m_inst.GetString("Name");
|
|
|
|
// isolate the drive letter for later.
|
|
_tcsncpy(driveLtr, driveName, driveName.length());
|
|
|
|
hDC = GetDC();
|
|
GetTextExtentPoint(hDC, TEXT("W"), 1, &size);
|
|
m_dwPieShadowHgt = size.cy * 2 / 3;
|
|
ReleaseDC(hDC);
|
|
|
|
// get the icon
|
|
HICON hiconT, hIcon = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(m_iconID));
|
|
|
|
// set it into the picture control.
|
|
if(hIcon)
|
|
{
|
|
hiconT = Static_SetIcon(GetDlgItem(IDC_DRV_ICON), hIcon);
|
|
|
|
// destroy the old icon.
|
|
if(hiconT)
|
|
{
|
|
DestroyIcon(hiconT);
|
|
}
|
|
}
|
|
|
|
CWindow hCtl(GetDlgItem(IDC_DRV_LABEL));
|
|
|
|
_bstr_t szFileSystem = m_inst.GetString("FileSystem");
|
|
|
|
if(szFileSystem.length() != 0)
|
|
SetDlgItemText(IDC_DRV_FS, szFileSystem);
|
|
|
|
//-------------------------------
|
|
// deal with Volume Label.
|
|
variant_t vol;
|
|
m_inst.Get("VolumeName", vol);
|
|
|
|
// if media isn't inserted...
|
|
if(vol.vt == VT_NULL)
|
|
{
|
|
hCtl.SetWindowText(L"");
|
|
hCtl.EnableWindow(FALSE);
|
|
}
|
|
else // media inserted.
|
|
{
|
|
// cast to bstr_t
|
|
wcscpy(szTemp, V_BSTR(&vol));
|
|
if(wcsncmp(szFileSystem, L"FAT", 1) == 0)
|
|
{
|
|
hCtl.SendMessage(EM_LIMITTEXT, 11);
|
|
}
|
|
else
|
|
{
|
|
hCtl.SendMessage(EM_LIMITTEXT, MAXLEN_NTFS_LABEL);
|
|
}
|
|
|
|
hCtl.SendMessage(EM_SETMODIFY, FALSE);
|
|
|
|
// cant change volumeName on various drives.
|
|
if((m_iconID == IDI_DRIVENET) ||
|
|
(m_iconID == IDI_DRIVECD) ||
|
|
(m_iconID == IDI_DRIVERAM))
|
|
{
|
|
hCtl.SendMessage(EM_SETREADONLY, TRUE);
|
|
}
|
|
|
|
wchar_t fmt[100], caption[100];
|
|
memset(fmt, 0, 100 * sizeof(wchar_t));
|
|
memset(caption, 0, 100 * sizeof(wchar_t));
|
|
|
|
if(::LoadString(HINST_THISDLL,
|
|
IDS_SHEET_CAPTION_FMT,
|
|
fmt, 100) == 0)
|
|
{
|
|
wcscpy(fmt, L"%s (%c:) Properties");
|
|
}
|
|
|
|
// if its locally mapped...
|
|
if(wcslen(m_mangled) == 0)
|
|
{
|
|
swprintf(caption, fmt, (wchar_t *)szTemp, driveLtr[0]);
|
|
}
|
|
else
|
|
{
|
|
// format sheet caption using m_mangled. (network drives)
|
|
swprintf(caption, fmt, m_mangled, driveLtr[0]);
|
|
}
|
|
hCtl.SetWindowText(szTemp);
|
|
::SetWindowText(GetParent(), caption);
|
|
}
|
|
|
|
SetDlgItemText(IDC_DRV_TYPE, (LPCTSTR)m_bstrDesc);
|
|
|
|
UpdateSpaceValues(m_inst);
|
|
|
|
#if OPSYS==NT
|
|
//UpdateCompressStatus(pdpsp);
|
|
#endif
|
|
|
|
// Do the 'Drive C' label. IDS_DRIVELETTER is formatted to extract the
|
|
// first letter of the driveName which is the drive letter w/o the ':'.
|
|
if (LoadString(HINST_THISDLL, IDS_DRIVELETTER, szFormat, ARRAYSIZE(szFormat)))
|
|
{
|
|
_stprintf(szTemp, szFormat, driveLtr[0]);
|
|
SetDlgItemText(IDC_DRV_LETTER, szTemp);
|
|
}
|
|
|
|
// on memphis, turn on the Disk Cleanup button
|
|
// if(g_bRunOnMemphis)
|
|
// {
|
|
// ShowWindow(GetDlgItem(m_hDlg, IDC_DRV_CLEANUP), SW_SHOW);
|
|
// EnableWindow(GetDlgItem(m_hDlg, IDC_DRV_CLEANUP), TRUE);
|
|
// }
|
|
|
|
HourGlass(false);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: UpdateSpaceValues
|
|
//
|
|
// DESCRIPTION:
|
|
// Updates the Used space, Free space and Capacity values on the drive
|
|
// general property page..
|
|
//
|
|
// NOTE:
|
|
// This function was separated from _DrvPrshtInit because drive space values
|
|
// must be updated after a compression/uncompression operation as well as
|
|
// during dialog initialization.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void DrivesPage::UpdateSpaceValues(CWbemClassObject &inst)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
TCHAR szTemp[80] = {0};
|
|
TCHAR szTemp2[30] = {0};
|
|
TCHAR szFormat[30] = {0};
|
|
|
|
m_qwTot = inst.GetI64("Size");
|
|
m_qwFree = inst.GetI64("FreeSpace");
|
|
|
|
if(m_qwTot == 0)
|
|
return;
|
|
|
|
if(LoadString(HINST_THISDLL, IDS_BYTES, szFormat, ARRAYSIZE(szFormat)))
|
|
{
|
|
|
|
//
|
|
// NT must be able to display 64-bit numbers; at least as much
|
|
// as is realistic. We've made the decision
|
|
// that volumes up to 100 Terrabytes will display the byte value
|
|
// and the short-format value. Volumes of greater size will display
|
|
// "---" in the byte field and the short-format value. Note that the
|
|
// short format is always displayed.
|
|
TCHAR szNumStr[MAX_PATH + 1] = {_T('\0')}; // For 64-bit int format.
|
|
NUMBERFMT NumFmt; // For 64-bit int format.
|
|
TCHAR szLocaleInfo[20] = {0}; // For 64-bit int format.
|
|
TCHAR szDecimalSep[5] = {0}; // Locale-specific.
|
|
TCHAR szThousandSep[5] = {0}; // Locale-specific.
|
|
const _int64 MaxDisplayNumber = 99999999999999; // 100TB - 1.
|
|
|
|
// Prepare number format info for current locale.
|
|
NumFmt.NumDigits = 0; // This is locale-insensitive.
|
|
NumFmt.LeadingZero = 0; // So is this.
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_SGROUPING,
|
|
szLocaleInfo,
|
|
ARRAYSIZE(szLocaleInfo));
|
|
_stscanf(szLocaleInfo, _T("%ld"), &(NumFmt.Grouping));
|
|
|
|
// NumFmt.Grouping = StrToLong(szLocaleInfo);
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_SDECIMAL,
|
|
szDecimalSep,
|
|
ARRAYSIZE(szDecimalSep));
|
|
NumFmt.lpDecimalSep = szDecimalSep;
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_STHOUSAND,
|
|
szThousandSep,
|
|
ARRAYSIZE(szThousandSep));
|
|
NumFmt.lpThousandSep = szThousandSep;
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_INEGNUMBER,
|
|
szLocaleInfo,
|
|
ARRAYSIZE(szLocaleInfo));
|
|
_stscanf(szLocaleInfo, _T("%ld"), &(NumFmt.NegativeOrder));
|
|
// NumFmt.NegativeOrder = StrToLong(szLocaleInfo);
|
|
|
|
#if 0
|
|
//
|
|
// Use this to test range of display behaviors.
|
|
// Total bytes displays "---" for too-large number.
|
|
// Used bytes displays max displayable number.
|
|
// Free bytes displays 1.
|
|
//
|
|
m_qwTot = MaxDisplayNumber + 1;
|
|
m_qwFree = 1;
|
|
#endif
|
|
|
|
if ((m_qwTot - m_qwFree) <= MaxDisplayNumber)
|
|
{
|
|
Int64ToString(m_qwTot - m_qwFree,
|
|
szNumStr,
|
|
ARRAYSIZE(szNumStr) + 1,
|
|
TRUE,
|
|
&NumFmt,
|
|
NUMFMT_ALL);
|
|
_stprintf(szTemp, szFormat, szNumStr, szTemp2);
|
|
SetDlgItemText(IDC_DRV_USEDBYTES, szTemp);
|
|
}
|
|
|
|
if (m_qwFree <= MaxDisplayNumber)
|
|
{
|
|
Int64ToString(m_qwFree,
|
|
szNumStr,
|
|
ARRAYSIZE(szNumStr) + 1,
|
|
TRUE,
|
|
&NumFmt,
|
|
NUMFMT_ALL);
|
|
_stprintf(szTemp, szFormat, szNumStr, szTemp2);
|
|
SetDlgItemText(IDC_DRV_FREEBYTES, szTemp);
|
|
}
|
|
|
|
if (m_qwTot <= MaxDisplayNumber)
|
|
{
|
|
Int64ToString(m_qwTot,
|
|
szNumStr,
|
|
ARRAYSIZE(szNumStr) + 1,
|
|
TRUE,
|
|
&NumFmt,
|
|
NUMFMT_ALL);
|
|
_stprintf(szTemp, szFormat, szNumStr, szTemp2);
|
|
SetDlgItemText(IDC_DRV_TOTBYTES, szTemp);
|
|
}
|
|
}
|
|
|
|
ShortSizeFormat64(m_qwTot - m_qwFree, szTemp);
|
|
SetDlgItemText(IDC_DRV_USEDMB, szTemp);
|
|
|
|
ShortSizeFormat64(m_qwFree, szTemp);
|
|
SetDlgItemText(IDC_DRV_FREEMB, szTemp);
|
|
|
|
ShortSizeFormat64(m_qwTot, szTemp);
|
|
SetDlgItemText(IDC_DRV_TOTMB, szTemp);
|
|
}
|
|
//--------------------------------------------------------------------
|
|
// Used to map int i to the formatting string.
|
|
const short pwOrders[] = {IDS_BYTES, IDS_ORDERKB, IDS_ORDERMB,
|
|
IDS_ORDERGB, IDS_ORDERTB, IDS_ORDERPB, IDS_ORDEREB};
|
|
|
|
/* converts numbers into short formats
|
|
* 532 -> 523 bytes
|
|
* 1340 -> 1.3KB
|
|
* 23506 -> 23.5KB
|
|
* -> 2.4MB
|
|
* -> 5.2GB
|
|
*/
|
|
LPTSTR DrivesPage::ShortSizeFormat64(__int64 dw64, LPTSTR szBuf)
|
|
{
|
|
int i; // rough size of the number.
|
|
_int64 wInt;
|
|
UINT wLen, wDec;
|
|
TCHAR szTemp[MAX_COMMA_NUMBER_SIZE] = {0}, szOrder[20] = {0}, szFormat[5] = {0};
|
|
|
|
if (dw64 < 1000) {
|
|
_stprintf(szTemp, _T("%d"), LODWORD(dw64));
|
|
i = 0;
|
|
goto AddOrder;
|
|
}
|
|
|
|
for (i = 1;
|
|
(i < ARRAYSIZE(szOrder) - 1) && (dw64 >= (1000L * 1024L));
|
|
dw64 >>= 10, i++);
|
|
/* do nothing */
|
|
|
|
wInt = dw64 >> 10;
|
|
AddCommas64(wInt, szTemp);
|
|
wLen = lstrlen(szTemp);
|
|
if (wLen < 3)
|
|
{
|
|
wDec = LODWORD(dw64 - wInt * 1024L) * 1000 / 1024;
|
|
// At this point, wDec should be between 0 and 1000
|
|
// we want get the top one (or two) digits.
|
|
wDec /= 10;
|
|
if (wLen == 2)
|
|
wDec /= 10;
|
|
|
|
// Note that we need to set the format before getting the
|
|
// intl char.
|
|
lstrcpy(szFormat, _T("%02d"));
|
|
|
|
szFormat[2] = _T('0') + 3 - wLen;
|
|
|
|
GetLocaleInfo(LOCALE_USER_DEFAULT,
|
|
LOCALE_SDECIMAL,
|
|
szTemp + wLen,
|
|
ARRAYSIZE(szTemp) - wLen);
|
|
|
|
wLen = lstrlen(szTemp);
|
|
wLen += _stprintf(szTemp + wLen, szFormat, wDec);
|
|
}
|
|
|
|
AddOrder:
|
|
LoadString(HINST_THISDLL, pwOrders[i], szOrder, ARRAYSIZE(szOrder));
|
|
_stprintf(szBuf, szOrder, (LPTSTR)szTemp);
|
|
|
|
return szBuf;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
const COLORREF c_crPieColors[] =
|
|
{
|
|
RGB( 0, 0, 255), // Blue
|
|
RGB(255, 0, 255), // Red-Blue
|
|
RGB( 0, 0, 128), // 1/2 Blue
|
|
RGB(128, 0, 128), // 1/2 Red-Blue
|
|
};
|
|
//---------------------------------------------------------
|
|
void DrivesPage::DrawItem(const DRAWITEMSTRUCT * lpdi)
|
|
{
|
|
COLORREF crDraw = RGB(0,0,0);
|
|
RECT rcItem = lpdi->rcItem;
|
|
HBRUSH hbDraw, hbOld;
|
|
|
|
// draw the pie.
|
|
if(lpdi->CtlID == IDC_DRV_PIE)
|
|
{
|
|
DWORD dwPctX10 = m_qwTot ?
|
|
(DWORD)((__int64)1000 *
|
|
(m_qwTot - m_qwFree) /m_qwTot) :
|
|
1000;
|
|
|
|
DrawPie(lpdi->hDC, &lpdi->rcItem,
|
|
dwPctX10,
|
|
m_qwFree == 0 || m_qwFree == m_qwTot,
|
|
m_dwPieShadowHgt, c_crPieColors);
|
|
}
|
|
else // draw one of the solid 'icons'
|
|
{
|
|
if(lpdi->CtlID == IDC_DRV_USEDCOLOR)
|
|
{
|
|
crDraw = c_crPieColors[DP_USEDCOLOR];
|
|
}
|
|
else if(lpdi->CtlID == IDC_DRV_FREECOLOR)
|
|
{
|
|
crDraw = c_crPieColors[DP_FREECOLOR];
|
|
}
|
|
|
|
// common draw code.
|
|
hbDraw = CreateSolidBrush(crDraw);
|
|
if (hbDraw)
|
|
{
|
|
hbOld = (HBRUSH)SelectObject(lpdi->hDC, hbDraw);
|
|
if (hbOld)
|
|
{
|
|
PatBlt(lpdi->hDC, rcItem.left, rcItem.top,
|
|
rcItem.right-rcItem.left,
|
|
rcItem.bottom-rcItem.top,
|
|
PATCOPY);
|
|
|
|
SelectObject(lpdi->hDC, hbOld);
|
|
}
|
|
|
|
DeleteObject(hbDraw);
|
|
|
|
} // endif hDraw
|
|
|
|
} //endif 'which control
|
|
} |