/*++ 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