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.
534 lines
18 KiB
534 lines
18 KiB
// ===========================================================================
|
|
// 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;
|
|
}
|
|
|