65.9K
CodeProject 正在变化。 阅读更多。
Home

读取 NTFS 卷上的配额信息

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.23/5 (6投票s)

2005 年 6 月 23 日

13分钟阅读

viewsIcon

44145

downloadIcon

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 类

此类是 IDiskQuotaControlIEnumDiskQuotaUsers 接口大部分功能的封装。大部分文本直接来自 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

    登录名缓冲区的字节数(以字符为单位)。如果 pszLogonNameNULL,则忽略。

  • pszDisplayName

    指向接收配额用户的显示名称缓冲区的指针。此值可以为 NULL。如果信息不可用,返回的字符串长度为零。

  • dwSizeDisplay

    显示名称缓冲区的字节数(以字符为单位)。如果 pszDisplayNameNULL,则忽略。

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

摘要

这两个类远非完整。它们反映了每个接口的“读取”方法,因此它们不更新任何配额信息。但是,它们可以很容易地扩展以实现此类功能。

尽情享用!

© . All rights reserved.