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.
634 lines
20 KiB
634 lines
20 KiB
//#pragma title( "Err.cpp - Basic error handling/message/logging" )
|
|
/*
|
|
Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
|
|
===============================================================================
|
|
Module - Err.cpp
|
|
System - Common
|
|
Author - Tom Bernhardt, Rich Denham
|
|
Created - 1994-08-22
|
|
Description - Implements the TError class that handles basic exception
|
|
handling, message generation, and logging functions.
|
|
Updates - 1997-09-12 RED replace TTime class
|
|
===============================================================================
|
|
*/
|
|
|
|
#ifdef USE_STDAFX
|
|
# include "stdafx.h"
|
|
#else
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
#ifndef WIN16_VERSION
|
|
#include <lm.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <share.h>
|
|
#include <time.h>
|
|
#include <rpc.h>
|
|
#include <rpcdce.h>
|
|
#include <sys\types.h>
|
|
#include <sys\stat.h>
|
|
|
|
#include "Common.hpp"
|
|
#include "Err.hpp"
|
|
#include "UString.hpp"
|
|
#include <ResStr.h>
|
|
#include "TReg.hpp"
|
|
|
|
#define TERR_MAX_MSG_LEN (2000)
|
|
#define BYTE_ORDER_MARK (0xFEFF)
|
|
|
|
TCriticalSection csLogError;
|
|
|
|
TError::TError(
|
|
int displevel ,// in -mimimum severity level to display
|
|
int loglevel ,// in -mimimum severity level to log
|
|
WCHAR const * filename ,// in -file name of log (NULL if none)
|
|
int logmode ,// in -0=replace, 1=append
|
|
int beeplevel // in -min error level for beeping
|
|
)
|
|
{
|
|
lastError = 0;
|
|
maxError = 0;
|
|
logLevel = loglevel;
|
|
dispLevel = displevel;
|
|
logFile = INVALID_HANDLE_VALUE;
|
|
beepLevel = beeplevel;
|
|
bWriteOnCurPos = FALSE;
|
|
LogOpen(filename, logmode, loglevel);
|
|
}
|
|
|
|
|
|
TError::~TError()
|
|
{
|
|
LogClose();
|
|
}
|
|
|
|
// Closes any existing open logFile and opens a new log file if the fileName is
|
|
// not null. If it is a null string, then a default fileName of "Temp.log" is
|
|
// used.
|
|
BOOL
|
|
TError::LogOpen(
|
|
WCHAR const * fileName ,// in -name of file including any path
|
|
int mode ,// in -0=overwrite, 1=append
|
|
int level ,// in -minimum level to log
|
|
bool bBeginNew // in -begin a new log file
|
|
)
|
|
{
|
|
BOOL retval=TRUE;
|
|
|
|
if ( logFile != INVALID_HANDLE_VALUE )
|
|
{
|
|
CloseHandle(logFile);
|
|
logFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if ( fileName && fileName[0] )
|
|
{
|
|
// Check to see if the file already exists
|
|
WIN32_FIND_DATA fDat;
|
|
HANDLE hFind;
|
|
BOOL bExisted = FALSE;
|
|
|
|
hFind = FindFirstFile(fileName,&fDat);
|
|
if ( hFind != INVALID_HANDLE_VALUE )
|
|
{
|
|
FindClose(hFind);
|
|
|
|
if (bBeginNew)
|
|
{
|
|
// rename existing log file
|
|
|
|
// get next sequence number from registry
|
|
DWORD dwSequence = 1;
|
|
static WCHAR c_szValueName[] = L"LogSeqNum";
|
|
TRegKey key(GET_STRING(IDS_HKLM_DomainAdmin_Key));
|
|
key.ValueGetDWORD(c_szValueName, &dwSequence);
|
|
|
|
// split path components
|
|
WCHAR szPath[_MAX_PATH];
|
|
WCHAR szDrive[_MAX_DRIVE];
|
|
WCHAR szDir[_MAX_DIR];
|
|
WCHAR szFName[_MAX_FNAME];
|
|
WCHAR szExt[_MAX_EXT];
|
|
_wsplitpath(fileName, szDrive, szDir, szFName, szExt);
|
|
|
|
// find name for backup that isn't already used...
|
|
|
|
for (bool bFoundName = false; bFoundName == false; dwSequence++)
|
|
{
|
|
// generate backup name using the sequence number
|
|
WCHAR szFNameSeq[_MAX_FNAME];
|
|
wsprintf(szFNameSeq, L"%s %04lu", szFName, dwSequence);
|
|
|
|
// make path from path components
|
|
_wmakepath(szPath, szDrive, szDir, szFNameSeq, szExt);
|
|
|
|
// check if file exists
|
|
WIN32_FIND_DATA fd;
|
|
HANDLE hFind = FindFirstFile(szPath, &fd);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
if (dwError == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
bFoundName = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FindClose(hFind);
|
|
}
|
|
}
|
|
|
|
if (bFoundName)
|
|
{
|
|
// attempt to rename file
|
|
if (MoveFile(fileName, szPath))
|
|
{
|
|
// save next sequence number in registry
|
|
key.ValueSetDWORD(c_szValueName, dwSequence);
|
|
}
|
|
else
|
|
{
|
|
bExisted = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!bExisted)
|
|
{
|
|
// get log history value from registry
|
|
|
|
TRegKey keyHistory(GET_STRING(IDS_HKLM_DomainAdmin_Key));
|
|
|
|
DWORD dwHistory = 20;
|
|
static WCHAR c_szValueName[] = L"LogHistory";
|
|
|
|
if (keyHistory.ValueGetDWORD(c_szValueName, &dwHistory) == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
keyHistory.ValueSetDWORD(c_szValueName, dwHistory);
|
|
}
|
|
|
|
keyHistory.Close();
|
|
|
|
if (dwSequence > dwHistory)
|
|
{
|
|
DWORD dwMinimum = dwSequence - dwHistory;
|
|
|
|
// generate migration log path specification
|
|
|
|
WCHAR szFNameSpec[_MAX_FNAME];
|
|
wsprintf(szFNameSpec, L"%s *", szFName);
|
|
_wmakepath(szPath, szDrive, szDir, szFNameSpec, szExt);
|
|
|
|
// for each migration older than minimum
|
|
|
|
WIN32_FIND_DATA fd;
|
|
|
|
HANDLE hFind = FindFirstFile(szPath, &fd);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
DWORD dwFileSequence;
|
|
|
|
if (swscanf(fd.cFileName, L"%*s %lu", &dwFileSequence) == 1)
|
|
{
|
|
// if file sequence less than minimum to keep...
|
|
|
|
if (dwFileSequence < dwMinimum)
|
|
{
|
|
// delete file
|
|
WCHAR szDeleteName[_MAX_FNAME];
|
|
_wsplitpath(fd.cFileName, 0, 0, szDeleteName, 0);
|
|
WCHAR szDeletePath[_MAX_PATH];
|
|
_wmakepath(szDeletePath, szDrive, szDir, szDeleteName, szExt);
|
|
DeleteFile(szDeletePath);
|
|
}
|
|
}
|
|
}
|
|
while (FindNextFile(hFind, &fd));
|
|
|
|
FindClose(hFind);
|
|
}
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
else
|
|
{
|
|
// overwrite or append to existing log file
|
|
|
|
bExisted = TRUE;
|
|
}
|
|
}
|
|
|
|
logFile = CreateFile(fileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
bExisted ? OPEN_EXISTING : CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if ( logFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
retval = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// the append case
|
|
if (mode == 1)
|
|
{
|
|
if (SetFilePointer(logFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
|
|
retval = FALSE;
|
|
}
|
|
|
|
// if it is a new file or in the overwrite mode, we write the
|
|
// byte order mark into it
|
|
if (retval && (!bExisted || mode == 0) )
|
|
{
|
|
// this is a new file we've just created
|
|
// we need to write the byte order mark to the beginning of the file
|
|
WCHAR x = BYTE_ORDER_MARK;
|
|
DWORD nWritten;
|
|
if (!WriteFile(logFile, &x, sizeof(x), &nWritten, NULL))
|
|
retval = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
logLevel = level;
|
|
|
|
return retval;
|
|
}
|
|
|
|
DWORD TError::ExtendSize(DWORD dwNumOfBytes)
|
|
{
|
|
DWORD rc = ERROR_SUCCESS;
|
|
const int size = 4096; // we write in 4K chunk
|
|
BYTE* buffer; // the buffer used to initialize the stream
|
|
DWORD orig; // the starting file pointer
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
// to extend by 0 byte, we don't need to do anything
|
|
if (dwNumOfBytes > 0)
|
|
{
|
|
// get the current file pointer
|
|
if ((orig = SetFilePointer(logFile, 0, NULL, FILE_CURRENT)) != INVALID_SET_FILE_POINTER)
|
|
{
|
|
buffer = new BYTE[size];
|
|
if (buffer != NULL)
|
|
{
|
|
memset((void*)buffer, 0, size);
|
|
// seek forward dwNumOfBytes bytes, set the end of file and
|
|
// then come back to the current file pointer
|
|
if (SetFilePointer(logFile, dwNumOfBytes, NULL, FILE_CURRENT) != INVALID_SET_FILE_POINTER
|
|
&& SetEndOfFile(logFile)
|
|
&& SetFilePointer(logFile, orig, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER)
|
|
{
|
|
// initialize the buffer
|
|
DWORD nWritten = size;
|
|
while (nWritten > 0 && dwNumOfBytes > 0)
|
|
{
|
|
if (!WriteFile(logFile,
|
|
(void*)buffer,
|
|
(dwNumOfBytes > size) ? size : dwNumOfBytes,
|
|
&nWritten,
|
|
NULL))
|
|
{
|
|
// if WriteFile failed, stop writing
|
|
break;
|
|
}
|
|
dwNumOfBytes -= nWritten;
|
|
}
|
|
|
|
// if dwNumOfBytes of bytes are written, we flush the file buffer
|
|
if (dwNumOfBytes == 0)
|
|
{
|
|
FlushFileBuffers(logFile);
|
|
}
|
|
}
|
|
|
|
rc = GetLastError(); // we catch any error code here
|
|
}
|
|
else
|
|
rc = ERROR_OUTOFMEMORY;
|
|
|
|
// clean up
|
|
if (buffer)
|
|
delete[] buffer;
|
|
|
|
// we always try to restore the file pointer
|
|
if (SetFilePointer(logFile, orig, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER
|
|
&& rc == ERROR_SUCCESS)
|
|
{
|
|
// we only need to get the error if we didn't fail before
|
|
rc = GetLastError();
|
|
}
|
|
|
|
}
|
|
else
|
|
rc = GetLastError();
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Writes formatted message to log file and flushes buffers
|
|
//-----------------------------------------------------------------------------
|
|
void TError::LogWrite(WCHAR const * msg)
|
|
{
|
|
csLogError.Enter();
|
|
|
|
WCHAR sTime[32];
|
|
static WCHAR sTemp[TERR_MAX_MSG_LEN];
|
|
DWORD size = sizeof(sTemp) / sizeof(sTemp[0]);
|
|
|
|
gTTime.FormatIsoLcl( gTTime.Now( NULL ), sTime );
|
|
_snwprintf(sTemp, size - 2, L"%s %s", sTime, msg); // leave room for "\r\n"
|
|
sTemp[size - 3] = L'\0'; // make sure the string terminates
|
|
DWORD dwLen = wcslen(sTemp);
|
|
|
|
// Get rid of the <CR> from the end of the message because it causes things
|
|
// to run together in the logs
|
|
if ( sTemp[dwLen-1] == 0x0d )
|
|
sTemp[dwLen-1] = 0x00;
|
|
dwLen = wcslen(sTemp);
|
|
wcscpy(&sTemp[dwLen], L"\r\n");
|
|
dwLen = wcslen(sTemp);
|
|
|
|
if ( logFile != INVALID_HANDLE_VALUE )
|
|
{
|
|
// make sure only one write at a time in the process
|
|
criticalSection.Enter();
|
|
BOOL bCanWrite = TRUE;
|
|
|
|
if (!bWriteOnCurPos)
|
|
{
|
|
if (SetFilePointer(logFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
|
|
bCanWrite = FALSE;
|
|
}
|
|
|
|
if (bCanWrite)
|
|
{
|
|
if (WriteFile(logFile, sTemp, dwLen * sizeof(WCHAR), &size, NULL))
|
|
FlushFileBuffers(logFile);
|
|
}
|
|
|
|
criticalSection.Leave();
|
|
}
|
|
|
|
csLogError.Leave();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Error message with format and arguments
|
|
//-----------------------------------------------------------------------------
|
|
void __cdecl
|
|
TError::MsgWrite(
|
|
int num ,// in -error number/level code
|
|
WCHAR const msg[] ,// in -error message to display
|
|
... // in -printf args to msg pattern
|
|
)
|
|
{
|
|
csLogError.Enter();
|
|
|
|
static WCHAR suffix[TERR_MAX_MSG_LEN];
|
|
va_list argPtr;
|
|
|
|
va_start(argPtr, msg);
|
|
_vsnwprintf(suffix, DIM(suffix) - 1, msg, argPtr);
|
|
suffix[DIM(suffix) - 1] = L'\0';
|
|
va_end(argPtr);
|
|
MsgProcess(num, suffix);
|
|
|
|
csLogError.Leave();
|
|
}
|
|
|
|
#ifndef WIN16_VERSION
|
|
//-----------------------------------------------------------------------------
|
|
// System Error message with format and arguments
|
|
//-----------------------------------------------------------------------------
|
|
void __cdecl
|
|
TError::SysMsgWrite(
|
|
int num ,// in -error number/level code
|
|
DWORD lastRc ,// in -error return code
|
|
WCHAR const msg[] ,// in -error message/pattern to display
|
|
... // in -printf args to msg pattern
|
|
)
|
|
{
|
|
csLogError.Enter();
|
|
|
|
static WCHAR suffix[TERR_MAX_MSG_LEN];
|
|
va_list argPtr;
|
|
int len;
|
|
|
|
// When an error occurs while in a constructor for a global object,
|
|
// the TError object may not yet exist. In this case, "this" is zero
|
|
// and we gotta get out of here before we generate a protection exception.
|
|
|
|
if ( !this )
|
|
return;
|
|
|
|
va_start(argPtr, msg);
|
|
len = _vsnwprintf(suffix, DIM(suffix) - 1, msg, argPtr);
|
|
|
|
// append the system message for the lastRc at the end.
|
|
if ( len < DIM(suffix) - 1 )
|
|
{
|
|
ErrorCodeToText(lastRc, DIM(suffix) - len - 1, suffix + len);
|
|
}
|
|
suffix[DIM(suffix) - 1] = L'\0';
|
|
va_end(argPtr);
|
|
MsgProcess(num, suffix);
|
|
|
|
csLogError.Leave();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// System Error message with format and arguments
|
|
//-----------------------------------------------------------------------------
|
|
void __cdecl
|
|
TError::SysMsgWrite(
|
|
int num ,// in -error number/level code
|
|
WCHAR const msg[] ,// in -error message/pattern to display
|
|
... // in -printf args to msg pattern
|
|
)
|
|
{
|
|
csLogError.Enter();
|
|
|
|
static WCHAR suffix[TERR_MAX_MSG_LEN];
|
|
va_list argPtr;
|
|
int len;
|
|
DWORD lastRc = GetLastError();
|
|
|
|
// When an error occurs while in a constructor for a global object,
|
|
// the TError object may not yet exist. In this case, "this" is zero
|
|
// and we gotta get out of here before we generate a protection exception.
|
|
|
|
if ( !this )
|
|
return;
|
|
|
|
va_start( argPtr, msg );
|
|
len = _vsnwprintf( suffix, DIM(suffix) - 1, msg, argPtr );
|
|
|
|
// append the system message for the lastRc at the end.
|
|
if ( len < DIM(suffix) - 1 )
|
|
{
|
|
ErrorCodeToText(lastRc, DIM(suffix) - len - 1, suffix + len);
|
|
}
|
|
suffix[DIM(suffix) - 1] = L'\0';
|
|
va_end(argPtr);
|
|
MsgProcess(num, suffix);
|
|
|
|
csLogError.Leave();
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Error message format, display and exception processing function
|
|
//-----------------------------------------------------------------------------
|
|
void __stdcall
|
|
TError::MsgProcess(
|
|
int num ,// in -error number/level code
|
|
WCHAR const * str // in -error string to display
|
|
)
|
|
{
|
|
csLogError.Enter();
|
|
|
|
static WCHAR fullmsg[TERR_MAX_MSG_LEN];
|
|
struct
|
|
{
|
|
USHORT frequency; // audio frequency
|
|
USHORT duration; // duration in mSec
|
|
} audio[] = {{ 300, 20},{ 500, 50},{ 700, 100},
|
|
{ 800, 200},{1000, 300},{1500, 400},
|
|
{2500, 750},{2500,1000},{2500,1000}};
|
|
|
|
if ( num >= 0 )
|
|
level = num / 10000; // 10000's position of error number
|
|
else
|
|
level = -1;
|
|
|
|
if ( level <= 0 )
|
|
{
|
|
wcsncpy(fullmsg, str, DIM(fullmsg));
|
|
fullmsg[DIM(fullmsg) - 1] = L'\0'; // ensure null termination
|
|
}
|
|
else
|
|
{
|
|
if ( num > maxError )
|
|
maxError = num;
|
|
_snwprintf(fullmsg, DIM(fullmsg), L"%s%1d:%04d %-s", (level <= 1) ? L"WRN" : L"ERR", level, num % 10000, str);
|
|
fullmsg[DIM(fullmsg) - 1] = L'\0'; // ensure null termination
|
|
}
|
|
|
|
lastError = num;
|
|
|
|
if ( level >= beepLevel )
|
|
Beep(audio[level].frequency, audio[level].duration);
|
|
|
|
if ( level >= logLevel )
|
|
LogWrite(fullmsg);
|
|
|
|
if ( level > 4 )
|
|
{
|
|
exit(level);
|
|
}
|
|
|
|
csLogError.Leave();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Return text for error code
|
|
//-----------------------------------------------------------------------------
|
|
|
|
WCHAR *
|
|
TError::ErrorCodeToText(
|
|
DWORD code ,// in -message code
|
|
DWORD lenMsg ,// in -length of message text area
|
|
WCHAR * msg // out-returned message text
|
|
)
|
|
{
|
|
static HMODULE hNetMsg = NULL;
|
|
DWORD rc;
|
|
WCHAR * pMsg;
|
|
|
|
msg[0] = '\0'; // force to null
|
|
|
|
if ( code >= NERR_BASE && code < MAX_NERR )
|
|
{
|
|
if ( !hNetMsg )
|
|
hNetMsg = LoadLibrary(L"netmsg.dll");
|
|
rc = 1;
|
|
}
|
|
else
|
|
{
|
|
rc = DceErrorInqText( code, msg );
|
|
// Change any imbedded CR or LF to blank.
|
|
for ( pMsg = msg;
|
|
*pMsg;
|
|
pMsg++ )
|
|
{
|
|
if ( (*pMsg == L'\x0D') || (*pMsg == L'\x0A') )
|
|
*pMsg = L' ';
|
|
}
|
|
// Remove trailing blanks
|
|
for ( pMsg--;
|
|
pMsg >= msg;
|
|
pMsg-- )
|
|
{
|
|
if ( *pMsg == L' ' )
|
|
*pMsg = L'\0';
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if ( rc )
|
|
{
|
|
if ( code >= NERR_BASE && code < MAX_NERR && hNetMsg )
|
|
{
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE
|
|
| FORMAT_MESSAGE_MAX_WIDTH_MASK
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
|
| 80,
|
|
hNetMsg,
|
|
code,
|
|
0,
|
|
msg,
|
|
lenMsg,
|
|
NULL );
|
|
}
|
|
else
|
|
{
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|
|
| FORMAT_MESSAGE_MAX_WIDTH_MASK
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
|
| 80,
|
|
NULL,
|
|
code,
|
|
0,
|
|
msg,
|
|
lenMsg,
|
|
NULL );
|
|
}
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
// Err.cpp - end of file
|