windows-nt/Source/XPSP1/NT/shell/osshell/dskquota/control/control.cpp

3321 lines
104 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
/* File: control.cpp
Description: Contains member function definitions for class DiskQuotaControl.
This class is the main point of focus for managing disk quota information
through the DSKQUOTA library. The user creates an instance of a
DiskQuotaControl object through CoCreateInstance and manages quota
information through it's IDiskQuotaControl interface.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h" // PCH
#pragma hdrstop
#include "connect.h"
#include "control.h"
#include "guidsp.h" // Private GUIDs.
#include "registry.h"
#include "sidcache.h"
#include "userbat.h"
#include "userenum.h"
#include "resource.h" // For IDS_NO_LIMIT.
#include <oleauto.h> // OLE automation
#include <comutil.h>
#include <sddl.h>
//
// Verify that build is UNICODE.
//
#if !defined(UNICODE)
# error This module must be compiled UNICODE.
#endif
//
// Size of user enumerator's buffer. Thought about this being a reg entry.
// Didn't make a lot of sense.
//
const UINT ENUMUSER_BUF_LEN = 2048;
//
// To add support for a new connection point type, just add a new IID to this
// array. Also add a corresponding enumeration constant in the DiskQuotaControl
// class declaration that identifies the location of the conn pt IID in
// m_rgpIConnPtsSupported[].
//
const IID * const DiskQuotaControl::m_rgpIConnPtsSupported[] = { &IID_IDiskQuotaEvents,
&IID_DIDiskQuotaControlEvents };
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::DiskQuotaControl
Description: Constructor.
Arguments: None.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
08/15/97 Added m_bInitialized member. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
DiskQuotaControl::DiskQuotaControl(
VOID
) : m_cRef(0),
m_bInitialized(FALSE),
m_pFSObject(NULL),
m_dwFlags(0),
m_pSidNameResolver(NULL),
m_rgConnPts(NULL),
m_cConnPts(0),
m_llDefaultQuotaThreshold(0),
m_llDefaultQuotaLimit(0)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::DiskQuotaControl")));
InterlockedIncrement(&g_cRefThisDll);
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::~DiskQuotaControl
Description: Destructor. Releases FSObject pointer.
Arguments: None.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
DiskQuotaControl::~DiskQuotaControl(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::~DiskQuotaControl")));
//
// See the comment in NotifyUserNameChanged for a discussion on the
// use of this mutex. In short, it prevents a deadlock between
// the resolver's thread and a client receiving a name-change
// notification. The wait here is INFINITE while the corresponding
// wait in NotifyUserNameChanged is limited.
//
AutoLockMutex lock(m_mutex, INFINITE);
if (NULL != m_pFSObject)
{
m_pFSObject->Release();
m_pFSObject = NULL;
}
ShutdownNameResolution();
if (NULL != m_rgConnPts)
{
for (UINT i = 0; i < m_cConnPts; i++)
{
if (NULL != m_rgConnPts[i])
{
m_rgConnPts[i]->Release();
m_rgConnPts[i] = NULL;
}
}
delete[] m_rgConnPts;
}
InterlockedDecrement(&g_cRefThisDll);
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::QueryInterface
Description: Returns an interface pointer to the object's IUnknown,
IDiskQuotaControl or IConnectionPointContainer interface. The object
referenced by the returned interface pointer is uninitialized. The
recipient of the pointer must call Initialize() before the object is
usable.
Arguments:
riid - Reference to requested interface ID.
ppvOut - Address of interface pointer variable to accept interface ptr.
Returns:
NOERROR - Success.
E_NOINTERFACE - Requested interface not supported.
E_INVALIDARG - ppvOut argument was NULL.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::QueryInterface(
REFIID riid,
LPVOID *ppvOut
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::QueryInterface")));
DBGPRINTIID(DM_CONTROL, DL_MID, riid);
if (NULL == ppvOut)
return E_INVALIDARG;
HRESULT hr = E_NOINTERFACE;
try
{
*ppvOut = NULL;
if (IID_IUnknown == riid ||
IID_IDiskQuotaControl == riid)
{
*ppvOut = this;
}
else if (IID_IConnectionPointContainer == riid)
{
hr = InitConnectionPoints();
if (SUCCEEDED(hr))
{
*ppvOut = static_cast<IConnectionPointContainer *>(this);
}
}
else if (IID_IDispatch == riid ||
IID_DIDiskQuotaControl == riid)
{
DiskQuotaControlDisp *pQCDisp = new DiskQuotaControlDisp(static_cast<PDISKQUOTA_CONTROL>(this));
*ppvOut = static_cast<DIDiskQuotaControl *>(pQCDisp);
}
if (NULL != *ppvOut)
{
((LPUNKNOWN)*ppvOut)->AddRef();
hr = NOERROR;
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
*ppvOut = NULL;
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::AddRef
Description: Increments object reference count.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
DiskQuotaControl::AddRef(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::AddRef")));
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef + 1));
ULONG ulReturn = m_cRef + 1;
InterlockedIncrement(&m_cRef);
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::Release
Description: Decrements object reference count. If count drops to 0,
object is deleted.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
DiskQuotaControl::Release(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::Release")));
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef - 1));
ULONG ulReturn = m_cRef - 1;
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
ulReturn = 0;
}
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::Initialize
Description: Initializes a quota controller object by opening the NTFS
"device" associated with the quota information. The caller passes the
name of an NTFS volume device to open. A C++ object is created which
encapsulates the required NTFS functionality. This object is known
as a "file system object" or FSObject.
Currently, NTFS only supports quotas on volumes. However, there is
talk of providing quotas for directories in the future. This library
has been designed with this expansion in mind.
By using an object hierarchy to represent the FSObject,
we are able to shield the quota control object from differences
in NTIO API functions dealing with volumes, directories and both
local and remote flavors of both.
Arguments:
pszPath - Name of NTFS path to open.
bReadWrite - TRUE = Read/write.
FALSE = Read only.
Returns:
NOERROR - Success.
E_INVALIDARG - pszPath arg was NULL.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
E_FAIL - Error getting volume information.
ERROR_ACCESS_DENIED (hr) - Insufficient access to open FS object.
ERROR_FILE_NOT_FOUND (hr) - Specified volume doesn't exist.
ERROR_PATH_NOT_FOUND (hr) - Specified volume doesn't exist.
ERROR_BAD_PATHNAME (hr) - Invalid path name provided.
ERROR_INVALID_NAME (hr) - Invalid path name provided.
ERROR_NOT_SUPPORTED (hr) - Quotas not supported by volume.
ERROR_ALREADY_INITIALIZED (hr) - Controller is already initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
06/06/96 Added ansi-unicode thunk. BrianAu
06/11/96 Added return of access granted value. BrianAu
09/05/96 Added exception handling. BrianAu
09/23/96 Take a "lazy" position on creating the BrianAu
SidNameResolver object. Should only create it when
it will be needed (user enumeration). Moved
creation from here to CreateEnumUsers.
07/03/97 Added dwAccess argument. BrianAu
08/15/97 Added "already initialized" check. BrianAu
Removed InitializeA().
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::Initialize(
LPCWSTR pszPath,
BOOL bReadWrite
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::Initialize")));
DBGPRINT((DM_CONTROL, DL_MID, TEXT("\tpath = \"%s\", bReadWrite = %d"),
pszPath ? pszPath : TEXT("<null>"), bReadWrite));
HRESULT hr = NOERROR;
if (m_bInitialized)
{
//
// Controller has already been initialized.
// Re-initialization is not allowed.
//
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
}
else
{
if (NULL == pszPath)
return E_INVALIDARG;
try
{
DWORD dwAccess = GENERIC_READ | (bReadWrite ? GENERIC_WRITE : 0);
hr = FSObject::Create(pszPath,
dwAccess,
&m_pFSObject);
m_bInitialized = SUCCEEDED(hr);
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::CreateEnumUsers
Description: Create a new enumerator object for enumerating over the users
in a volume's quota information file. The returned interface supports
the normal OLE 2 enumeration members Next(), Reset(), Skip() and Clone().
Arguments:
rgpSids [optional] - Pointer to a list of SID pointers. If
provided, only those users with SIDs included in the list are
returned. This argument may be NULL in which case ALL users are
included. Any element containing a NULL pointer will terminate
the list.
cpSids [optional] - If pSidList is not NULL, this arg contains
the count of entries in rgpSids. If rgpSids is not NULL and this
argument contains 0, rgpSids is assumed to contain a terminating
NULL pointer entry.
fNameResolution - Can be one of the following:
DISKQUOTA_USERNAME_RESOLVE_NONE
DISKQUOTA_USERNAME_RESOLVE_SYNC
DISKQUOTA_USERNAME_RESOLVE_ASYNC
ppEnum - Address of interface variable to accept the IEnumDiskQuotaUser
interface pointer.
Returns:
NOERROR - Success.
E_INVALIDARG - ppEnum arg is NULL.
E_OUTOFMEMORY - Insufficient memory to create enumerator object.
ERROR_ACCESS_DENIED (hr) - Need READ access to create enumerator.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
08/11/96 Added access control. BrianAu
09/05/96 Added exception handling. BrianAu
09/23/96 Added lazy creation of SidNameResolver object. BrianAu
Moved it from InitializeW().
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaControl::CreateEnumUsers(
PSID *rgpSids,
DWORD cpSids,
DWORD fNameResolution,
IEnumDiskQuotaUsers **ppEnum
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::CreateEnumUsers")));
HRESULT hr = E_FAIL;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == ppEnum)
return E_INVALIDARG;
if (!m_pFSObject->GrantedAccess(GENERIC_READ))
{
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
DiskQuotaUserEnum *pEnumUsers = NULL;
try
{
if (NULL == m_pSidNameResolver)
{
//
// If there's no SID/Name resolver object, create one.
// We do this "as needed" because user enumeration is
// the only controller function that requires a resolver.
// If the client doesn't need a resolver, why create one?
//
SidNameResolver *pResolver = NULL;
//
// Create user SID/Name resolver object.
//
pResolver = new SidNameResolver(*this);
hr = pResolver->QueryInterface(IID_ISidNameResolver,
(LPVOID *)&m_pSidNameResolver);
if (SUCCEEDED(hr))
{
hr = m_pSidNameResolver->Initialize();
if (FAILED(hr))
{
//
// If resolver initialization fails, we can assume
// that the resolver's thread hasn't been created so
// it's OK to just call Release() instead of
// Shutdown() followed by Release(). This is strongly
// dependent on the initialization logic in the resolver's
// Initialize method. There's a comment there also.
//
m_pSidNameResolver->Release();
m_pSidNameResolver = NULL;
pResolver = NULL;
}
}
}
if (NULL != m_pSidNameResolver)
{
//
// Create and initialize the enumerator object.
//
pEnumUsers = new DiskQuotaUserEnum(static_cast<IDiskQuotaControl *>(this),
m_pSidNameResolver,
m_pFSObject);
//
// This can throw OutOfMemory.
//
hr = pEnumUsers->Initialize(fNameResolution,
ENUMUSER_BUF_LEN,
rgpSids,
cpSids);
if (SUCCEEDED(hr))
{
hr = pEnumUsers->QueryInterface(IID_IEnumDiskQuotaUsers,
(LPVOID *)ppEnum);
}
else
{
//
// Something failed after enumerator object was created.
//
delete pEnumUsers;
}
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
delete pEnumUsers;
hr = E_OUTOFMEMORY;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::CreateUserBatch
Description: Create a new user batch control object. Batch control is
provided to take advantage of the inherent batching properties of the
NTIOAPI. If many user records are being altered at one time, it is
much more efficient to mark each of the users for "deferred update",
submit each user object to the batch and then flush the batch to disk.
Arguments:
ppUserBatch - Address of interface variable to accept the IDiskQuotaUserBatch
interface pointer.
Returns:
NOERROR - Success.
E_INVALIDARG - ppOut arg is NULL.
E_OUTOFMEMORY - Insufficient memory to create batch object.
ERROR_ACCESS_DENIED (hr) - Need WRITE access to create batch.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
08/11/96 Added access control. BrianAu
09/05/96 Added exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::CreateUserBatch(
PDISKQUOTA_USER_BATCH *ppUserBatch
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::CreateUserBatch")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == ppUserBatch)
return E_INVALIDARG;
if (!m_pFSObject->GrantedAccess(GENERIC_WRITE))
{
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
try
{
DiskQuotaUserBatch *pUserBatch = new DiskQuotaUserBatch(m_pFSObject);
hr = pUserBatch->QueryInterface(IID_IDiskQuotaUserBatch,
(LPVOID *)ppUserBatch);
}
catch(CAllocException& e) // From new or m_UserList ctor.
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::AddUserSid
Description: Adds a new user to the volume's quota information file.
If successful, returns an interface to the new user object. When
the caller is finished with the interface, they must call Release()
through that interface pointer. Uses the default limit and threshold.
Arguments:
pSid - Pointer to single SID structure.
fNameResolution - Method of SID-to-name resolution. Can be one of the
following:
DISKQUOTA_USERNAME_RESOLVE_NONE
DISKQUOTA_USERNAME_RESOLVE_SYNC
DISKQUOTA_USERNAME_RESOLVE_ASYNC
ppUser - Address of interface pointer variable to accept
pointer to the new user object's IDiskQuotaUser interface.
Returns:
SUCCESS - Success.
S_FALSE - User already exists. Not added.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
E_INVALIDARG - pSid or ppUser were NULL.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
09/30/96 Added implementation. Was E_NOTIMPL. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::AddUserSid(
PSID pSid,
DWORD fNameResolution,
PDISKQUOTA_USER *ppUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::AddUserSid")));
HRESULT hr = E_FAIL;
PDISKQUOTA_USER pIUser = NULL;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pSid || NULL == ppUser)
return E_INVALIDARG;
LONGLONG llLimit = 0;
LONGLONG llThreshold = 0;
LONGLONG llUsed = 0;
*ppUser = NULL;
//
// Check to see if the user already exists in the quota file.
//
try
{
hr = FindUserSid(pSid,
DISKQUOTA_USERNAME_RESOLVE_NONE,
&pIUser);
if (SUCCEEDED(hr))
{
//
// The NTIOAPI says the user exists.
// We'll need the quota info to determine if we
// still allow addition of the "new" user. This is needed because
// of the weird way the NTIOAPI enumerates users. If you ask it to
// enumerate specified user(s), the returned information will include
// info for users that do not have (but could have) a record in the
// quota file. Since the quota system allows automatic addition of
// users, it considers any users with write access to have a record
// in the quota file. Such users are returned with a quota threshold
// and limit of 0. Therefore, we treat records marked for deletion
// or those with 0 used, 0 limit and 0 threshold as "non existing".
// I use the term "ghost" for these users.
//
pIUser->GetQuotaLimit(&llLimit);
pIUser->GetQuotaThreshold(&llThreshold);
pIUser->GetQuotaUsed(&llUsed);
ULARGE_INTEGER a,b,c;
a.QuadPart = llLimit;
b.QuadPart = llThreshold;
c.QuadPart = llUsed;
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("Found user: Limit = 0x%08X 0x%08X, Threshold = 0x%08X 0x%08X, Used = 0x%08X 0x%08X"),
a.HighPart, a.LowPart, b.HighPart, b.LowPart, c.HighPart, c.LowPart));
BOOL bIsGhost = ((MARK4DEL == llLimit) ||
( 0 == llLimit &&
0 == llThreshold &&
0 == llUsed));
if (!bIsGhost)
{
//
// User already exists.
//
hr = S_FALSE;
}
else
{
DWORD cbSid = GetLengthSid(pSid);
//
// User not in quota file OR in quota file but marked for deletion.
// Just set it's limit and threshold to the volume defaults.
//
pIUser->SetQuotaThreshold(m_llDefaultQuotaThreshold, TRUE);
hr = pIUser->SetQuotaLimit(m_llDefaultQuotaLimit, TRUE);
if (SUCCEEDED(hr) && NULL != m_pSidNameResolver)
{
//
// We have a good user object and have set the quota parameters.
// Get the user's domain, name and full name from the network DC
// using the resolution type specified by the caller.
//
switch(fNameResolution)
{
case DISKQUOTA_USERNAME_RESOLVE_ASYNC:
m_pSidNameResolver->FindUserNameAsync(pIUser);
break;
case DISKQUOTA_USERNAME_RESOLVE_SYNC:
m_pSidNameResolver->FindUserName(pIUser);
break;
case DISKQUOTA_USERNAME_RESOLVE_NONE:
default:
break;
}
}
}
*ppUser = pIUser;
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
if (FAILED(hr))
{
*ppUser = NULL;
if (NULL != pIUser)
{
pIUser->Release();
}
}
return hr;
}
STDMETHODIMP
DiskQuotaControl::AddUserName(
LPCWSTR pszLogonName,
DWORD fNameResolution,
PDISKQUOTA_USER *ppUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::AddUserName")));
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pszLogonName || NULL == ppUser)
return E_INVALIDARG;
HRESULT hr = E_FAIL;
try
{
BYTE Sid[MAX_SID_LEN];
DWORD cbSid = sizeof(Sid);
SID_NAME_USE eSidUse;
if (SUCCEEDED(m_NTDS.LookupAccountByName(NULL, // system
pszLogonName, // key
NULL, // no container ret
NULL, // no display name ret
&Sid[0],
&cbSid,
&eSidUse)))
{
hr = AddUserSid(&Sid[0], fNameResolution, ppUser);
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::ShutdownNameResolution
Description: Release the SID/Name resolver. This terminates the
resolver thread for clients who don't want to wait for the controller
object to be destroyed. Note that subsequent calls to CreateEnumUsers,
AddUserSid, AddUserName, FindUserSid or FindUserName can restart
the resolver.
Arguments: None.
Returns: Always returns NOERROR
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
08/29/97 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::ShutdownNameResolution(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::ShutdownNameResolution")));
if (NULL != m_pSidNameResolver)
{
//
// Shutdown and release the resolver.
// Since it's running on it's own thread, we must wait for the thread
// to exit.
// Note that if the thread is off resolving a name from the DC, this could
// take a bit.
//
m_pSidNameResolver->Shutdown(TRUE); // TRUE == Wait for thread exit.
m_pSidNameResolver->Release();
m_pSidNameResolver = NULL;
}
return NOERROR;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::GiveUserNameResolutionPriority
Description: A very long name for a very simple function.
This function merely finds the user object in the name resolver's
input queue and moves it to the head of the queue.
Arguments:
pUser - Address of interface pointer for the user object's
IDiskQuotaUser interface.
Returns:
NOERROR - Success.
S_FALSE - User object not in resolver queue.
E_OUTOFMEMORY - Insufficient memory.
E_INVALIDARG - pUser is NULL.
E_UNEXPECTED - Unexpected error. Caught an exception or the
Sid-Name resolver hasn't been created.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/18/97 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::GiveUserNameResolutionPriority(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GiveUserNameResolutionPriority")));
HRESULT hr = E_UNEXPECTED;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pUser)
return E_INVALIDARG;
//
// SidNameResolver::PromoteUserToQueueHeader catches exceptions and
// converts them to HRESULTs. No need for try-catch block here.
//
if (NULL != m_pSidNameResolver)
{
hr = m_pSidNameResolver->PromoteUserToQueueHead(pUser);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::FindUserSid
Description: Finds a single user record in a volume's quota information
file. Returns an interface to the corresponding user object. When
the caller is finished with the interface, they must call Release()
through that interface pointer.
>>>>>>>>> IMPORTANT NOTE <<<<<<<<<
This method will return a user object even if there is no quota
record for the user in the quota file. While that may sound
strange, it is consistent with the idea of automatic user addition
and default quota settings. If there is currently no user record
for the requested user, and the user would be added to the quota
file if they were to request disk space, the returned user object
will have a quota threshold of 0 and a quota limit of 0.
Arguments:
pSid - Pointer to single SID structure identifying the user.
fNameResolution - Can be one of the following:
DISKQUOTA_USERNAME_RESOLVE_NONE
DISKQUOTA_USERNAME_RESOLVE_SYNC
DISKQUOTA_USERNAME_RESOLVE_ASYNC
ppUser - Address of interface pointer variable to accept pointer to
the user object's IDiskQuotaUser interface.
Returns:
NOERROR - Success.
E_INVALIDARG - Either pSid or ppUser were NULL.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
ERROR_INVALID_SID (hr) - Invalid SID.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
ERROR_NO_SUCH_USER (hr) - User not found in volume's quota information.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
08/14/96 Changed name from FindUser to FindUserSid to BrianAu
accomodate the addition of the FindUserName
methods. No change in functionality.
09/05/96 Added exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::FindUserSid(
PSID pSid,
DWORD fNameResolution,
PDISKQUOTA_USER *ppUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::FindUserSid")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pSid || NULL == ppUser)
return E_INVALIDARG;
if (!m_pFSObject->GrantedAccess(GENERIC_READ))
{
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
if (!IsValidSid(pSid))
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_SID);
}
else
{
PENUM_DISKQUOTA_USERS pEnumUsers = NULL;
try
{
DWORD cbSid = GetLengthSid(pSid);
*ppUser = NULL;
//
// Create a user enumerator for the user's SID.
// Can throw OutOfMemory.
//
hr = CreateEnumUsers(&pSid, 1, fNameResolution, &pEnumUsers);
if (SUCCEEDED(hr))
{
DWORD dwUsersFound = 0;
PDISKQUOTA_USER pUser = NULL;
//
// Enumerate 1 record to get the user's info.
// Only one record required since the enumerator object
// was created from a single SID. Can throw OutOfMemory.
//
hr = pEnumUsers->Next(1, &pUser, &dwUsersFound);
if (S_OK == hr)
{
//
// Return user object interface to caller.
//
*ppUser = pUser;
}
else if (S_FALSE == hr)
{
//
// Note: We should never hit this.
// The quota system always returns a user record
// for a user SID. If the record doesn't currently
// exist, one with default limit and threshold is
// returned. This is consistent with the idea
// of automatic user record addition implemented
// by the NTFS quota system. Just in case we do,
// I want to return something intelligent.
//
hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
}
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
if (NULL != pEnumUsers)
{
//
// Release the enumerator.
//
pEnumUsers->Release();
}
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::FindUserName
Description: Finds a single user record in a volume's quota information
file. Returns an interface to the corresponding user object. When
the caller is finished with the interface, they must call Release()
through that interface pointer.
If the name is not already cached in the SidNameCache, the function
queries the network domain controller. This operation may take some
time (on the order of 0 - 10 seconds).
Arguments:
pszLogonName - Address of user's logon name string.
i.e. "REDMOND\brianau" or "brianau@microsoft.com"
ppUser - Address of interface pointer variable to accept pointer to
the user object's IDiskQuotaUser interface.
Returns:
NOERROR - Success.
E_INVALIDARG - Name string is blank or NUL ptr was passed.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
ERROR_NO_SUCH_USER (hr) - User not found in quota file.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
08/14/96 Initial creation. BrianAu
09/05/96 Added domain name string. BrianAu
Added exception handling.
08/15/97 Removed ANSI version. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::FindUserName(
LPCWSTR pszLogonName,
PDISKQUOTA_USER *ppUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::FindUserName")));
HRESULT hr = E_FAIL; // Assume failure.
BOOL bAskDomainController = TRUE;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pszLogonName || NULL == ppUser)
return E_INVALIDARG;
if (TEXT('\0') == *pszLogonName)
return E_INVALIDARG;
//
// Check for client's access to quota file before we do any
// time-expensive operations.
//
if (!m_pFSObject->GrantedAccess(GENERIC_READ))
{
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
PSID pSid = NULL; // For cache query.
SID Sid[MAX_SID_LEN]; // For DC query.
//
// These nested try-catch blocks look really gross and may
// be unnecessary. I should probably just punt if one of the
// inner blocks really does throw an exception and return
// E_UNEXPECTED instead of trying to continue. [brianau]
//
try
{
SidNameCache *pSidCache;
hr = SidNameCache_Get(&pSidCache);
if (SUCCEEDED(hr))
{
//
// See if the SID/Name pair is in the cache.
//
try
{
hr = pSidCache->Lookup(pszLogonName, &pSid);
if (SUCCEEDED(hr))
{
//
// We have a SID. No need to ask DC.
//
bAskDomainController = FALSE;
}
}
catch(CAllocException& e)
{
//
// Just catch the exception.
// This will cause us to go to the DC for the SID.
//
DBGERROR((TEXT("C++ exception during SID cache lookup in FindUserName")));
pSid = &Sid[0];
}
}
if (bAskDomainController)
{
DBGASSERT((FAILED(hr)));
//
// Still don't have a SID. Ask the DC.
// This can take some time (Ho Hum.........)
//
CString strDisplayName;
CString strContainerName;
SID_NAME_USE eUse;
DWORD cbSid = sizeof(Sid);
if (SUCCEEDED(m_NTDS.LookupAccountByName(NULL,
pszLogonName,
&strContainerName,
&strDisplayName,
&Sid[0],
&cbSid,
&eUse)))
{
pSid = &Sid[0];
//
// Add it to the cache for later use.
//
if (NULL != pSidCache)
{
pSidCache->Add(&Sid[0],
strContainerName,
pszLogonName,
strDisplayName);
}
hr = NOERROR;
}
}
if (SUCCEEDED(hr))
{
//
// We have a SID.
// Now create the actual user object using FindUserSid().
//
hr = FindUserSid(pSid, DISKQUOTA_USERNAME_RESOLVE_SYNC, ppUser);
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
if (&Sid[0] != pSid)
{
//
// We received a heap-allocated SID from SidNameCache::Lookup.
// Need to free the buffer.
//
delete[] pSid;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::DeleteUser
Description: Deletes a user from a volume's quota information and quota
tracking. The IDiskQuotaUser pointer may be obtained either through
enumeration or DiskQuotaControl::FindUser().
NOTE: At this time, we're not sure how (or if) deletion will be done.
This function remains un-implemented until we figure it out.
Arguments:
pUser - Pointer to quota user object's IDiskQuotaUser interface.
Returns:
NOERROR - Success.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
E_FAIL - NTIO error writing user data.
E_INVALIDARG - pUser argument was NULL.
ERROR_FILE_EXISTS (hr) - Couldn't delete. User still has bytes charge.
ERROR_ACCESS_DENIED (hr) - Insufficient access.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
09/28/96 Added implementation. Was E_NOTIMPL. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::DeleteUser(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::DeleteUser")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pUser)
return E_INVALIDARG;
try
{
LONGLONG llValue;
//
// Invalidate user object to force a refresh of data from the
// quota file. Want to make sure this is current information before
// we tell the caller that the user can't be deleted.
//
pUser->Invalidate();
hr = pUser->GetQuotaUsed(&llValue);
if (SUCCEEDED(hr))
{
if (0 == llValue)
{
//
// User has 0 bytes in use. OK to delete.
// Write -2 to user's limit and threshold.
//
pUser->SetQuotaThreshold(MARK4DEL, TRUE);
hr = pUser->SetQuotaLimit(MARK4DEL, TRUE);
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
}
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::QueryQuotaInformation
Description: Read quota information from disk to member variables.
Arguments: None.
Returns:
NOERROR - Success.
E_FAIL - Any other error.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaControl::QueryQuotaInformation(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::QueryQuotaInformation")));
HRESULT hr = NOERROR;
DISKQUOTA_FSOBJECT_INFORMATION Info;
hr = m_pFSObject->QueryObjectQuotaInformation(&Info);
if (SUCCEEDED(hr))
{
m_llDefaultQuotaThreshold = Info.DefaultQuotaThreshold;
m_llDefaultQuotaLimit = Info.DefaultQuotaLimit;
m_dwFlags = (Info.FileSystemControlFlags & DISKQUOTA_FLAGS_MASK);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::SetQuotaInformation
Description: Writes quota information from member variables to disk.
Arguments:
dwChangeMask - A bit mask with one or more of the following bits set:
FSObject::ChangeState
FSObject::ChangeLogFlags
FSObject::ChangeThreshold
FSObject::ChangeLimit
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaControl::SetQuotaInformation(
DWORD dwChangeMask
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::SetQuotaInformation")));
HRESULT hr = NOERROR;
DISKQUOTA_FSOBJECT_INFORMATION Info;
Info.DefaultQuotaThreshold = m_llDefaultQuotaThreshold;
Info.DefaultQuotaLimit = m_llDefaultQuotaLimit;
Info.FileSystemControlFlags = m_dwFlags;
hr = m_pFSObject->SetObjectQuotaInformation(&Info, dwChangeMask);
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::SetDefaultQuotaThreshold
Description: Sets the default quota threshold value applied to new user
quota records. Value is in bytes.
Arguments:
llThreshold - Threshold value.
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
ERROR_INVALID_PARAMETER - llThreshold was less than -2.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
11/11/98 Added check for value < -2. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::SetDefaultQuotaThreshold(
LONGLONG llThreshold
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetDefaultQuotaThreshold")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (MARK4DEL > llThreshold)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
m_llDefaultQuotaThreshold = llThreshold;
hr = SetQuotaInformation(FSObject::ChangeThreshold);
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::SetDefaultQuotaLimit
Description: Sets the default quota limit value applied to new user
quota records. Value is in bytes.
Arguments:
llThreshold - Limit value.
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
ERORR_NOT_READY (hr) - Object not initialized.
ERROR_INVALID_PARAMETER - llLimit was less than -2.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
11/11/98 Added check for value < -2. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::SetDefaultQuotaLimit(
LONGLONG llLimit
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetDefaultQuotaLimit")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (MARK4DEL > llLimit)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
m_llDefaultQuotaLimit = llLimit;
hr = SetQuotaInformation(FSObject::ChangeLimit);
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::GetDefaultQuotaItem
Description: Retrieves one of the default quota items (limit, threshold)
applied to new user quota records. Value is in bytes.
Arguments:
pllItem - Address of item (limit, threshold, used) value item to
retrieve (member variable).
pllValueOut - Address of LONGLONG variable to receive value.
Returns:
NOERROR - Success.
E_INVALIDARG - pdwLowPart or pdwHighPart is NULL.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaControl::GetDefaultQuotaItem(
PLONGLONG pllItem,
PLONGLONG pllValueOut
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::GetDefaultQuotaItem")));
HRESULT hr = NOERROR;
if (NULL == pllItem || NULL == pllValueOut)
return E_INVALIDARG;
try
{
hr = QueryQuotaInformation();
if (SUCCEEDED(hr))
{
*pllValueOut = *pllItem;
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::GetDefaultQuotaThreshold
Description: Retrieves the default quota threshold applied to new user
quota records. Value is in bytes.
Arguments:
pllThreshold - Address of LONGLONG to receive threshold value.
Returns:
NOERROR - Success.
E_INVALIDARG - pdwLowPart or pdwHighPart is NULL.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::GetDefaultQuotaThreshold(
PLONGLONG pllThreshold
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaThreshold")));
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
return GetDefaultQuotaItem(&m_llDefaultQuotaThreshold, pllThreshold);
}
STDMETHODIMP
DiskQuotaControl::GetDefaultQuotaThresholdText(
LPWSTR pszText,
DWORD cchText
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaThresholdText")));
if (NULL == pszText)
return E_INVALIDARG;
LONGLONG llValue;
HRESULT hr = GetDefaultQuotaThreshold(&llValue);
if (SUCCEEDED(hr))
{
if (NOLIMIT == llValue)
{
LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText);
}
else
{
XBytes::FormatByteCountForDisplay(llValue, pszText, cchText);
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::GetDefaultQuotaLimit
Description: Retrieves the default quota limit applied to new user
quota records. Value is in bytes.
Arguments:
pllThreshold - Address of LONGLONG to receive limit value.
Returns:
NOERROR - Success.
E_INVALIDARG - pdwLowPart or pdwHighPart is NULL.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
E_FAIL - Any other error. // BUBUG: conflict?
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::GetDefaultQuotaLimit(
PLONGLONG pllLimit
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaLimit")));
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
return GetDefaultQuotaItem(&m_llDefaultQuotaLimit, pllLimit);
}
STDMETHODIMP
DiskQuotaControl::GetDefaultQuotaLimitText(
LPWSTR pszText,
DWORD cchText
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaLimitText")));
if (NULL == pszText)
return E_INVALIDARG;
LONGLONG llValue;
HRESULT hr = GetDefaultQuotaLimit(&llValue);
if (SUCCEEDED(hr))
{
if (NOLIMIT == llValue)
{
LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText);
}
else
{
XBytes::FormatByteCountForDisplay(llValue, pszText, cchText);
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::GetQuotaState
Description: Retrieve the state of the quota system.
Arguments:
pdwState - Address of DWORD to accept the quota state value.
Returned value is formatted as follows:
Bit(s) Definition
------- ---------------------------------------------------
00-01 0 = Disabled (DISKQUOTA_STATE_DISABLED)
1 = Tracking (DISKQUOTA_STATE_TRACK)
2 = Enforcing (DISKQUOTA_STATE_ENFORCE)
3 = Invalid value.
02-07 Reserved
08 1 = Quota file incomplete.
09 1 = Rebuilding quota file.
10-31 Reserved.
Use the macros defined in dskquota.h to query the
values and bits in this state DWORD.
Returns:
NOERROR - Success.
E_INVALIDARG - pdwState arg is NULL.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/02/96 Initial creation. BrianAu
08/19/96 Added DISKQUOTA_FILEFLAG_MASK. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::GetQuotaState(
LPDWORD pdwState
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetQuotaState")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pdwState)
return E_INVALIDARG;
try
{
hr = QueryQuotaInformation();
if (SUCCEEDED(hr))
{
DWORD dwMask = DISKQUOTA_STATE_MASK | DISKQUOTA_FILEFLAG_MASK;
*pdwState = (m_dwFlags & dwMask);
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::SetQuotaState
Description: Sets the quota state flags in the volume's quota info file.
The quota state may be one of the following:
- Disabled.
- Tracking quotas (no enforcement).
- Enforcing quota limits.
Arguments:
dwState - New state of quota system. The bits in this DWORD are
defined as follows:
Bit(s) Definition
------- ---------------------------------------------------
00-01 0 = Disabled (DISKQUOTA_STATE_DISABLED)
1 = Tracking (DISKQUOTA_STATE_TRACK)
2 = Enforcing (DISKQUOTA_STATE_ENFORCE)
3 = Invalid value.
02-07 Reserved
08 1 = Quota file incomplete (read only)
09 1 = Rebuilding quota file (read only)
10-31 Reserved.
Use the macros defined in dskquota.h to set the
values and bits in this state DWORD.
Returns:
NOERROR - Success.
E_INVALIDARG - Invalid state value.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/02/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::SetQuotaState(
DWORD dwState
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetQuotaState")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
try
{
if (dwState <= DISKQUOTA_STATE_MASK)
{
m_dwFlags &= ~DISKQUOTA_STATE_MASK; // Clear current state bits.
m_dwFlags |= dwState; // Set new state bits.
hr = SetQuotaInformation(FSObject::ChangeState);
}
else
{
hr = E_INVALIDARG;
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::GetQuotaLogFlags
Description: Retrieve the state of the quota logging system.
Arguments:
pdwFlags - Address of DWORD to accept the quota logging flags.
The bits in the flags DWORD are defined as follows:
Bit(s) Definition
------- ---------------------------------------------------
00 1 = Logging user threshold violations.
01 1 = Logging user limit violations.
02 1 = Logging volume threshold violations.
03 1 = Logging volume limit violations.
04-31 Reserved.
Use the macros defined in dskquota.h to query the
values and bits in this flags DWORD.
Returns:
NOERROR - Success.
E_INVALIDARG - pdwFlags arg is NULL.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/02/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::GetQuotaLogFlags(
LPDWORD pdwFlags
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetQuotaLogFlags")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
if (NULL == pdwFlags)
return E_INVALIDARG;
try
{
hr = QueryQuotaInformation();
if (SUCCEEDED(hr))
{
*pdwFlags = ((m_dwFlags & DISKQUOTA_LOGFLAG_MASK) >> DISKQUOTA_LOGFLAG_SHIFT);
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::SetQuotaLogFlags
Description: Sets the quota logging state flags in the volume's quota
info file.
Arguments:
dwFlags - New state of quota logging.
The bits in the flags DWORD are defined as follows:
Bit(s) Definition
------- ---------------------------------------------------
00 1 = Logging user threshold violations.
01 1 = Logging user limit violations.
02 1 = Logging volume threshold violations.
03 1 = Logging volume limit violations.
04-31 Reserved.
Use the macros defined in dskquota.h to set the
values and bits in this flags DWORD.
Returns:
NOERROR - Success.
E_UNEXPECTED - Unexpected exception.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
ERROR_NOT_READY (hr) - Object not initialized.
E_FAIL - Any other error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/02/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::SetQuotaLogFlags(
DWORD dwFlags
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetQuotaLogFlags")));
HRESULT hr = NOERROR;
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
try
{
m_dwFlags &= ~DISKQUOTA_LOGFLAG_MASK;
m_dwFlags |= (dwFlags << DISKQUOTA_LOGFLAG_SHIFT);
hr = SetQuotaInformation(FSObject::ChangeLogFlags);
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::InitConnectionPoints
Description: Private function for initializing the connection point
objects supported by IConnectionPointContainer. Called from
DiskQuotaControl::Initialize(). To add a new connection point
type, merely add a new record to the m_rgConnPtDesc[] array in the
DiskQuotaControl class declaration. All of the other related code
in DiskQuotaControl will adjust to it automatically.
Arguments: None.
Returns:
NOERROR - Success.
E_UNEXPECTED - A connection point pointer was non-NULL.
Exceptions: OutOfMemory.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/19/96 Initial creation. BrianAu
09/05/96 Added exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaControl::InitConnectionPoints(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::InitConnectionPoints")));
if (NULL != m_rgConnPts)
{
//
// Already initialized.
//
return NOERROR;
}
HRESULT hr = NOERROR;
ConnectionPoint *pConnPt = NULL;
m_cConnPts = ARRAYSIZE(m_rgpIConnPtsSupported);
try
{
m_rgConnPts = new PCONNECTIONPOINT[m_cConnPts];
//
// For each of the connection point IIDs in m_rgpIConnPtsSupported[]...
//
for (UINT i = 0; i < m_cConnPts && SUCCEEDED(hr); i++)
{
m_rgConnPts[i] = NULL;
//
// Create connection point object and query for IConnectionPoint interface.
//
pConnPt = new ConnectionPoint(static_cast<IConnectionPointContainer *>(this),
*m_rgpIConnPtsSupported[i]);
hr = pConnPt->QueryInterface(IID_IConnectionPoint, (LPVOID *)&m_rgConnPts[i]);
if (FAILED(hr))
{
//
// Either Initialize or QI failed.
//
delete pConnPt;
pConnPt = NULL;
}
}
}
catch(CAllocException& e)
{
delete pConnPt;
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::FindConnectionPoint
Description: Queries the quota control object for a specific connection
point type. If that type is supported, a pointer to the connection
point's IConnectionPoint interface is returned.
Arguments:
riid - Interface ID of desired connection point interface.
Supported interfaces:
IID_IDiskQuotaUserEvents
- OnNameChanged()
Returns:
NOERROR - Success.
E_INVALIDARG - ppConnPtOut arg is NULL.
E_NOINTERFACE - Requested interface is not supported.
E_UNEXPECTED - Connection point object pointer is NULL.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/19/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::FindConnectionPoint(
REFIID riid,
IConnectionPoint **ppConnPtOut
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::FindConnectionPoint")));
DBGPRINTIID(DM_CONTROL, DL_HIGH, riid);
HRESULT hr = E_NOINTERFACE;
if (NULL == ppConnPtOut)
return E_INVALIDARG;
*ppConnPtOut = NULL;
for (UINT i = 0; i < m_cConnPts && NULL == *ppConnPtOut; i++)
{
if (*m_rgpIConnPtsSupported[i] == riid)
{
if (NULL != m_rgConnPts[i])
{
//
// We have an IID match.
// Now get the conn pt interface pointer.
//
hr = m_rgConnPts[i]->QueryInterface(IID_IConnectionPoint, (LPVOID *)ppConnPtOut);
}
else
{
hr = E_UNEXPECTED;
break;
}
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::EnumConnectionPoints
Description: Creates a connection point enumerator object.
Using this object, the client can enumerate through all of the
connection point interfaces supported by the quota controller.
Arguments:
ppEnum - Address of interface pointer variable to receive the
IEnumConnectionPoints interface.
Returns:
NOERROR - Success.
E_OUTOFMEMORY - Insufficient memory to create object(s).
E_INVALIDARG - ppEnum arg was NULL.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/19/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::EnumConnectionPoints(
IEnumConnectionPoints **ppEnum
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::EnumConnectionPoints")));
HRESULT hr = NOERROR;
if (NULL == ppEnum)
return E_INVALIDARG;
PCONNECTIONPOINT rgCP[ARRAYSIZE(m_rgpIConnPtsSupported)];
ConnectionPointEnum *pEnum = NULL;
*ppEnum = NULL;
for (UINT i = 0; i < m_cConnPts; i++)
{
//
// Make a copy of each connection point pointer
// to give to the enumerator's Initialize() method.
//
m_rgConnPts[i]->AddRef();
rgCP[i] = m_rgConnPts[i];
}
try
{
pEnum = new ConnectionPointEnum(static_cast<IConnectionPointContainer *>(this),
m_cConnPts, rgCP);
hr = pEnum->QueryInterface(IID_IEnumConnectionPoints, (LPVOID *)ppEnum);
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
if (FAILED(hr) && NULL != pEnum)
{
delete pEnum;
*ppEnum = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::InvalidateSidNameCache
Description: Invalidates the contents of the SidNameCache so that future
requests for account names from the cache must be resolved through
the DC. As names are resolved, they are again added to the cache.
Arguments: None.
Returns:
NOERROR - Cache invalidated.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
E_FAIL - No cache object available or couldn't get lock on
cache files. Either way, cache wasn't invalidated.
ERROR_NOT_READY (hr) - Object not initialized.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
07/24/96 Initial creation. BrianAu
09/20/96 Updated for new cache design. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaControl::InvalidateSidNameCache(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::InvalidateSidNameCache")));
if (!m_bInitialized)
return HRESULT_FROM_WIN32(ERROR_NOT_READY);
SidNameCache *pCache;
HRESULT hr = SidNameCache_Get(&pCache);
if (SUCCEEDED(hr))
{
if (!pCache->Clear())
{
hr = E_FAIL;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaControl::NotifyUserNameChanged
Description: Notify all user IDiskQuotaControl Event connections that a
user's name has changed.
Arguments:
pUser - Address of user object's IDiskQuotaUser interface.
Returns:
NOERROR - Success.
E_OUTOFMEMORY - Insufficient memory to create enumerator.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
07/22/96 Initial creation. BrianAu
08/25/97 Added support for IPropertyNotifySink BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaControl::NotifyUserNameChanged(
PDISKQUOTA_USER pUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::NotifyUserNameChanged")));
HRESULT hr = NOERROR;
PCONNECTIONPOINT pConnPt = NULL;
bool bAbort = false;
INT rgiConnPt[] = { ConnPt_iQuotaEvents,
ConnPt_iQuotaEventsDisp };
for (INT i = 0; i < ARRAYSIZE(rgiConnPt) && !bAbort; i++)
{
if (NULL != (pConnPt = m_rgConnPts[ rgiConnPt[i] ]))
{
PENUMCONNECTIONS pEnum = NULL;
pConnPt->AddRef();
hr = pConnPt->EnumConnections(&pEnum);
if (SUCCEEDED(hr))
{
CONNECTDATA cd;
while(!bAbort && NOERROR == pEnum->Next(1, &cd, NULL))
{
DBGASSERT((NULL != cd.pUnk));
LPUNKNOWN pEventSink = NULL;
hr = cd.pUnk->QueryInterface(*(m_rgpIConnPtsSupported[ rgiConnPt[i] ]),
(LPVOID *)&pEventSink);
//
// Guard with a critical section mutex. The NT5 quota UI
// may deadlock after closing the details view window
// without this critical section. It's possible, other
// clients of the quota controller could do the same.
// Here's what happens:
// The controller calls OnUserNameChanged which
// is implemented by the DetailsView object. This
// function updates the details view for the specified
// quota user. Update involves sending/posting
// messages to the listview object. On destruction
// of the listview window (user closing the window),
// the quota controller is released. If the
// DetailsView held the last ref count to the
// controller, the controller commands the SID/Name
// resolver to shutdown. The resolver's Shutdown
// command posts a WM_QUIT to the resolver's input
// queue and blocks until the resolver's thread
// exits normally. The problem is that the DetailsView
// thread is blocked waiting for the resolver's thread
// to exit but the resolver's thread is blocked
// because the DetailsView thread can't process it's
// listview update messages. This results in deadlock.
// This critical section prevents this.
//
if (SUCCEEDED(hr))
{
if (WAIT_OBJECT_0 != m_mutex.Wait(2000))
{
//
// DiskQuotaControl dtor must own this mutex.
// Since the control is being destroyed, no sense in
// continuing.
//
DBGERROR((TEXT("Mutex timeout in DiskQuotaControl::NotifyUserNameChanged")));
bAbort = true;
}
else
{
AutoLockMutex lock(m_mutex); // Exception-safe release.
try
{
//
// Calling client code. Handle any exceptions.
//
switch(rgiConnPt[i])
{
case ConnPt_iQuotaEvents:
hr = ((PDISKQUOTA_EVENTS)pEventSink)->OnUserNameChanged(pUser);
break;
case ConnPt_iQuotaEventsDisp:
{
IDispatch *pEventDisp = NULL;
hr = pEventSink->QueryInterface(IID_IDispatch, (LPVOID *)&pEventDisp);
if (SUCCEEDED(hr))
{
IDispatch *pUserDisp = NULL;
hr = pUser->QueryInterface(IID_IDispatch, (LPVOID *)&pUserDisp);
if (SUCCEEDED(hr))
{
UINT uArgErr;
VARIANTARG va;
DISPPARAMS params;
VariantClear(&va);
V_VT(&va) = VT_DISPATCH;
V_DISPATCH(&va) = pUserDisp;
params.rgvarg = &va;
params.rgdispidNamedArgs = NULL;
params.cArgs = 1;
params.cNamedArgs = 0;
hr = pEventDisp->Invoke(DISPID_DISKQUOTAEVENTS_USERNAMECHANGED,
IID_NULL,
GetThreadLocale(),
DISPATCH_METHOD,
&params,
NULL,
NULL,
&uArgErr);
if (FAILED(hr))
{
DBGERROR((TEXT("Error 0x%08X firing async notification event with IDispatch::Invoke"), hr));
}
pUserDisp->Release();
}
else
{
DBGERROR((TEXT("Error 0x%08X getting IDispatch interface from user object for async notification."), hr));
}
pEventDisp->Release();
}
else
{
DBGERROR((TEXT("Error 0x%08X getting IDispatch interface from connection point for async notification."), hr));
}
break;
}
default:
//
// Shouldn't hit this.
//
DBGERROR((TEXT("Invalid connection point ID")));
break;
}
}
catch(CAllocException& e)
{
//
// Ignore an allocation exception and try to continue.
//
}
}
pEventSink->Release();
}
cd.pUnk->Release();
}
pEnum->Release();
}
pConnPt->Release();
}
}
return hr;
}
DiskQuotaControlDisp::DiskQuotaControlDisp(
PDISKQUOTA_CONTROL pQC
) : m_cRef(0),
m_pQC(pQC),
m_pUserEnum(NULL)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControlDisp::DiskQuotaControlDisp")));
if (NULL != m_pQC)
{
m_pQC->AddRef();
}
//
// This is the default resolution style for OLE automation.
// I've used ASYNC as the default so it won't hang the caller
// on enumeration if many of the names aren't resolved.
// If they want sync resolution, they can set the
// UserNameResolution property.
//
m_fOleAutoNameResolution = DISKQUOTA_USERNAME_RESOLVE_ASYNC;
m_Dispatch.Initialize(static_cast<IDispatch *>(this),
LIBID_DiskQuotaTypeLibrary,
IID_DIDiskQuotaControl,
L"DSKQUOTA.DLL");
}
DiskQuotaControlDisp::~DiskQuotaControlDisp(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControlDisp::~DiskQuotaControlDisp")));
if (NULL != m_pUserEnum)
{
m_pUserEnum->Release();
}
if (NULL != m_pQC)
{
m_pQC->Release();
}
}
STDMETHODIMP_(ULONG)
DiskQuotaControlDisp::AddRef(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControlDisp::AddRef")));
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef + 1));
ULONG ulReturn = m_cRef + 1;
InterlockedIncrement(&m_cRef);
return ulReturn;
}
STDMETHODIMP_(ULONG)
DiskQuotaControlDisp::Release(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControlDisp::Release")));
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef - 1));
ULONG ulReturn = m_cRef - 1;
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
ulReturn = 0;
}
return ulReturn;
}
STDMETHODIMP
DiskQuotaControlDisp::QueryInterface(
REFIID riid,
LPVOID *ppvOut
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControlDisp::QueryInterface")));
DBGPRINTIID(DM_CONTROL, DL_MID, riid);
HRESULT hr = E_NOINTERFACE;
if (NULL == ppvOut)
return E_INVALIDARG;
*ppvOut = NULL;
if (IID_IUnknown == riid)
{
*ppvOut = this;
}
else if (IID_IDispatch == riid)
{
*ppvOut = static_cast<IDispatch *>(this);
}
else if (IID_DIDiskQuotaControl == riid)
{
*ppvOut = static_cast<DIDiskQuotaControl *>(this);
}
else if (IID_IDiskQuotaControl == riid ||
IID_IConnectionPointContainer == riid)
{
//
// Return the quota controller's vtable interface.
// This allows code to "typecast" (COM-style) between
// the dispatch interface and vtable interface.
//
return m_pQC->QueryInterface(riid, ppvOut);
}
if (NULL != *ppvOut)
{
((LPUNKNOWN)*ppvOut)->AddRef();
hr = NOERROR;
}
return hr;
}
//
// IDispatch::GetIDsOfNames
//
STDMETHODIMP
DiskQuotaControlDisp::GetIDsOfNames(
REFIID riid,
OLECHAR **rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispId
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GetIDsOfNames")));
//
// Let our dispatch object handle this.
//
return m_Dispatch.GetIDsOfNames(riid,
rgszNames,
cNames,
lcid,
rgDispId);
}
//
// IDispatch::GetTypeInfo
//
STDMETHODIMP
DiskQuotaControlDisp::GetTypeInfo(
UINT iTInfo,
LCID lcid,
ITypeInfo **ppTypeInfo
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GetTypeInfo")));
//
// Let our dispatch object handle this.
//
return m_Dispatch.GetTypeInfo(iTInfo, lcid, ppTypeInfo);
}
//
// IDispatch::GetTypeInfoCount
//
STDMETHODIMP
DiskQuotaControlDisp::GetTypeInfoCount(
UINT *pctinfo
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GetTypeInfoCount")));
//
// Let our dispatch object handle this.
//
return m_Dispatch.GetTypeInfoCount(pctinfo);
}
//
// IDispatch::Invoke
//
STDMETHODIMP
DiskQuotaControlDisp::Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *puArgErr
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::Invoke")));
//
// Let our dispatch object handle this.
//
return m_Dispatch.Invoke(dispIdMember,
riid,
lcid,
wFlags,
pDispParams,
pVarResult,
pExcepInfo,
puArgErr);
}
//
// Dispatch property "QuotaState" (put)
//
// Sets the state of the quota system on the volume.
// See DiskQuotaControl::SetQuotaState for details.
//
// Valid states: 0 = Disabled.
// 1 = Tracking
// 2 = Enforcing
STDMETHODIMP
DiskQuotaControlDisp::put_QuotaState(
QuotaStateConstants State
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_QuotaState")));
if (dqStateMaxValue < State)
{
//
// State can only be 0, 1 or 2.
//
return E_INVALIDARG;
}
//
// No exception handling required.
// DiskQuotaControl::SetQuotaState handles exceptions.
//
return m_pQC->SetQuotaState(State);
}
//
// Dispatch property "QuotaState" (get)
//
// Retrieves the state of the quota system on the volume.
// See DiskQuotaControl::GetQuotaState for details.
//
// State returned: 0 = Disabled.
// 1 = Tracking
// 2 = Enforcing
//
STDMETHODIMP
DiskQuotaControlDisp::get_QuotaState(
QuotaStateConstants *pState
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_QuotaState")));
DWORD dwState;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaState handles exceptions.
//
HRESULT hr = m_pQC->GetQuotaState(&dwState);
if (SUCCEEDED(hr))
{
*pState = (QuotaStateConstants)(dwState & DISKQUOTA_STATE_MASK);
}
return hr;
}
//
// Dispatch property "QuotaFileIncomplete" (get)
//
// Determines if the state of the quota file is "incomplete".
//
STDMETHODIMP
DiskQuotaControlDisp::get_QuotaFileIncomplete(
VARIANT_BOOL *pbIncomplete
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_QuotaFileIncomplete")));
DWORD dwState;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaState handles exceptions.
//
HRESULT hr = m_pQC->GetQuotaState(&dwState);
if (SUCCEEDED(hr))
{
*pbIncomplete = DISKQUOTA_FILE_INCOMPLETE(dwState) ? VARIANT_TRUE : VARIANT_FALSE;
}
return hr;
}
//
// Dispatch property "QuotaFileRebuilding" (get)
//
// Determines if the state of the quota file is "rebuilding".
//
STDMETHODIMP
DiskQuotaControlDisp::get_QuotaFileRebuilding(
VARIANT_BOOL *pbRebuilding
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_QuotaFileRebuilding")));
DWORD dwState;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaState handles exceptions.
//
HRESULT hr = m_pQC->GetQuotaState(&dwState);
if (SUCCEEDED(hr))
{
*pbRebuilding = DISKQUOTA_FILE_REBUILDING(dwState) ? VARIANT_TRUE : VARIANT_FALSE;
}
return hr;
}
//
// Dispatch property "LogQuotaThreshold" (put)
//
// Sets the "log warning threshold" flag on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::put_LogQuotaThreshold(
VARIANT_BOOL bLog
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_LogQuotaThreshold")));
DWORD dwFlags;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaLogFlags and SetQuotaLogFlags handle
// exceptions.
//
HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags);
if (SUCCEEDED(hr))
{
hr = m_pQC->SetQuotaLogFlags(DISKQUOTA_SET_LOG_USER_THRESHOLD(dwFlags, VARIANT_TRUE == bLog));
}
return hr;
}
//
// Dispatch property "LogQuotaThreshold" (get)
//
// Retrieves the "log warning threshold" flag on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::get_LogQuotaThreshold(
VARIANT_BOOL *pbLog
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_LogQuotaThreshold")));
DWORD dwFlags;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaLogFlags handles exceptions.
//
HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags);
if (SUCCEEDED(hr))
{
*pbLog = DISKQUOTA_IS_LOGGED_USER_THRESHOLD(dwFlags) ? VARIANT_TRUE : VARIANT_FALSE;
}
return hr;
}
//
// Dispatch property "LogQuotaLimit" (put)
//
// Sets the "log quota limit" flag on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::put_LogQuotaLimit(
VARIANT_BOOL bLog
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_LogQuotaLimit")));
DWORD dwFlags;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaLogFlags handles exceptions.
//
HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags);
if (SUCCEEDED(hr))
{
hr = m_pQC->SetQuotaLogFlags(DISKQUOTA_SET_LOG_USER_LIMIT(dwFlags, VARIANT_TRUE == bLog));
}
return hr;
}
//
// Dispatch property "LogQuotaLimit" (get)
//
// Retrieves the "log quota limit" flag on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::get_LogQuotaLimit(
VARIANT_BOOL *pbLog
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_LogQuotaLimit")));
DWORD dwFlags;
//
// No exception handling required.
// DiskQuotaControl::GetQuotaLogFlags handles exceptions.
//
HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags);
if (SUCCEEDED(hr))
{
*pbLog = DISKQUOTA_IS_LOGGED_USER_LIMIT(dwFlags) ? VARIANT_TRUE : VARIANT_FALSE;
}
return hr;
}
//
// Dispatch property "DefaultQuotaThreshold" (put)
//
// Sets the default quota threshold on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::put_DefaultQuotaThreshold(
double Threshold
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_DefaultQuotaThreshold")));
if (MAXLONGLONG < Threshold)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
//
// No exception handling required.
// DiskQuotaControl::GetDefaultQuotaThreshold handles exceptions.
//
return m_pQC->SetDefaultQuotaThreshold((LONGLONG)Threshold);
}
//
// Dispatch property "DefaultQuotaThreshold" (get)
//
// Retrieves the default quota threshold on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::get_DefaultQuotaThreshold(
double *pThreshold
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaThreshold")));
LONGLONG llTemp;
//
// No exception handling required.
// DiskQuotaControl::GetDefaultQuotaThreshold handles exceptions.
//
HRESULT hr = m_pQC->GetDefaultQuotaThreshold(&llTemp);
if (SUCCEEDED(hr))
{
*pThreshold = (double)llTemp;
}
return hr;
}
STDMETHODIMP
DiskQuotaControlDisp::get_DefaultQuotaThresholdText(
BSTR *pThresholdText
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaThresholdText")));
TCHAR szValue[40];
HRESULT hr;
hr = m_pQC->GetDefaultQuotaThresholdText(szValue, ARRAYSIZE(szValue));
if (SUCCEEDED(hr))
{
*pThresholdText = SysAllocString(szValue);
if (NULL == *pThresholdText)
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
//
// Dispatch property "DefaultQuotaLimit" (put)
//
// Sets the default quota limit on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::put_DefaultQuotaLimit(
double Limit
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_DefaultQuotaLimit")));
if (MAXLONGLONG < Limit)
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
//
// No exception handling required.
// DiskQuotaControl::SetDefaultQuotaLimit handles exceptions.
//
return m_pQC->SetDefaultQuotaLimit((LONGLONG)Limit);
}
//
// Dispatch property "DefaultQuotaLimit" (get)
//
// Retrieves the default quota limit on the volume.
//
STDMETHODIMP
DiskQuotaControlDisp::get_DefaultQuotaLimit(
double *pLimit
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaLimit")));
LONGLONG llTemp;
//
// No exception handling required.
// DiskQuotaControl::GetDefaultQuotaLimit handles exceptions.
//
HRESULT hr = m_pQC->GetDefaultQuotaLimit(&llTemp);
if (SUCCEEDED(hr))
{
*pLimit = (double)llTemp;
}
return hr;
}
STDMETHODIMP
DiskQuotaControlDisp::get_DefaultQuotaLimitText(
BSTR *pLimitText
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaLimitText")));
TCHAR szValue[40];
HRESULT hr = m_pQC->GetDefaultQuotaLimitText(szValue, ARRAYSIZE(szValue));
if (SUCCEEDED(hr))
{
*pLimitText = SysAllocString(szValue);
if (NULL == *pLimitText)
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
STDMETHODIMP
DiskQuotaControlDisp::put_UserNameResolution(
UserNameResolutionConstants ResolutionType
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_UserNameResolution")));
if (dqResolveMaxValue < ResolutionType)
{
return E_INVALIDARG;
}
m_fOleAutoNameResolution = (DWORD)ResolutionType;
return NOERROR;
}
STDMETHODIMP
DiskQuotaControlDisp::get_UserNameResolution(
UserNameResolutionConstants *pResolutionType
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_UserNameResolution")));
if (NULL == pResolutionType)
return E_INVALIDARG;
*pResolutionType = (UserNameResolutionConstants)m_fOleAutoNameResolution;
return NOERROR;
}
//
// Dispatch method "Initialize"
//
// Initializes the quota control object for a given path and
// access mode. See DiskQuotaControl::Initialize for details.
//
STDMETHODIMP
DiskQuotaControlDisp::Initialize(
BSTR path,
VARIANT_BOOL bReadWrite
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::Initialize")));
//
// No exception handling required.
// DiskQuotaControl::Initialize handles exceptions.
//
return m_pQC->Initialize(reinterpret_cast<LPCWSTR>(path), VARIANT_TRUE == bReadWrite);
}
//
// Dispatch method "AddUser"
//
// Adds new user quota record.
// See DiskQuotaControl::AddUserName for details.
//
STDMETHODIMP
DiskQuotaControlDisp::AddUser(
BSTR LogonName,
DIDiskQuotaUser **ppUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::AddUser")));
//
// No exception handling required.
// DiskQuotaControl::AddUserName handles exceptions.
//
PDISKQUOTA_USER pUser = NULL;
HRESULT hr = m_pQC->AddUserName(reinterpret_cast<LPCWSTR>(LogonName),
m_fOleAutoNameResolution,
&pUser);
if (SUCCEEDED(hr))
{
//
// Retrieve the user object's IDispatch interface.
//
hr = pUser->QueryInterface(IID_IDispatch, (LPVOID *)ppUser);
pUser->Release();
}
return hr;
}
//
// Dispatch method "DeleteUser"
//
// Marks a user quota record for deletion.
// See DiskQuotaControl::DeleteUser for details.
//
STDMETHODIMP
DiskQuotaControlDisp::DeleteUser(
DIDiskQuotaUser *pUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::DeleteUser")));
HRESULT hr = E_INVALIDARG;
if (NULL != pUser)
{
//
// No exception handling required.
// DiskQuotaControl::DeleteUser handles exceptions.
//
PDISKQUOTA_USER pUserToDelete = NULL;
hr = pUser->QueryInterface(IID_IDiskQuotaUser, (LPVOID *)&pUserToDelete);
if (SUCCEEDED(hr))
{
hr = m_pQC->DeleteUser(pUserToDelete);
pUserToDelete->Release();
}
}
return hr;
}
//
// Dispatch method "FindUser"
//
// Locates a user quota entry based on the user's name strings.
// Creates a corresponding user object and returns it to the caller.
// See DiskQuotaControl::FindUserName for details.
//
STDMETHODIMP
DiskQuotaControlDisp::FindUser(
BSTR LogonName,
DIDiskQuotaUser **ppUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::FindUser")));
//
// No exception handling required.
// DiskQuotaControl::FindUserName handles exceptions.
//
HRESULT hr = NOERROR;
LPCWSTR pszName = reinterpret_cast<LPCWSTR>(LogonName);
PSID psid = NULL;
PDISKQUOTA_USER pUser = NULL;
if (ConvertStringSidToSid(pszName, &psid))
{
hr = m_pQC->FindUserSid(psid,
m_fOleAutoNameResolution,
&pUser);
LocalFree(psid);
psid = NULL;
}
else
{
hr = m_pQC->FindUserName(pszName, &pUser);
}
if (SUCCEEDED(hr))
{
DBGASSERT((NULL != pUser));
//
// Query for the user's IDispatch interface and release the pointer
// we received from FindUserName.
//
hr = pUser->QueryInterface(IID_IDispatch, (LPVOID *)ppUser);
pUser->Release();
}
return hr;
}
//
// Dispatch method "InvalidateSidNameCache"
//
// Invalidates the SID/Name cache.
// See DiskQuotaControl::InvalidateSidNameCache for details.
//
STDMETHODIMP
DiskQuotaControlDisp::InvalidateSidNameCache(
void
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::InvalidateSidNameCache")));
//
// No exception handling required.
// DiskQuotaControl::InvalidateSidNameCache handles exceptions.
//
return m_pQC->InvalidateSidNameCache();
}
//
// Dispatch method "GiveUserNameResolutionPriority"
//
// Promotes a user object to the front of the SID/Name resolver's input queue.
// See DiskQuotaControl::GiveUserNameResolutionPriority.
//
STDMETHODIMP
DiskQuotaControlDisp::GiveUserNameResolutionPriority(
DIDiskQuotaUser *pUser
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GiveUserNameResolutionPriority")));
HRESULT hr = E_INVALIDARG;
if (NULL != pUser)
{
//
// No exception handling required.
// DiskQuotaControl::GiveUserNameResolutionPriority handles exceptions.
//
PDISKQUOTA_USER pUserToPromote = NULL;
hr = pUser->QueryInterface(IID_IDiskQuotaUser, (LPVOID *)&pUserToPromote);
if (SUCCEEDED(hr))
{
hr = m_pQC->GiveUserNameResolutionPriority(pUserToPromote);
pUserToPromote->Release();
}
}
return hr;
}
//
// This function is called by an automation controller when a new enumerator is
// required. In particular, Visual Basic calls it when it encounters a
// "for each" loop. The name "_NewEnum" is hard-wired and can't change.
//
STDMETHODIMP
DiskQuotaControlDisp::_NewEnum(
IDispatch **ppEnum
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::_NewEnum")));
HRESULT hr = E_INVALIDARG;
if (NULL != ppEnum)
{
try
{
//
// Create a Collection object using the current settings for
// name resolution.
//
DiskQuotaUserCollection *pCollection = new DiskQuotaUserCollection(m_pQC,
m_fOleAutoNameResolution);
hr = pCollection->Initialize();
if (SUCCEEDED(hr))
{
//
// The caller of _NewEnum (probably VB) wants the IEnumVARIANT
// interface.
//
hr = pCollection->QueryInterface(IID_IEnumVARIANT, (LPVOID *)ppEnum);
}
else
{
delete pCollection;
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
hr = E_OUTOFMEMORY;
}
}
return hr;
}
//
// Shutdown the SID/Name resolver. Note that this happens automatically
// when the control object is destroyed.
//
STDMETHODIMP
DiskQuotaControlDisp::ShutdownNameResolution(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::ShutdownNameResolution")));
return m_pQC->ShutdownNameResolution();
}
//
// Given a logon name in SAM-compatible or UPN format, translate
// the name to the corresponding account's SID. The returned SID is
// formatted as a string.
//
STDMETHODIMP
DiskQuotaControlDisp::TranslateLogonNameToSID(
BSTR LogonName,
BSTR *psid
)
{
NTDS ntds;
BYTE sid[MAX_SID_LEN];
SID_NAME_USE eSidUse;
DWORD cbSid = ARRAYSIZE(sid);
HRESULT hr = ntds.LookupAccountByName(NULL,
reinterpret_cast<LPCWSTR>(LogonName),
NULL,
NULL,
sid,
&cbSid,
&eSidUse);
if (SUCCEEDED(hr))
{
LPTSTR pszSid = NULL;
if (ConvertSidToStringSid((PSID)sid, &pszSid))
{
*psid = SysAllocString(pszSid);
if (NULL == *psid)
{
hr = E_OUTOFMEMORY;
}
LocalFree(pszSid);
pszSid = NULL;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}