windows-nt/Source/XPSP1/NT/enduser/windows.com/lib/logging/iulogger.cpp
2020-09-26 16:20:57 +08:00

1414 lines
37 KiB
C++

//=======================================================================
//
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
//
// File: IULogger.cpp: implementation of the CIULogger class.
//
// Description:
//
// See IULogger.h
//
//=======================================================================
#if defined(DBG)
#include <windows.h>
#include <ole2.h>
#include <tchar.h>
#include <MemUtil.h>
#include <fileutil.h>
#include <Logging.h>
#include <strsafe.h>
//
// declare constants used to control log exclusions
//
const DWORD LOG_BLOCK = 0x00000001; // log function/block in/out
const DWORD LOG_XML_DETAIL = 0x00000002; // log detailed XML operation
const DWORD LOG_INTERNET = 0x00000004; // log action related to Internet activities, e.g., downld
const DWORD LOG_SOFTWARE = 0x00000008; // log details about software detection/installation
const DWORD LOG_DRIVER = 0x00000010; // log actions related to driver detection/installation
const DWORD LOG_TRUST = 0x00000020; // log actions related to wintrust checking
const DWORD LOG_DOWNLOAD = 0x00000040; // log actions related to download
const DWORD LOG_XML_BSTR_DETAIL = 0x00000080; // log XML BSTRs
const DWORD LOG_ERROR = 0x00008000; // you can not exclude this type of logs from output
const DWORD LOG_ALL = 0xFFFFFFFF; // default, all above
//
// const for longest line of XML we will output
//
const DWORD LOG_XML_BUFF_LEN = 128;
//
// const for specifying the intent array size increament.
// each element in array holds indent data for one thread
//
const int c_IndentArrayChunk = 16;
//
// define the log header format
//
// It is constructed as: <date> <time> <thread id>
//
const TCHAR szLogHeaderFmt[] = _T("yyyy/mm/dd hh:nn:ss:sss xxxxxxxx ");
//
// initialization of static members
//
int CIULogger::m_Size = 0;
int CIULogger::m_siIndentStep = 0; // init to use tab char
CIULogger::_THREAD_INDENT* CIULogger::m_psIndent = NULL;
DWORD CIULogger::m_sdwLogMask = LOG_ALL;
HANDLE CIULogger::m_shFile = INVALID_HANDLE_VALUE;
bool CIULogger::m_fLogDebugMsg = false;
bool CIULogger::m_fLogFile = false;
bool CIULogger::m_fLogUsable = false;
HANDLE CIULogger::m_hMutex = NULL;
int CIULogger::m_cFailedWaits = 0;
int CIULogger::m_fFlushEveryTime = FALSE;
//
// Defines for Mutex (borrowed from freelog)
//
// NOTE: globals and statics are per-module (e.g. iuctl, iuengine), but Mutex is per-processes
// due to the name being constructed from the log file name (contains process ID).
//
#define MUTEX_TIMEOUT 1000 // Don't wait more than 1 second to write to logfile
#define MAX_MUTEX_WAITS 4 // Don't keep trying after this many failures
//
// global variable
//
//
// reference count to control log file open/close
//
LONG g_RefCount = 0;
//
// critical sectoin handling multi-threading
// access of indent array case
//
//
CRITICAL_SECTION g_LogCs;
//
// we need to declare a global object so refcount wont't
// be zero, otherwise in multi-threading mode ref count
// can be fooled and AV when one object thinks m_psIdent not NULL
// but another object in another (parent) thread freed m_psIndent
// in destructor (only if parent thread quits)
//
CIULogger g_DummyLogObj(NULL);
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CIULogger::CIULogger(char* szBlockName)
: m_Index(-1), m_LineNum(0)
{
if (0 == g_RefCount)
{
//
// This must be the g_DummyLogObj (or another global
// instance) during initialization of globals. Since
// this is the first instance created, we must init CS
//
InitializeCriticalSection(&g_LogCs);
}
//
// Protect the statics while in the ctor
//
EnterCriticalSection(&g_LogCs);
m_dwTickBegin = GetTickCount();
m_dwThreadId = GetCurrentThreadId();
m_fProcessLog = (NULL == szBlockName);
ZeroMemory(m_szBlockName, sizeof(m_szBlockName));
g_RefCount++;
if (1 == g_RefCount)
{
//
// this is the first time to call this class, we need to
// find out whether we should log and where to log to
//
ReadRegistrySettings();
//
// allocate memory for thread indent array initially
//
SetIndent(0);
//
// if the indent level is not negative, then it's okay to log
//
m_fLogFile = (INVALID_HANDLE_VALUE != m_shFile);
m_fLogUsable = (m_fLogFile || m_fLogDebugMsg) && (NULL != m_psIndent);
}
if (m_fLogUsable)
{
if (!m_fProcessLog)
{
//
// this is probably a new thread, so we need to find the index
// for this thread.
//
SetIndent(0);
//
// do block logging, if permitted
//
if (0x0 != (m_sdwLogMask & LOG_BLOCK) && szBlockName && _T('\0') != szBlockName[0])
{
StringCchCopyA(m_szBlockName, ARRAYSIZE(m_szBlockName), szBlockName);
USES_IU_CONVERSION;
char szOut[sizeof(m_szBlockName) + 10];
//
// Implicit "Enter " before block name to save log space
//
if (SUCCEEDED(StringCchPrintfA(szOut, ARRAYSIZE(szOut), "%hs\r\n", szBlockName)))
{
_LogOut(A2T(szOut));
}
}
SetIndent(+1);
}
}
LeaveCriticalSection(&g_LogCs);
}
CIULogger::~CIULogger()
{
EnterCriticalSection(&g_LogCs);
if (m_fLogUsable)
{
//
// decrease the indent level by 1 if we increased indent
//
if (!m_fProcessLog)
{
SetIndent(-1);
}
//
// write log file for exiting block, if allowed and block name exists
//
if (0x0 != (m_sdwLogMask & LOG_BLOCK) && _T('\0') != m_szBlockName[0])
{
USES_IU_CONVERSION;
char szOut[1024];
//
// "Exit " shortened to "~" to save log space
//
if (SUCCEEDED(StringCchPrintfA(szOut, ARRAYSIZE(szOut), "~%hs, %d msec\r\n", m_szBlockName, GetTickCount() - m_dwTickBegin)))
{
_LogOut(A2T(szOut));
}
}
}
//
// reduce reference cnt
//
g_RefCount--;
//
// g_RefCount will go to zero before leaving dtor if this is the last global instance
// in this module
//
if (0 == g_RefCount)
{
//
// close file if the file is open
//
if (m_fLogFile && INVALID_HANDLE_VALUE != m_shFile) // redundent?
{
CloseHandle(m_shFile);
m_shFile = INVALID_HANDLE_VALUE;
}
if(NULL != m_hMutex)
{
CloseHandle(m_hMutex);
}
//
// free memory of indent array
//
if (NULL != m_psIndent)
{
HeapFree(GetProcessHeap(), 0, m_psIndent);
m_psIndent = NULL;
}
}
LeaveCriticalSection(&g_LogCs);
//
// This is the last global instance (probably g_DummyLogObj) and is
// being destructed before the DLL unloads
//
if (0 == g_RefCount)
{
DeleteCriticalSection(&g_LogCs);
}
}
//
// Mutex stuff borrowed from freelog
// fixcode: This should not be required here since chk logging is per process only
BOOL CIULogger::AcquireMutex()
{
// In rare case where mutex not created, we allow file operations
// with no synchronization
if (m_hMutex == NULL)
return TRUE;
// Don't keep waiting if we've been blocked in the past
if (m_cFailedWaits >= MAX_MUTEX_WAITS)
return FALSE;
BOOL fResult = TRUE;
if (WaitForSingleObject(m_hMutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fResult = FALSE;
m_cFailedWaits++;
}
return fResult;
}
void CIULogger::ReleaseMutex()
{
if (m_hMutex != NULL) // Note: AcquireMutex succeeds even if m_hMutex is NULL
{
::ReleaseMutex(m_hMutex);
}
}
////////////////////////////////////////////////////////////////////////
//
// log with no flag, so can not be removed by excluding directives
//
////////////////////////////////////////////////////////////////////////
void CIULogger::Log(LPCTSTR szLogFormat, ...)
{
if (m_fLogUsable)
{
USES_IU_CONVERSION;
va_list va;
va_start (va, szLogFormat);
_Log(LOG_ALL, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// log error, so can not be removed by excluding directives
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogError(LPCTSTR szLogFormat, ...)
{
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_ERROR, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// Helper for LogErrorMsg and LogInfoMsg (which supply message to prepend)
//
////////////////////////////////////////////////////////////////////////
void CIULogger::_LogFormattedMsg(DWORD dwErrCode, LPCTSTR pszErrorInfo)
{
if (m_fLogUsable)
{
//
// try to retrive system msg
//
LPTSTR lpszBuffer = NULL, lpszLogMsg = NULL;
LPVOID lpMsg = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, // no source, use system msg
dwErrCode,
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
(LPTSTR)&lpMsg,
0,
NULL);
if (NULL == lpMsg)
{
//
// if we failed to get the msg, then output generic
// error/info log
//
LogError(_T("Unknown %s Line %d: 0x%08x\n"), pszErrorInfo, m_LineNum, dwErrCode);
}
else
{
lpszBuffer = (LPTSTR) lpMsg;
int nLen = lstrlen(lpszBuffer);
lpszLogMsg = (LPTSTR) LocalAlloc(0, (nLen + 128) * sizeof(TCHAR));
if (NULL != lpszLogMsg)
{
//
// insert Error/Info keyword
//
if (FAILED(StringCchPrintf(lpszLogMsg, ARRAYSIZE(lpszLogMsg), _T("%s Line %d: 0x%08x: %s"), pszErrorInfo, m_LineNum, dwErrCode, lpszBuffer)))
{
// Couldn't build the right string, so just output the system msg
LocalFree(lpszLogMsg);
lpszLogMsg = lpszBuffer;
}
}
else
{
//
// failed to get buffer? unlikely, anyway,
// we have no option but just output the system msg
//
lpszLogMsg = lpszBuffer;
}
//
// write log out
//
_LogOut(lpszLogMsg);
//
// clean up buffer
//
if (lpszLogMsg != lpszBuffer)
{
LocalFree(lpszLogMsg);
}
LocalFree(lpszBuffer);
}
}
}
////////////////////////////////////////////////////////////////////////
//
// similar to LogError, but try to log the system msg based
// on the error code. If the sysmsg not avail, log
// "Unknown error with error code 0x%08x"
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogErrorMsg(DWORD dwErrCode)
{
_LogFormattedMsg(dwErrCode, _T("Error"));
}
////////////////////////////////////////////////////////////////////////
//
// similar to LogErrorMsg but prepends with "Info" rather than "Error"
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogInfoMsg(DWORD dwErrCode)
{
_LogFormattedMsg(dwErrCode, _T("Info"));
}
////////////////////////////////////////////////////////////////////////
//
// log with type INTERNET, this function will do nothing
// if the Internet exclusion directive is detected from reg
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogInternet(LPCTSTR szLogFormat, ...)
{
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_INTERNET, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// log with type XML, this function will do nothing
// if the XML exclusion directive is detected from reg
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogXML(LPCTSTR szLogFormat, ...)
{
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_XML_DETAIL, szLogFormat, va);
va_end (va);
}
}
void CIULogger::_NukeCrLf(LPTSTR pszBuffer)
{
while (*pszBuffer)
{
if (_T('\r') == *pszBuffer || _T('\n') == *pszBuffer)
{
//
// Overwrite <CR> and <LF> with space
//
*pszBuffer = _T(' ');
}
pszBuffer++;
}
}
////////////////////////////////////////////////////////////////////////
//
// log BSTR containing valid XML. This gets around length limitations
// of LogOutput and attempts to break lines following ">". This
// output is sent for both fre and chk builds unless excluded from reg.
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogXmlBSTR(BSTR bstrXML)
{
USES_IU_CONVERSION;
LPTSTR pszLine;
LPTSTR pszTemp;
LPTSTR pszStop;
LPTSTR pszLastGT;
TCHAR szXmlBuff[LOG_XML_BUFF_LEN];
HRESULT hr;
if (NULL == bstrXML)
{
return;
}
if (m_fLogUsable && (m_sdwLogMask & LOG_XML_BSTR_DETAIL) )
{
#if !(defined(UNICODE) || defined(_UNICODE))
DWORD dwANSIBuffLen = SysStringLen(bstrXML) + 1;
LPSTR pszANSIBuff = (LPSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwANSIBuffLen);
if (NULL == pszANSIBuff)
{
//
// We're toast - just return without logging
//
return;
}
LPTSTR pTempTchar = OLE2T(bstrXML);
if (NULL != pTempTchar)
{
if (FAILED(StringCchCopyA(pszANSIBuff, dwANSIBuffLen, pTempTchar)))
{
goto done;
}
}
pszLine = pszANSIBuff;
#else
pszLine = bstrXML;
#endif
while (*pszLine)
{
//
// Skip <CR> & <LF> chars
//
while (_T('\r') == *pszLine || _T('\n') == *pszLine)
{
pszLine++;
if (NULL == *pszLine)
{
break;
}
}
if (NULL == *pszLine)
{
break;
}
pszTemp = pszLine;
pszStop = pszLine + LOG_XML_BUFF_LEN - 1;
pszLastGT = NULL;
//
// Try to find the last '>' char that will fit in buffer
//
while (*pszTemp && pszTemp < pszStop)
{
if (_T('>') == *pszTemp)
{
pszLastGT = pszTemp;
}
pszTemp++;
}
if (pszLastGT)
{
//
// Break the line at the last '>' that fits into LOG_XML_BUFF_LEN
//
hr = StringCchCopy(szXmlBuff, (int) (pszLastGT - pszLine) + 2, pszLine);
//
// STRSAFE_E_INSUFFICIENT_BUFFER is returned if the string is truncated.
// This is normal since we are just copying a portion of the XML at
// a time so it won't be too long to log.
//
if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
{
_NukeCrLf(szXmlBuff);
_LogOut(szXmlBuff);
pszLine = pszLastGT + 1;
}
else
{
break;
}
}
else if (*pszTemp)
{
//
// We're forced to break the line at LOG_XML_BUFF_LEN with no '>' in range
//
hr = StringCchCopy(szXmlBuff, LOG_XML_BUFF_LEN, pszLine);
if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
{
_NukeCrLf(szXmlBuff);
_LogOut(szXmlBuff);
pszLine += LOG_XML_BUFF_LEN -1;
}
else
{
break;
}
}
else
{
//
// Output any leftover XML to end of BSTR
//
_NukeCrLf(pszLine);
_LogOut(pszLine);
//
// Set to end of BSTR so we bust out of outer while
//
pszLine += lstrlen(pszLine);
}
}
#if !(defined(UNICODE) || defined(_UNICODE))
done:
if (pszANSIBuff)
{
HeapFree(GetProcessHeap(), 0, pszANSIBuff);
pszANSIBuff = NULL;
}
#endif
}
}
////////////////////////////////////////////////////////////////////////
//
// log with type SOFTWARE, this function will do nothing
// if the SOFTWARE exclusion directive is detected from reg
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogSoftware(LPCTSTR szLogFormat, ...)
{
USES_IU_CONVERSION;
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_SOFTWARE, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// log with type DOWNLOAD, this function will do nothing
// if the LogDownload exclusion directive is detected from reg
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogDownload(LPCTSTR szLogFormat, ...)
{
USES_IU_CONVERSION;
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_DOWNLOAD, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// log with type DRIVER, this function will do nothing
// if the DRIVER exclusion directive is detected from reg
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogDriver(LPCTSTR szLogFormat, ...)
{
USES_IU_CONVERSION;
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_DRIVER, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// log with type CHECKTRUST, this function will do nothing
// if the CHECKTRUST exclusion directive is detected from reg
//
////////////////////////////////////////////////////////////////////////
void CIULogger::LogTrust(LPCTSTR szLogFormat, ...)
{
USES_IU_CONVERSION;
if (m_fLogUsable)
{
va_list va;
va_start (va, szLogFormat);
_Log(LOG_TRUST, szLogFormat, va);
va_end (va);
}
}
////////////////////////////////////////////////////////////////////////
//
// actual base logging function
// if it actually logged, or just returned
// because directives say don't make this kind of log
//
////////////////////////////////////////////////////////////////////////
void CIULogger::_Log(DWORD LogType, LPCTSTR pszLogFormat, va_list va)
{
USES_IU_CONVERSION;
TCHAR szOut[5 * 1024];
LPTSTR pszFormat;
DWORD dwFormatLen;
if (!m_fLogUsable || (0x0 == (m_sdwLogMask & LogType)) || NULL == pszLogFormat)
{
return;
}
if (LOG_ERROR == LogType)
{
//
// for error case, we try to add "Error Line %d: " in front of the log
//
dwFormatLen = lstrlen(pszLogFormat) + 128;
pszFormat = (TCHAR*) MemAlloc(dwFormatLen * sizeof(TCHAR));
if (NULL != pszFormat)
{
if (FAILED(StringCchPrintf(pszFormat, dwFormatLen, _T("Error Line %d: %s"), m_LineNum, pszLogFormat)))
{
pszFormat = (LPTSTR)pszLogFormat;
}
}
else
{
pszFormat = (LPTSTR)pszLogFormat;
}
}
else
{
pszFormat = (LPTSTR)pszLogFormat;
}
if (SUCCEEDED(StringCchVPrintf(szOut, ARRAYSIZE(szOut), pszFormat, va)))
{
_LogOut(szOut);
}
return;
}
//
// function to write the log to log file
// also taking care of indentation
//
void CIULogger::_LogOut(LPTSTR pszLog)
{
if (NULL == pszLog)
return;
//
// Protect static variables and indent values
//
EnterCriticalSection(&g_LogCs);
int n = GetIndent();
int i,
nLogLen, // length of log string passed in
nTotalLen; // length of constructed
HANDLE hHeap = GetProcessHeap();
LPTSTR pszWholeLog;
LPTSTR pszCurrentPos;
DWORD dwCurrentLen;
DWORD dwWritten;
TCHAR szTab = (m_siIndentStep < 1) ? szTab = _T('\t') : szTab = _T(' ');
//
// find out length for log header
//
if (m_siIndentStep > 0)
{
//
// if positive number, it means the number of
// space chars to use for each indent, rather
// than using a tab
//
n *= m_siIndentStep;
}
nLogLen = lstrlen(pszLog);
//
// verify this log is \r\n ended
//
if (nLogLen > 1 && _T('\n') == pszLog[nLogLen-1])
{
//
// if there is no catriege return, just a \n,
// then remove \n
//
if (_T('\r') != pszLog[nLogLen-2])
{
nLogLen--;
pszLog[nLogLen] = _T('\0');
}
}
nTotalLen = n + sizeof(szLogHeaderFmt)/sizeof(TCHAR) + nLogLen + 3;
//
// allocate memory to construct the log
//
pszWholeLog = (LPTSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, nTotalLen * sizeof(TCHAR));
if (NULL == pszWholeLog)
{
//
// nothing we can do in this case, bail.
//
LeaveCriticalSection(&g_LogCs);
return;
}
//
// get log header
//
GetLogHeader(pszWholeLog, nTotalLen);
//
// construct indent
//
pszCurrentPos = pszWholeLog + lstrlen(pszWholeLog);
dwCurrentLen = nTotalLen - lstrlen(pszWholeLog);
for (i = 0; i < n; i++)
{
pszCurrentPos[i] = szTab;
}
pszCurrentPos[i] = _T('\0');
//
// add log to whilelog buffer
//
if (FAILED(StringCchCat(pszCurrentPos, dwCurrentLen, pszLog)))
{
goto done;
}
//
// Always terminate lines with <CR> <LF>
//
if (_T('\n') != pszLog[nLogLen-1])
{
if (FAILED(StringCchCat(pszCurrentPos, dwCurrentLen, _T("\r\n"))))
{
goto done;
}
}
//
// write log
//
nTotalLen = lstrlen(pszWholeLog);
if (m_fLogFile)
{
if (TRUE == AcquireMutex())
{
//
// Another module (e.g. if we are iuengine, maybe iuctl) may have written
// to the iu_xxx.log file, so we need to seek to the end before writing
//
SetFilePointer(m_shFile, 0, NULL, FILE_END);
WriteFile(m_shFile, pszWholeLog, nTotalLen * sizeof(TCHAR), &dwWritten, NULL);
if (m_fFlushEveryTime)
{
FlushFileBuffers(m_shFile);
}
ReleaseMutex();
}
}
if (m_fLogDebugMsg)
{
OutputDebugString(pszWholeLog);
}
done:
HeapFree(hHeap, 0, pszWholeLog);
LeaveCriticalSection(&g_LogCs);
return;
}
//////////////////////////////////////////////////////////////////////
//
// Timestamp Helper
//
//////////////////////////////////////////////////////////////////////
void CIULogger::GetLogHeader(LPTSTR pszBuffer, DWORD cchBufferLen)
{
SYSTEMTIME st = {0};
if (pszBuffer == NULL)
{
return;
}
GetLocalTime(&st);
//
// print out as the pre-defined format:
// szTimeStampFmt[]
//
if (FAILED(StringCchPrintf(pszBuffer, cchBufferLen,
_T("%4d/%02d/%02d|%02d:%02d:%02d:%03d|%08x| "),
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds,
m_dwThreadId)))
{
// It wont fit, just set it to an empty string
pszBuffer[0] = 0;
}
}
//
// function to retrieve the indent of current thread
//
int CIULogger::GetIndent(void)
{
if (m_Index < 0 || !m_fLogUsable)
{
return 0;
}
else
{
return m_psIndent[m_Index].iIndent;
}
}
//
// function to change indention of current thread
//
void CIULogger::SetIndent(int IndentDelta)
{
int i;
bool fQuit = false;
EnterCriticalSection(&g_LogCs);
if (m_Index < 0)
{
//
// try to find the index
//
if (NULL == m_psIndent)
{
//
// if no indent array created yet
//
m_psIndent = (_THREAD_INDENT*)
HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
c_IndentArrayChunk * sizeof(_THREAD_INDENT)
);
if (NULL != m_psIndent)
{
m_Size = c_IndentArrayChunk;
}
else
{
LeaveCriticalSection(&g_LogCs);
return;
}
}
for (i = 0; i < m_Size && m_psIndent[i].dwThreadId != 0; i++)
{
if (m_psIndent[i].dwThreadId == m_dwThreadId)
{
m_Index = i;
break;
}
}
if (m_Index < 0)
{
//
// this thread is not in the array yet
//
for (i = 0; i < m_Size; i++)
{
if (0 == m_psIndent[i].dwThreadId)
{
break;
}
}
if (i < m_Size)
{
//
// fill the next empty slot in array
//
m_psIndent[i].dwThreadId = m_dwThreadId;
m_psIndent[i].iIndent = 0;
m_Index = i;
}
else
{
//
// array is full, no empty slot anymore
// need to increase the indent array size
//
int iSize = m_Size + c_IndentArrayChunk;
_THREAD_INDENT* pNewArray = (_THREAD_INDENT*)
HeapReAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
m_psIndent,
iSize * sizeof(_THREAD_INDENT)
);
if (NULL != pNewArray)
{
m_psIndent = pNewArray;
m_Size = iSize;
m_psIndent[i].dwThreadId = m_dwThreadId;
m_psIndent[i].iIndent = 0;
m_Index = i;
}
}
}
}
if (m_Index >= 0)
{
m_psIndent[m_Index].iIndent += IndentDelta;
}
LeaveCriticalSection(&g_LogCs);
}
//
// read registry value helper -- protected by g_LogCs in ctor
//
void CIULogger::ReadRegistrySettings(void)
{
//
// declare constants used to retrive logging settings
//
const TCHAR REGKEY_IUTCTL[] = _T("Software\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\IUControlLogging");
const TCHAR REGVAL_LOGFILE[] = _T("Logging File");
const TCHAR REGVAL_LOGDEBUGMSG[] = _T("Logging DebugMsg");
const TCHAR REGVAL_LOGINDENT[] = _T("LogIndentStep");
const TCHAR REGVAL_LOGNOBLOCK[] = _T("LogExcludeBlock");
const TCHAR REGVAL_LOGNOXML[] = _T("LogExcludeXML");
const TCHAR REGVAL_LOGNOXMLBSTR[] = _T("LogExcludeXmlBSTR");
const TCHAR REGVAL_LOGNOINET[] = _T("LogExcludeInternet");
const TCHAR REGVAL_LOGNODRIVER[] = _T("LogExcludeDriver");
const TCHAR REGVAL_LOGNOSW[] = _T("LogExcludeSoftware");
const TCHAR REGVAL_LOGNOTRUST[] = _T("LogExcludeTrust");
const TCHAR REGVAL_LOGDOWNLOAD[] = _T("LogExcludeDownload");
const TCHAR REGVAL_LOGFLUSH[] = _T("FlushLogEveryTime"); // added by charlma 11/27/01 to improve logging performance
// only flush everytime if this flag is set to 1
HKEY hKey = NULL;
TCHAR szFilePath[MAX_PATH] = {0};
DWORD dwSize = sizeof(szFilePath);
DWORD dwData;
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUTCTL, 0, KEY_READ, &hKey))
{
//
// there is no reg key setting available, so we will not
// output any log to anywhere - this is the released mode
//
return;
}
//
// try to read out the file path for log file.
//
if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGVAL_LOGFILE, 0, 0, (LPBYTE)&szFilePath, &dwSize) && dwSize > 0 && szFilePath[0] != _T('\0'))
{
TCHAR szLogFile[MAX_PATH];
TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFName[_MAX_FNAME], szExt[_MAX_EXT];
//
// TODO: changed to use private version splitpath()
//
//_tsplitpath(szFilePath, szDrive, szDir, szFName, szExt);
MySplitPath(szFilePath, szDrive, szDir, szFName, szExt);
//
// construct the log file name with process id embedded
//
if (FAILED(StringCchPrintf(szLogFile, ARRAYSIZE(szLogFile),
_T("%s%s%s_%d%s"),
szDrive,
szDir,
szFName,
GetCurrentProcessId(),
szExt)))
{
// Can't construct log filename, so nothing we can do.
RegCloseKey(hKey);
return;
}
m_shFile = CreateFile(
szLogFile,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL);
if (INVALID_HANDLE_VALUE != m_shFile)
{
if (INVALID_SET_FILE_POINTER == SetFilePointer(m_shFile, 0, NULL, FILE_END))
{
CloseHandle(m_shFile);
m_shFile = INVALID_HANDLE_VALUE;
}
else
{
//
// we have successfully opened the log file
// so increase the indent level to 0 for
// top level logging. this will cause
// the indent array created
//
SetIndent(0);
//
// Unicode files need a 0xFEFF header
//
#if defined(UNICODE) || defined(_UNICODE)
const WORD wUnicodeHeader = 0xFEFF;
//
// if the file is zero length, then this is a new file
// we need to add unicode header
//
DWORD dwFileSize;
if ( -1 != (dwFileSize = GetFileSize(m_shFile, NULL)))
{
if (0 == dwFileSize)
{
WriteFile(m_shFile, &wUnicodeHeader, sizeof(WORD), &dwFileSize, NULL);
}
}
#endif
}
//
// Now create the Mutex we will use to protect future writes (we are in global ctor now...)
//
// construct the log file name with process id embedded, but no drive or '\' in path
// so we can use it to name our mutex (file will be per-process).
//
if (FAILED(StringCchPrintf(szLogFile, ARRAYSIZE(szLogFile),
_T("%s_%d%s"),
szFName,
GetCurrentProcessId(),
szExt)))
{
// If that doesn't work, just use a simple named mutex
m_hMutex = ::CreateMutex(NULL, FALSE, szFName);
}
else
{
m_hMutex = ::CreateMutex(NULL, FALSE, szLogFile);
}
}
}
//
// try to find out if we should output debug msg to debugger
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGDEBUGMSG, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
m_fLogDebugMsg = true;
}
//
// keep reading other *optional* log directives
//
//
// read whether we should exlude block data
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNOBLOCK, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_BLOCK);
}
//
// read whether we should exlude XML related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNOXML, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_XML_DETAIL);
}
//
// read whether we should exlude XML BSTR related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNOXMLBSTR, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_XML_BSTR_DETAIL);
}
//
// read whether we should exlude internet related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNOINET, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_INTERNET);
}
//
// read whether we should exlude driver related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNODRIVER, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_DRIVER);
}
//
// read whether we should exlude driver related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNOSW, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_SOFTWARE);
}
//
// read whether we should exlude wintrust checking related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGNOTRUST, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_TRUST);
}
//
// read whether we should exlude wintrust checking related logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGDOWNLOAD, 0, 0, (LPBYTE)&dwData, &dwSize)
&& (0x1 == dwData))
{
//
// remove block logging bit
//
m_sdwLogMask &= (~LOG_DOWNLOAD);
}
//
// read whether we should use tab or space(s) for each indent step
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGINDENT, 0, 0, (LPBYTE)&dwData, &dwSize)
&& ((int)dwData > 0))
{
//
// use space char(s) (_T(' ')). If nagetive or 0, _Logout will use tab char
//
m_siIndentStep = (int) dwData;
}
//
// read whether we should flush everytime we do file logging
//
dwData = 0x0;
dwSize = sizeof(dwData);
if (m_shFile != INVALID_HANDLE_VALUE &&
(ERROR_SUCCESS ==
RegQueryValueEx(hKey, REGVAL_LOGFLUSH, 0, 0, (LPBYTE)&dwData, &dwSize)))
{
m_fFlushEveryTime = (0x1 == dwData);
}
//
// finished registry checking
//
RegCloseKey(hKey);
}
#endif // defined(DBG)