|
|
//-----------------------------------------------------------------------------
//
//
// 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; }
|