读取 NTFS 卷上的配额信息






4.23/5 (6投票s)
2005 年 6 月 23 日
13分钟阅读

44145

509
包装了 IDiskQuotaControl 接口大部分功能的类。
引言
在我因另一个不相关的原因浏览 MSDN 时,我偶然发现了 IDiskQuotaControl 接口。虽然它听起来很有趣,但我缺乏 COM 经验,迫使我继续进行我最初的目标。几个月后,我决定再次探索 IDiskQuotaControl
接口。我一路上的发现对我来说非常有帮助。
原始接口指针
我开始时只是包含 dskquota.h 头文件。为了利用该文件中的接口和属性,我使用了类似的代码:
IDiskQuotaControl *pDiskQuotaControl; IEnumDiskQuotaUsers *pEnumDiskQuotaUsers; HRESULT hr; hr = CoCreateInstance(CLSID_DiskQuotaControl, NULL, CLSCTX_INPROC_SERVER, IID_IDiskQuotaControl, (PVOID *) &pDiskQuotaControl); if (SUCCEEDED(hr)) { hr = pDiskQuotaControl->Initialize(_bstr_t("c:\\"), TRUE); if (SUCCEEDED(hr)) { hr = pDiskQuotaControl->CreateEnumUsers(NULL, 0, DISKQUOTA_USERNAME_RESOLVE_SYNC, &pEnumDiskQuotaUsers); if (SUCCEEDED(hr)) { ... pEnumDiskQuotaUsers->Release(); } } pDiskQuotaControl->Release(); }
“太棒了,”我想。“我想知道我能否让那个 CoCreateInstance()
调用变得更简洁一些。这时我回想起几年前在一个 COM 项目中读到的关于智能指针的内容。回顾那段代码作为起点,我进行了必要的更改。
智能指针
首先,我导入了包含在 dskquota.dll 中的磁盘配额类型库。
#import <dskquota.dll> rename("DiskQuotaTypeLibrary", "DQ")
我重命名了命名空间,因为它的名字太长了。现在我准备将原始接口指针更改为智能指针。我从一个简单的东西开始:
DQ::DIDiskQuotaControlPtr ptrDiskQuotaControl; HRESULT hr = ptrDiskQuotaControl.CreateInstance(__uuidof(DQ::DiskQuotaControl)); if (SUCCEEDED(hr)) { hr = ptrDiskQuotaControl->Initialize(_bstr_t("c:\\"), TRUE); if (NOERROR == hr) { ... } }
“哇,这也能用。”我继续尝试使用 CreateEnumUsers()
方法。这时事情开始变得糟糕。首先,该方法不存在。其次,第四个参数是一个 IEnumDiskQuotaUsers 智能指针,它也不存在。现在我真的被难住了,尤其是我注意到 MSDN 文档显示了其他信息。我琢磨了一会儿,然后寻求帮助。有人告诉我,它看起来像一个只有 dispatch 接口的类型库,但我仍然可以使用智能指针。我只需要做的更改是删除 #import
语句,并使用 _COM_SMARTPTR_TYPEDEF()
宏添加我自己的类型。
_COM_SMARTPTR_TYPEDEF(IDiskQuotaControl, IID_IDiskQuotaControl); _COM_SMARTPTR_TYPEDEF(IEnumDiskQuotaUsers, IID_IEnumDiskQuotaUsers); _COM_SMARTPTR_TYPEDEF(IDiskQuotaUser, IID_IDiskQuotaUser);
现在我取得了一些进展!我能够创建 IDiskQuotaControl
接口的实例,初始化它,并调用 CreateEnumUsers()
方法,就像我使用原始接口指针时一样。
IEnumDiskQuotaUsersPtr ptrEnumDQUsers; hr = ptrDiskQuotaControl->CreateEnumUsers(NULL, 0, DISKQUOTA_USERNAME_RESOLVE_SYNC, &ptrEnumDQUsers); if (NOERROR == hr) { IDiskQuotaUserPtr ptrDQUser; hr = ptrEnumDQUsers->Next(1, &ptrDQUser, NULL); if (NOERROR == hr) { ... } }
为了确保我确实能够枚举每个已注册用户,我使用了:
TCHAR szLogonName[128]; szDisplayname[128]; while (ptrEnumDQUsers->Next(1, &ptrDQUser, NULL) == NOERROR) { hr = ptrDQUser->GetName(NULL, 0, szLogonName, sizeof(szLogonName), szDisplayName, sizeof(szDisplayName)); if (NOERROR == hr) TRACE(_T("Logon name = ]%s[\n"), szLogonName); }
是的,它奏效了!我的下一个目标是设法将所有这些包装成一个或两个有用的类,以隐藏其中一些 COM 的复杂性。
CDiskQuotaControl 类
此类是 IDiskQuotaControl
和 IEnumDiskQuotaUsers
接口大部分功能的封装。大部分文本直接来自 MSDN。
CDiskQuotaControl |
构造一个 CDiskQuotaControl 对象。 | |
初始化 | 通过以只读模式打开 NTFS 卷来初始化一个新的 CDiskQuotaControl 对象。返回值指示卷是否支持 NTFS 磁盘配额以及调用者是否具有足够的访问权限。 | |
EnumFirstUser | 创建一个枚举器对象来枚举卷上的配额用户,然后检索枚举序列中的第一个用户。用户的帐户信息是同步解析的。 | |
EnumNextUser | 检索枚举序列中的下一个用户。 | |
GetDefaultQuotaLimit | 检索卷的默认配额限制。 | |
GetDefaultQuotaLimitText | 检索卷的默认配额限制。限制以文本字符串表示,例如“10.5 MB”。如果卷没有限制,返回的字符串是“无限制”。如果缓冲区太小,字符串将被截断以适应缓冲区。 | |
GetDefaultQuotaThreshold | 检索卷的默认配额警告阈值。 | |
GetDefaultQuotaThresholdText | 检索卷的默认警告阈值。此阈值以文本字符串表示,例如“10.5 MB”。如果卷没有阈值,返回的字符串是“无限制”。如果缓冲区太小,字符串将被截断以适应缓冲区。 | |
IsStateDisabled | 确定卷上的配额是否已启用。 | |
IsStateTracked | 确定卷上的配额是否正在跟踪(即,限制未强制执行)。 | |
IsStateEnforced | 确定卷上的配额是否已强制执行。 | |
IsStateIncomplete | 确定卷的配额信息是否已过时。 | |
IsStateRebuilding | 确定卷的配额信息是否正在重建。 |
HRESULT Initialize( PCTSTR pszVolume )
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_FILE_NOT_FOUND |
找不到请求的文件或对象。 | ||
ERROR_INITIALIZED |
控制器对象已初始化。不允许多次初始化。 | ||
ERROR_INVALID_NAME |
请求的文件路径无效。 | ||
ERROR_NOTSUPPORTED |
文件系统不支持配额。 | ||
ERROR_PATH_NOT_FOUND |
找不到请求的文件路径。 |
参数
pszVolume
指定卷根目录的路径。
HRESULT EnumFirstUser( CDiskQuotaUser *pDiskQuotaUser )
HRESULT EnumNextUser( CDiskQuotaUser *pDiskQuotaUser ) const
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_NOT_READY |
CDiskQuotaControl 对象未初始化。 | ||
E_INVALIDARG |
pDiskQuotaUser 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 | ||
S_FALSE |
如果未能成功检索枚举序列中的第一个或下一个用户。 |
参数
pDiskQuotaUser
指向
CDiskQuotaUser
对象的指针。
HRESULT GetDefaultQuotaLimit( PLONGLONG pLimit ) const
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_NOT_READY |
CDiskQuotaControl 对象未初始化。 | ||
E_INVALIDARG |
pLimit 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
pLimit
指向接收配额限制变量的指针。如果此值为 -1,则用户具有无限配额。
HRESULT GetDefaultQuotaLimitText( CString &strDefaultQuotaText ) const
HRESULT GetDefaultQuotaLimitText( PTSTR pszDefaultQuotaText, const DWORD dwSize ) const
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_NOT_READY |
CDiskQuotaControl 对象未初始化。 | ||
E_INVALIDARG |
pszDefaultQuotaText 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
strDefaultQuotaText
对接收文本字符串的
CString
对象的引用。pszDefaultQuotaText
指向接收文本字符串缓冲区的指针。
dwSize
缓冲区大小(以字符为单位)。
HRESULT GetDefaultQuotaThreshold( PLONGLONG pThreshold ) const
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_NOT_READY |
CDiskQuotaControl 对象未初始化。 | ||
E_INVALIDARG |
pThreshold 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
pThreshold
指向接收默认警告阈值(以字节为单位)的变量的指针。
HRESULT GetDefaultQuotaThresholdText( CString &strDefaultQuotaText ) const
HRESULT GetDefaultQuotaThresholdText( PTSTR pszDefaultQuotaText, const DWORD dwSize ) const
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_NOT_READY |
CDiskQuotaControl 对象未初始化。 | ||
E_INVALIDARG |
pszDefaultQuotaText 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
strDefaultQuotaText
对接收文本字符串的
CString
对象的引用。pszDefaultQuotaText
指向接收文本字符串缓冲区的指针。
dwSize
缓冲区大小(以字符为单位)。
bool IsStateDisabled( void ) const;
返回值
如果卷上的配额已禁用,则为 true
;否则为 false
。
bool IsStateTracked( void ) const;
返回值
如果卷上的配额已启用但限制未强制执行,则为 true
;否则为 false
。
bool IsStateEnforced( void ) const;
返回值
如果卷上的配额已启用且限制已强制执行,则为 true
;否则为 false
。
bool IsStateIncomplete( void ) const;
返回值
如果配额信息已过时,则为 true
;否则为 false
。
bool IsStateRebuilding( void ) const;
返回值
如果配额信息正在重建,则为 true
;否则为 false
。
CDiskQuotaUser 类
此类是 IDiskQuotaUser
接口大部分功能的封装。大部分文本直接来自 MSDN。
CDiskQuotaUser |
构造一个 CDiskQuotaUser 对象,该对象代表卷配额信息文件中的单个用户配额条目。 | |
GetName | 检索与磁盘配额用户关联的名称字符串。 | |
GetQuotaThresholdText | 检索用户的卷警告阈值。此阈值以文本字符串表示,例如“10.5 MB”。如果用户的阈值是无限的,返回的字符串是“无限制”。如果目标缓冲区太小,字符串将被截断以适应缓冲区。 | |
GetQuotaThreshold | 检索用户在卷上的警告阈值。阈值是由卷的配额管理员设置的任意值。您可以使用它来识别接近其硬配额限制的用户。 | |
GetQuotaLimitText | 检索用户的卷配额限制。此限制以文本字符串表示,例如“10.5 MB”。如果用户没有配额限制,返回的字符串是“无限制”。如果目标缓冲区太小,字符串将被截断以适应缓冲区。 | |
GetQuotaLimit | 检索用户在卷上的配额限制值。限制被设置为卷用户可用的最大磁盘空间量。 | |
GetQuotaUsedText | 检索用户的卷配额使用量。此值以文本字符串表示,例如“10.5 MB”。如果目标缓冲区太小,字符串将被截断以适应缓冲区。 | |
GetQuotaUsed | 检索用户在卷上的配额使用量。这是用户在卷上存储的信息量。请注意,这是未压缩的信息量。因此,NTFS 压缩的使用不影响此值。 | |
GetQuotaInformation | 检索用户的警告阈值、硬配额限制和配额使用量的值。 | |
IsStatusResolved | 确定用户的 SID 是否解析为用户帐户。 | |
IsStatusUnavailable | 确定用户帐户是否可用。网络 DC 可能不可用。 | |
IsStatusDeleted | 确定用户的帐户是否已从域中删除。 | |
IsStatusInvalid | 确定用户帐户是否无效。 | |
IsStatusUnknown | 确定用户帐户是否未知。 | |
IsStatusUnresolved | 确定用户的 SID 是否未解析为用户帐户。 |
HRESULT GetName( CString &strLogonName, CString &strDisplayName ) const;
HRESULT GetName( PTSTR pszLogonName, const DWORD dwSizeLogon, PTSTR pszDisplayName, const DWORD dwSizeDisplay ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 |
参数
strLogonName
对接收用户登录计算机所用名称的
CString
对象的引用。返回名称的格式取决于目录服务信息是否可用。strDisplayName
对接收配额用户的显示名称的
CString
对象的引用。如果信息不可用,返回的字符串为空。pszLogonName
指向接收用户登录计算机所用名称缓冲区的指针。此值可以为
NULL
。返回名称的格式取决于目录服务信息是否可用。dwSizeLogon
登录名缓冲区的字节数(以字符为单位)。如果
pszLogonName
为NULL
,则忽略。pszDisplayName
指向接收配额用户的显示名称缓冲区的指针。此值可以为
NULL
。如果信息不可用,返回的字符串长度为零。dwSizeDisplay
显示名称缓冲区的字节数(以字符为单位)。如果
pszDisplayName
为NULL
,则忽略。
HRESULT GetQuotaThresholdText( CString &strQuotaThresholdText ) const;
HRESULT GetQuotaThresholdText( PTSTR pszQuotaThresholdText, const DWORD dwSize ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pszQuotaThresholdText 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
strQuotaThresholdText
对接收文本字符串的
CString
对象的引用。pszQuotaThresholdText
指向接收文本字符串缓冲区的指针。
dwSize
目标缓冲区大小(以字符为单位)。
HRESULT GetQuotaThreshold( PLONGLONG pLimit ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pLimit 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
pLimit
指向接收警告阈值变量的指针。
HRESULT GetQuotaLimitText( CString &strQuotaLimitText ) const;
HRESULT GetQuotaLimitText( PTSTR pszQuotaLimitText, const DWORD dwSize ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pszQuotaLimitText 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
strQuotaLimitText
对接收文本字符串的
CString
对象的引用。pszQuotaLimitText
指向接收文本字符串缓冲区的指针。
dwSize
缓冲区大小(以字符为单位)。
HRESULT GetQuotaLimit( PLONGLONG pLimit ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pLimit 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
pLimit
指向接收限制值的变量的指针。如果此值为 -1,则用户具有无限配额。
HRESULT GetQuotaUsedText( CString &strQuotaUsedText ) const;
HRESULT GetQuotaUsedText( PTSTR pszQuotaUsedText, const DWORD dwSize ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pszQuotaUsedText 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
strQuotaUsedText
对接收文本字符串的
CString
对象的引用。pszQuotaUsedText
指向接收文本字符串缓冲区的指针。
dwSize
缓冲区大小(以字节为单位)。
HRESULT GetQuotaUsed( PLONGLONG pUsed ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pUsed 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
pUsed
指向接收配额使用量变量的指针。
HRESULT GetQuotaInformation( PDISKQUOTA_USER_INFORMATION pDQUserInfo, const DWORD dwSize ) const;
返回值
值 | 含义 | ||
NOERROR |
成功。 | ||
ERROR_ACCESS_DENIED |
调用者访问权限不足。 | ||
ERROR_LOCK_FAILED |
未能获得独占锁。 | ||
E_INVALIDARG |
pDQUserInfo 参数为 NULL 。 | ||
E_OUTOFMEMORY |
内存不足。 | ||
E_FAIL |
发生了意外的文件系统错误。 | ||
E_UNEXPECTED |
发生了意外的异常。 |
参数
pDQUserInfo
指向接收配额信息的
DISKQUOTA_USER_INFORMATION
结构的指针。dwSize
配额信息结构的大小(以字节为单位)。
bool IsStatusResolved( void ) const;
返回值
如果用户的 SID 已解析为用户帐户,则为 true
;否则为 false
。
bool IsStatusUnavailable( void ) const;
返回值
如果用户帐户不可用(例如,域控制器可能不可用),则为 true
;否则为 false
。
bool IsStatusDeleted( void ) const;
返回值
如果用户帐户已被删除,则为 true
;否则为 false
。
bool IsStatusInvalid( void ) const;
返回值
如果用户帐户无效,则为 true
;否则为 false
。
bool IsStatusUnknown( void ) const;
返回值
如果用户帐户未知,则为 true
;否则为 false
。
bool IsStatusUnresolved( void ) const;
返回值
如果用户的 SID 未解析为用户帐户,则为 true
;否则为 false
。
示例
CDiskQuotaControl DiskQuotaControl; HRESULT hr = DiskQuotaControl.Initialize(strVolume); if (ERROR_NOT_SUPPORTED == hr) TRACE(CString((LPCTSTR) IDS_NOT_SUPPORTED)); if (NOERROR == hr) { if (DiskQuotaControl.IsStateDisabled()) TRACE(CString((LPCTSTR) IDS_NOT_ENABLED)); else if (DiskQuotaControl.IsStateTracked()) TRACE(CString((LPCTSTR) IDS_NOT_TRACKED)); else if (DiskQuotaControl.IsStateEnforced()) TRACE(CString((LPCTSTR) IDS_NOT_ENFORCED)); else if (DiskQuotaControl.IsStateIncomplete()) TRACE(CString((LPCTSTR) IDS_INCOMPLETE)); else if (DiskQuotaControl.IsStateRebuilding()) TRACE(CString((LPCTSTR) IDS_REBUILDING)); else TRACE(CString((LPCTSTR) IDS_UNKNOWN)); DISKQUOTA_USER_INFORMATION DQUserInfo; CString strLogonName, strDisplayName, strQuotaThresholdText, strQuotaLimitText, strQuotaUsedText; // enumerate through each of the users hr = DiskQuotaControl.EnumFirstUser(&DiskQuotaUser); while (S_OK == hr) { // get information about the user's numbers DiskQuotaUser.GetName(strLogonName, strDisplayName); DiskQuotaUser.GetQuotaThresholdText(strQuotaThresholdText); DiskQuotaUser.GetQuotaLimitText(strQuotaLimitText); DiskQuotaUser.GetQuotaUsedText(strQuotaUsedText); DiskQuotaUser.GetQuotaInformation(&DQUserInfo, sizeof(DISKQUOTA_USER_INFORMATION)); // determine if the user is above/below their quota if (DQUserInfo.QuotaUsed > DQUserInfo.QuotaLimit && DQUserInfo.QuotaLimit >= 0) TRACE(CString((LPCTSTR) IDS_ABOVE_LIMIT)); else if (DQUserInfo.QuotaUsed > DQUserInfo.QuotaThreshold && DQUserInfo.QuotaThreshold >= 0) TRACE(CString((LPCTSTR) IDS_WARNING)); else TRACE(CString((LPCTSTR) IDS_OK)); TRACE(strLogonName); hr = DiskQuotaControl.EnumNextUser(&DiskQuotaUser); } }
附加功能
本节中的代码不在本文的范围内,但我还是想展示它,因为它对我以及任何可能需要它的人都很有用。
我见过许多不同种类的在运行时调整控件尺寸或位置的方法。有些示例非常冗长,而另一些则没那么糟,或者根本就很难理解。我编写了一个简单的函数,我认为它既小又易于理解。该函数是从对话框的 OnSize()
方法调用的。
在对话框的 OnInitDialog()
方法中,我们需要获取客户区的初始大小。这样我们就有了一个移动的参考点。看起来像这样:
GetClientRect(m_rectOrig); m_nWidth = m_rectOrig.Width(); m_nHeight = m_rectOrig.Height();
请注意,m_rectOrig
是一个成员变量,而不是一个局部变量。它在 OnGetMinMaxInfo()
方法中使用,以确保我们不会将对话框最小化得太小。
用于调整子控件大小和移动的函数如下所示:
void RepositionChildControl( CWnd *pWnd, const int dx, const int dy, const UINT uAnchor ) { // make sure the dialog exists if (NULL != pWnd->m_hWnd) { CRect rect; pWnd->GetWindowRect(rect); ScreenToClient(rect); if (uAnchor & resizeVERT) // 0x0020 rect.bottom += dy; else if (uAnchor & anchorBOTTOM) // 0x0004 rect.OffsetRect(0, dy); if (uAnchor & resizeHORZ) // 0x0010 rect.right += dx; else if (uAnchor & anchorRIGHT) // 0x0002 rect.OffsetRect(dx, 0); pWnd->MoveWindow(rect); } }
每当对话框正在调整大小时,都会调用 OnSize()
方法。在这里,我们调用 RepositionChildControl()
。
void OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // how much did the dialog grow/shrink by? int nWidthOffset = cx - m_nWidth; int nHeightOffset = cy - m_nHeight; // this control is anchored at the top and right, // and is sized horizontally RepositionChildControl(&m_lblStatus, nWidthOffset, nHeightOffset, anchorTOP | anchorRIGHT | resizeHORZ); // this control is anchored at the top and left, // and is sized horizontally and vertically RepositionChildControl(&m_lcDQInfo, nWidthOffset, nHeightOffset, anchorTOP | anchorLEFT | resizeBOTH); // this control is anchored at the top and right RepositionChildControl(&m_btnCancel, nWidthOffset, nHeightOffset, anchorTOP | anchorRIGHT); // save dialog's new dimensions m_nWidth = cx; m_nHeight = cy; }
由于这种移动确实会引起一点闪烁,有兴趣的读者可以将每个控件添加到 CWnd*
数组中。然后在对话框的 OnEraseBkgnd()
方法中,使用 CDC::ExcludeClipRect()
从对话框的 rect
中减去每个控件的 rect
。
摘要
这两个类远非完整。它们反映了每个接口的“读取”方法,因此它们不更新任何配额信息。但是,它们可以很容易地扩展以实现此类功能。
尽情享用!