windows-nt/Source/XPSP1/NT/printscan/wia/common/stirt/stilog.cpp
2020-09-26 16:20:57 +08:00

774 lines
17 KiB
C++

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
stilog.cpp
Abstract:
Class to handle file based logging
Author:
Vlad Sadovsky (VladS) 01-Sep-1997
History:
--*/
//
// Include Headers
//
#include "cplusinc.h"
#include "sticomm.h"
//
// Static definitions and variables
//
static const TCHAR szDefaultName[] = TEXT("Sti_Trace.log");
static const TCHAR szDefaultTracerName[] = TEXT("STI");
static const TCHAR szMutexNamePrefix[] = TEXT("StiTraceMutex");
static const TCHAR szColumns[] = TEXT("Severity TracerName [Process::ThreadId] Time MessageText\r\n\r\n");
static const TCHAR szOpenedLog[] = TEXT("\n****** Opened file log at %s %s .Tracer (%s) , called from [%#s::%#lx]\n");
static const TCHAR szClosedLog[] = TEXT("\n******Closed trace log on %s %s Tracer (%s) , called from [%#s::%#lx]\n");
//
// Functions
//
//
inline BOOL
FormatStdTime(
IN const SYSTEMTIME * pstNow,
IN OUT TCHAR * pchBuffer,
IN int cbBuffer
)
{
return ( GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
( LOCALE_NOUSEROVERRIDE | TIME_FORCE24HOURFORMAT|
TIME_NOTIMEMARKER),
pstNow, NULL, pchBuffer, cbBuffer)
!= 0);
} // FormatStdTime()
inline BOOL
FormatStdDate( IN const SYSTEMTIME * pstNow,
IN OUT TCHAR * pchBuffer,
IN int cbBuffer)
{
return ( GetDateFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE,
pstNow, NULL, pchBuffer, cbBuffer)
!= 0);
} // FormatStdDate()
inline TCHAR
TraceFlagToLetter(
IN DWORD dwType
)
{
if (dwType & STI_TRACE_ERROR) {
return TEXT('e');
}
else if (dwType & STI_TRACE_WARNING) {
return TEXT('w');
}
else if (dwType & STI_TRACE_INFORMATION) {
return TEXT('i');
}
else {
return TEXT('t');
}
}
STI_FILE_LOG::STI_FILE_LOG(
IN LPCTSTR lpszTracerName,
IN LPCTSTR lpszLogName,
IN DWORD dwFlags, // = 0
IN HMODULE hMessageModule // =NULL
) :
m_hMutex(INVALID_HANDLE_VALUE)
/*++
Description
Constructor function for given log object.
Arguments:
lpszSource: file name for log. Relative to Windows directory
Note:
--*/
{
LPCTSTR lpActualLogName;
TCHAR szTempName[MAX_PATH];
LPTSTR pszFilePart;
DWORD dwError;
DWORD cbName;
DWORD dwLevel;
DWORD dwMode;
BOOL fTruncate = FALSE;
m_dwSignature = SIGNATURE_FILE_LOG;
lpActualLogName = szDefaultName;
m_hLogWindow = NULL;
m_lWrittenHeader = FALSE;
m_hDefaultMessageModule = hMessageModule ? hMessageModule : GetModuleHandle(NULL);
dwLevel = STI_TRACE_ERROR;
dwMode = STI_TRACE_ADD_TIME | STI_TRACE_ADD_THREAD;
m_hLogFile = INVALID_HANDLE_VALUE;
*m_szLogFilePath = TEXT('\0');
m_dwMaxSize = STI_MAX_LOG_SIZE;
ReportError(NO_ERROR);
//
// Read global settings from the registry
//
RegEntry re(REGSTR_PATH_STICONTROL REGSTR_PATH_LOGGING,HKEY_LOCAL_MACHINE);
if (re.IsValid()) {
m_dwMaxSize = re.GetNumber(REGSTR_VAL_LOG_MAXSIZE,STI_MAX_LOG_SIZE);
//
// If we need to check append flag, read it from the registry
//
if (dwFlags & STIFILELOG_CHECK_TRUNCATE_ON_BOOT) {
fTruncate = re.GetNumber(REGSTR_VAL_LOG_TRUNCATE_ON_BOOT,FALSE);
}
}
//
// Open file for logging
//
if (lpszLogName && *lpszLogName ) {
lpActualLogName = lpszLogName;
}
//
// lpszTracerName is ANSI.
//
if (lpszTracerName && *lpszTracerName ) {
lstrcpyn(m_szTracerName,lpszTracerName,sizeof(m_szTracerName) / sizeof(m_szTracerName[0]));
m_szTracerName[sizeof(m_szTracerName) / sizeof(m_szTracerName[0]) -1] = TEXT('\0');
}
else {
lstrcpy(m_szTracerName,szDefaultTracerName);
}
//
// Approximate process name with name of the executable binary used to create process
//
*szTempName = TEXT('\0');
::GetModuleFileName(NULL,szTempName,sizeof(szTempName) / sizeof(szTempName[0]));
pszFilePart = _tcsrchr(szTempName,TEXT('\\'));
pszFilePart = (pszFilePart && *pszFilePart) ? ::CharNext(pszFilePart) : szTempName;
lstrcpyn(m_szProcessName,pszFilePart,sizeof(m_szProcessName)/sizeof(TCHAR));
m_szProcessName[sizeof(m_szProcessName)/sizeof(TCHAR) - 1] = TEXT('\0');
//
// Read flags for this logger
//
re.MoveToSubKey(lpszTracerName);
if (re.IsValid()) {
dwLevel = re.GetNumber(REGSTR_VAL_LOG_LEVEL,STI_TRACE_ERROR)
& STI_TRACE_MESSAGE_TYPE_MASK;
dwMode = re.GetNumber(REGSTR_VAL_LOG_MODE,STI_TRACE_ADD_THREAD)
& STI_TRACE_MESSAGE_FLAGS_MASK;
}
m_dwReportMode = dwLevel | dwMode;
//
// Open log file
//
cbName = ::GetWindowsDirectory(m_szLogFilePath,sizeof(m_szLogFilePath)/sizeof(m_szLogFilePath[0]));
if (( cbName == 0) || !*m_szLogFilePath ) {
ReportError(::GetLastError());
return;
}
lstrcat(lstrcat(m_szLogFilePath,TEXT("\\")),lpActualLogName);
m_hLogFile = ::CreateFile(m_szLogFilePath,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL, // security attributes
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL); // template file handle
// if ( m_hLogFile!= INVALID_HANDLE_VALUE) {
if (IS_VALID_HANDLE(m_hLogFile)) {
if(fTruncate) {
::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN );
::SetEndOfFile( m_hLogFile );
}
::SetFilePointer( m_hLogFile, 0, NULL, FILE_END);
lstrcpy(szTempName,szMutexNamePrefix);
lstrcat(szTempName,lpActualLogName);
m_hMutex = ::CreateMutex(NULL,
FALSE,
szTempName
);
if ( !IS_VALID_HANDLE(m_hMutex ) ) {
// ASSERT
}
}
if (!IS_VALID_HANDLE(m_hLogFile) || !IS_VALID_HANDLE(m_hMutex )) {
ReportError(::GetLastError());
}
else {
//
// Success
//
}
} /* STI_FILE_LOG::STI_FILE_LOG() */
STI_FILE_LOG::~STI_FILE_LOG( VOID)
/*++
Description:
Destructor function for given STI_FILE_LOG object.
--*/
{
SYSTEMTIME stCurrentTime;
TCHAR szFmtDate[20];
TCHAR szFmtTime[32];
TCHAR szTextBuffer[128];
if(m_lWrittenHeader) {
GetLocalTime(&stCurrentTime);
FormatStdDate( &stCurrentTime, szFmtDate, 15);
FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0]));
::wsprintf(szTextBuffer,szClosedLog,
szFmtDate,szFmtTime,
m_szTracerName,
m_szProcessName,
::GetCurrentThreadId()
);
WriteStringToLog(szTextBuffer);
}
if (IS_VALID_HANDLE(m_hMutex)) {
::CloseHandle(m_hMutex);
m_hMutex = INVALID_HANDLE_VALUE;
}
if (IS_VALID_HANDLE(m_hLogFile)) {
::FlushFileBuffers( m_hLogFile);
::CloseHandle(m_hLogFile);
m_hLogFile = INVALID_HANDLE_VALUE;
}
m_dwSignature = SIGNATURE_FILE_LOG_FREE;
} /* STI_FILE_LOG::~STI_FILE_LOG() */
//
// IUnknown methods. Used only for reference counting
//
STDMETHODIMP
STI_FILE_LOG::QueryInterface( REFIID riid, LPVOID * ppvObj)
{
return E_FAIL;
}
STDMETHODIMP_(ULONG)
STI_FILE_LOG::AddRef( void)
{
::InterlockedIncrement(&m_cRef);
return m_cRef;
}
STDMETHODIMP_(ULONG)
STI_FILE_LOG::Release( void)
{
LONG cNew;
if(!(cNew = ::InterlockedDecrement(&m_cRef))) {
delete this;
}
return cNew;
}
void
STI_FILE_LOG::
WriteLogSessionHeader(
VOID
)
/*++
Description
Arguments:
Note:
--*/
{
SYSTEMTIME stCurrentTime;
TCHAR szFmtDate[32];
TCHAR szFmtTime[32];
TCHAR szTextBuffer[128];
GetLocalTime(&stCurrentTime);
FormatStdDate( &stCurrentTime, szFmtDate, sizeof(szFmtDate) / sizeof(szFmtDate[0]));
FormatStdTime( &stCurrentTime, szFmtTime, sizeof(szFmtTime) / sizeof(szFmtTime[0]));
::wsprintf(szTextBuffer,szOpenedLog,
szFmtDate,szFmtTime,
m_szTracerName,
m_szProcessName,
::GetCurrentThreadId()
);
WriteStringToLog(szTextBuffer);
WriteStringToLog(szColumns);
}
void
STI_FILE_LOG::
ReportMessage(
DWORD dwType,
LPCTSTR pszMsg,
...
)
/*++
Description
Arguments:
Note:
--*/
{
va_list list;
TCHAR *pchBuff = NULL;
DWORD cch;
va_start (list, pszMsg);
pchBuff = NULL;
cch = ::FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING,
pszMsg,
0,
0,
(LPTSTR) &pchBuff,
1024,
&list
);
vReportMessage(dwType,pszMsg,list);
va_end(list);
}
void
STI_FILE_LOG::
ReportMessage(
DWORD dwType,
DWORD idMessage,
...
)
/*++
Description
Arguments:
Note:
--*/
{
va_list list;
TCHAR *pchBuff = NULL;
DWORD cch;
va_start (list, idMessage);
//
// Read message and add inserts
//
pchBuff = NULL;
cch = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_MAX_WIDTH_MASK |
FORMAT_MESSAGE_FROM_HMODULE,
m_hDefaultMessageModule ,
idMessage,
0,
(LPTSTR) &pchBuff,
1024,
&list
);
if (pchBuff && cch) {
vReportMessage(dwType,pchBuff,list);
LocalFree(pchBuff);
}
va_end(list);
}
void
STI_FILE_LOG::
vReportMessage(
DWORD dwType,
LPCTSTR pszMsg,
va_list arglist
)
/*++
Description
Arguments:
Note:
--*/
{
TCHAR achTextBuffer[1024];
DWORD dwReportMode;
if ((QueryError() != NOERROR) ||
(!m_hLogFile || m_hLogFile == INVALID_HANDLE_VALUE)) {
return;
}
// Do we need to display this message ?
if (!((dwType & m_dwReportMode) & STI_TRACE_MESSAGE_TYPE_MASK)) {
return;
}
// Start message with type
*achTextBuffer = TraceFlagToLetter(dwType);
*(achTextBuffer+1) = TEXT(' ');
*(achTextBuffer+2) = TEXT('\0');
// Add name of tracer source
lstrcat(lstrcat(achTextBuffer,m_szTracerName),TEXT(" "));;
dwReportMode = m_dwReportMode | (dwType & STI_TRACE_MESSAGE_FLAGS_MASK);
//
// Prepare header if needed
//
if (dwReportMode & STI_TRACE_MESSAGE_FLAGS_MASK ) {
if (dwReportMode & STI_TRACE_ADD_THREAD ) {
//
// Add process::thread ID
//
TCHAR szThreadId[40];
::wsprintf(szThreadId,TEXT("[%#s::%#lx] "),m_szProcessName,::GetCurrentThreadId());
::lstrcat(::lstrcat(achTextBuffer,szThreadId),TEXT(" "));
}
if (dwReportMode & STI_TRACE_ADD_TIME ) {
// Add current time
SYSTEMTIME stCurrentTime;
TCHAR szFmtTime[32];
*szFmtTime = TEXT('\0');
::GetLocalTime(&stCurrentTime);
FormatStdTime( &stCurrentTime, szFmtTime, 15);
lstrcat(lstrcat(achTextBuffer,szFmtTime),TEXT(" "));;
}
}
lstrcat(achTextBuffer,TEXT("\t"));
//
// Now add message text itself
//
::wvsprintf(achTextBuffer+::lstrlen(achTextBuffer), pszMsg, arglist);
::lstrcat(achTextBuffer , TEXT("\r\n"));
//
// Write to the file, flushing buffers if message type is ERROR
//
WriteStringToLog(achTextBuffer,(dwType & STI_TRACE_MESSAGE_TYPE_MASK) & STI_TRACE_ERROR);
}
VOID
STI_FILE_LOG::
WriteStringToLog(
LPCTSTR pszTextBuffer,
BOOL fFlush // = FALSE
)
{
DWORD dwcbWritten;
LONG lHeaderWrittenFlag;
//
// Integrity check. Make sure the mutex and file handles are valid - if not,
// then bail.
//
if (!(IS_VALID_HANDLE(m_hMutex) && IS_VALID_HANDLE(m_hLogFile))) {
#ifdef DEBUG
//OutputDebugString(TEXT("STILOG File or Mutex handle is invalid. "));
#endif
return ;
}
//
// If had not done yet, write session header here. Note this is recursive call
// and flag marking header should be set first.
//
lHeaderWrittenFlag = InterlockedExchange(&m_lWrittenHeader,1L);
if (!lHeaderWrittenFlag) {
WriteLogSessionHeader();
}
TAKE_MUTEX t(m_hMutex);
//
// Check that log file size is not exceeding the limit. Assuming log file size is set to fit
// into 32bit field, so we don't bother looking at high dword.
//
// Nb: We are not saving backup copy of the log, expecting that it never grows that large
//
BY_HANDLE_FILE_INFORMATION fi;
if (!GetFileInformationByHandle(m_hLogFile,&fi)) {
#ifdef DEBUG
OutputDebugString(TEXT("STILOG could not get file size for log file. "));
#endif
return ;
}
if ( fi.nFileSizeHigh !=0 || (fi.nFileSizeLow > m_dwMaxSize) ){
::SetFilePointer( m_hLogFile, 0, NULL, FILE_BEGIN );
::SetEndOfFile( m_hLogFile );
::GetFileInformationByHandle(m_hLogFile,&fi);
}
#ifdef USE_FILE_LOCK
::LockFile(m_hLogFile,fi.nFileSizeLow,fi.nFileSizeHigh,4096,0);
#endif
::SetFilePointer( m_hLogFile, 0, NULL, FILE_END);
#ifdef _UNICODE
UINT len = lstrlen(pszTextBuffer);
CHAR *ansiBuffer = (CHAR *)alloca(len + 2);
if(ansiBuffer) {
::WideCharToMultiByte(CP_ACP,
0,
pszTextBuffer,
-1,
ansiBuffer,
len + 1,
NULL,
NULL);
::WriteFile(m_hLogFile,
ansiBuffer,
len,
&dwcbWritten,
NULL);
}
#else
::WriteFile(m_hLogFile,
pszTextBuffer,
lstrlen(pszTextBuffer),
&dwcbWritten,
NULL);
#endif
#ifdef USE_FILE_LOCK
::UnlockFile(m_hLogFile,fi.nFileSizeLow,fi.nFileSizeHigh,4096,0);
#endif
if (fFlush) {
// Flush buffers to disk
FlushFileBuffers(m_hLogFile);
}
if(QueryReportMode() & STI_TRACE_LOG_TOUI) {
ULONG_PTR dwResult;
SendMessageTimeout(m_hLogWindow,
STIMON_MSG_LOG_MESSAGE,
0,
(LPARAM)pszTextBuffer,
0,
100,
&dwResult
);
}
#ifdef DEBUG
::OutputDebugString(pszTextBuffer);
#endif
}
//
// C-APIs
//
HANDLE
WINAPI
CreateStiFileLog(
IN LPCTSTR lpszTracerName,
IN LPCTSTR lpszLogName,
IN DWORD dwReportMode
)
/*++
Description
Arguments:
Note:
--*/
{
HANDLE hRet = INVALID_HANDLE_VALUE;
STI_FILE_LOG* pStiFileLog = NULL;
pStiFileLog = new STI_FILE_LOG(lpszTracerName,lpszLogName);
if(pStiFileLog){
if (pStiFileLog->IsValid()) {
hRet = static_cast<HANDLE>(pStiFileLog);
pStiFileLog->SetReportMode(pStiFileLog->QueryReportMode() | dwReportMode);
} else {
//
// Notice that we delete this object rather than calling
// CloseStiFileLog. We do this because it the filelog is
// not valid (maybe it had an internal creation error),
// CloseStiFileLog won't try to delete it, hence we delete
// it here.
//
delete pStiFileLog;
pStiFileLog = NULL;
hRet = INVALID_HANDLE_VALUE;
}
} else {
::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return hRet;
}
DWORD
WINAPI
CloseStiFileLog(
IN HANDLE hFileLog
)
{
STI_FILE_LOG* pStiFileLog = NULL;
pStiFileLog = static_cast<STI_FILE_LOG*>(hFileLog);
if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) ||
!pStiFileLog->IsValid()) {
::SetLastError(ERROR_INVALID_HANDLE);
return ERROR_INVALID_HANDLE;
}
delete pStiFileLog;
return NOERROR;
}
DWORD
WINAPI
ReportStiLogMessage(
IN HANDLE hFileLog,
IN DWORD dwType,
IN LPCTSTR psz,
...
)
{
STI_FILE_LOG* pStiFileLog = NULL;
pStiFileLog = static_cast<STI_FILE_LOG*>(hFileLog);
if (IsBadWritePtr(pStiFileLog,sizeof(STI_FILE_LOG)) ||
!pStiFileLog->IsValid()) {
::SetLastError(ERROR_INVALID_HANDLE);
return ERROR_INVALID_HANDLE;
}
va_list list;
va_start (list, psz);
pStiFileLog->vReportMessage(dwType,psz,list);
va_end(list);
return NOERROR;
}
/********************************* End of File ***************************/