Leaked source code of windows server 2003
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.
 
 
 
 
 
 

981 lines
31 KiB

//-----------------------------------------------------------------------------
//
//
// File: aqutil.cpp
//
// Description:
//
// Author: Mike Swafford (MikeSwa)
//
// History:
// 7/20/98 - MikeSwa Created
//
// Copyright (C) 1998 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include "aqprecmp.h"
#include "aqutil.h"
//---[ HrIncrementIMailMsgUsageCount ]-------------------------------------------
//
//
// Description:
// Calls IMailMsgQueueMgmt::AddUsage. Handles calling QueryInterface
// for the right interface
// Parameters:
// pIUnknown - ptr to IUknown for MailMsg
// Returns:
// S_OK on success
// History:
// 7/20/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrIncrementIMailMsgUsageCount(IUnknown *pIUnknown)
{
TraceFunctEnterEx((LPARAM) pIUnknown, "HrIncrementIMailMsgUsageCount");
HRESULT hr = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
_ASSERT(pIUnknown);
hr = pIUnknown->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
if (FAILED(hr))
goto Exit;
hr = pIMailMsgQueueMgmt->AddUsage();
if (FAILED(hr))
goto Exit;
Exit:
if (pIMailMsgQueueMgmt)
pIMailMsgQueueMgmt->Release();
TraceFunctLeave();
return hr;
}
//---[ HrReleaseIMailMsgUsageCount ]-------------------------------------------
//
//
// Description:
// Calls IMailMsgQueueMgmt::ReleaseUsage. Handles calling QueryInterface
// for the right interface
// Parameters:
// pIUnknown - ptr to IUknown for MailMsg
// Returns:
// S_OK on success
// History:
// 7/20/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrReleaseIMailMsgUsageCount(IUnknown *pIUnknown)
{
TraceFunctEnterEx((LPARAM) pIUnknown, "HrReleaseIMailMsgUsageCount");
HRESULT hr = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
_ASSERT(pIUnknown);
hr = pIUnknown->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
if (FAILED(hr))
goto Exit;
hr = pIMailMsgQueueMgmt->ReleaseUsage();
if (FAILED(hr))
goto Exit;
Exit:
if (pIMailMsgQueueMgmt)
pIMailMsgQueueMgmt->Release();
TraceFunctLeave();
return hr;
}
//---[ HrDeleteIMailMsg ]------------------------------------------------------
//
//
// Description:
// Deletes a Msg and releases its usage count
// Parameters:
// pIUnknown Ptr to mailmsg
// Returns:
// S_OK on success
// History:
// 7/21/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrDeleteIMailMsg(IUnknown *pIUnknown)
{
TraceFunctEnterEx((LPARAM) pIUnknown, "HrDeleteIMailMsg");
HRESULT hr = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
_ASSERT(pIUnknown);
hr = pIUnknown->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
if (FAILED(hr))
goto Exit;
hr = pIMailMsgQueueMgmt->Delete(NULL);
if (FAILED(hr))
goto Exit;
Exit:
if (pIMailMsgQueueMgmt)
pIMailMsgQueueMgmt->Release();
TraceFunctLeave();
return hr;
}
//---[ HrWalkMailMsgQueueForShutdown ]------------------------------------------
//
//
// Description:
// Function to walk an IMailMsg queue at shutdown and clear out all of the
// IMailMsgs
// Parameters:
// IN pIMailMsgProperties //ptr to data on queue
// IN PVOID pvContext //list of queues to prepare for DSN
// OUT BOOL *pfContinue, //TRUE if we should continue
// OUT BOOL *pfDelete); //TRUE if item should be deleted
// Returns:
// S_OK
// History:
// 7/20/98 - MikeSwa Created
// 7/7/99 - Added async shutdown
//-----------------------------------------------------------------------------
HRESULT HrWalkMailMsgQueueForShutdown(IN IMailMsgProperties *pIMailMsgProperties,
IN PVOID pvContext, OUT BOOL *pfContinue,
OUT BOOL *pfDelete)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrWalkMailMsgQueueForShutdown");
Assert(pfContinue);
Assert(pfDelete);
HRESULT hrTmp = S_OK;
CAQSvrInst *paqinst = (CAQSvrInst *) pvContext;
_ASSERT(pIMailMsgProperties);
_ASSERT(paqinst);
*pfContinue = TRUE;
*pfDelete = TRUE;
//call server stop hint function
paqinst->ServerStopHintFunction();
//Add to queue so async thread will have final release and the associated I/O
pIMailMsgProperties->AddRef();
paqinst->HrQueueWorkItem(pIMailMsgProperties, fMailMsgShutdownCompletion);
TraceFunctLeave();
return S_OK;
}
//---[ fMailMsgShutdownCompletion ]---------------------------------------------
//
//
// Description:
// CAsyncWorkQueue completion function to allow multi-threaded shutdown
// of a MailMsgQueue
// Parameters:
// IN pvContext - A mailmsg to release
// IN dwStatus - The status passed in by the async work queue
// Returns:
// TRUE always
// History:
// 7/7/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fMailMsgShutdownCompletion(PVOID pvContext, DWORD dwStatus)
{
IMailMsgProperties *pIMailMsgProperties = (IMailMsgProperties *) pvContext;
HRESULT hr = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
_ASSERT(pIMailMsgProperties);
if (!pIMailMsgProperties)
goto Exit;
//Bounce the usage count to force this thread to do any necessary commits
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt,
(PVOID *) &pIMailMsgQueueMgmt);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
if (FAILED(hr))
goto Exit;
hr = pIMailMsgQueueMgmt->ReleaseUsage();
if (FAILED(hr))
goto Exit;
hr = pIMailMsgQueueMgmt->AddUsage();
if (FAILED(hr))
goto Exit;
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
if (pIMailMsgQueueMgmt)
pIMailMsgQueueMgmt->Release();
return TRUE;
}
//---[ HrWalkMsgRefQueueForShutdown ]--------------------------------
//
//
// Description:
// Function to walk a queue containing msgrefs at shutdown and
// clear out all of the IMailMsgs
// Parameters:
// IN CMsgRef pmsgref, //ptr to data on queue
// IN PVOID pvContext //list of queues to prepare for DSN
// OUT BOOL *pfContinue, //TRUE if we should continue
// OUT BOOL *pfDelete); //TRUE if item should be deleted
// Returns:
// S_OK - *always*
// History:
// 7/20/98 - MikeSwa Created
// 7/7/99 - MikeSwa Added async shutdown
//-----------------------------------------------------------------------------
HRESULT HrWalkMsgRefQueueForShutdown(IN CMsgRef *pmsgref,
IN PVOID pvContext, OUT BOOL *pfContinue,
OUT BOOL *pfDelete)
{
TraceFunctEnterEx((LPARAM) pmsgref, "HrWalkMsgRefQueueForShutdown");
Assert(pfContinue);
Assert(pfDelete);
CAQSvrInst *paqinst = (CAQSvrInst *) pvContext;
_ASSERT(pmsgref);
_ASSERT(paqinst);
*pfContinue = TRUE;
*pfDelete = TRUE;
//call server stop hint function
if (paqinst)
paqinst->ServerStopHintFunction();
//Add to queue so async thread will have final release and the associated I/O
pmsgref->AddRef();
paqinst->HrQueueWorkItem(pmsgref, fMsgRefShutdownCompletion);
TraceFunctLeave();
return S_OK;
}
//---[ fMsgRefShutdownCompletion ]---------------------------------------------
//
//
// Description:
// CAsyncWorkQueue completion function to allow multi-threaded shutdown
// of a MailMsgQueue
// Parameters:
// IN pvContext - A mailmsg to release
// IN dwStatus - The status passed in by the async work queue
// Returns:
// TRUE always
// History:
// 7/7/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fMsgRefShutdownCompletion(PVOID pvContext, DWORD dwStatus)
{
CMsgRef *pmsgref = (CMsgRef *) pvContext;
_ASSERT(pmsgref);
if (!pmsgref)
return TRUE;
//Call prepare to shutdown to force async threads to be the ones
//doing that actual IO
pmsgref->PrepareForShutdown();
pmsgref->Release();
return TRUE;
}
//---[ CalcDMTPerfCountersIteratorFn ]-----------------------------------------
//
//
// Description:
// Iterator function used to walk the DMT and generate the perf counters
// we are interested in.
// Parameters:
// IN pvContext - pointer to context (current total of pending msgs)
// IN pvData - CDomainEntry for the given domain
// IN fWildcardData - TRUE if data is a wildcard entry (ignored)
// OUT pfContinue - TRUE if iterator should continue to the next entry
// OUT pfDelete - TRUE if entry should be deleted
// Returns:
// -
// History:
// 7/29/98 - MikeSwa Created
// 7/31/98 - MikeSwa Modified (Added link state counters)
// 9/22/98 - MikeSwa Changed from domain flags to link flags
//
// Note:
// Currently the status is stored on the domain entry (and not the link).
// At some point we will have to differentiate this, and add flags to
// the link. In this funciton the cLinkCount variable is a temporary
// workaround.
//-----------------------------------------------------------------------------
VOID CalcDMTPerfCountersIteratorFn(PVOID pvContext, PVOID pvData,
BOOL fWildcard, BOOL *pfContinue,
BOOL *pfDelete)
{
TraceFunctEnterEx((LPARAM) pvData, "CalcMsgsPendingRetryIteratorFn");
CDomainEntry *pdentry = (CDomainEntry *) pvData;
AQPerfCounters *pAQPerfCounters = (AQPerfCounters *) pvContext;
CLinkMsgQueue *plmq = NULL;
CDomainEntryLinkIterator delit;
DWORD cRetryMsgsOnCurrentLink = 0;
DWORD dwLinkStateFlags = 0;
HRESULT hr = S_OK;
BOOL fLinkEnabled = TRUE;
_ASSERT(pvContext);
_ASSERT(pvData);
_ASSERT(pfContinue);
_ASSERT(pfDelete);
_ASSERT(sizeof(AQPerfCounters) == pAQPerfCounters->cbVersion);
//Always continue, and never delete
*pfContinue = TRUE;
*pfDelete = FALSE;
hr = delit.HrInitialize(pdentry);
if (FAILED(hr))
goto Exit;
while (plmq = delit.plmqGetNextLinkMsgQueue(plmq))
{
dwLinkStateFlags = plmq->dwGetLinkState();
fLinkEnabled = TRUE;
// msgs on the retry queue should be added to remote retry queue counter
cRetryMsgsOnCurrentLink = plmq->cGetRetryMsgCount();
if (!(LINK_STATE_RETRY_ENABLED & dwLinkStateFlags))
{
//Link is pending retry
fLinkEnabled = FALSE;
// also add #of msgs NOT on retry queue
cRetryMsgsOnCurrentLink += plmq->cGetTotalMsgCount();
}
pAQPerfCounters->cCurrentMsgsPendingRemoteRetry += cRetryMsgsOnCurrentLink;
if (!(LINK_STATE_SCHED_ENABLED & dwLinkStateFlags))
{
//Link is pending a scheduled connection
fLinkEnabled = FALSE;
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingScheduling++;
}
if (LINK_STATE_PRIV_CONFIG_TURN_ETRN & dwLinkStateFlags)
{
//Link is a TURN/ETRN link
if (!((LINK_STATE_PRIV_ETRN_ENABLED | LINK_STATE_PRIV_TURN_ENABLED)
& dwLinkStateFlags))
fLinkEnabled = FALSE; //link is not currently being serviced
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingTURNETRN++;
}
if (LINK_STATE_ADMIN_HALT & dwLinkStateFlags)
{
//Link is currently frozen by admin
fLinkEnabled = FALSE;
pAQPerfCounters->cCurrentRemoteNextHopLinksFrozenByAdmin++;
}
if (fLinkEnabled)
{
//There are no flags set that indicate this link is not enabled
pAQPerfCounters->cCurrentRemoteNextHopLinksEnabled++;
}
}
Exit:
if (plmq)
plmq->Release();
TraceFunctLeave();
}
//---[ dwInterlockedSetBits ]--------------------------------------------------
//
//
// Description:
// Set bits in a DWORD in a thread sate manner
// Parameters:
// IN pdwTarget Ptr to DWORD to modify
// IN dwFlagMask bits to set
// Returns:
// Original value
// History:
// 8/3/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD dwInterlockedSetBits(DWORD *pdwTarget, DWORD dwFlagMask)
{
DWORD dwChk;
DWORD dwTmp;
_ASSERT(pdwTarget);
do
{
dwChk = *pdwTarget;
dwTmp = dwChk | dwFlagMask;
if (dwChk == dwTmp) //no work to be done
break;
} while (InterlockedCompareExchange((PLONG) pdwTarget,
(LONG) dwTmp,
(LONG) dwChk) != (LONG) dwChk);
return dwChk;
}
//---[ dwInterlockedUnsetBits ]------------------------------------------------
//
//
// Description:
// Unset bits in a DWORD in a thread sate manner
// Parameters:
// IN pdwTarget Ptr to DWORD to modify
// IN dwFlagMask bits to unset
// Returns:
// Original value
// History:
// 8/3/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD dwInterlockedUnsetBits(DWORD *pdwTarget, DWORD dwFlagMask)
{
DWORD dwChk;
DWORD dwTmp;
_ASSERT(pdwTarget);
do
{
dwChk = *pdwTarget;
dwTmp = dwChk & ~dwFlagMask;
if (dwChk == dwTmp) //no work to be done
break;
} while (InterlockedCompareExchange((PLONG) pdwTarget,
(LONG) dwTmp,
(LONG) dwChk) != (LONG) dwChk);
return dwChk;
}
//---[ HrWalkPreLocalQueueForDSN ]---------------------------------------------
//
//
// Description:
// Function to walk the pre-local delivery queue for DSN generation
// Parameters:
// IN CMsgRef pmsgref ptr to data on queue
// IN PVOID pvContext ptr to CAQSvrInst
// OUT BOOL *pfContinue TRUE if we should continue
// OUT BOOL *pfDelete TRUE if item should be deleted
// Returns:
// S_OK on success
// History:
// 8/14/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrWalkPreLocalQueueForDSN(IN CMsgRef *pmsgref, IN PVOID pvContext,
OUT BOOL *pfContinue, OUT BOOL *pfDelete)
{
TraceFunctEnterEx((LPARAM) pmsgref, "HrWalkPreLocalQueueForDSN");
HRESULT hr = S_OK;
DWORD dwDSNFlags = 0;
CLinkMsgQueue *plmq = NULL;
BOOL fShutdownLock = FALSE;
CAQStats aqstats;
CAQSvrInst *paqinst = (CAQSvrInst *)pvContext;
_ASSERT(pfContinue);
_ASSERT(pfDelete);
_ASSERT(paqinst);
*pfContinue = TRUE; //always keep walking queue
*pfDelete = FALSE; //keep message in queue unless we NDR it
if (!paqinst)
goto Exit;
if (!paqinst->fTryShutdownLock())
{
//If we got a shutdown hint...we should bail
*pfContinue = FALSE;
goto Exit;
}
fShutdownLock = TRUE;
hr = pmsgref->HrSendDelayOrNDR(CMsgRef::MSGREF_DSN_LOCAL_QUEUE |
CMsgRef::MSGREF_DSN_SEND_DELAY,
NULL, AQUEUE_E_MSG_EXPIRED, &dwDSNFlags);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pmsgref, "ERROR: HrSendDelayOrNDR failed - hr 0x%08X", hr);
goto Exit;
}
//We need to remove this message from the queue
if ((CMsgRef::MSGREF_DSN_SENT_NDR | CMsgRef::MSGREF_HANDLED) & dwDSNFlags)
{
*pfDelete = TRUE;
//Update relevant counters
paqinst->DecPendingLocal();
//
// Get local link and update stats
//
plmq = paqinst->pdmtGetDMT()->plmqGetLocalLink();
if (plmq)
{
pmsgref->GetStatsForMsg(&aqstats);
plmq->HrNotify(&aqstats, FALSE);
}
}
Exit:
if (fShutdownLock)
paqinst->ShutdownUnlock();
if (plmq)
plmq->Release();
TraceFunctLeave();
return hr;
}
//---[ HrReGetMessageType ]-----------------------------------------------------
//
//
// Description:
// Regets the message type after a retry failure
// Parameters:
// IN pIMailMsgProperties Message we are interested in
// IN pIMessageRouter Router for that message
// IN OUT pdwMessageType Old/New message type for the message
// Returns:
// S_OK on success
// Passes through errors from ReleaseMessageType & GetMessageType
// History:
// 9/14/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrReGetMessageType(IN IMailMsgProperties *pIMailMsgProperties,
IN IMessageRouter *pIMessageRouter,
IN OUT DWORD *pdwMessageType)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrReGetMessageType");
HRESULT hr = S_OK;
//$$REVIEW - we might not have to get a new message type here... we
//might only need it on a specific error code returned by HrInitialize.
//Get New Messagetype... in case that changed
hr = pIMessageRouter->ReleaseMessageType(*pdwMessageType, 1);
if (FAILED(hr))
{
_ASSERT(SUCCEEDED(hr) && "ReleaseMessageType failed... may leak message types");
ErrorTrace((LPARAM) pIMailMsgProperties,
"ERROR: ReleaseMessageType failed! - hr 0x%08X", hr);
hr = S_OK; //we are about to retry anyway
}
hr = pIMessageRouter->GetMessageType(pIMailMsgProperties, pdwMessageType);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"ERROR: Unable to re-get message type - HR 0x%08X", hr);
goto Exit; //we cannot recover from this
}
Exit:
TraceFunctLeave();
return hr;
}
//Used to guarantee uniqueue files names
DWORD g_cUniqueueFileNames = 0;
//---[ GetUniqueFileName ]-----------------------------------------------------
//
//
// Description:
// Creates a uniqueue file name
// Parameters:
// pft Ptr to current filetime
// szFileBuffer Buffer to put string into... should be
// at least UNIQUEUE_FILENAME_BUFFER_SIZE
// szExtension Extension for file name... if longer than three chars,
// you will need to increase the size of szFileBuffer
// accordingly.
// Returns:
// -
// History:
// 10/9/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void GetUniqueFileName(IN FILETIME *pft, IN LPSTR szFileBuffer,
IN LPSTR szExtension)
{
DWORD cbFileNameSize = 0;
DWORD cUnique = InterlockedIncrement((PLONG) &g_cUniqueueFileNames);
SYSTEMTIME systime;
_ASSERT(szFileBuffer);
_ASSERT(szExtension);
FileTimeToSystemTime(pft, &systime);
cbFileNameSize = wsprintf(
szFileBuffer,
"%05.5x%02.2d%02.2d%02.2d%02.2d%02.2d%04.4d%08X.%s",
systime.wMilliseconds,
systime.wSecond, systime.wMinute, systime.wHour,
systime.wDay, systime.wMonth, systime.wYear,
cUnique, szExtension);
//Assert that are constant is big enough
//By default... allow room for ".eml" extension
_ASSERT((cbFileNameSize + 4 - lstrlen(szExtension)) < UNIQUEUE_FILENAME_BUFFER_SIZE);
}
//---[ HrLinkAllDomains ]-------------------------------------------------------
//
//
// Description:
// Ultility function to link all domains together for recipient enumeration
// (primarly used to NDR an entire message).
//
// Parameters:
// pIMailMsgProperties IMailMsgProperties to link domains together for
// Returns:
// S_OK on success
// History:
// 10/14/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrLinkAllDomains(IN IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrLinkAllDomains");
HRESULT hr = S_OK;
DWORD cDomains = 0;
DWORD iCurrentDomain = 1;
IMailMsgRecipients *pIMailMsgRecipients = NULL;
_ASSERT(pIMailMsgProperties);
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
(void **) &pIMailMsgRecipients);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgRecipients failed");
if (FAILED(hr))
goto Exit;
hr = pIMailMsgRecipients->DomainCount(&cDomains);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: Unable to get DomainCount - hr 0x%08X", hr);
goto Exit;
}
//Set up domain list for all domains
for (iCurrentDomain = 1; iCurrentDomain < cDomains; iCurrentDomain++)
{
hr = pIMailMsgRecipients->SetNextDomain(iCurrentDomain-1, iCurrentDomain,
FLAG_OVERWRITE_EXISTING_LINKS);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: SetNextDomain Failed - hr 0x%08X", hr);
goto Exit;
}
}
//handle single domain case
if (1 == cDomains)
{
hr = pIMailMsgRecipients->SetNextDomain(0, 0, FLAG_SET_FIRST_DOMAIN);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: SetNextDomain Failed for single domain- hr 0x%08X", hr);
goto Exit;
}
}
Exit:
if (pIMailMsgRecipients)
pIMailMsgRecipients->Release();
TraceFunctLeave();
return hr;
}
//Parses a GUID from a string... returns TRUE on success
//---[ fAQParseGuidString ]----------------------------------------------------
//
//
// Description:
// Attempts to parse a GUID from a string of hex digits..
// Can handle punctuation, spaces and even leading "0x"'s.
// Parameters:
// IN szGuid String to parse GUID from
// IN cbGuid Max size of GUID string buffer
// OUT guidID GUID parsed from string
// Returns:
// TRUE if a guid value could be parsed from the string
// FALSE if a guid value could not be parsed from the string
// History:
// 10/15/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fAQParseGuidString(LPSTR szGuid, DWORD cbGuid, GUID *pguid)
{
const DWORD NO_SUCH_VALUE = 0xFF;
BOOL fParsed = FALSE;
BOOL fLastCharZero = FALSE; //Used to handle "0x"
DWORD *pdwGuid = (DWORD *) pguid;
DWORD cDigits = 0;
DWORD dwValue = NO_SUCH_VALUE;
LPSTR szCurrent = szGuid;
LPSTR szStop = szGuid + cbGuid/sizeof(CHAR);
//Use DWORD array to populate GUID
*pdwGuid = 0;
while ((szStop > szCurrent) && (*szCurrent))
{
dwValue = NO_SUCH_VALUE;
if (('0' <= *szCurrent) && ('9' >= *szCurrent))
dwValue = *szCurrent-'0';
else if (('a' <= *szCurrent) && ('f' >= *szCurrent))
dwValue = 10 + *szCurrent-'a';
else if (('A' <= *szCurrent) && ('F' >= *szCurrent))
dwValue = 10 + *szCurrent-'A';
else if (fLastCharZero &&
(('x' == *szCurrent) || ('X' == *szCurrent)))
{
//back out last shift (we don't have to subtract anything, since
//the value was zero).
_ASSERT(cDigits);
if (0 == (cDigits % 8)) //happened when we changed DWORDs
pdwGuid--;
else
*pdwGuid /= 16; //undo last shift
cDigits--;
}
//Set flag for handling 0x sequence
if (0 != dwValue)
fLastCharZero = FALSE;
else
fLastCharZero = TRUE;
//In all string guid representations... a valid hex number is at least
//2 characters long... so 0x0 should be mapped to 0x00 and 0xa should
//be mapped to 0x0a. Check and see if such a situation is happening
if ((NO_SUCH_VALUE == dwValue) && (0 != (cDigits % 2)) &&
(',' == *szCurrent))
{
//undo last add and shift. The next if clause will re-write
//the last value in at the proper point
*pdwGuid /= 16;
dwValue = *pdwGuid & 0x0000000F;
*pdwGuid &= 0xFFFFFFF0;
*pdwGuid *= 16;
}
//Add value to GUID if hex character
if (NO_SUCH_VALUE != dwValue)
{
*pdwGuid += dwValue;
if (0 == (++cDigits % 8))
{
//We have reached a DWORD boundary... move on
if (32 == cDigits)
{
//quit when we have enough
fParsed = TRUE;
break;
}
pdwGuid++;
*pdwGuid = 0;
}
else
*pdwGuid *= 16;
}
szCurrent++;
}
//Handle ending 0xa (should be 0x0a) digits
if (!fParsed && (31 == cDigits))
{
dwValue = *pdwGuid & 0x000000FF;
_ASSERT(!(dwValue & 0x0000000F));
*pdwGuid &= 0xFFFFFF00;
dwValue /= 16;
*pdwGuid += dwValue;
fParsed = TRUE;
}
return fParsed;
}
//---[ InterlockedAddSubtractULARGE ]------------------------------------------
//
//
// Description:
// Performs "interlocked" Add/Subtract on ULARGE_INTEGER structures.
//
// Uses s_slUtilityData to synchronize if neccessary.
// Parameters:
// IN puliValue ULARGE to modify
// IN puliNew ULARGE to modify value with
// IN fAdd TRUE if we are adding new value
// FALSE if we are subtracting
// Returns:
// -
// History:
// 11/2/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void InterlockedAddSubtractULARGE(ULARGE_INTEGER *puliValue,
ULARGE_INTEGER *puliNew, BOOL fAdd)
{
_ASSERT(puliValue);
_ASSERT(puliNew);
ULARGE_INTEGER uliTmp = {0};
BOOL fDone = FALSE;
DWORD dwTmp = 0;
DWORD dwHighPart = 0;
DWORD dwLowPart = 0;
static CShareLockNH s_slUtilityData; //Used to synchronize global updates of ULONG
s_slUtilityData.ShareLock();
BOOL fShareLock = TRUE; //FALSE implies Exclusive lock
while (!fDone)
{
uliTmp.QuadPart = puliValue->QuadPart;
dwHighPart = uliTmp.HighPart;
dwLowPart = uliTmp.LowPart;
if (fAdd)
uliTmp.QuadPart += puliNew->QuadPart; //add volume
else
uliTmp.QuadPart -= puliNew->QuadPart;
//First see of the high part needs updating
if (dwHighPart != uliTmp.HighPart)
{
if (fShareLock)
{
//This only happens every 4GB of data per queue..
//which means we shouldn't be hitting this lock that
//often
s_slUtilityData.ShareUnlock();
s_slUtilityData.ExclusiveLock();
fShareLock = FALSE;
//Go back to top of loop and re-get data
continue;
}
//At this point it is just safe for us to update the values
puliValue->QuadPart = uliTmp.QuadPart;
}
else if (dwLowPart != uliTmp.LowPart)
{
//Only need to update the low DWORD
dwTmp = (DWORD) InterlockedCompareExchange(
(PLONG) &(puliValue->LowPart),
(LONG) uliTmp.LowPart,
(LONG) dwLowPart);
if (dwLowPart != dwTmp)
continue; //update failed
}
fDone = TRUE;
}
if (fShareLock)
s_slUtilityData.ShareUnlock();
else
s_slUtilityData.ExclusiveUnlock();
}
//---[ HrValidateMessageContent ]----------------------------------------------
//
//
// Description:
// Validates a message based on its content handle. If the backing store
// has been deleted, and the handle is not cached, we should detect this.
// Parameters:
// pIMailMsgProperties - MailMsg to validate
// Returns:
// HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) Message belongs to this store
// driver but is no longer valid
// other error code from store driver interface or mailmsg
// History:
// 4/13/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrValidateMessageContent(IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrValidateMessageContent");
IMailMsgBind *pBindInterface = NULL;
PFIO_CONTEXT pIMsgFileHandle = NULL;
HRESULT hr = S_OK;
//
// Attempt to query interface for the binding interface
//
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgBind,
(void **)&pBindInterface);
if (FAILED(hr) || !pBindInterface)
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to QI for IID_IMailMsgBind - hr 0x%08X", hr);
goto Exit;
}
//
// Request the PFIO_CONTEXT for this message
//
hr = pBindInterface->GetBinding(&pIMsgFileHandle, NULL);
DebugTrace((LPARAM) pIMailMsgProperties,
"GetBinding return hr - 0x%08X", hr);
Exit:
if (pBindInterface)
{
pBindInterface->ReleaseContext();
pBindInterface->Release();
}
TraceFunctLeave();
return hr;
}