/////////////////////////////////////////////////////////////////////////////// /* File: user.cpp Description: Contains member function definitions for class DiskQuotaUser. The DiskQuotaUser object represents a user's record in a volume's quota information file. The holder of a user object's IDiskQuotaUser interface can query and modify information for that user as security privileges permit. A user object is obtained through a UserEnumerator object (IEnumDiskQuotaUsers) which is itself obtained through IDiskQuotaControl::CreateEnumUsers(). Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 08/20/96 Added m_dwID member to DiskQuotaUser. BrianAu 09/05/96 Added exception handling. BrianAu 03/18/98 Replaced "domain", "name" and "full name" with BrianAu "container", "logon name" and "display name" to better match the actual contents. This was in reponse to making the quota UI DS-aware. The "logon name" is now a unique key as it contains both account name and domain-like information. i.e. "REDMOND\brianau" or "brianau@microsoft.com". */ /////////////////////////////////////////////////////////////////////////////// #include "pch.h" // PCH #pragma hdrstop #include #include "user.h" #include "resource.h" // For IDS_NO_LIMIT. // // Verify that build is UNICODE. // #if !defined(UNICODE) # error This module must be compiled UNICODE. #endif // // Only one of these for all users. (static member). // LONG DiskQuotaUser::m_cUsersAlive = 0; // Cnt of users alive now. ULONG DiskQuotaUser::m_ulNextUniqueId = 0; HANDLE DiskQuotaUser::m_hMutex = NULL; DWORD DiskQuotaUser::m_dwMutexWaitTimeout = 5000; // 5 seconds. CArray DiskQuotaUser::m_ContainerNameCache; /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::DiskQuotaUser Description: Constructor. Arguments: pFSObject - Pointer to "file system" object. It is through this pointer that the object accesses the ntioapi functions. Caller must call AddRef() for this pointer prior to calling Initialize(). Returns: Nothing. Exceptions: OutOfMemory. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 09/05/96 Added domain name string. BrianAu */ /////////////////////////////////////////////////////////////////////////////// DiskQuotaUser::DiskQuotaUser( FSObject *pFSObject ) : m_cRef(0), m_ulUniqueId(InterlockedIncrement((LONG *)&m_ulNextUniqueId)), m_pSid(NULL), m_pszLogonName(NULL), m_pszDisplayName(NULL), m_pFSObject(pFSObject), m_bNeedCacheUpdate(TRUE), // Data cache, not domain name cache. m_iContainerName(-1), m_dwAccountStatus(DISKQUOTA_USER_ACCOUNT_UNRESOLVED) { DBGTRACE((DM_USER, DL_HIGH, TEXT("DiskQuotaUser::DiskQuotaUser"))); DBGPRINT((DM_USER, DL_HIGH, TEXT("\tthis = 0x%08X"), this)); DBGASSERT((NULL != m_pFSObject)); m_llQuotaUsed = 0; m_llQuotaThreshold = 0; m_llQuotaLimit = 0; // // Initialize the domain name cache and class-wide locking mutex. // These members are static so we only do it once. // InterlockedIncrement(&m_cUsersAlive); if (NULL == DiskQuotaUser::m_hMutex) { DiskQuotaUser::m_hMutex = CreateMutex(NULL, FALSE, NULL); m_ContainerNameCache.SetSize(25); m_ContainerNameCache.SetGrow(25); } } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::~DiskQuotaUser Description: Destructor Arguments: None. Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// DiskQuotaUser::~DiskQuotaUser( VOID ) { DBGTRACE((DM_USER, DL_HIGH, TEXT("DiskQuotaUser::~DiskQuotaUser"))); DBGPRINT((DM_USER, DL_HIGH, TEXT("\tthis = 0x%08X"), this)); Destroy(); if (InterlockedDecrement(&m_cUsersAlive) == 0) { // // If active user count is 0, destroy the domain name cache and // class-wide mutex. // DestroyContainerNameCache(); if (NULL != DiskQuotaUser::m_hMutex) { CloseHandle(DiskQuotaUser::m_hMutex); DiskQuotaUser::m_hMutex = NULL; } } } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::QueryInterface Description: Returns an interface pointer to the object's IUnknown or IDiskQuotaUser interface. Only IID_IUnknown and IID_IDiskQuotaUser are recognized. 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 DiskQuotaUser::QueryInterface( REFIID riid, LPVOID *ppvOut ) { DBGTRACE((DM_USER, DL_MID, TEXT("DiskQuotaUser::QueryInterface"))); DBGPRINTIID(DM_USER, DL_MID, riid); HRESULT hResult = E_NOINTERFACE; if (NULL == ppvOut) return E_INVALIDARG; try { *ppvOut = NULL; if (IID_IUnknown == riid || IID_IDiskQuotaUser == riid) { *ppvOut = static_cast(this); } else if (IID_IDispatch == riid || IID_DIDiskQuotaUser == riid) { // // Create a disk quota user "dispatch" object to handle all of // the automation duties. This object takes a pointer to the real // user object so that it can call the real object to do the real // work. The reason we use a special "dispatch" object is so that // we can maintain identical names for dispatch and vtable methods // that perform the same function. Otherwise, if the DiskQuotaUser // object implements both IDiskQuotaUser and DIDiskQuotaUser methods, // we could not have two methods named Invalidate (one for vtable // and one for dispatch. // DiskQuotaUserDisp *pUserDisp = new DiskQuotaUserDisp(static_cast(this)); *ppvOut = static_cast(pUserDisp); } if (NULL != *ppvOut) { ((LPUNKNOWN)*ppvOut)->AddRef(); hResult = NOERROR; } } catch(CAllocException& e) { *ppvOut = NULL; hResult = E_OUTOFMEMORY; } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::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) DiskQuotaUser::AddRef( VOID ) { DBGTRACE((DM_USER, DL_LOW, TEXT("DiskQuotaUser::AddRef"))); DBGPRINT((DM_USER, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, m_cRef, m_cRef + 1)); ULONG ulReturn = m_cRef + 1; InterlockedIncrement(&m_cRef); return ulReturn; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::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) DiskQuotaUser::Release( VOID ) { DBGTRACE((DM_USER, DL_LOW, TEXT("DiskQuotaUser::Release"))); DBGPRINT((DM_USER, 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: DiskQuotaUser::Initialize Description: Initializes a new DiskQuotaUser object from a quota information record read from a volume's quota information file. Arguments: pfqi [optional] - Pointer to a record of type FILE_QUOTA_INFORMATION. If not NULL, the data from this record is used to initialize the new user object. Returns: NOERROR - Success. E_UNEXPECTED - SID buffer too small (shouldn't happen). ERROR_INVALID_SID (hr) - SID in quota information is invalid. ERROR_ACCESS_DENIED (hr) - Need READ access to quota device. Exceptions: OutOfMemory. 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 */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::Initialize( PFILE_QUOTA_INFORMATION pfqi ) { HRESULT hResult = NOERROR; DBGASSERT((NULL != m_pFSObject)); // // Need READ access to create a user object. // if (m_pFSObject->GrantedAccess(GENERIC_READ)) { if (NULL != pfqi) // pfqi is optional. { if (0 < pfqi->SidLength && IsValidSid(&pfqi->Sid)) { // // Allocate space for SID structure. // m_pSid = (PSID) new BYTE[pfqi->SidLength]; // // Copy SID structure to object. // if (CopySid(pfqi->SidLength, m_pSid, &pfqi->Sid)) { // // Initialize user's quota data values. // If error copying SID, don't bother with these. // m_llQuotaUsed = pfqi->QuotaUsed.QuadPart; m_llQuotaThreshold = pfqi->QuotaThreshold.QuadPart; m_llQuotaLimit = pfqi->QuotaLimit.QuadPart; } else { // // The only reason CopySid can fail is // STATUS_BUFFER_TOO_SMALL. Since we allocated the buffer // above, this should never fail. // DBGASSERT((FALSE)); hResult = E_UNEXPECTED; // Error copying SID. } } else { DBGERROR((TEXT("DiskQuotaUser::Initialize - Invalid SID or Bad Sid Length (%d)"), pfqi->SidLength)); hResult = HRESULT_FROM_WIN32(ERROR_INVALID_SID); } } } else hResult = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::Destroy Description: Destroys a user object by deleting its SID buffer and releasing its FSObject pointer. Arguments: None. Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 09/05/96 Added domain name string. BrianAu */ /////////////////////////////////////////////////////////////////////////////// VOID DiskQuotaUser::Destroy( VOID ) { // // Delete the SID buffer. // delete [] m_pSid; m_pSid = NULL; // // Delete the logon name buffer. // delete[] m_pszLogonName; m_pszLogonName = NULL; // // Delete the display name buffer. // delete[] m_pszDisplayName; m_pszDisplayName = NULL; if (NULL != m_pFSObject) { // // Release hold on File System object. // m_pFSObject->Release(); m_pFSObject = NULL; } } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::DestroyContainerNameCache Description: Destroys the container name cache. Should only be called when there are not more active user objects. The container name cache is a static member of DiskQuotaUser. Arguments: None. Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 09/06/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// VOID DiskQuotaUser::DestroyContainerNameCache( VOID ) { // // Remove all container name strings from the cache. No need to lock // the cache object before clearing it. It will handle the locking // and unlocking. // m_ContainerNameCache.Clear(); } // // Return user object's unique ID. // STDMETHODIMP DiskQuotaUser::GetID( ULONG *pulID ) { *pulID = m_ulUniqueId; return NOERROR; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetAccountStatus Description: Retrieves the account name resolution status for the user object. Arguments: pdwAccountStatus - Address of variable to recieve status. The following values (see dskquota.h) can be returned in this variable: DISKQUOTA_USER_ACCOUNT_RESOLVED DISKQUOTA_USER_ACCOUNT_UNAVAILABLE DISKQUOTA_USER_ACCOUNT_DELETED DISKQUOTA_USER_ACCOUNT_INVALID DISKQUOTA_USER_ACCOUNT_UNKNOWN DISKQUOTA_USER_ACCOUNT_UNRESOLVED Returns: NOERROR - Success. E_INVALIDARG - pdwAccountStatus arg is NULL. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/11/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::GetAccountStatus( LPDWORD pdwAccountStatus ) { if (NULL == pdwAccountStatus) return E_INVALIDARG; *pdwAccountStatus = m_dwAccountStatus; return NOERROR; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::SetName Description: Sets the account names of the user object. It is intended that the SidNameResolver object will call this member when it has resolved a user's SID into an account name. This function is not included in IDiskQuotaUser. Therefore, it is not for public consumption. Arguments: pszContainer - Address of buffer containing container name string. pszLogonName - Address of buffer containing user's logon name string. pszDisplayName - Address of buffer containing user's display name string. Returns: NOERROR - Success. E_INVALIDARG - pszName or pszDomain arg is NULL. E_OUTOFMEMORY - Insufficient memory. ERROR_LOCK_FAILED (hr) - Couldn't lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/11/96 Initial creation. BrianAu 09/05/96 Added domain name string. BrianAu 09/22/96 Added full name string. BrianAu 12/10/96 Added class-scope user lock. BrianAu 05/18/97 Removed access token. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::SetName( LPCWSTR pszContainer, LPCWSTR pszLogonName, LPCWSTR pszDisplayName ) { HRESULT hResult = NOERROR; if (NULL == pszContainer || NULL == pszLogonName || NULL == pszDisplayName) return E_INVALIDARG; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { // // Delete existing name buffer. // delete[] m_pszLogonName; m_pszLogonName = NULL; delete[] m_pszDisplayName; m_pszDisplayName = NULL; try { // // Save name and full name in user object. // Cache container string in container name cache and // save cache index in user object. // INT index = -1; m_pszLogonName = StringDup(pszLogonName); m_pszDisplayName = StringDup(pszDisplayName); CacheContainerName(pszContainer, &m_iContainerName); } catch(CAllocException& e) { hResult = E_OUTOFMEMORY; } DiskQuotaUser::ReleaseLock(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetName Description: Retrieves the domain and account names from the user object. It is intended that the client of the user object will register a callback (event sink) with a DiskQuotaControl object. When the resolver has resolved the SID to an account name, the resolver will set the user object's name string and the client will be notified. The client then calls this method to get the user's name. Arguments: pszContainerBuffer - Address of destination buffer for container name string. cchContainerBuffer - Size of container destination buffer in characters. pszLogonNameBuffer - Address of destination buffer for logon name string. cchLogonNameBuffer - Size of logon name destination buffer in characters. pszDisplayNameBuffer - Address of destination buffer for display name string. cchDisplayNameBuffer - Size of display name destination buffer in characters. Returns: NOERROR - Success. ERROR_LOCK_FAILED (hr) - Failed to lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/11/96 Initial creation. BrianAu 09/05/96 Added domain name string. BrianAu 09/22/96 Added full name string. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::GetName( LPWSTR pszContainerBuffer, DWORD cchContainerBuffer, LPWSTR pszLogonNameBuffer, DWORD cchLogonNameBuffer, LPWSTR pszDisplayNameBuffer, DWORD cchDisplayNameBuffer ) { HRESULT hResult = NOERROR; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { if (NULL != pszContainerBuffer) { if (-1 != m_iContainerName) { GetCachedContainerName(m_iContainerName, pszContainerBuffer, cchContainerBuffer); } else lstrcpyn(pszContainerBuffer, TEXT(""), cchContainerBuffer); } if (NULL != pszLogonNameBuffer) { lstrcpyn(pszLogonNameBuffer, (NULL != m_pszLogonName) ? m_pszLogonName : TEXT(""), cchLogonNameBuffer); } if (NULL != pszDisplayNameBuffer) { lstrcpyn(pszDisplayNameBuffer, (NULL != m_pszDisplayName) ? m_pszDisplayName : TEXT(""), cchDisplayNameBuffer); } DiskQuotaUser::ReleaseLock(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetSidLength Description: Retrieves the length of the user's SID in bytes. Arguments: pcbSid - Address of DWORD to accept SID length value. Returns: NOERROR - Success. E_INVALIDARG - pcbSid argument is NULL. ERROR_INVALID_SID (hr) - Invalid SID. ERROR_LOCK_FAILED (hr) - Couldn't lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::GetSidLength( LPDWORD pcbSid ) { HRESULT hResult = NOERROR; if (NULL == pcbSid) return E_INVALIDARG; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { if (NULL != m_pSid && IsValidSid(m_pSid)) { *pcbSid = GetLengthSid(m_pSid); } else hResult = HRESULT_FROM_WIN32(ERROR_INVALID_SID); DiskQuotaUser::ReleaseLock(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetSid Description: Retrieves the user's SID to a caller-provided buffer. The caller should call GetSidLength() to obtain the required buffer size before calling GetSid(). Arguments: pSid - Address of destination buffer for SID. This argument type must be PBYTE to work with the MIDL compiler. Since PSID is really just LPVOID and since MIDL doesn't like pointers to void, we have to use something other than PSID. cbSidBuf - Size of destination buffer in bytes. Returns: NOERROR - Success. E_INVALIDARG - pSID is NULL. ERROR_INVALID_SID (hr) - User's SID is invalid. ERROR_INSUFFICIENT_BUFFER (hr) - Insufficient dest buffer size. ERROR_LOCK_FAILED (hr) - Couldn't lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::GetSid( PBYTE pSid, DWORD cbSidBuf ) { HRESULT hResult = NOERROR; if (NULL == pSid) return E_INVALIDARG; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { if (NULL != m_pSid && IsValidSid(m_pSid)) { if (!CopySid(cbSidBuf, (PSID)pSid, m_pSid)) { // // The only reason CopySid can fail is STATUS_BUFFER_TOO_SMALL. // Force status code to INSUFFICIENT_BUFFER. // DBGERROR((TEXT("ERROR in DiskQuotaUser::GetSid. CopySid() failed. Result = 0x%08X."), GetLastError())); hResult = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } } else { DBGERROR((TEXT("ERROR in DiskQuotaUser::GetSid. Invalid SID."))); hResult = HRESULT_FROM_WIN32(ERROR_INVALID_SID); } DiskQuotaUser::ReleaseLock(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::RefreshCachedInfo Description: Refreshes a user object's cached quota information from the volume's quota information file. Arguments: None. Returns: NOERROR - Success. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. E_FAIL - Unexpected NTIOAPI error. This function can propagate errors from the NTIOAPI system. A few known ones are mapped to HResults in fsobject.cpp (see HResultFromNtStatus). All others are mapped to E_FAIL. Exceptions: OutOfMemory. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/05/96 Initial creation. BrianAu 09/05/96 Added exception handling. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::RefreshCachedInfo( VOID ) { HRESULT hResult = NOERROR; DWORD cbBuffer = FILE_QUOTA_INFORMATION_MAX_LEN; PSIDLIST pSidList = NULL; DWORD cbSidList = 0; PSID pSids[] = { m_pSid, NULL }; PBYTE pbBuffer = NULL; try { pbBuffer = new BYTE[cbBuffer]; // // This can throw OutOfMemory. // hResult = CreateSidList(pSids, 0, &pSidList, &cbSidList); if (SUCCEEDED(hResult)) { hResult = m_pFSObject->QueryUserQuotaInformation( pbBuffer, // Buffer to receive data. cbBuffer, // Buffer size in bytes. TRUE, // Single entry requested. pSidList, // Sid. cbSidList, // Length of Sid. NULL, // Starting Sid TRUE); // Start search at first user. if (SUCCEEDED(hResult) || ERROR_NO_MORE_ITEMS == HRESULT_CODE(hResult)) { PFILE_QUOTA_INFORMATION pfqi = (PFILE_QUOTA_INFORMATION)pbBuffer; m_llQuotaUsed = pfqi->QuotaUsed.QuadPart; m_llQuotaThreshold = pfqi->QuotaThreshold.QuadPart; m_llQuotaLimit = pfqi->QuotaLimit.QuadPart; m_bNeedCacheUpdate = FALSE; // // Don't return ERROR_NO_MORE_ITEMS to caller. // They won't care. // hResult = NOERROR; } delete[] pSidList; } delete[] pbBuffer; } catch(CAllocException& e) { hResult = E_OUTOFMEMORY; delete[] pbBuffer; delete[] pSidList; } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::WriteCachedInfo Description: Writes quota information cached in a user object to the volume's quota information file. Arguments: None. Returns: NOERROR - Success. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. E_FAIL - Some other NTIOAPI error. E_UNEXPECTED - CopySid failed. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 07/31/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::WriteCachedInfo( VOID ) { HRESULT hResult = NOERROR; BYTE Buffer[FILE_QUOTA_INFORMATION_MAX_LEN]; PFILE_QUOTA_INFORMATION pfqi = (PFILE_QUOTA_INFORMATION)Buffer; pfqi->NextEntryOffset = 0; pfqi->SidLength = GetLengthSid(m_pSid); pfqi->QuotaUsed.QuadPart = m_llQuotaUsed; pfqi->QuotaLimit.QuadPart = m_llQuotaLimit; pfqi->QuotaThreshold.QuadPart = m_llQuotaThreshold; if (CopySid(pfqi->SidLength, &(pfqi->Sid), m_pSid)) hResult = m_pFSObject->SetUserQuotaInformation(pfqi, sizeof(Buffer)); else hResult = E_UNEXPECTED; if (FAILED(hResult)) { // // Something failed. // Invalidate cached information so next request reads from disk. // Invalidate(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetQuotaInformation Description: Retrieves a user's quota limit, threshold and used quota values in a single method. Since the user interface is marshaled across thread boundaries, this can be a big performance improvement if you want all three values. Arguments: pbInfo - Address of destination buffer. Should be sized for structure DISKQUOTA_USER_INFORMATION. cbInfo - Number of bytes in destination buffer. Should be sizeof(DISKQUOTA_USER_INFORMATION). Returns: NOERROR - Success. E_INVALIDARG - pbInfo argument is NULL. E_OUTOFMEMORY - Insufficient memory. ERROR_INSUFFICIENT_BUFFER (hr) - Destination buffer is too small. ERROR_LOCK_FAILED (hr) - Couldn't lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 07/31/96 Initial creation. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::GetQuotaInformation( LPVOID pbInfo, DWORD cbInfo ) { HRESULT hResult = NOERROR; if (NULL == pbInfo) return E_INVALIDARG; try { if (cbInfo < sizeof(DISKQUOTA_USER_INFORMATION)) { hResult = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } else { if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { // // Refresh cached info from disk if needed. // Can throw OutOfMemory. // if (m_bNeedCacheUpdate) hResult = RefreshCachedInfo(); if (SUCCEEDED(hResult)) { PDISKQUOTA_USER_INFORMATION pui = (PDISKQUOTA_USER_INFORMATION)pbInfo; pui->QuotaUsed = m_llQuotaUsed; pui->QuotaThreshold = m_llQuotaThreshold; pui->QuotaLimit = m_llQuotaLimit; } DiskQuotaUser::ReleaseLock(); } } } catch(CAllocException& e) { hResult = E_OUTOFMEMORY; } return hResult; } STDMETHODIMP DiskQuotaUser::GetQuotaUsedText( LPWSTR pszText, DWORD cchText ) { if (NULL == pszText) return E_INVALIDARG; LONGLONG llValue; HRESULT hr = GetQuotaUsed(&llValue); if (SUCCEEDED(hr)) { if (NOLIMIT == llValue) { LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText); } else { XBytes::FormatByteCountForDisplay(llValue, pszText, cchText); } } return hr; } STDMETHODIMP DiskQuotaUser::GetQuotaThresholdText( LPWSTR pszText, DWORD cchText ) { if (NULL == pszText) return E_INVALIDARG; LONGLONG llValue; HRESULT hr = GetQuotaThreshold(&llValue); if (SUCCEEDED(hr)) { if (NOLIMIT == llValue) { LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText); } else { XBytes::FormatByteCountForDisplay(llValue, pszText, cchText); } } return hr; } STDMETHODIMP DiskQuotaUser::GetQuotaLimitText( LPWSTR pszText, DWORD cchText ) { if (NULL == pszText) return E_INVALIDARG; LONGLONG llValue; HRESULT hr = GetQuotaLimit(&llValue); if (SUCCEEDED(hr)) { if (NOLIMIT == llValue) { LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText); } else { XBytes::FormatByteCountForDisplay(llValue, pszText, cchText); } } return hr; } STDMETHODIMP DiskQuotaUser::SetQuotaThreshold( LONGLONG llThreshold, BOOL bWriteThrough ) { if (MARK4DEL > llThreshold) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); return SetLargeIntegerQuotaItem(&m_llQuotaThreshold, llThreshold, bWriteThrough); } STDMETHODIMP DiskQuotaUser::SetQuotaLimit( LONGLONG llLimit, BOOL bWriteThrough ) { if (MARK4DEL > llLimit) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); return SetLargeIntegerQuotaItem(&m_llQuotaLimit, llLimit, bWriteThrough); } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetLargeIntegerQuotaItem Description: Retrieves a single quota information item (used, limit, threshold) for the user. If the cached data is invalid, fresh data is read in from disk. Arguments: pllItem - Address of cached member item. pllValueOut - Address of LONGLONG to receive item's value. Returns: NOERROR - Success. E_INVALIDARG - Either pdwLowPart or pdwHighPart arg was NULL. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. ERROR_LOCK_FAILED (hr) - Couldn't lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/05/96 Initial creation. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::GetLargeIntegerQuotaItem( PLONGLONG pllItem, PLONGLONG pllValueOut ) { HRESULT hResult = NOERROR; DBGASSERT((NULL != pllItem)); if (NULL == pllItem || NULL == pllValueOut) return E_INVALIDARG; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { if (m_bNeedCacheUpdate) try { hResult = RefreshCachedInfo(); } catch(CAllocException& e) { hResult = E_OUTOFMEMORY; } if (SUCCEEDED(hResult)) { *pllValueOut = *pllItem; } DiskQuotaUser::ReleaseLock(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::SetLargeIntegerQuotaItem Description: Sets the quota information for a given quota item (limit or threshold). If the bWriteThrough argument is TRUE, the information is also written through to the volume's quota file. Otherwise, it is just cached in the user object. Arguments: pllItem - Address of cached member item. llValue - LONGLONG value to assign to member item. bWriteThrough - TRUE = Write data through to disk. FALSE = Only cache data in user object. Returns: NOERROR - Success. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. ERROR_LOCK_FAILED (hr) - Couldn't lock user object. E_FAIL - Some other NTIOAPI error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 08/06/96 Initial creation. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::SetLargeIntegerQuotaItem( PLONGLONG pllItem, LONGLONG llValue, BOOL bWriteThrough) { DBGASSERT((NULL != pllItem)); HRESULT hResult = NOERROR; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { *pllItem = llValue; if (bWriteThrough) hResult = WriteCachedInfo(); DiskQuotaUser::ReleaseLock(); } return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::CacheContainerName Description: Class DiskQuotaUser maintains a static member that is a cache of account container names. It is likely that there will be few distinct container names in use on a volume. Therefore, there's no need to store a container name for each user object. We cache the names and store only an index into the cache in each user object. This method adds a name to the cache and returns the index of the name in the cache. If the name already exists in the cache, it is not added. Arguments: pszContainer - Address of container name string to add to cache. pCacheIndex [optional] - Address of integer variable to receive the cache index of the container name string. May be NULL. Returns: S_OK - Success. S_FALSE - Name already in cache. E_FAIL - No cache object. Exceptions: OutOfMemory. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 09/05/09 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::CacheContainerName( LPCTSTR pszContainer, INT *pCacheIndex ) { DBGASSERT((NULL != pszContainer)); HRESULT hResult = S_OK; INT iCacheIndex = -1; UINT cItems = 0; m_ContainerNameCache.Lock(); cItems = m_ContainerNameCache.Count(); for (UINT i = 0; i < cItems; i++) { // // See if the name is already in the cache. // if (0 == m_ContainerNameCache[i].Compare(pszContainer)) { iCacheIndex = i; hResult = S_FALSE; // Already cached. break; } } if (S_OK == hResult) { // // Not in the cache. Add it. // try { m_ContainerNameCache.Append(CString(pszContainer)); iCacheIndex = m_ContainerNameCache.UpperBound(); } catch(CAllocException& e) { hResult = E_OUTOFMEMORY; } } m_ContainerNameCache.ReleaseLock(); if (NULL != pCacheIndex) *pCacheIndex = iCacheIndex; return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetCachedContainerName Description: Retrieves an account container name string from the container name cache. Arguments: iCacheIndex - User's index in domain name cache. pszContainer - Destination buffer to receive container name string. cchContainer - Number of characters in destination buffer. Returns: NOERROR - Success. E_UNEXPECTED - No name at index iCacheIndex. Returns "" as name. E_FAIL - No domain name cache object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 09/05/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaUser::GetCachedContainerName( INT iCacheIndex, LPTSTR pszContainer, UINT cchContainer ) { DBGASSERT((NULL != pszContainer)); DBGASSERT((-1 != iCacheIndex)); HRESULT hResult = NOERROR; m_ContainerNameCache.Lock(); DBGASSERT((iCacheIndex < m_ContainerNameCache.Count())); lstrcpyn(pszContainer, m_ContainerNameCache[iCacheIndex], cchContainer); m_ContainerNameCache.ReleaseLock(); return hResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::Lock Description: Call this to obtain an exclusive lock to the user object. In actuality, there is only one lock for all user objects so you're really getting an exclusive lock to all users. Since there can be a high number of users, it was decided to use a single class-wide lock instead of a unique lock for each user object. Arguments: None. Returns: TRUE = Obtained exclusive lock. FALSE = Couldn't get a lock. Either mutex hasn't been created or the mutex wait timeout expired, or the wait operation failed. Either way, we couldn't get the lock. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 12/10/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// BOOL DiskQuotaUser::Lock( VOID ) { BOOL bResult = FALSE; if (NULL != DiskQuotaUser::m_hMutex) { DWORD dwWaitResult = WaitForSingleObject(DiskQuotaUser::m_hMutex, DiskQuotaUser::m_dwMutexWaitTimeout); bResult = (WAIT_OBJECT_0 == dwWaitResult); } return bResult; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::ReleaseLock Description: Call this to release a lock obtained with DiskQuotaUser::Lock. Arguments: None. Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 12/10/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// VOID DiskQuotaUser::ReleaseLock( VOID ) { if (NULL != DiskQuotaUser::m_hMutex) { ReleaseMutex(DiskQuotaUser::m_hMutex); } } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::SetAccountStatus Description: Stores the status of the user's account in the user object. User accounts may be "unresolved", "unavailable", "resolved", "deleted", "invalid" or "unknown". These states correspond to the values obtained through LookupAccountSid. Arguments: dwStatus - DISKQUOTA_USER_ACCOUNT_UNRESOLVED DISKQUOTA_USER_ACCOUNT_UNAVAILABLE DISKQUOTA_USER_ACCOUNT_RESOLVED DISKQUOTA_USER_ACCOUNT_DELETED DISKQUOTA_USER_ACCOUNT_UNKNOWN DISKQUOTA_USER_ACCOUNT_INVALID Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/18/97 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// VOID DiskQuotaUser::SetAccountStatus( DWORD dwStatus ) { DBGASSERT((DISKQUOTA_USER_ACCOUNT_UNRESOLVED == dwStatus || DISKQUOTA_USER_ACCOUNT_UNAVAILABLE == dwStatus || DISKQUOTA_USER_ACCOUNT_RESOLVED == dwStatus || DISKQUOTA_USER_ACCOUNT_DELETED == dwStatus || DISKQUOTA_USER_ACCOUNT_INVALID == dwStatus || DISKQUOTA_USER_ACCOUNT_UNKNOWN == dwStatus)); m_dwAccountStatus = dwStatus; } // // The following functions implement the DiskQuotaUser "dispatch" object that // is created to handle OLE automation duties for the DiskQuotaUser object. // The functions are all fairly basic and require little explanation. // Therefore, I'll spare you the function headers. In most cases, // the property and method functions call directly through to their // corresponding functions in class DiskQuotaUser. // DiskQuotaUserDisp::DiskQuotaUserDisp( PDISKQUOTA_USER pUser ) : m_cRef(0), m_pUser(pUser) { DBGTRACE((DM_USER, DL_HIGH, TEXT("DiskQuotaUserDisp::DiskQuotaUserDisp"))); DBGPRINT((DM_USER, DL_HIGH, TEXT("\tthis = 0x%08X"), this)); if (NULL != m_pUser) { m_pUser->AddRef(); } m_Dispatch.Initialize(static_cast(this), LIBID_DiskQuotaTypeLibrary, IID_DIDiskQuotaUser, L"DSKQUOTA.DLL"); } DiskQuotaUserDisp::~DiskQuotaUserDisp( VOID ) { DBGTRACE((DM_USER, DL_HIGH, TEXT("DiskQuotaUserDisp::~DiskQuotaUserDisp"))); DBGPRINT((DM_USER, DL_HIGH, TEXT("\tthis = 0x%08X"), this)); if (NULL != m_pUser) { m_pUser->Release(); } } STDMETHODIMP DiskQuotaUserDisp::QueryInterface( REFIID riid, LPVOID *ppvOut ) { DBGTRACE((DM_USER, DL_MID, TEXT("DiskQuotaUserDisp::QueryInterface"))); DBGPRINTIID(DM_USER, DL_MID, riid); HRESULT hResult = E_NOINTERFACE; if (NULL == ppvOut) return E_INVALIDARG; *ppvOut = NULL; if (IID_IUnknown == riid) { *ppvOut = this; } else if (IID_IDispatch == riid) { *ppvOut = static_cast(this); } else if (IID_DIDiskQuotaUser == riid) { *ppvOut = static_cast(this); } else if (IID_IDiskQuotaUser == riid) { // // Return the quota user's vtable interface. // This allows code to "typecast" (COM-style) between // the dispatch interface and vtable interface. // return m_pUser->QueryInterface(riid, ppvOut); } if (NULL != *ppvOut) { ((LPUNKNOWN)*ppvOut)->AddRef(); hResult = NOERROR; } return hResult; } STDMETHODIMP_(ULONG) DiskQuotaUserDisp::AddRef( VOID ) { ULONG ulReturn = m_cRef + 1; DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserDisp::AddRef, 0x%08X %d -> %d"), this, ulReturn - 1, ulReturn)); InterlockedIncrement(&m_cRef); return ulReturn; } STDMETHODIMP_(ULONG) DiskQuotaUserDisp::Release( VOID ) { ULONG ulReturn = m_cRef - 1; DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserDisp::Release, 0x%08X %d -> %d"), this, ulReturn + 1, ulReturn)); if (InterlockedDecrement(&m_cRef) == 0) { delete this; ulReturn = 0; } return ulReturn; } // // IDispatch::GetIDsOfNames // STDMETHODIMP DiskQuotaUserDisp::GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId ) { DBGTRACE((DM_USER, DL_LOW, TEXT("DiskQuotaUserDisp::GetIDsOfNames"))); // // Let our dispatch object handle this. // return m_Dispatch.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); } // // IDispatch::GetTypeInfo // STDMETHODIMP DiskQuotaUserDisp::GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo **ppTypeInfo ) { DBGTRACE((DM_USER, DL_LOW, TEXT("DiskQuotaUserDisp::GetTypeInfo"))); // // Let our dispatch object handle this. // return m_Dispatch.GetTypeInfo(iTInfo, lcid, ppTypeInfo); } // // IDispatch::GetTypeInfoCount // STDMETHODIMP DiskQuotaUserDisp::GetTypeInfoCount( UINT *pctinfo ) { DBGTRACE((DM_USER, DL_LOW, TEXT("DiskQuotaUserDisp::GetTypeInfoCount"))); // // Let our dispatch object handle this. // return m_Dispatch.GetTypeInfoCount(pctinfo); } // // IDispatch::Invoke // STDMETHODIMP DiskQuotaUserDisp::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr ) { DBGTRACE((DM_USER, DL_LOW, TEXT("DiskQuotaUserDisp::Invoke"))); DBGPRINT((DM_USER, DL_LOW, TEXT("DispId = %d"), dispIdMember)); DBGPRINTIID(DM_USER, DL_LOW, riid); // // Let our dispatch object handle this. // return m_Dispatch.Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } // // Return user object's unique ID. // STDMETHODIMP DiskQuotaUserDisp::get_ID( long *pID ) { return m_pUser->GetID((ULONG *)pID); } STDMETHODIMP DiskQuotaUserDisp::get_AccountContainerName( BSTR *pContainerName ) { TCHAR szName[MAX_DOMAIN] = { TEXT('\0') }; HRESULT hr = m_pUser->GetName(szName, ARRAYSIZE(szName), NULL, 0, NULL, 0); if (SUCCEEDED(hr)) { *pContainerName = SysAllocString(szName); if (NULL == *pContainerName) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaUserDisp::get_LogonName( BSTR *pLogonName ) { TCHAR szName[MAX_USERNAME] = { TEXT('\0') }; HRESULT hr = m_pUser->GetName(NULL, 0, szName, ARRAYSIZE(szName), NULL, 0); if (SUCCEEDED(hr)) { *pLogonName = SysAllocString(szName); if (NULL == *pLogonName) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaUserDisp::get_DisplayName( BSTR *pDisplayName ) { TCHAR szName[MAX_FULL_USERNAME] = { TEXT('\0') }; HRESULT hr = m_pUser->GetName(NULL, 0, NULL, 0, szName, ARRAYSIZE(szName)); if (SUCCEEDED(hr)) { *pDisplayName = SysAllocString(szName); if (NULL == *pDisplayName) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaUserDisp::get_QuotaThreshold( double *pThreshold ) { LONGLONG llValue; HRESULT hr = m_pUser->GetQuotaThreshold(&llValue); if (SUCCEEDED(hr)) *pThreshold = (double)llValue; return hr; } STDMETHODIMP DiskQuotaUserDisp::put_QuotaThreshold( double Threshold ) { if (MAXLONGLONG < Threshold) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); return m_pUser->SetQuotaThreshold((LONGLONG)Threshold, TRUE); } STDMETHODIMP DiskQuotaUserDisp::get_QuotaThresholdText( BSTR *pThresholdText ) { TCHAR szValue[40]; HRESULT hr = m_pUser->GetQuotaThresholdText(szValue, ARRAYSIZE(szValue)); if (SUCCEEDED(hr)) { *pThresholdText = SysAllocString(szValue); if (NULL == *pThresholdText) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaUserDisp::get_QuotaLimit( double *pQuotaLimit ) { LONGLONG llValue; HRESULT hr = m_pUser->GetQuotaLimit(&llValue); if (SUCCEEDED(hr)) *pQuotaLimit = (double)llValue; return hr; } STDMETHODIMP DiskQuotaUserDisp::put_QuotaLimit( double Limit ) { if (MAXLONGLONG < Limit) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); return m_pUser->SetQuotaLimit((LONGLONG)Limit, TRUE); } STDMETHODIMP DiskQuotaUserDisp::get_QuotaLimitText( BSTR *pLimitText ) { TCHAR szValue[40]; HRESULT hr = m_pUser->GetQuotaLimitText(szValue, ARRAYSIZE(szValue)); if (SUCCEEDED(hr)) { *pLimitText = SysAllocString(szValue); if (NULL == *pLimitText) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaUserDisp::get_QuotaUsed( double *pUsed ) { LONGLONG llValue; HRESULT hr = m_pUser->GetQuotaUsed(&llValue); if (SUCCEEDED(hr)) *pUsed = (double)llValue; return hr; } STDMETHODIMP DiskQuotaUserDisp::get_QuotaUsedText( BSTR *pUsedText ) { TCHAR szValue[40]; HRESULT hr = m_pUser->GetQuotaUsedText(szValue, ARRAYSIZE(szValue)); if (SUCCEEDED(hr)) { *pUsedText = SysAllocString(szValue); if (NULL == *pUsedText) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaUserDisp::get_AccountStatus( AccountStatusConstants *pStatus ) { DWORD dwStatus; HRESULT hr = m_pUser->GetAccountStatus(&dwStatus); if (SUCCEEDED(hr)) { *pStatus = (AccountStatusConstants)dwStatus; } return hr; } // // Methods. // STDMETHODIMP DiskQuotaUserDisp::Invalidate( void ) { return m_pUser->Invalidate(); } #ifdef NEVER // ---------------------------------------------------------------------------- // OBSOLETE CODE // ---------------------------------------------------------------------------- // // I originally provided the GetSidString code in the IDiskQuotaUser interface. // It was useful during development but I decided that it would be // relatively useless to most users of the interface. If they want to // format the SID as a string, they can learn about it on MSDN. That's // where I got the original code (See in dskquota\common\utils.cpp). // I've left it here in case a need is ever identified. // // [brianau - 08/05/97] // /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaUser::GetSidString Description: Retrieves the account SID from the user object in character format. Arguments: pszBuffer - Address of destination buffer for account SID string. cchBuffer - Size of destination buffer in characters. Returns: NOERROR - Success. E_INVALIDARG - pszBuffer arg is NULL. ERROR_INSUFFICIENT_BUFFER (hr) - Caller's buffer is too small. ERROR_LOCK_FAILED (hr) - Failed to lock user object. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 07/23/96 Initial creation. BrianAu 12/10/96 Added class-scope user lock. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaUser::GetSidString( LPWSTR pszBuffer, DWORD cchBuffer ) { HRESULT hResult = NOERROR; if (NULL == pszBuffer) return E_INVALIDARG; if (!DiskQuotaUser::Lock()) { hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED); } else { DWORD cchSid = cchBuffer; if (!SidToString(m_pSid, pszBuffer, &cchSid)) hResult = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); DiskQuotaUser::ReleaseLock(); } return hResult; } #endif // NEVER