|
|
// ===========================================================================
// File: DBGLOG.CXX
// CDLDebugLog: Class for Debug Logs
// DebugLogElement: Node class for debug log messages
//
#include <cdlpch.h>
#include <stdio.h>
extern HINSTANCE g_hInst;
// Initialize static variables
CList<CDLDebugLog *, CDLDebugLog *> CDLDebugLog::s_dlogList; TCHAR CDLDebugLog::s_szMessage[MAX_DEBUG_STRING_LENGTH]; CMutexSem CDLDebugLog::s_mxsDLogList; BOOL CDLDebugLog::s_bMessage(FALSE); CMutexSem CDLDebugLog::s_mxsMessage;
CDLDebugLog::CDLDebugLog() :m_DebugLogList(), m_fAddedDebugLogHead(FALSE), m_iRefCount(0) { m_szFileName[0] = '\0'; m_szUrlName[0] = '\0'; m_szMainClsid[0] = '\0'; m_szMainType[0] = '\0'; m_szMainExt[0] = '\0'; m_szMainUrl[0] = '\0'; }
CDLDebugLog::~CDLDebugLog() { Clear(); }
int CDLDebugLog::AddRef() { return ++m_iRefCount; }
int CDLDebugLog::Release() { ASSERT(m_iRefCount > 0); m_iRefCount--; if(m_iRefCount <= 0) { delete this; return 0; } else return m_iRefCount; }
CDLDebugLog * CDLDebugLog::MakeDebugLog() { return new CDLDebugLog(); }
// Delete all new'd data and start a new log
void CDLDebugLog::Clear() { DebugLogElement *pMsg = NULL; LISTPOSITION pos; int iNumMessages; int i;
// delete new'd messages
iNumMessages = m_DebugLogList.GetCount(); pos = m_DebugLogList.GetHeadPosition(); for (i = 0; i < iNumMessages; i++) { pMsg = m_DebugLogList.GetNext(pos); delete pMsg; } m_DebugLogList.RemoveAll(); m_fAddedDebugLogHead = FALSE;
// commit any lingering CacheEntry
if(m_szUrlName[0]) { FILETIME ftExpireTime; FILETIME ftTime;
GetSystemTimeAsFileTime(&ftTime); ftExpireTime.dwLowDateTime = (DWORD)0; ftExpireTime.dwHighDateTime = (DWORD)0; CommitUrlCacheEntry(m_szUrlName, m_szFileName, ftExpireTime, ftTime, NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0); } m_szUrlName[0] = 0; m_szFileName[0] = 0; }
// Initializes the main clsid, type, ext, and codebase of the debuglog from the
// corresponding values in the CCodeDownload
// If any of the CCodeDownload's parameters are NULL, they are ignored
// (An assertion is thrown if they are all NULL)
// for Retail: returns false if they are all NULL
BOOL CDLDebugLog::Init(CCodeDownload * pcdl) { int iRes = 0;
if(pcdl->GetMainDistUnit()) { iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainDistUnit(), -1, m_szMainClsid, MAX_DEBUG_STRING_LENGTH, NULL, NULL); ASSERT(iRes != 0); } if(pcdl->GetMainType()) { iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainType(), -1, m_szMainType, MAX_DEBUG_STRING_LENGTH, NULL, NULL); ASSERT(iRes != 0); } if(pcdl->GetMainExt()) { iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainExt(), -1, m_szMainExt, MAX_DEBUG_STRING_LENGTH, NULL, NULL); ASSERT(iRes != 0); } if(pcdl->GetMainURL()) { iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainURL(), -1, m_szMainUrl, INTERNET_MAX_URL_LENGTH, NULL, NULL); ASSERT(iRes != 0); }
ASSERT(iRes != 0);
return(iRes != 0); }
// Initializes the main clsid, type, ext, and codebase of the debuglog
// If any of the parameters are NULL, they are ignored
// (An assertion is thrown if they are all NULL)
// for Retail: returns false if they are all NULL
BOOL CDLDebugLog::Init(LPCWSTR wszMainClsid, LPCWSTR wszMainType, LPCWSTR wszMainExt, LPCWSTR wszMainUrl) { int iRes = 0;
if(wszMainClsid) { iRes = WideCharToMultiByte(CP_ACP, 0, wszMainClsid, -1, m_szMainClsid, MAX_DEBUG_STRING_LENGTH, NULL, NULL); ASSERT(iRes != 0); } if(wszMainType) { iRes = WideCharToMultiByte(CP_ACP, 0, wszMainType, -1, m_szMainType, MAX_DEBUG_STRING_LENGTH, NULL, NULL); ASSERT(iRes != 0); } if(wszMainExt) { WideCharToMultiByte(CP_ACP, 0, wszMainExt, -1, m_szMainExt, MAX_DEBUG_STRING_LENGTH, NULL, NULL); ASSERT(iRes != 0); } if(wszMainUrl) { WideCharToMultiByte(CP_ACP, 0, wszMainUrl, -1, m_szMainUrl, INTERNET_MAX_URL_LENGTH, NULL, NULL); ASSERT(iRes != 0); }
ASSERT(iRes != 0); return(iRes != 0); }
// Make the cache file for the debug log using the current main clsid, type, ext,
// and url data. The previous values in m_szUrlName and m_szFileName are cleaned
// up and written over.
void CDLDebugLog::MakeFile() { TCHAR szExtension[] = TEXT("HTM");
if(m_szFileName[0]) { return; // Only make the file if we don't already have one
}
m_szUrlName[0] = 0; m_szFileName[0] = 0;
if(m_szMainClsid[0]) { wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!name=%s", m_szMainClsid); } else if(m_szMainType[0]) { wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!type=%s", m_szMainType); } else if(m_szMainExt[0]) { wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!ext=%s", m_szMainExt); } else { wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!"); }
CreateUrlCacheEntry(m_szUrlName, 0, szExtension, m_szFileName, 0); }
// ---------------------------------------------------------------------------
// %%Function: CDLDebugLog::DebugOut(int iOption, const char *pscFormat, ...)
// Replacement for UrlMkDebugOut() and CCodeDownload::CodeDownloadDebugOut
// calls to log code download debug/error messages
// ---------------------------------------------------------------------------
void CDLDebugLog::DebugOut(int iOption, BOOL fOperationFailed, UINT iResId, ...) { // Temp solution to prevent buffer overruns in debug logging code.
// Long term, the printfs should be constrained. It will be a must
// if URLs become fully dynamic.
static char szDebugString[MAX_DEBUG_STRING_LENGTH*5]; static char szFormatString[MAX_DEBUG_FORMAT_STRING_LENGTH]; va_list args;
LoadString(g_hInst, iResId, szFormatString, MAX_DEBUG_FORMAT_STRING_LENGTH); va_start(args, iResId); vsprintf(szDebugString, szFormatString, args); va_end(args);
DebugOutPreFormatted(iOption, fOperationFailed, szDebugString); }
// Debug out taken a preformatted string instead of a resid format address and
// an arbitrary list of arguments. szDebugString is the string which will be outputted
// as the debug statement
void CDLDebugLog::DebugOutPreFormatted(int iOption, BOOL fOperationFailed, LPTSTR szDebugString) { static TCHAR szUrlMkDebugOutString[MAX_DEBUG_STRING_LENGTH]; DebugLogElement *pdbglog = NULL; DebugLogElement *pdbglogHead = NULL;
pdbglog = new DebugLogElement(szDebugString); if(! pdbglog) return; wnsprintf(szUrlMkDebugOutString, MAX_DEBUG_STRING_LENGTH-1, "CODE DL:%s", szDebugString); UrlMkDebugOut((iOption,szUrlMkDebugOutString));
if (pdbglog != NULL) { m_DebugLogList.AddTail(pdbglog); if (fOperationFailed && !m_fAddedDebugLogHead) { m_fAddedDebugLogHead = TRUE; pdbglogHead = new DebugLogElement("--- Detailed Error Log Follows ---\n"); if(! pdbglogHead) return; m_DebugLogList.AddHead(pdbglogHead); pdbglogHead = new DebugLogElement(*pdbglog); if(! pdbglogHead) return; m_DebugLogList.AddHead(pdbglogHead); } } }
#define DEBUG_LOG_HTML_START TEXT("<html><pre>\n")
#define DEBUG_LOG_HTML_END TEXT("\n</pre></html>")
#define PAD_DIGITS_FOR_STRING(x) (((x) > 9) ? TEXT("") : TEXT("0"))
// ---------------------------------------------------------------------------
// %%Function: CDLDebugLog::DumpDebugLog()
// Output the debug error log. This log is written as a cache entry.
// pszCacheFileName is an outparam for the name of the file in the cache,
// cbBufLen is the length of the buffer
// szErrorMsg is the error message causing the dump, and hrError the hr causing
// the dump
// ---------------------------------------------------------------------------
void CDLDebugLog::DumpDebugLog(LPTSTR pszCacheFileName, int cbBufLen, LPTSTR szErrorMsg, HRESULT hrError) { DebugLogElement *pMsg = NULL; LPCSTR pszStr = NULL; LISTPOSITION pos = NULL; int iNumMessages = 0; int i = 0; HANDLE hFile = INVALID_HANDLE_VALUE; FILETIME ftTime; FILETIME ftExpireTime; DWORD dwBytes = 0; SYSTEMTIME systime; static TCHAR pszHeader[MAX_DEBUG_STRING_LENGTH]; static const TCHAR *ppszMonths[] = {TEXT("Jan"), TEXT("Feb"), TEXT("Mar"), TEXT("Apr"), TEXT("May"), TEXT("Jun"), TEXT("Jul"), TEXT("Aug"), TEXT("Sep"), TEXT("Oct"), TEXT("Nov"), TEXT("Dec")}; // Get the filename and put it in the out LPTSTR
if(!m_szFileName[0]) MakeFile(); if(pszCacheFileName) lstrcpyn(pszCacheFileName,m_szFileName, cbBufLen);
iNumMessages = m_DebugLogList.GetCount(); pos = m_DebugLogList.GetHeadPosition(); if (pos) { pMsg = m_DebugLogList.GetAt(pos); if (pMsg != NULL) { hFile = CreateFile(m_szFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { // Write the header
WriteFile(hFile, DEBUG_LOG_HTML_START, strlen(DEBUG_LOG_HTML_START), &dwBytes, NULL); GetLocalTime(&systime); wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "*** Code Download Log entry (%s%d %s %d @ %s%d:%s%d:%s%d) ***\n", PAD_DIGITS_FOR_STRING(systime.wDay), systime.wDay, ppszMonths[systime.wMonth - 1], systime.wYear, PAD_DIGITS_FOR_STRING(systime.wHour), systime.wHour, PAD_DIGITS_FOR_STRING(systime.wMinute), systime.wMinute, PAD_DIGITS_FOR_STRING(systime.wSecond), systime.wSecond); WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
// Write what error caused this dump
wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "Code Download Error: (hr = %lx) %s\n", hrError, (szErrorMsg == NULL) ? ("(null)") : (szErrorMsg)); WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "Operation failed. Detailed Information:\n" " CodeBase: %s\n" " CLSID: %s\n" " Extension: %s\n" " Type: %s\n\n", m_szMainUrl, m_szMainClsid, m_szMainExt, m_szMainType); WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
// Write the Debug Log
pos = m_DebugLogList.GetHeadPosition(); iNumMessages = m_DebugLogList.GetCount(); for (i = 0; i < iNumMessages; i++) { pMsg = m_DebugLogList.GetNext(pos); pszStr = pMsg->GetLogMessage(); WriteFile(hFile, pszStr, strlen(pszStr), &dwBytes, NULL); }
// Close and clean
WriteFile(hFile, DEBUG_LOG_HTML_END, strlen(DEBUG_LOG_HTML_END), &dwBytes, NULL); CloseHandle(hFile); GetSystemTimeAsFileTime(&ftTime); ftExpireTime.dwLowDateTime = (DWORD)0; ftExpireTime.dwHighDateTime = (DWORD)0; CommitUrlCacheEntry(m_szUrlName, m_szFileName, ftExpireTime, ftTime, NORMAL_CACHE_ENTRY, NULL, 0, NULL, 0); m_fAddedDebugLogHead = FALSE; m_szUrlName[0] = NULL; m_szFileName[0] = NULL; } } } }
// returns TRUE if there is already a message saved
// (If there is a message saved already, it will not be written over
// unless bOverwrite is true)
BOOL CDLDebugLog::SetSavedMessage(LPCTSTR szMessage, BOOL bOverwrite) { CLock lck(s_mxsMessage); BOOL bRet = FALSE;
if(s_bMessage) bRet = TRUE;
if((!s_bMessage) || bOverwrite) { lstrcpyn(s_szMessage, szMessage, MAX_DEBUG_STRING_LENGTH); s_bMessage=TRUE; }
return bRet; }
LPCTSTR CDLDebugLog::GetSavedMessage() { CLock lck(s_mxsMessage); if(!s_bMessage) // Make sure to return an empty string if one has not been set
{ s_szMessage[0] = '\0'; } return s_szMessage; }
// add a debug log to the global list
void CDLDebugLog::AddDebugLog(CDLDebugLog * dlog) { CLock lck(s_mxsDLogList); if(dlog) s_dlogList.AddTail(dlog); }
// Remove a given debug log from the global list
void CDLDebugLog::RemoveDebugLog(CDLDebugLog * dlog) { CLock lck(s_mxsDLogList); if(dlog) { POSITION pos = s_dlogList.Find(dlog); if(pos != NULL) s_dlogList.RemoveAt(pos); } }
// Gets the debug log with the given clsid, mime type, file extension or url code base.
// Naming priority is the same as at debug file urlname creation: clsid, then type, then ext, then url
// The names are checked in this order (so two logs with the same extension but different classids will never
// be confused, since clsids are checked before extensions)
// If an earlier name is NULL or doesn't produce a match, the next name is checked.
// If all names fails to produce a match, the return value is NULL
CDLDebugLog * CDLDebugLog::GetDebugLog(LPCWSTR wszMainClsid, LPCWSTR wszMainType, LPCWSTR wszMainExt, LPCWSTR wszMainUrl) { TCHAR szComparer[MAX_DEBUG_STRING_LENGTH]; POSITION pos = NULL; szComparer[0] = NULL; CDLDebugLog * dlogCur = NULL; const int iSwitchMax = 4; LPCWSTR ppwszMains[] = {wszMainClsid, wszMainType, wszMainExt, wszMainUrl}; int iSwitch = iSwitchMax; int iRes = 0;
// Find the first non-empty name
for(iSwitch = 0; iSwitch < iSwitchMax; iSwitch++) { if((ppwszMains[iSwitch]) && (ppwszMains[iSwitch])[0]) break; }
if(iSwitch >= iSwitchMax) return NULL;
// Grab mutex for accesing the list
CLock lck(s_mxsDLogList);
// Loop over the names; a higher array value means a lower priority name;
// don't check a lower priority name unless all higher priority names have
// failed
while(iSwitch < iSwitchMax) { iRes = WideCharToMultiByte(CP_ACP, 0, ppwszMains[iSwitch], -1, szComparer, MAX_DEBUG_STRING_LENGTH, NULL, NULL); if(iRes == 0) return NULL;
// Look through the entire list for a debuglog whose name (clsid, type, ext, or url)
// matches the given one
for(pos = s_dlogList.GetHeadPosition(); pos != NULL; ) { dlogCur = s_dlogList.GetNext(pos); switch(iSwitch) { case 0: if(!lstrcmp(szComparer, dlogCur->GetMainClsid())) return dlogCur; break; case 1: if(!lstrcmp(szComparer, dlogCur->GetMainType())) return dlogCur; break; case 2: if(!lstrcmp(szComparer, dlogCur->GetMainExt())) return dlogCur; break; case 3: if(!lstrcmp(szComparer, dlogCur->GetMainUrl())) return dlogCur; break; default: break; } } iSwitch++; }
// No match of any of the non-NULL passed in names was found
return NULL; }
DebugLogElement::DebugLogElement(const DebugLogElement &ref) { SetLogMessage(ref.m_szMessage); }
DebugLogElement::DebugLogElement(LPSTR szMessage) : m_szMessage(NULL) { SetLogMessage(szMessage); }
DebugLogElement::~DebugLogElement() { if (m_szMessage != NULL) { delete [] m_szMessage; } }
HRESULT DebugLogElement::SetLogMessage(LPSTR szMessage) { HRESULT hr = S_OK;
if (m_szMessage != NULL) { delete [] m_szMessage; m_szMessage = NULL; } m_szMessage = new char[strlen(szMessage) + 1]; if (m_szMessage != NULL) { strcpy(m_szMessage, szMessage); } else { hr = E_OUTOFMEMORY; }
return hr; }
|