You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1343 lines
45 KiB
1343 lines
45 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
/* File: sidname.cpp
|
|
|
|
Description: Implements the SID-to-NAME resolver. It is anticipated that
|
|
resolving a user's SID to it's corresponding name can be a lengthy
|
|
process. We don't want the quota controller's client to experience
|
|
slow user enumerations just because it takes a long time to resolve
|
|
a name. Therefore, this object was created to perform the
|
|
SID-to-name resolutions on a background thread and notify the
|
|
client whenever a name has been resolved. That way, the client
|
|
can display a list of users then fill in the names as names
|
|
are resolved.
|
|
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/12/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.h" // PCH
|
|
#pragma hdrstop
|
|
|
|
#include "control.h"
|
|
#include "guidsp.h" // Private GUIDs.
|
|
#include "registry.h"
|
|
#include "sidname.h"
|
|
#include "sidcache.h"
|
|
|
|
//
|
|
// Verify that build is UNICODE.
|
|
//
|
|
#if !defined(UNICODE)
|
|
# error This module must be compiled UNICODE.
|
|
#endif
|
|
|
|
|
|
//
|
|
// SID/Name resolver messages (SNRM_XXXXXXXX).
|
|
//
|
|
#define SNRM_CLEAR_INPUT_QUEUE (WM_USER + 1)
|
|
#define SNRM_EXIT_THREAD (WM_USER + 2)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::SidNameResolver
|
|
|
|
Description: SidNameResolver constructor.
|
|
|
|
Arguments:
|
|
rQuotaController - Reference to quota controller that this resolver is
|
|
working for.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/12/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameResolver::SidNameResolver(
|
|
DiskQuotaControl& rQuotaController)
|
|
: m_cRef(0),
|
|
m_rQuotaController(rQuotaController),
|
|
m_hsemQueueNotEmpty(NULL),
|
|
m_hMutex(NULL),
|
|
m_dwResolverThreadId(0),
|
|
m_hResolverThread(NULL),
|
|
m_heventResolverThreadReady(NULL)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::SidNameResolver")));
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::~SidNameResolver
|
|
|
|
Description: SidNameResolver destructor.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/12/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameResolver::~SidNameResolver(void)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::~SidNameResolver")));
|
|
|
|
if (NULL != m_hsemQueueNotEmpty)
|
|
{
|
|
CloseHandle(m_hsemQueueNotEmpty);
|
|
}
|
|
if (NULL != m_hMutex)
|
|
{
|
|
CloseHandle(m_hMutex);
|
|
}
|
|
if (NULL != m_hResolverThread)
|
|
{
|
|
CloseHandle(m_hResolverThread);
|
|
}
|
|
if (NULL != m_heventResolverThreadReady)
|
|
{
|
|
CloseHandle(m_heventResolverThreadReady);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::QueryInterface
|
|
|
|
Description: Returns an interface pointer to the object's IUnknown or
|
|
ISidNameResolver interface.
|
|
Only IID_IUnknown, IID_ISidNameResolver 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:
|
|
NO_ERROR - Success.
|
|
E_NOINTERFACE - Requested interface not supported.
|
|
E_INVALIDARG - ppvOut argument was NULL.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/07/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP
|
|
SidNameResolver::QueryInterface(
|
|
REFIID riid,
|
|
LPVOID *ppvOut
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::QueryInterface")));
|
|
DBGPRINTIID(DM_RESOLVER, DL_MID, riid);
|
|
|
|
HRESULT hResult = E_NOINTERFACE;
|
|
|
|
if (NULL == ppvOut)
|
|
return E_INVALIDARG;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
if (IID_IUnknown == riid || IID_ISidNameResolver == riid)
|
|
{
|
|
*ppvOut = this;
|
|
((LPUNKNOWN)*ppvOut)->AddRef();
|
|
hResult = NOERROR;
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::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)
|
|
SidNameResolver::AddRef(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_LOW, TEXT("SidNameResolver::AddRef")));
|
|
ULONG cRef = InterlockedIncrement(&m_cRef);
|
|
DBGPRINT((DM_RESOLVER, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, cRef - 1, cRef ));
|
|
return cRef;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::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)
|
|
SidNameResolver::Release(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_LOW, TEXT("SidNameResolver::Release")));
|
|
|
|
ASSERT( 0 != m_cRef );
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
|
|
DBGPRINT((DM_RESOLVER, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, cRef + 1, cRef ));
|
|
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::Initialize
|
|
|
|
Description: Initializes a SidNameResolver object.
|
|
This function performs lot's of initialization steps so I chose to
|
|
use the "jump to label on failure" approach. It avoids a lot of
|
|
deeply nested "ifs".
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
E_FAIL - Initialization failed.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/11/96 Initial creation. BrianAu
|
|
08/14/96 Moved SID/Name cache initialization to BrianAu
|
|
FindCachedUserName() method. Only initialize cache
|
|
when it is truly needed.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP
|
|
SidNameResolver::Initialize(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::Initialize")));
|
|
HRESULT hResult = NO_ERROR;
|
|
DWORD dwThreadId = 0;
|
|
|
|
//
|
|
// Configure the user queue so it grows in chunks of 100.
|
|
//
|
|
m_UserQueue.SetSize(100);
|
|
m_UserQueue.SetGrow(100);
|
|
|
|
//
|
|
// IMPORTANT: There is code in the QuotaControl object that
|
|
// counts on the thread being created LAST in this function.
|
|
// See DiskQuotaControl::CreateEnumUsers.
|
|
// Thread creation must be the last thing performed in this
|
|
// function. The caller assumes that if this function returns
|
|
// E_FAIL, no thread was created.
|
|
//
|
|
m_hMutex = CreateMutex(NULL, FALSE, NULL);
|
|
if (NULL == m_hMutex)
|
|
goto InitFailed;
|
|
|
|
m_hsemQueueNotEmpty = CreateSemaphore(NULL, // Default security.
|
|
0, // Initially empty queue.
|
|
0x7FFFFFFF, // Max count (a lot).
|
|
NULL); // No name.
|
|
if (NULL == m_hsemQueueNotEmpty)
|
|
goto InitFailed;
|
|
|
|
m_heventResolverThreadReady = CreateEvent(NULL, // Default security.
|
|
TRUE, // Manual reset.
|
|
FALSE, // Initially non-signaled.
|
|
NULL); // No name.
|
|
if (NULL == m_heventResolverThreadReady)
|
|
goto InitFailed;
|
|
|
|
|
|
hResult = CreateResolverThread(&m_hResolverThread, &m_dwResolverThreadId);
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("Resolver thread. hThread = 0x%08X, idThread = %d"),
|
|
m_hResolverThread, m_dwResolverThreadId));
|
|
|
|
if (FAILED(hResult))
|
|
goto InitFailed;
|
|
InitFailed:
|
|
|
|
//
|
|
// Failure only returns E_FAIL.
|
|
//
|
|
if (FAILED(hResult))
|
|
hResult = E_FAIL;
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::Shutdown
|
|
|
|
Description: Commands the resolver to terminate its activities.
|
|
When the resolver's client is through with the resolver's services,
|
|
it should call Shutdown() followed by IUnknown::Release(). The function
|
|
sends a WM_QUIT message to the resolver thread.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
NO_ERROR - Success
|
|
E_FAIL - Failed to send WM_QUIT message.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/29/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP
|
|
SidNameResolver::Shutdown(
|
|
BOOL bWait
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::Shutdown")));
|
|
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("\tThread ID = %d"), m_dwResolverThreadId));
|
|
|
|
BOOL bResult = FALSE;
|
|
if (0 != m_dwResolverThreadId)
|
|
{
|
|
bResult = PostThreadMessage(m_dwResolverThreadId, WM_QUIT, 0, 0);
|
|
if (bResult && bWait && NULL != m_hResolverThread)
|
|
{
|
|
//
|
|
// Wait for thread to terminate normally.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("Resolver waiting for thread to exit...")));
|
|
WaitForSingleObject(m_hResolverThread, INFINITE);
|
|
}
|
|
}
|
|
|
|
return bResult ? NO_ERROR : E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::GetUserSid
|
|
|
|
Description: Private method that allocates a SID buffer and retrieves
|
|
the user's SID into that buffer. The caller must free the returned
|
|
buffer when done with it.
|
|
|
|
Arguments:
|
|
pUser - Pointer to user's IDiskQuotaUser interface.
|
|
|
|
ppSid - Address of a PSID variable to receive the address of the
|
|
allocated SID buffer.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
07/08/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::GetUserSid(
|
|
PDISKQUOTA_USER pUser,
|
|
PSID *ppSid
|
|
)
|
|
{
|
|
HRESULT hResult = NO_ERROR;
|
|
DWORD cbSid = 0;
|
|
|
|
DBGASSERT((NULL != pUser));
|
|
hResult = pUser->GetSidLength(&cbSid);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
PSID pUserSid = NULL;
|
|
|
|
pUserSid = (PSID) new BYTE[cbSid];
|
|
|
|
hResult = pUser->GetSid((PBYTE)pUserSid, cbSid);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
DBGASSERT((IsValidSid(pUserSid)));
|
|
DBGASSERT((NULL != ppSid));
|
|
*ppSid = pUserSid; // Return address of buffer to caller.
|
|
// Caller must free it when done.
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - GetSid failed.")));
|
|
delete[] pUserSid; // Failed to get SID. Free buffer.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - GetUserSid failed.")));
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::AddUserToResolverQueue
|
|
|
|
Description: Adds a user pointer to the resolver's input queue.
|
|
|
|
Arguments:
|
|
pUser - Address of IDiskQuotaUser ptr.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/09/96 Initial creation. BrianAu
|
|
09/03/96 Added exception handling. BrianAu
|
|
12/10/96 Removed interface marshaling. Using free-threading BrianAu
|
|
model.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::AddUserToResolverQueue(
|
|
PDISKQUOTA_USER pUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::AddUserToResolverQueue")));
|
|
DBGASSERT((NULL != pUser));
|
|
HRESULT hResult = NO_ERROR;
|
|
|
|
//
|
|
// Add user object pointer to resolver input queue.
|
|
// This can throw OutOfMemory.
|
|
//
|
|
pUser->AddRef();
|
|
try
|
|
{
|
|
m_UserQueue.Add(pUser);
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
pUser->Release();
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Increment queue's semaphore count.
|
|
// Means there's something in queue to process.
|
|
//
|
|
ReleaseSemaphore(m_hsemQueueNotEmpty, 1, NULL);
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::RemoveUserFromResolverQueue
|
|
|
|
Description: Removes a user pointer from the resolver's input queue.
|
|
|
|
Arguments:
|
|
ppUser - Address of pointer variable to receive IDiskQuotaUser ptr.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
E_UNEXPECTED - Resolver queue was empty.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
08/09/96 Initial creation. BrianAu
|
|
12/10/96 Removed interface marshaling. Using free-threading BrianAu
|
|
model.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::RemoveUserFromResolverQueue(
|
|
PDISKQUOTA_USER *ppUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::RemoveUserFromResolverQueue")));
|
|
DBGASSERT((NULL != ppUser));
|
|
HRESULT hResult = E_UNEXPECTED;
|
|
|
|
*ppUser = NULL;
|
|
|
|
if (!m_UserQueue.IsEmpty() &&
|
|
m_UserQueue.Remove(*ppUser))
|
|
{
|
|
hResult = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - Input queue unexpectedly empty.")));
|
|
}
|
|
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::PromoteUserToQueueHead
|
|
|
|
Description: Promotes a user object to the head of the resolver queue
|
|
if the user object is in the queue. This can be used to
|
|
give specific user objects higher priority if desired.
|
|
In particular, the initial requirement behind this feature
|
|
is so that user objects highlighted in the details list view
|
|
get higher name-resolution priority so that the user
|
|
(app user) feels the UI is responsive to their inputs.
|
|
|
|
Arguments:
|
|
pUser - Address of IDiskQuotaUser interface for user object.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
S_FALSE - User record not in queue.
|
|
E_OUTOFMEMORY - Insufficient memory adding item.
|
|
E_UNEXPECTED - Exception caught. User record not promoted.
|
|
E_INVALIDARG - pUser argument was NULL.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
05/18/97 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP
|
|
SidNameResolver::PromoteUserToQueueHead(
|
|
PDISKQUOTA_USER pUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::PromoteUserToQueueHead")));
|
|
HRESULT hResult = S_FALSE;
|
|
|
|
if (NULL == pUser)
|
|
return E_INVALIDARG;
|
|
|
|
m_UserQueue.Lock();
|
|
try
|
|
{
|
|
//
|
|
// Find the user in the resolver's queue.
|
|
//
|
|
INT iUser = m_UserQueue.Find(pUser);
|
|
if (-1 != iUser)
|
|
{
|
|
//
|
|
// Note we don't mess with the ref count of the
|
|
// user object. We're merely deleting a user and re
|
|
// inserting it into the queue. The queue's original
|
|
// AddRef() is retained.
|
|
//
|
|
m_UserQueue.Delete(iUser);
|
|
m_UserQueue.Add(pUser);
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
m_UserQueue.ReleaseLock();
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::ResolveSidToName
|
|
|
|
Description: Finds the name corresponding to a user's SID.
|
|
Once the name is found, it is sent to the user object for storage.
|
|
|
|
Arguments:
|
|
pUser - Pointer to user objects's IDiskQuotaUser interface.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
E_FAIL - Couldn't resolve SID to a name.
|
|
ERROR_NONE_MAPPED (hr) - No SID-to-Name mapping available.
|
|
No need to try again.
|
|
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/11/96 Initial creation. BrianAu
|
|
08/09/96 Set hResult to E_FAIL when LookupAccountSid fails. BrianAu
|
|
09/05/96 Added user domain name string. BrianAu
|
|
05/18/97 Changed to report deleted SIDs. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::ResolveSidToName(
|
|
PDISKQUOTA_USER pUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::ResolveSidToName")));
|
|
HRESULT hResult = NO_ERROR;
|
|
array_autoptr<BYTE> ptrUserSid;
|
|
|
|
DBGASSERT((NULL != pUser));
|
|
|
|
hResult = GetUserSid(pUser, (PSID *)(ptrUserSid.getaddr()));
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
SID_NAME_USE SidNameUse = SidTypeUnknown;
|
|
CString strContainer;
|
|
CString strLogonName;
|
|
CString strDisplayName;
|
|
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Calling LookupAccountBySid.")));
|
|
|
|
hResult = m_rQuotaController.m_NTDS.LookupAccountBySid(
|
|
NULL,
|
|
(PSID)(ptrUserSid.get()),
|
|
&strContainer,
|
|
&strLogonName,
|
|
&strDisplayName,
|
|
&SidNameUse);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
switch(SidNameUse)
|
|
{
|
|
case SidTypeDeletedAccount:
|
|
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_DELETED);
|
|
break;
|
|
|
|
case SidTypeInvalid:
|
|
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_INVALID);
|
|
break;
|
|
|
|
case SidTypeUnknown:
|
|
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_UNKNOWN);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
//
|
|
// Valid account.
|
|
//
|
|
SidNameCache *pSidCache;
|
|
HRESULT hr = SidNameCache_Get(&pSidCache);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Add SID/Name pair to cache.
|
|
// Indicate failure only with a debug msg. If cache
|
|
// addition fails, we'll still work OK, just slower.
|
|
// This can throw OutOfMemory.
|
|
//
|
|
hr = pSidCache->Add((PSID)(ptrUserSid.get()),
|
|
strContainer,
|
|
strLogonName,
|
|
strDisplayName);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("SIDNAME - Addition of %s\\%s failed."), strLogonName.Cstr()));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the user object's account name strings.
|
|
//
|
|
hResult = static_cast<DiskQuotaUser *>(pUser)->SetName(
|
|
strContainer,
|
|
strLogonName,
|
|
strDisplayName);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_RESOLVED);
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("SIDNAME - SetName failed in ResolveSidToName. hResult = 0x%08X"),
|
|
hResult));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Failed asynch name resolution.
|
|
//
|
|
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_UNAVAILABLE);
|
|
if (ERROR_NONE_MAPPED == GetLastError())
|
|
hResult = HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
|
|
else
|
|
hResult = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("SIDNAME - GetUserSid failed in ResolveSidToName, hResult = 0x%08X"),
|
|
hResult));
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::FindCachedUserName
|
|
|
|
Description: Accepts a user object's IDiskQuotaUser interface pointer and
|
|
looks for it's SID/Name pair in the SID/Name cache. If found, the
|
|
name is set in the user object and the function returns NO_ERROR.
|
|
|
|
Arguments:
|
|
pUser - Pointer to user objects's IDiskQuotaUser interface.
|
|
|
|
Returns:
|
|
NO_ERROR - Success. User's SID found in cache.
|
|
ERROR_FILE_NOT_FOUND (hr) - User's SID not in cache.
|
|
|
|
Exceptions: OutOfMemory
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/27/96 Initial creation. BrianAu
|
|
08/14/96 Moved initialization of SID/Name cache from BrianAu
|
|
SidNameResolver::Initialize().
|
|
09/05/96 Added user domain name string. BrianAu
|
|
09/21/96 New cache design. 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 "[email protected]".
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::FindCachedUserName(
|
|
PDISKQUOTA_USER pUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::FindCachedUserName")));
|
|
HRESULT hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // Assume not found.
|
|
|
|
PSID pUserSid = NULL;
|
|
|
|
hResult = GetUserSid(pUser, &pUserSid);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
LPTSTR pszContainer = NULL;
|
|
LPTSTR pszLogonName = NULL;
|
|
LPTSTR pszDisplayName = NULL;
|
|
|
|
try
|
|
{
|
|
//
|
|
// Can throw OutOfMemory.
|
|
//
|
|
SidNameCache *pSidCache;
|
|
hResult = SidNameCache_Get(&pSidCache);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
//
|
|
// Check cache for SID/Name pair.
|
|
// This can throw OutOfMemory.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Query cache for user 0x%08X."), pUser));
|
|
hResult = pSidCache->Lookup(pUserSid,
|
|
&pszContainer,
|
|
&pszLogonName,
|
|
&pszDisplayName);
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
//
|
|
// Name was cached. Set it in the user object and return NO_ERROR.
|
|
//
|
|
hResult = static_cast<DiskQuotaUser *>(pUser)->SetName(
|
|
pszContainer,
|
|
pszLogonName,
|
|
pszDisplayName);
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
static_cast<DiskQuotaUser *>(pUser)->SetAccountStatus(DISKQUOTA_USER_ACCOUNT_RESOLVED);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the return value so the caller knows to resolve
|
|
// the user name.
|
|
//
|
|
hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
DBGERROR((TEXT("RESOLVER - SID/Name cache not available.")));
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
|
|
delete[] pszContainer;
|
|
delete[] pszLogonName;
|
|
delete[] pszDisplayName;
|
|
delete[] pUserSid;
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::FindUserName
|
|
|
|
Description: Accepts a user object's IDiskQuotaUser interface pointer and
|
|
looks for it's SID/Name pair in the SID/Name cache. If the information
|
|
is not cached, the function calls ResolveSidToName to synchronously
|
|
determine the SID's account name. The function blocks until the name
|
|
is retrieved.
|
|
|
|
Arguments:
|
|
pUser - Pointer to user objects's IDiskQuotaUser interface.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
E_FAIL - Couldn't resolve SID to name.
|
|
ERROR_NONE_MAPPED (hr) - No SID-to-Name mapping found.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/27/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP
|
|
SidNameResolver::FindUserName(
|
|
PDISKQUOTA_USER pUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::FindUserName")));
|
|
HRESULT hResult = NO_ERROR;
|
|
|
|
DBGASSERT((NULL != pUser));
|
|
hResult = FindCachedUserName(pUser); // Can throw OutOfMemory.
|
|
if (ERROR_FILE_NOT_FOUND == HRESULT_CODE(hResult))
|
|
{
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - User 0x%08X not cached. Resolving..."),
|
|
pUser));
|
|
hResult = ResolveSidToName(pUser); // Can throw OutOfMemory.
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - User 0x%08X found in cache."), pUser));
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::FindUserNameAsync
|
|
|
|
Description: Accepts a user object's IDiskQuotaUser interface pointer and
|
|
looks for it's SID/Name pair in the SID/Name cache. If the information
|
|
is not cached, the user object is submitted to the resolver for
|
|
background processing and asynchronous client notification when the
|
|
operation is complete.
|
|
|
|
Arguments:
|
|
pUser - Pointer to user objects's IDiskQuotaUser interface.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
E_FAIL - Resolver thread not active. Can't resolve Async.
|
|
S_FALSE - User name not in cache. Submitted for background
|
|
processing. Client will be notified when name is
|
|
found and set in user object.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/11/96 Initial creation. BrianAu
|
|
06/25/96 Added SID/Name caching. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP
|
|
SidNameResolver::FindUserNameAsync(
|
|
PDISKQUOTA_USER pUser
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_MID, TEXT("SidNameResolver::FindUserNameAsync")));
|
|
HRESULT hResult = NO_ERROR;
|
|
|
|
DBGASSERT((NULL != pUser));
|
|
|
|
hResult = FindCachedUserName(pUser);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == HRESULT_CODE(hResult))
|
|
{
|
|
if (0 != m_dwResolverThreadId)
|
|
{
|
|
DBGPRINT((DM_RESOLVER, DL_MID,
|
|
TEXT("RESOLVER - User 0x%08X not cached. Submitting to Resolver."),
|
|
pUser));
|
|
|
|
//
|
|
// Name was not cached. Add the user object to the resolver's input queue
|
|
// so that the name can be located by the resolver's background thread.
|
|
//
|
|
hResult = AddUserToResolverQueue(pUser);
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - Thread not active. Can't resolve user 0x%08X async."),
|
|
pUser));
|
|
hResult = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - User 0x%08X found in cache."),
|
|
pUser));
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::ClearInputQueue.
|
|
|
|
Description: Called by a SidNameResolver thread after the thread receives
|
|
a WM_QUIT message. This function removes all user object pointers
|
|
from the input queue before the thread exists.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
NO_ERROR - Always returns NO_ERROR.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/18/96 Initial creation. BrianAu
|
|
12/10/96 Moved Semaphore reduction from method BrianAu
|
|
HandleShutdownMessages then deleted that method.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::ClearInputQueue(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::ClearInputQueue")));
|
|
PDISKQUOTA_USER pUser = NULL;
|
|
|
|
//
|
|
// Decrement the queue-not-empty semaphore to 0 so that the thread
|
|
// doesn't try to remove any more queue entries.
|
|
// Set the resolver's thread ID to 0 so that FindNameAsync will not
|
|
// submit any more users to the resolver.
|
|
//
|
|
m_dwResolverThreadId = 0;
|
|
while(WAIT_OBJECT_0 == WaitForSingleObject(m_hsemQueueNotEmpty, 0))
|
|
NULL;
|
|
//
|
|
// Remove all remaining items from input queue
|
|
// Remove will return E_FAIL if list is empty.
|
|
//
|
|
m_UserQueue.Lock();
|
|
while(m_UserQueue.Count() > 0)
|
|
{
|
|
HRESULT hResult = RemoveUserFromResolverQueue(&pUser);
|
|
if (SUCCEEDED(hResult) && NULL != pUser)
|
|
{
|
|
pUser->Release(); // Release user object.
|
|
}
|
|
}
|
|
m_UserQueue.ReleaseLock();
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::ThreadOnQueueNotEmpty
|
|
|
|
Description: Called by a SidNameResolver thread when the resolver's input
|
|
queue is not empty. This function removes the next entry
|
|
from the queue, resolves the user's SID to its name, sets the name
|
|
in the user object and notifies the client of the name change.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
NO_ERROR - Always returns NO_ERROR.
|
|
|
|
Exceptions: OutOfMemory
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/18/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::ThreadOnQueueNotEmpty(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_LOW, TEXT("SidNameResolver::ThreadOnQueueNotEmpty")));
|
|
HRESULT hResult = NO_ERROR;
|
|
PDISKQUOTA_USER pUser = NULL;
|
|
LPSTREAM pstm = NULL;
|
|
|
|
//
|
|
// Remove item from queue
|
|
// RemoveFirst() will return E_FAIL if list is empty.
|
|
//
|
|
try
|
|
{
|
|
hResult = RemoveUserFromResolverQueue(&pUser);
|
|
if (SUCCEEDED(hResult) && NULL != pUser)
|
|
{
|
|
ResolveSidToName(pUser);
|
|
|
|
//
|
|
// If successful or not, notify client event sink.
|
|
// Even if we couldn't resolve the name, the object's account
|
|
// status has changed. The client will want to respond to this.
|
|
//
|
|
// Don't bother with return value. We don't care if it fails.
|
|
// The client will but we don't.
|
|
//
|
|
m_rQuotaController.NotifyUserNameChanged(pUser);
|
|
pUser->Release(); // Release pointer obtained from resolver queue.
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - Error removing stream ptr from input queue.")));
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
if (NULL != pUser)
|
|
pUser->Release();
|
|
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::ThreadProc
|
|
|
|
Description: This thread procedure sits in a loop handling events and
|
|
thread messages.
|
|
|
|
Conditions that cause the thread to exit.
|
|
|
|
1. OleInitalize() fails.
|
|
2. Thread receives a WM_QUIT message.
|
|
3. Wait function failure or timeout.
|
|
|
|
Arguments:
|
|
pvParam - "this" pointer for the SidNameResolver object.
|
|
Required since ThreadProc must be static to be a THREADPROC.
|
|
|
|
Returns:
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/11/96 Initial creation. BrianAu
|
|
03/22/00 Changed dwParam for ia64 compat. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameResolver::ThreadProc(
|
|
LPVOID pvParam
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::ThreadProc")));
|
|
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("\tThreadID = %d"), GetCurrentThreadId()));
|
|
SidNameResolver *pThis = (SidNameResolver *)pvParam;
|
|
BOOL bExitThread = FALSE;
|
|
|
|
//
|
|
// Make sure DLL stays loaded while this thread is active.
|
|
//
|
|
InterlockedIncrement(&g_cRefThisDll);
|
|
|
|
//
|
|
// Must call CoInitializeEx() for each thread.
|
|
//
|
|
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
|
|
{
|
|
BOOL bReadyToReceiveMsgs = FALSE;
|
|
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER: Thread %d entering msg loop."), GetCurrentThreadId()));
|
|
while(!bExitThread)
|
|
{
|
|
MSG msg;
|
|
try
|
|
{
|
|
//
|
|
// Allow blocked thread to respond to sent messages.
|
|
//
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && !bExitThread)
|
|
{
|
|
if ( WM_QUIT != msg.message )
|
|
{
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER: Thread %d dispatching msg %d"),
|
|
GetCurrentThreadId(), msg.message));
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Rcvd WM_QUIT. Clear the resolver's input queue and
|
|
// exit the msg loop.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER: Thread %d received WM_QUIT"),
|
|
GetCurrentThreadId()));
|
|
pThis->ClearInputQueue();
|
|
bExitThread = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!bExitThread)
|
|
{
|
|
DWORD dwWaitResult = 0;
|
|
|
|
if (!bReadyToReceiveMsgs)
|
|
{
|
|
//
|
|
// Tell the thread creation function that it can
|
|
// now return. The thread is ready to accept messages.
|
|
//
|
|
SetEvent(pThis->m_heventResolverThreadReady);
|
|
bReadyToReceiveMsgs = TRUE;
|
|
}
|
|
|
|
//
|
|
// Sleep if the queue is empty.
|
|
// Wake up on queue-not-empty or any thread messages.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Thread %d waiting for messages..."),
|
|
GetCurrentThreadId()));
|
|
dwWaitResult = MsgWaitForMultipleObjects(
|
|
1,
|
|
&(pThis->m_hsemQueueNotEmpty),
|
|
FALSE,
|
|
INFINITE,
|
|
QS_ALLINPUT);
|
|
|
|
switch(dwWaitResult)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
//
|
|
// Have data in input queue. Process one user.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Something added to input queue.")));
|
|
pThis->ThreadOnQueueNotEmpty();
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
//
|
|
// Received input message(s).
|
|
// Loop back around and handle them.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Thread %d rcvd message(s)."),
|
|
GetCurrentThreadId()));
|
|
break;
|
|
|
|
case WAIT_FAILED:
|
|
case WAIT_TIMEOUT:
|
|
default:
|
|
//
|
|
// Something bad happened.
|
|
//
|
|
DBGPRINT((DM_RESOLVER, DL_MID, TEXT("RESOLVER - Thread %d wait failed."),
|
|
GetCurrentThreadId()));
|
|
|
|
DBGASSERT((FALSE));
|
|
bExitThread = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - Out of memory")));
|
|
bExitThread = TRUE;
|
|
}
|
|
}
|
|
CoUninitialize();
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("RESOLVER - OleInitialize failed for thread %d."),
|
|
GetCurrentThreadId()));
|
|
}
|
|
|
|
DBGPRINT((DM_RESOLVER, DL_HIGH, TEXT("RESOLVER - Exit thread %d"), GetCurrentThreadId()));
|
|
|
|
pThis->m_dwResolverThreadId = 0;
|
|
|
|
ASSERT( 0 != g_cRefThisDll );
|
|
InterlockedDecrement(&g_cRefThisDll);
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameResolver::CreateResolverThread
|
|
|
|
Description: Creates a thread that will process user objects and resolve
|
|
their SIDs to account names.
|
|
|
|
Arguments:
|
|
phThread [optional] - Address of handle variable to receive handle of
|
|
new thread. Can be NULL.
|
|
|
|
pdwThreadId [optional] - Address of DWORD to receive thread ID.
|
|
Can be NULL.
|
|
|
|
Returns:
|
|
NO_ERROR - Started thread.
|
|
E_FAIL - Couldn't start thread.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
06/27/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameResolver::CreateResolverThread(
|
|
PHANDLE phThread,
|
|
LPDWORD pdwThreadId
|
|
)
|
|
{
|
|
DBGTRACE((DM_RESOLVER, DL_HIGH, TEXT("SidNameResolver::CreateResolverThread")));
|
|
HRESULT hResult = NO_ERROR;
|
|
DWORD dwThreadId = 0;
|
|
HANDLE hThread = NULL;
|
|
|
|
//
|
|
// Launch new thread.
|
|
//
|
|
hThread = CreateThread(NULL, // No security attributes.
|
|
0, // Default stack size.
|
|
&ThreadProc,
|
|
this, // Static thread proc needs this.
|
|
0, // Not suspended.
|
|
&dwThreadId);
|
|
if (NULL != hThread)
|
|
{
|
|
if (NULL != phThread)
|
|
*phThread = hThread; // Caller want's handle value.
|
|
else
|
|
CloseHandle(hThread); // Caller doesn't want it.
|
|
|
|
if (NULL != pdwThreadId)
|
|
*pdwThreadId = dwThreadId; // Caller want's thread ID.
|
|
|
|
//
|
|
// Wait here until the thread is ready to receive thread messages.
|
|
// This event is set in ThreadProc.
|
|
//
|
|
WaitForSingleObject(m_heventResolverThreadReady, INFINITE);
|
|
}
|
|
else
|
|
{
|
|
hResult = E_FAIL;
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|