windows-nt/Source/XPSP1/NT/net/tapi/skywalker/tapi3/utils.cpp
2020-09-26 16:20:57 +08:00

1117 lines
26 KiB
C++

/*++
Copyright (c) 1998 - 1999 Microsoft Corporation
Module Name:
utils.cpp
Abstract:
Author:
mquinton - 6/30/98
Notes:
Revision History:
--*/
#include "stdafx.h"
HRESULT
ProcessMessage(
PT3INIT_DATA,
PASYNCEVENTMSG
);
////////////////////////////////////////////////////////////////////
// CRetryQueue::QueueEvent
//
// Queues a TAPI event message object to be processed later
////////////////////////////////////////////////////////////////////
BOOL CRetryQueue::QueueEvent(PASYNCEVENTMSG pEvent)
{
PRETRY_QUEUE_ENTRY pNewQueueEntry;
PASYNCEVENTMSG pEventCopy;
LOG((TL_TRACE, "QueueEvent - enter"));
//
// we want to do as little as possible inside the lock so preallocate
// everything we can before acquiring it
//
//
// create a new queue entry
//
pNewQueueEntry = (PRETRY_QUEUE_ENTRY)ClientAlloc( sizeof(RETRY_QUEUE_ENTRY) );
if (pNewQueueEntry == NULL)
{
LOG((TL_ERROR, "QueueEvent - out of memory for new entry - losing message"));
return FALSE;
}
//
// create a copy of the event
//
pEventCopy = (PASYNCEVENTMSG)ClientAlloc(pEvent->TotalSize);
if ( pEventCopy == NULL)
{
LOG((TL_ERROR, "QueueEvent - out of memory for pEventCopy - losing message"));
ClientFree(pNewQueueEntry);
return FALSE;
}
//
// initialize the copy of the event that we have created
//
memcpy( pEventCopy, pEvent, pEvent->TotalSize );
//
// initialize queue entry with our copy of the event
//
pNewQueueEntry->dwRetryCount = MAX_REQUEUE_TRIES;
pNewQueueEntry->pMessage = pEventCopy;
Lock();
//
// is the queue accepting new entries?
//
if (!m_bAcceptNewEntries)
{
LOG((TL_TRACE,
"QueueEvent - can't queue -- the queue is closed"));
ClientFree(pNewQueueEntry);
ClientFree(pEventCopy);
Unlock();
return FALSE;
}
//
// attempt to add queue entry to the list
//
try
{
m_RetryQueueList.push_back(pNewQueueEntry);
}
catch(...)
{
LOG((TL_ERROR, "QueueEvent - out of memory - losing message"));
ClientFree(pNewQueueEntry);
ClientFree(pEventCopy);
Unlock();
return FALSE;
}
Unlock();
LOG((TL_INFO, "QueueEvent - Queued pEntry ----> %p", pNewQueueEntry ));
LOG((TL_INFO, " pEvent ----> %p", pEventCopy ));
return TRUE;
}
////////////////////////////////////////////////////////////////////
// CRetryQueue::QueueEvent
//
// Requeues a TAPI event message object to be processed later
////////////////////////////////////////////////////////////////////
void CRetryQueue::RequeueEvent(PRETRY_QUEUE_ENTRY pQueueEntry)
{
LOG((TL_TRACE, "RequeueEvent - enter"));
// just reuse the old entry
// add to list
Lock();
if (!m_bAcceptNewEntries)
{
LOG((TL_ERROR,
"RequeueEvent - attemped to requeue after the queue was closed"));
//
// this should not have happened -- see how we got here
//
_ASSERTE(FALSE);
Unlock();
return;
}
try
{
m_RetryQueueList.push_back(pQueueEntry);
}
catch(...)
{
LOG((TL_ERROR, "RequeueEvent - out of memory - losing message"));
}
Unlock();
LOG((TL_INFO, "RequeueEvent - Requeuing pEntry is ----> %p", pQueueEntry ));
LOG((TL_INFO, " Requeuing pEvent is ----> %p", pQueueEntry->pMessage ));
LOG((TL_INFO, " Requeuing count is ----> %lx", pQueueEntry->dwRetryCount ));
}
////////////////////////////////////////////////////////////////////
// CRetryQueue::DequeueEvent
//
// Pulls an event from the queue
////////////////////////////////////////////////////////////////////
BOOL CRetryQueue::DequeueEvent(PRETRY_QUEUE_ENTRY * ppEvent)
{
BOOL bResult = TRUE;
LOG((TL_TRACE, "DequeueEvent - enter"));
Lock();
if (m_RetryQueueList.size() > 0)
{
*ppEvent = m_RetryQueueList.front();
try
{
m_RetryQueueList.pop_front();
}
catch(...)
{
LOG((TL_INFO, "DequeueEvent - pop m_RetryQueueList failed"));
bResult = FALSE;
}
if( bResult )
{
bResult = !IsBadReadPtr(*ppEvent, sizeof( RETRY_QUEUE_ENTRY ) );
}
LOG((TL_INFO, "DequeueEvent - returning %p", *ppEvent));
}
else
{
LOG((TL_INFO, "DequeueEvent - no event"));
// return false if there are no more messages
bResult = FALSE;
}
Unlock();
return bResult;
}
////////////////////////////////////////////////////////////////////
// CRetryQueue::ProcessQueue
//
////////////////////////////////////////////////////////////////////
void CRetryQueue::ProcessQueue()
{
PRETRY_QUEUE_ENTRY pQueueEntry;
PASYNCEVENTMSG pAsyncEventMsg;
PT3INIT_DATA pInitData = NULL;
DWORD dwCount;
Lock();
dwCount = m_RetryQueueList.size();
Unlock();
LOG((TL_TRACE, "ProcessQueue - enter dwCount----> %lx",dwCount));
while(dwCount-- > 0 )
{
if( DequeueEvent(&pQueueEntry) )
{
pAsyncEventMsg = pQueueEntry->pMessage;
//
// InitContext contains the handle. get the original pointer from the handle
//
pInitData = (PT3INIT_DATA)GetHandleTableEntry(pAsyncEventMsg->InitContext);
LOG(( TL_INFO,
"ProcessQueue - msg=%d, hDev=x%x, p1=x%x, p2=x%x, p3=x%x, pInitData=%p",
pAsyncEventMsg->Msg,
pAsyncEventMsg->hDevice,
pAsyncEventMsg->Param1,
pAsyncEventMsg->Param2,
pAsyncEventMsg->Param3,
pInitData
));
if SUCCEEDED(ProcessMessage(
pInitData,
pAsyncEventMsg
) )
{
// We're Done with the message so free it & the used queue entry
LOG((TL_INFO, "ProcessQueue - sucessfully processed event message ----> %p",
pAsyncEventMsg ));
ClientFree(pAsyncEventMsg);
ClientFree(pQueueEntry);
}
else
{
//
// if we don't have any retries left for this entry or if the
// queue is now closed, do cleanup. otherwise, requeue
//
if( (--(pQueueEntry->dwRetryCount) == 0) || (!m_bAcceptNewEntries))
{
// We're giving up with this one, so free the message & the used queue entry
//
// note that we can have potential leaks here if queue entry is
// holding references to other things that we don't know how to
// free
//
LOG((TL_ERROR, "ProcessQueue - used all retries, deleting event message ----> %p",
pAsyncEventMsg ));
ClientFree(pAsyncEventMsg);
ClientFree(pQueueEntry);
}
else
{
// Queue it one more time, reuse the queu entry ....
RequeueEvent(pQueueEntry);
//
// we failed to process the workitem. it is possible that
// another thread is waiting for a timeslot so it is
// scheduled and gets a chance to prepare everything so our
// next processing attempt is successful.
//
// to increase the chances of that thread being scheduled
// (and out success on the next processing attempt), sleep
// a little.
//
extern DWORD gdwTapi3RetryProcessingSleep;
LOG((TL_INFO,
"ProcessQueue - requeued item. Sleeping for %ld ms",
gdwTapi3RetryProcessingSleep));
Sleep(gdwTapi3RetryProcessingSleep);
}
}
}
}
LOG((TL_TRACE, "ProcessQueue - exit"));
}
void
CRetryQueue::RemoveNewCallHub(DWORD dwCallHub)
{
RetryQueueListType::iterator iter, end;
Lock();
iter = m_RetryQueueList.begin();
end = m_RetryQueueList.end();
for ( ; iter != end; iter++ )
{
PRETRY_QUEUE_ENTRY pEntry = *iter;
if(pEntry->pMessage != NULL)
{
if ( (pEntry->pMessage->Msg == LINE_APPNEWCALLHUB) &&
(pEntry->pMessage->Param1 == dwCallHub) )
{
ClientFree(pEntry->pMessage);
ClientFree(pEntry);
m_RetryQueueList.erase( iter ); // erase appears to create a problem with
// the iter so that we loop too many times & AV.
iter = m_RetryQueueList.begin(); // Restarting at beginning again fixs this.
}
}
}
Unlock();
}
////////////////////////////////////////////////////////////////////
//
// CRetryQueue::OpenForNewEntries
//
// after this function returns, the queue will accept new entries
//
////////////////////////////////////////////////////////////////////
void CRetryQueue::OpenForNewEntries()
{
LOG((TL_TRACE, "OpenForNewEntries - enter"));
Lock();
m_bAcceptNewEntries = TRUE;
Unlock();
LOG((TL_TRACE, "OpenForNewEntries - exit"));
}
////////////////////////////////////////////////////////////////////
//
// CRetryQueue::CloseForNewEntries
//
// new entries will be denied after this function returns
//
////////////////////////////////////////////////////////////////////
void CRetryQueue::CloseForNewEntries()
{
LOG((TL_TRACE, "CloseForNewEntries - enter"));
Lock();
m_bAcceptNewEntries = FALSE;
Unlock();
LOG((TL_TRACE, "CloseForNewEntries - exit"));
}
////////////////////////////////////////////////////////////////////
// CRetryQueue::~CRetryQueue
//
////////////////////////////////////////////////////////////////////
CRetryQueue::~CRetryQueue()
{
RetryQueueListType::iterator i,j;
PRETRY_QUEUE_ENTRY pQueueEntry;
Lock();
// walk list deleting entries
i = m_RetryQueueList.begin();
j = m_RetryQueueList.end();
while ( i != j )
{
pQueueEntry = *i++;
if(pQueueEntry->pMessage != NULL)
ClientFree(pQueueEntry->pMessage);
ClientFree(pQueueEntry);
}
m_RetryQueueList.clear();
Unlock();
DeleteCriticalSection( &m_cs );
};
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Initialize
//
// dwMaxEntries - max entries in the array
// dwSize - size of buffers ( may grow )
// dwType - type of buffer ( see BUFFERTYPE_ constants above )
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CStructCache::Initialize( DWORD dwMaxEntries, DWORD dwSize, DWORD dwType )
{
DWORD dw;
Lock();
m_dwMaxEntries = min( MAXCACHEENTRIES, dwMaxEntries );
m_dwUsedEntries = 0;
m_dwType = dwType;
// zero the array
ZeroMemory( &m_aEntries, sizeof (CACHEENTRY) * MAXCACHEENTRIES );
// go through an allocate buffers
for ( dw = 0; dw < m_dwMaxEntries; dw++ )
{
LPDWORD pdwBuffer;
pdwBuffer = (LPDWORD) ClientAlloc( dwSize );
if ( NULL == pdwBuffer )
{
LOG((TL_ERROR, "Initialize - out of memory"));
//
// cleanup -- free whatever was allocated
//
for (int i = 0; i < dw; i++)
{
ClientFree(m_aEntries[i].pBuffer);
m_aEntries[i].pBuffer = NULL;
}
m_dwMaxEntries = 0;
Unlock();
return E_OUTOFMEMORY;
}
// tapi structures have the size as the first
// DWORD. Initialize this here
pdwBuffer[0] = dwSize;
// save the buffer
m_aEntries[dw].pBuffer = (LPVOID)pdwBuffer;
}
Unlock();
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Shutdown
//
// free the memory
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CStructCache::Shutdown()
{
DWORD dw;
Lock();
for (dw = 0; dw < m_dwMaxEntries; dw++)
{
if ( NULL != m_aEntries[dw].pBuffer )
{
ClientFree( m_aEntries[dw].pBuffer );
m_aEntries[dw].pBuffer = NULL;
}
}
Unlock();
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// GetBuffer
//
// pNewObject - object to get the buffer
// ppReturnStuct - buffer for pNewObject to use
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CStructCache::GetBuffer( UINT_PTR pNewObject, LPVOID * ppReturnStruct )
{
Lock();
// have we used all the entries?
if ( m_dwUsedEntries < m_dwMaxEntries )
{
// nope - so just take the first free one
*ppReturnStruct = m_aEntries[m_dwUsedEntries].pBuffer;
m_aEntries[m_dwUsedEntries].pObject = pNewObject;
// in number used
m_dwUsedEntries++;
}
else
{
// yes, so take the buffer from the LRU one
UINT_PTR pObject;
// get the object that is losing it's buffer
// and the buffer
pObject = m_aEntries[m_dwMaxEntries-1].pObject;
*ppReturnStruct = m_aEntries[m_dwMaxEntries-1].pBuffer;
switch ( m_dwType )
{
// inform the object that it's losing
// it's buffer
case BUFFERTYPE_ADDRCAP:
{
CAddress * pAddress;
pAddress = (CAddress *)pObject;
if( pAddress != NULL)
{
pAddress->SetAddrCapBuffer( NULL );
}
break;
}
case BUFFERTYPE_LINEDEVCAP:
{
CAddress * pAddress;
pAddress = (CAddress *)pObject;
if( pAddress != NULL)
{
pAddress->SetLineDevCapBuffer( NULL );
}
break;
}
case BUFFERTYPE_PHONECAP:
{
CPhone * pPhone;
pPhone = (CPhone *)pObject;
if( pPhone != NULL)
{
pPhone->SetPhoneCapBuffer( NULL );
}
break;
}
default:
break;
}
// move all elements in the array "down" one
MoveMemory(
&(m_aEntries[1]),
&(m_aEntries[0]),
(m_dwMaxEntries-1) * sizeof(CACHEENTRY)
);
// put the new object at the front of the array
m_aEntries[0].pObject = pNewObject;
m_aEntries[0].pBuffer = *ppReturnStruct;
ZeroMemory(
((LPDWORD)(*ppReturnStruct)) + 1,
((LPDWORD)(*ppReturnStruct))[0] - sizeof(DWORD)
);
}
Unlock();
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// SetBuffer
//
// this is called when the buffer had to be realloced. The
// owning object freed the original buffer, and is setting the
// newly alloced buffer.
//
// pObject - object that realloc'd
// pNewStruct - new struct
//
// Note the implementation is straightforward here - just run
// through the array looking for the object
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CStructCache::SetBuffer( UINT_PTR pObject, LPVOID pNewStruct )
{
DWORD dw;
Lock();
for ( dw = 0; dw < m_dwUsedEntries; dw++ )
{
if ( m_aEntries[dw].pObject == pObject )
{
m_aEntries[dw].pBuffer = pNewStruct;
break;
}
}
Unlock();
return S_OK;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// InvalidateBuffer
//
// This is called when the owning object (pObject) is being released
// to prevent problems in getBuffer() when a cache entry is reused &
// we inform the object that it's losing it's buffer.
// We set the pObject member in the cache entry to 0 & prevent
// getBuffer from accessing the original owner object which may have
// been released.
//
// pObject - object that realloc'd
// pNewStruct - new struct
//
// Note the implementation is straightforward here - just run
// through the array looking for the object
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT CStructCache::InvalidateBuffer( UINT_PTR pObject )
{
DWORD dw;
Lock();
for ( dw = 0; dw < m_dwUsedEntries; dw++ )
{
if ( m_aEntries[dw].pObject == pObject )
{
m_aEntries[dw].pObject = NULL;
break;
}
}
Unlock();
return S_OK;
}
PWSTR
MyLoadString( UINT uID )
{
PWSTR pTempBuffer = NULL;
int iSize, iCurrentSize = 128;
do
{
if ( NULL != pTempBuffer )
{
ClientFree( pTempBuffer );
}
iCurrentSize *= 2;
pTempBuffer = (PWSTR) ClientAlloc( iCurrentSize * sizeof( WCHAR ) );
if (NULL == pTempBuffer)
{
LOG((TL_ERROR, "MyLoadString - alloc failed" ));
return NULL;
}
iSize = ::LoadStringW(
_Module.GetResourceInstance(),
uID,
pTempBuffer,
iCurrentSize
);
if ( 0 == iSize )
{
LOG((
TL_ERROR,
"MyLoadString - LoadString failed - %lx",
GetLastError()
));
return NULL;
}
} while ( (iSize >= (iCurrentSize - 1) ) );
return pTempBuffer;
}
#ifdef TRACELOG
BOOL g_bLoggingEnabled = FALSE;
DWORD sg_dwTraceID = INVALID_TRACEID;
char sg_szTraceName[100]; // saves name of dll
DWORD sg_dwTracingToDebugger = 0;
DWORD sg_dwTracingToConsole = 0;
DWORD sg_dwTracingToFile = 0;
DWORD sg_dwDebuggerMask = 0;
BOOL TRACELogRegister(LPCTSTR szName)
{
HKEY hTracingKey;
char szTracingKey[100];
const char szDebuggerTracingEnableValue[] = "EnableDebuggerTracing";
const char szConsoleTracingEnableValue[] = "EnableConsoleTracing";
const char szFileTracingEnableValue[] = "EnableFileTracing";
const char szTracingMaskValue[] = "ConsoleTracingMask";
sg_dwTracingToDebugger = 0;
sg_dwTracingToConsole = 0;
sg_dwTracingToFile = 0;
#ifdef UNICODE
wsprintfA(szTracingKey, "Software\\Microsoft\\Tracing\\%ls", szName);
#else
wsprintfA(szTracingKey, "Software\\Microsoft\\Tracing\\%s", szName);
#endif
if ( ERROR_SUCCESS == RegOpenKeyExA(HKEY_LOCAL_MACHINE,
szTracingKey,
0,
KEY_READ,
&hTracingKey) )
{
DWORD dwDataSize = sizeof (DWORD);
DWORD dwDataType;
RegQueryValueExA(hTracingKey,
szDebuggerTracingEnableValue,
0,
&dwDataType,
(LPBYTE) &sg_dwTracingToDebugger,
&dwDataSize);
RegQueryValueExA(hTracingKey,
szConsoleTracingEnableValue,
0,
&dwDataType,
(LPBYTE) &sg_dwTracingToConsole,
&dwDataSize);
RegQueryValueExA(hTracingKey,
szFileTracingEnableValue,
0,
&dwDataType,
(LPBYTE) &sg_dwTracingToFile,
&dwDataSize);
RegQueryValueExA(hTracingKey,
szTracingMaskValue,
0,
&dwDataType,
(LPBYTE) &sg_dwDebuggerMask,
&dwDataSize);
RegCloseKey (hTracingKey);
}
else
{
//
// the key could not be opened. in case the key does not exist,
// register with rtutils so that the reg keys get created
//
#ifdef UNICODE
wsprintfA(sg_szTraceName, "%ls", szName);
#else
wsprintfA(sg_szTraceName, "%s", szName);
#endif
//
// tracing should not have been initialized
//
_ASSERTE(sg_dwTraceID == INVALID_TRACEID);
//
// note that this trace id will not be cleaned up. this is ok -- this
// is a leak of one registration "handle" and it only happens the
// first time the dll gets loaded.
//
sg_dwTraceID = TraceRegister(szName);
sg_dwTraceID = INVALID_TRACEID;
}
if (sg_dwTracingToDebugger || sg_dwTracingToConsole || sg_dwTracingToFile)
{
//
// we want to try to initialize logging
//
if (sg_dwTracingToConsole || sg_dwTracingToFile)
{
#ifdef UNICODE
wsprintfA(sg_szTraceName, "%ls", szName);
#else
wsprintfA(sg_szTraceName, "%s", szName);
#endif
//
// tracing should not have been initialized
//
_ASSERTE(sg_dwTraceID == INVALID_TRACEID);
//
// register
//
sg_dwTraceID = TraceRegister(szName);
}
//
// if tracing registration succeeded or debug tracing is on, set the
// global logging flag
//
if ( sg_dwTracingToDebugger || (sg_dwTraceID != INVALID_TRACEID) )
{
g_bLoggingEnabled = TRUE;
LOG((TL_TRACE, "TRACELogRegister - logging configured" ));
return TRUE;
}
else
{
//
// TraceRegister failed and debugger logging is off
//
return FALSE;
}
}
//
// logging is not enabled
//
return TRUE;
}
void TRACELogDeRegister()
{
if (g_bLoggingEnabled)
{
LOG((TL_TRACE, "TRACELogDeRegister - disabling logging" ));
sg_dwTracingToDebugger = 0;
sg_dwTracingToConsole = 0;
sg_dwTracingToFile = 0;
if (sg_dwTraceID != INVALID_TRACEID)
{
TraceDeregister(sg_dwTraceID);
sg_dwTraceID = INVALID_TRACEID;
}
g_bLoggingEnabled = FALSE;
}
}
void TRACELogPrint(IN DWORD dwDbgLevel, IN LPCSTR lpszFormat, IN ...)
{
char szTraceBuf[MAXDEBUGSTRINGLENGTH + 1];
va_list arglist;
if ( ( sg_dwTracingToDebugger > 0 ) &&
( 0 != ( dwDbgLevel & sg_dwDebuggerMask ) ) )
{
// retrieve local time
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
wsprintfA(szTraceBuf,
"%s:[%02u:%02u:%02u.%03u,tid=%x:] [%s] ",
sg_szTraceName,
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond,
SystemTime.wMilliseconds,
GetCurrentThreadId(),
TraceLevel(dwDbgLevel));
va_list ap;
va_start(ap, lpszFormat);
_vsnprintf(&szTraceBuf[lstrlenA(szTraceBuf)],
MAXDEBUGSTRINGLENGTH - lstrlenA(szTraceBuf),
lpszFormat,
ap
);
lstrcatA (szTraceBuf, "\n");
OutputDebugStringA (szTraceBuf);
va_end(ap);
}
if (sg_dwTraceID != INVALID_TRACEID)
{
wsprintfA(szTraceBuf, "[%s] %s", TraceLevel(dwDbgLevel), lpszFormat);
va_start(arglist, lpszFormat);
TraceVprintfExA(sg_dwTraceID, dwDbgLevel | TRACE_USE_MSEC, szTraceBuf, arglist);
va_end(arglist);
}
}
void TRACELogPrint(IN DWORD dwDbgLevel, HRESULT hr, IN LPCSTR lpszFormat, IN ...)
{
char szTraceBuf[MAXDEBUGSTRINGLENGTH + 1];
LPVOID lpMsgBuf = NULL; // Temp buffer for error code
va_list arglist;
// Get the error message relating to our HRESULT
TAPIFormatMessage(hr, &lpMsgBuf);
if ( ( sg_dwTracingToDebugger > 0 ) &&
( 0 != ( dwDbgLevel & sg_dwDebuggerMask ) ) )
{
// retrieve local time
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
wsprintfA(szTraceBuf,
"%s:[%02u:%02u:%02u.%03u,tid=%x:] [%s] ",
sg_szTraceName,
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond,
SystemTime.wMilliseconds,
GetCurrentThreadId(),
TraceLevel(dwDbgLevel)
);
va_list ap;
va_start(ap, lpszFormat);
_vsnprintf(&szTraceBuf[lstrlenA(szTraceBuf)],
MAXDEBUGSTRINGLENGTH - lstrlenA(szTraceBuf),
lpszFormat,
ap
);
wsprintfA(&szTraceBuf[lstrlenA(szTraceBuf)],
" Returned[%lx] %s\n",
hr,
lpMsgBuf);
OutputDebugStringA (szTraceBuf);
va_end(ap);
}
if (sg_dwTraceID != INVALID_TRACEID)
{
wsprintfA(szTraceBuf, "[%s] %s Returned[%lx] %s", TraceLevel(dwDbgLevel), lpszFormat,hr, lpMsgBuf );
va_start(arglist, lpszFormat);
TraceVprintfExA(sg_dwTraceID, dwDbgLevel | TRACE_USE_MSEC, szTraceBuf, arglist);
va_end(arglist);
}
if(lpMsgBuf != NULL)
{
LocalFree( lpMsgBuf ); // Free the temp buffer.
}
}
char *TraceLevel(DWORD dwDbgLevel)
{
switch(dwDbgLevel)
{
case TL_ERROR: return "ERROR";
case TL_WARN: return "WARN ";
case TL_INFO: return "INFO ";
case TL_TRACE: return "TRACE";
case TL_EVENT: return "EVENT";
default: return " ??? ";
}
}
#endif // TRACELOG