Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1445 lines
38 KiB

//+----------------------------------------------------------------------------
//
// File: cmlog.cpp
//
// Module: CMLOG.LIB
//
// Synopsis: Connection Manager Logging File i/o class
//
// Copyright (c) 1998-2000 Microsoft Corporation
//
// Author: 25-May-2000 SumitC Created
//
// Note:
//
//-----------------------------------------------------------------------------
#define CMLOG_IMPLEMENTATION
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
#include "cmmaster.h"
#include "CmLogStr.h"
#include "cmlog.h"
#include "cmlogutil.h"
#include "getmodulever.cpp"
//
// Constants
//
LPCTSTR c_szSep = TEXT("\\");
LPCTSTR c_szDotLog = TEXT(".log");
LPCTSTR c_szNewLine = TEXT("\r\n");
LPCTSTR c_szEmpty = TEXT("");
#define CHECKEMPTY(sz) ((sz) ? (sz) : c_szEmpty)
LPCTSTR c_szLineOfStars = TEXT("******************************************************************");
LPCTSTR c_szFieldSeparator = TEXT("\t");
//
// Byte order mark constant, written as the first two bytes to a Unicode file to mark it as such
//
const WCHAR c_wchBOM = BYTE_ORDER_MARK;
//
// Globals
//
extern HINSTANCE g_hInst;
//
// utility macros
//
#define INBETWEEN(x, a, b) ( ( (x) >= (a) ) && ( (x) <= (b) ) )
//
// local function declarations
//
LPTSTR GetLogDesc(_CMLOG_ITEM eItem);
LPTSTR GetLogFormat(_CMLOG_ITEM eItem, BOOL fUnicode);
typedef struct _CM_LOG_ITEM_DESC
{
enum _CMLOG_ITEM eLogItem; // id of the log item (enum is in cmlog.h)
UINT idDesc; // resource id of the description string
UINT idFormat; // resource id of the format string used
}
CMLOGITEM;
//
//
// Array with information about each log entry. All logging is driven by this table.
// See above for column details.
//
//
static CMLOGITEM s_aCmLogItems[] =
{
{ LOGGING_ENABLED_EVENT, IDS_LOGDESC_LOGENABLED, 0 },
{ LOGGING_DISABLED_EVENT, IDS_LOGDESC_LOGDISABLED, 0 },
{ PREINIT_EVENT, IDS_LOGDESC_PREINIT, IDS_LOGFMT_PREINIT, },
{ PRECONNECT_EVENT, IDS_LOGDESC_PRECONNECT, IDS_LOGFMT_PRECONNECT },
{ PREDIAL_EVENT, IDS_LOGDESC_PREDIAL, IDS_LOGFMT_PREDIAL },
{ PRETUNNEL_EVENT, IDS_LOGDESC_PRETUNNEL, IDS_LOGFMT_PRETUNNEL },
{ CONNECT_EVENT, IDS_LOGDESC_CONNECT, 0 },
{ CUSTOMACTIONDLL, IDS_LOGDESC_CUSTOMACTIONDLL, IDS_LOGFMT_CUSTOMACTIONDLL },
{ CUSTOMACTIONEXE, IDS_LOGDESC_CUSTOMACTIONEXE, IDS_LOGFMT_CUSTOMACTIONEXE },
{ CUSTOMACTION_NOT_ALLOWED, IDS_LOGDESC_CUSTOMACTION_NOT_ALLOWED, IDS_LOGFMT_CUSTOMACTION_NOT_ALLOWED},
{ CUSTOMACTION_WONT_RUN, IDS_LOGDESC_CUSTOMACTION_WONT_RUN, IDS_LOGFMT_CUSTOMACTION_WONT_RUN},
{ DISCONNECT_EVENT, IDS_LOGDESC_DISCONNECT, IDS_LOGFMT_DISCONNECT },
{ RECONNECT_EVENT, IDS_LOGDESC_RECONNECT, 0 },
{ RETRY_AUTH_EVENT, IDS_LOGDESC_RETRYAUTH, 0 },
{ CALLBACK_NUMBER_EVENT, IDS_LOGDESC_CALLBACKNUMBER, IDS_LOGFMT_CALLBACKNUMBER },
{ PASSWORD_EXPIRED_EVENT, IDS_LOGDESC_PWDEXPIRED, IDS_LOGFMT_PWDEXPIRED },
{ PASSWORD_RESET_EVENT, IDS_LOGDESC_PWDRESET, IDS_LOGFMT_PWDRESET },
{ CUSTOM_BUTTON_EVENT, IDS_LOGDESC_CUSTOMBUTTON, 0 },
{ ONCANCEL_EVENT, IDS_LOGDESC_ONCANCEL, 0 },
{ ONERROR_EVENT, IDS_LOGDESC_ONERROR, IDS_LOGFMT_ONERROR },
{ CLEAR_LOG_EVENT, IDS_LOGDESC_CLEARLOG, 0 },
{ DISCONNECT_EXT, IDS_LOGDESC_EXT_DISCONNECT, 0 },
{ DISCONNECT_INT_MANUAL, IDS_LOGDESC_INT_DISCONNECT_MANUAL, 0 },
{ DISCONNECT_INT_AUTO, IDS_LOGDESC_INT_DISCONNECT_AUTO, 0 },
{ DISCONNECT_EXT_LOST_CONN, IDS_LOGDESC_EXT_DISCONNECT_LOST_CONN, 0 },
{ PB_DOWNLOAD_SUCCESS, IDS_LOGDESC_PB_DOWNLOAD_SUCCESS, IDS_LOGFMT_PB_DOWNLOAD_SUCCESS },
{ PB_DOWNLOAD_FAILURE, IDS_LOGDESC_PB_DOWNLOAD_FAILURE, IDS_LOGFMT_PB_DOWNLOAD_FAILURE },
{ PB_UPDATE_SUCCESS, IDS_LOGDESC_PB_UPDATE_SUCCESSFUL, IDS_LOGFMT_PB_UPDATE_SUCCESSFUL },
{ PB_UPDATE_FAILURE_PBS, IDS_LOGDESC_PB_UPDATE_FAILED_PBS, IDS_LOGFMT_PB_UPDATE_FAILED_PBS },
{ PB_UPDATE_FAILURE_CMPBK, IDS_LOGDESC_PB_UPDATE_FAILED_CMPBK, IDS_LOGFMT_PB_UPDATE_FAILED_CMPBK },
{ PB_ABORTED, IDS_LOGDESC_PB_ABORTED, 0 },
{ USER_FORMATTED, 0, 0 }
};
int s_cCmLogItems = sizeof(s_aCmLogItems) / sizeof(CMLOGITEM);
#define VERIFY_CMLOG_ITEM_OK(x) INBETWEEN(x, 1, s_cCmLogItems)
//
// Usage Note: Caller/User of logging must:
// p = new CmLogFile
// p->Init( instancehandle, fIsItAnAllUserProfile, "name of connectoid" )
// p->SetParams( ... the params ... )
// if (p->m_fEnabled)
// p->Start
// else
// p->Stop
//
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::CmLogFile
//
// Desc: constructor
//
// Args: none
//
// Return: n/a
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
CmLogFile::CmLogFile()
{
m_fInitialized = FALSE;
m_hfile = NULL;
m_dwSize = 0;
m_pszServiceName = NULL;
m_szModule[0] = TEXT('\0');
m_pszLogFile = NULL;
m_dwMaxSize = 0;
m_fEnabled = FALSE;
m_pszLogFileDir = NULL;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::~CmLogFile
//
// Desc: destructor
//
// Args: none
//
// Return: n/a
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
CmLogFile::~CmLogFile()
{
if (m_fInitialized)
{
DeInit();
}
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Init
//
// Desc: Initializes the CmLogFile object
//
// Args: [hInst] -- instance handle
// [fAllUser] -- is this an all user profile?
// [pszServiceName] -- long service name
//
// Return: HRESULT
//
// Notes: There are both Ansi and Unicode versions for this function
//
// History: 18-Jul-2000 SumitC Created
// 11-Apr-2001 SumitC Added Ansi version
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::Init(HINSTANCE hInst, BOOL fAllUser, LPCSTR pszAnsiServiceName)
{
LPWSTR pszServiceName = SzToWzWithAlloc(pszAnsiServiceName);
HRESULT hr = pszServiceName ? Init(hInst, fAllUser, pszServiceName) : E_OUTOFMEMORY;
CmFree(pszServiceName);
return hr;
}
HRESULT
CmLogFile::Init(HINSTANCE hInst, BOOL fAllUser, LPCWSTR pszServiceName)
{
HRESULT hr = S_OK;
// if m_fInitialized is already true, assert and exit
CMASSERTMSG(!m_fInitialized, TEXT("CmLogFile::Init - called twice"));
if (TRUE == m_fInitialized)
{
hr = E_UNEXPECTED;
goto Cleanup;
}
CMASSERTMSG(pszServiceName && pszServiceName[0], TEXT("CmLogFile::Init - invalid servicename, investigate"));
if ((NULL == pszServiceName) || (TEXT('\0') == pszServiceName[0]))
{
hr = E_INVALIDARG;
goto Cleanup;
}
// set the args as member vars
m_fAllUser = fAllUser;
m_pszServiceName = CmStrCpyAlloc(pszServiceName);
if (NULL == m_pszServiceName)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
//
// store away the module name
//
if (FALSE == CmGetModuleBaseName(hInst, m_szModule))
{
lstrcpyU(m_szModule, TEXT("cm"));
}
// if all is well, set m_fInitialized to true
m_fInitialized = TRUE;
Cleanup:
CMTRACEHR(TEXT("CmLogFile::Init"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::SetParams
//
// Desc: Read logging params from the CMS file
//
// Args: [fEnabled] -- is logging enabled?
// [dwMaxFileSize] -- maximum file size, in KB.
// [pszLogFileDir] -- put logging files in this dir.
//
// Return: HRESULT
//
// Notes: There are both Ansi and Unicode versions for this function
//
// History: 18-Jul-2000 SumitC Created
// 11-Apr-2001 SumitC Added Ansi version
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::SetParams(BOOL fEnabled, DWORD dwMaxFileSize, LPCSTR pszAnsiLogFileDir)
{
LPWSTR pszLogFileDir = SzToWzWithAlloc(pszAnsiLogFileDir);
HRESULT hr = pszLogFileDir ? SetParams(fEnabled, dwMaxFileSize, pszLogFileDir) : E_OUTOFMEMORY;
CmFree(pszLogFileDir);
return hr;
}
HRESULT
CmLogFile::SetParams(BOOL fEnabled, DWORD dwMaxFileSize, LPCWSTR pszLogFileDir)
{
HRESULT hr = S_OK;
LPTSTR szUnexpanded = NULL;
CIni * pIni = NULL;
//
// logging must be stopped for this function to be called
//
CMASSERTMSG(NULL == m_hfile, TEXT("CmLogFile::SetParams - m_hfile must be null when this is called"));
if (m_hfile)
{
CMTRACE(TEXT("CmLogFile::SetParams was called during logging - must call Stop first"));
hr = E_UNEXPECTED;
goto Cleanup;
}
//
// EnableLogging (BOOL)
//
m_fEnabled = fEnabled;
//
// MaxFileSize (DWORD)
//
m_dwMaxSize = dwMaxFileSize;
if (0 == m_dwMaxSize)
{
m_dwMaxSize = c_dwMaxFileSize;
}
m_dwMaxSize *= 1024; // size was in KB, convert to bytes.
//
// FileDirectory (string)
//
if (CmStrStr(pszLogFileDir, TEXT("%")))
{
//
// now expand the string we have
//
LPTSTR sz = NULL;
DWORD cch = ExpandEnvironmentStringsU(pszLogFileDir, NULL, 0);
//
// if cch is zero, the pszLogFileDir string supplied is essentially bogus,
// i.e. it contains '%' indicating there's a macro to be expanded, but
// ExpandEnvironmentStrings can't expand it. Here we let m_pszLogFileDir
// be set to NULL (the logging code will then use the Temp dir.
//
if (cch)
{
sz = (LPTSTR) CmMalloc(cch * sizeof(TCHAR));
if (NULL == sz)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
if (cch != ExpandEnvironmentStringsU(pszLogFileDir, sz, cch))
{
hr = HRESULT_FROM_WIN32(GetLastError());
CmFree(sz);
goto Cleanup;
}
// success...
}
CmFree(m_pszLogFileDir);
m_pszLogFileDir = sz;
}
else
{
CmFree(m_pszLogFileDir);
if (pszLogFileDir)
{
m_pszLogFileDir = CmStrCpyAlloc(pszLogFileDir);
if (NULL == m_pszLogFileDir)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
}
else
{
m_pszLogFileDir = NULL;
}
}
Cleanup:
CMTRACEHR(TEXT("CmLogFile::SetParams"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Start
//
// Desc: Start logging
//
// Args: [fBanner] -- write a banner when starting
//
// Return: HRESULT
//
// Notes:
//
// History: 18-Jul-2000 SumitC Created
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::Start(BOOL fBanner)
{
HRESULT hr = S_OK;
// if already started, or already Initialized, or not enabled, exit
CMASSERTMSG(!m_hfile, TEXT("CmLogFile::Start - already started!"));
CMASSERTMSG(m_fInitialized, TEXT("CmLogFile::Start - must be initialized"));
CMASSERTMSG(m_fEnabled, TEXT("CmLogFile::Start - must be enabled"));
if (NULL != m_hfile || FALSE == m_fInitialized || FALSE == m_fEnabled)
{
hr = E_UNEXPECTED;
goto Cleanup;
}
// open log file
hr = OpenFile();
if (S_OK != hr)
{
goto Cleanup;
}
// set m_dwSize while doing so.
m_dwSize = GetFileSize(m_hfile, NULL);
if (DWORD(-1) == m_dwSize)
{
hr = HRESULT_FROM_WIN32(GetLastError());
m_dwSize = 0;
goto Cleanup;
}
//
// no matter what the size of the file, we only clear an 'over the size limit'
// file at the start of a call. The fBanner param covers this.
//
if (fBanner)
{
// check file size, if over size Clear the file
if (m_dwSize > m_dwMaxSize)
{
Clear(); // this writes a banner as well
}
else
{
// log banner
Banner();
}
}
CMASSERTMSG(m_hfile, TEXT("CmLogFile::Start - at end of fn, m_hfile must be valid"));
Cleanup:
CMTRACEHR(TEXT("CmLogFile::Start"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Stop
//
// Desc: Stops logging
//
// Args: none
//
// Return: HRESULT
//
// Notes:
//
// History: 18-Jul-2000 SumitC Created
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::Stop()
{
HRESULT hr = S_OK;
//
// if initialized is false, assert and exit
//
CMASSERTMSG(m_fInitialized, TEXT("CmLogFile::Stop - must be initialized"));
if (FALSE == m_fInitialized)
{
hr = E_UNEXPECTED;
goto Cleanup;
}
//
// if already stopped, exit - nothing to do
//
if (NULL == m_hfile || FALSE == m_fEnabled)
{
hr = E_UNEXPECTED;
goto Cleanup;
}
//
// end log and close file
//
CloseFile();
m_fEnabled = FALSE;
CMASSERTMSG(NULL == m_hfile, TEXT("CmLogFile::Stop - at end of fn, m_hfile must be NULL"));
Cleanup:
CMTRACEHR(TEXT("CmLogFile::Stop"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::DeInit
//
// Desc: Uninitializes cm logging
//
// Args: none
//
// Return: HRESULT
//
// Notes:
//
// History: 18-Jul-2000 SumitC Created
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::DeInit()
{
HRESULT hr = S_OK;
//
// if initialized is false, assert and exit
//
CMASSERTMSG(m_fInitialized, TEXT("CmLogFile::DeInit - must be initialized"));
if (FALSE == m_fInitialized)
{
hr = E_UNEXPECTED;
goto Cleanup;
}
//
// end log and close file
//
CloseFile();
CmFree(m_pszServiceName);
m_pszServiceName = NULL;
CmFree(m_pszLogFileDir);
m_pszLogFileDir = NULL;
CmFree(m_pszLogFile);
m_pszLogFile = NULL;
m_fInitialized = FALSE;
Cleanup:
CMTRACEHR(TEXT("CmLogFile::DeInit"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Log
//
// Desc: Logs a connection manager or connection point services event
//
// Args: [fUnicode] - are the args Unicode or ANSI?
// [eLogItem] - word containing source, type & description of log item
// [...] - optional args (depends on log item)
//
// Return: void
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
void
CmLogFile::Log(_CMLOG_ITEM eLogItem, ...)
{
TCHAR sz[2*MAX_PATH]; // REVIEW: Is this big enough? Could we dynamically allocate it?
LPTSTR pszTmp = NULL;
CMASSERTMSG(m_fInitialized, TEXT("CmLogFile::Log - must be initialized"));
CMASSERTMSG((m_hfile && m_fEnabled) || (!m_hfile && !m_fEnabled), TEXT("CmLogFile::Log - m_hfile and m_fenabled must be in sync"));
if (NULL == m_hfile || NULL == m_fEnabled)
{
// Start hasn't been called yet, or logging is disabled. Nothing to do.
goto Cleanup;
}
//
// Verify that the log item is a valid one
//
CMASSERTMSG(VERIFY_CMLOG_ITEM_OK(eLogItem), TEXT("CmLogFile::Log - eItem must represent valid Log item"));
#if DBG
pszTmp = GetLogDesc(eLogItem);
CMTRACE2(TEXT("Logging item = %d, desc = %s"), eLogItem, CHECKEMPTY(pszTmp));
CmFree(pszTmp);
#endif
if (VERIFY_CMLOG_ITEM_OK(eLogItem))
{
switch (eLogItem)
{
case USER_FORMATTED:
va_list valArgs;
va_start(valArgs, eLogItem);
lstrcpyU(sz, va_arg(valArgs, LPTSTR));
FormatWrite(eLogItem, sz);
va_end(valArgs);
break;
default:
//
// Format the arguments, and log the result
//
lstrcpyU(sz, c_szEmpty);
pszTmp = GetLogFormat(eLogItem, TRUE);
if (pszTmp)
{
va_list valArgs;
va_start(valArgs, eLogItem);
wvsprintfU(sz, pszTmp, valArgs);
CmFree(pszTmp);
FormatWrite(eLogItem, sz);
va_end(valArgs);
}
else
{
FormatWrite(eLogItem, NULL);
}
}
}
else
{
CMTRACE2(TEXT("Illegal CmLog entry %d (0x%x)"), eLogItem, eLogItem);
CMASSERTMSG(FALSE, TEXT("Illegal CmLog type - check trace, then edit code to fix"));
}
Cleanup:
;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Write
//
// Desc: Actually writes out the logged string (to debug console and logfile)
//
// Args: [szLog] - string to log
//
// Return: void
//
// Notes: *ALL* writes to the log file must be done using this function
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::Write(LPTSTR szLog)
{
HRESULT hr = S_OK;
DWORD cb = 0;
DWORD cbActuallyWritten = 0;
LPSTR szLogAnsi = NULL;
CMASSERTMSG(m_hfile, TEXT("CmLogFile::Write - m_hfile must be valid, check code"));
if (NULL == m_hfile)
{
hr = E_UNEXPECTED;
goto Cleanup;
}
#if 0
//
// Dump string to debug console as well
//
CMTRACE(szLog);
#endif
//
// Check for max size, open new log file if necessary
//
if (OS_NT)
{
cb = lstrlenW(szLog) * sizeof(TCHAR);
}
else
{
szLogAnsi = WzToSzWithAlloc(szLog);
cb = lstrlenA(szLogAnsi) * sizeof(CHAR);
}
#if 0
// I'm leaving this here, but for now logging will not terminate a log file
// during a log even if it goes past the max size.
//
if (m_dwSize + cb > m_dwMaxSize)
{
Clear();
}
#endif
//
// Write string to logfile
//
SetFilePointer(m_hfile, 0, NULL, FILE_END);
if (OS_NT)
{
WriteFile(m_hfile, szLog, cb, &cbActuallyWritten, 0);
}
else
{
WriteFile(m_hfile, szLogAnsi, cb, &cbActuallyWritten, 0);
}
if (cb != cbActuallyWritten)
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE(TEXT("CMLOG: incomplete write to logfile"));
goto Cleanup;
}
m_dwSize += cb;
Cleanup:
CmFree(szLogAnsi);
CMTRACEHR(TEXT("CmLogFile::Write"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::FormatWrite
//
// Desc: Formats a log message with additional information and call Write fn
//
// Args: [eItem] - id of item being logged
// [szArgs] - string containing all the args.
//
// Return: void
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
void
CmLogFile::FormatWrite(_CMLOG_ITEM eItem, LPTSTR szArgs)
{
TCHAR szLog[2*MAX_PATH]; // REVIEW: Is this big enough? Could we dynamically allocate it?
TCHAR sz[2*MAX_PATH]; // REVIEW: Is this big enough? Could we dynamically allocate it?
CMASSERTMSG(VERIFY_CMLOG_ITEM_OK(eItem), TEXT("CmLogFile::FormatWrite - eItem must represent valid Log item"));
lstrcpyU(szLog, TEXT(""));
//
// Thread and Module name
//
TCHAR szModuleWithParens[11];
wsprintfU(szModuleWithParens, TEXT("[%s]"), m_szModule);
wsprintfU(sz, TEXT("%-10s%s"), szModuleWithParens, c_szFieldSeparator);
lstrcatU(szLog, sz);
//
// Time
//
LPTSTR pszTime = NULL;
CmGetDateTime(NULL, &pszTime);
if (pszTime)
{
lstrcatU(szLog, pszTime);
lstrcatU(szLog, c_szFieldSeparator);
CmFree(pszTime);
}
//
// Description
//
if (USER_FORMATTED == eItem)
{
wsprintfU(sz, TEXT("%02d%s%s\r\n"), eItem, c_szFieldSeparator, szArgs);
}
else
{
LPTSTR pszDesc = GetLogDesc(eItem);
if (szArgs)
{
wsprintfU(sz, TEXT("%02d%s%s%s%s\r\n"),
eItem, c_szFieldSeparator, CHECKEMPTY(pszDesc), c_szFieldSeparator, szArgs);
}
else
{
wsprintfU(sz, TEXT("%02d%s%s\r\n"),
eItem, c_szFieldSeparator, CHECKEMPTY(pszDesc));
}
CmFree(pszDesc);
}
lstrcatU(szLog, sz);
//
// Write it out...
//
Write(szLog);
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::OpenFile
//
// Desc: Utility function to open the log file
//
// Args: none
//
// Return: HRESULT (S_OK for success, else error)
//
// Notes:
//
// History: 22-Jul-2000 SumitC Created
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::OpenFile()
{
HRESULT hr = S_OK;
HANDLE hDir = NULL;
LPTSTR pszUsers = NULL;
BOOL fFileOpened = FALSE;
CMASSERTMSG(m_pszServiceName, TEXT("CmLogFile::OpenFile - m_pszServiceName must be valid"));
if (m_fAllUser)
{
// this is the more common case, so no suffix
pszUsers = CmStrCpyAlloc(TEXT(""));
}
else
{
LPTSTR pszTmp = CmLoadString(g_hInst, IDS_LOGSTR_SINGLEUSER);
if (pszTmp)
{
pszUsers = (LPTSTR) CmMalloc((lstrlenU(pszTmp) + 4) * sizeof(TCHAR));
if (pszUsers)
{
wsprintfU(pszUsers, TEXT(" (%s)"), pszTmp);
}
CmFree(pszTmp);
}
}
if (NULL == pszUsers)
{
hr = E_OUTOFMEMORY;
CMTRACE1(TEXT("CmLogFile::OpenFile - couldn't get Users strings, hr=%x"), hr);
goto Cleanup;
}
//
// To open a log file, we first try the location provided by the user. If
// that fails for whatever reason, we try GetTempPath. If that fails, no
// logging.
//
for (int i = 0; (i < 2) && (FALSE == fFileOpened); ++i)
{
TCHAR szBuf[2 * MAX_PATH];
CMTRACE1(TEXT("CmLogFile::OpenFile, iteration %d."), i + 1);
//
// get the directory name
//
switch (i)
{
case 0:
if (m_pszLogFileDir)
{
lstrcpyU(szBuf, m_pszLogFileDir);
}
else
{
continue;
}
break;
case 1:
if (0 == GetTempPathU(2 * MAX_PATH, szBuf))
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE1(TEXT("GetTempPath failed with error 0x%x"), hr);
goto Cleanup;
}
break;
default:
MYDBGASSERT(0);
goto Cleanup;
break;
}
CMTRACE1(TEXT("CmLogFile::OpenFile, directory name is %s"), szBuf);
//
// see if the directory exists, if not try to create it
//
DWORD dwAttrib = GetFileAttributesU(szBuf);
if (-1 == dwAttrib)
{
// directory does not exist
CMTRACE(TEXT("CmLogFile::OpenFile - directory does not exist, trying to create it"));
if (FALSE == CreateDirectoryU(szBuf, NULL))
{
DWORD dw = GetLastError();
if (ERROR_ALREADY_EXISTS != dw)
{
// real failure
hr = HRESULT_FROM_WIN32(dw);
CMTRACE2(TEXT("CmLogFile::OpenFile - Failed to create logging directory (%s), hr=%x"), szBuf, hr);
continue;
}
//
// On Win95/98, CreateDirectory fails with ERROR_ALREADY_EXISTS
// if the dir already exists. i.e. we have a dir, so keep going.
//
CMTRACE(TEXT("CmLogFile::OpenFile - directory created"));
}
}
else
{
CMTRACE(TEXT("CmLogFile::OpenFile - directory already exists"));
if (0 == (FILE_ATTRIBUTE_DIRECTORY & dwAttrib))
{
// there is a file of that name
CMTRACE(TEXT("CmLogFile::OpenFile - there is a file of the same name as requested dir"));
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
continue;
}
else if (FILE_ATTRIBUTE_READONLY & dwAttrib)
{
// the directory is readonly
CMTRACE(TEXT("CmLogFile::OpenFile - the directory is readonly"));
hr = E_ACCESSDENIED;
continue;
}
}
//
// the directory exists, try to create/open the logfile
//
if (*c_szSep != szBuf[lstrlenU(szBuf) - 1])
{
lstrcatU(szBuf, c_szSep);
}
lstrcatU(szBuf, m_pszServiceName);
lstrcatU(szBuf, pszUsers);
lstrcatU(szBuf, c_szDotLog);
m_hfile = CreateFileU(szBuf,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//
// Since we asked for open existing, the file may just need to be created
//
if (INVALID_HANDLE_VALUE == m_hfile)
{
m_hfile = CreateFileU(szBuf,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if ((INVALID_HANDLE_VALUE != m_hfile) && OS_NT)
{
//
// Set the Byte order mark on the file
//
DWORD cbActuallyWritten = 0;
WriteFile(m_hfile, &c_wchBOM, sizeof(c_wchBOM), &cbActuallyWritten, 0);
if (sizeof(c_wchBOM) != cbActuallyWritten)
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE(TEXT("CMLOG: Unable to set the Byte order mark while opening the file"));
goto Cleanup;
}
m_dwSize += sizeof(c_wchBOM);
}
}
if (INVALID_HANDLE_VALUE == m_hfile)
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE2(TEXT("CmLogFile::OpenFile - Failed to open log file in dir %s with error 0x%x"), szBuf, hr);
continue;
}
//
// Success!!
//
CmFree(m_pszLogFile);
m_pszLogFile = CmStrCpyAlloc(szBuf);
if (NULL == m_pszLogFile)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
hr = S_OK;
fFileOpened = TRUE;
}
#if DBG
if (S_OK == hr)
{
CMASSERTMSG(m_hfile, TEXT("CmLogFile::OpenFile - at end. m_hfile must be valid here"));
}
#endif
Cleanup:
CmFree(pszUsers);
CMTRACEHR(TEXT("CmLogFile::OpenFile"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::CloseFile
//
// Desc: Closes the logging file
//
// Args: none
//
// Return: HRESULT
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
HRESULT
CmLogFile::CloseFile()
{
HRESULT hr = S_OK;
if (m_hfile)
{
//
// Close the file
//
FlushFileBuffers(m_hfile);
CloseHandle(m_hfile);
m_hfile = NULL;
}
CMTRACEHR(TEXT("CmLogFile::CloseFile"), hr);
return hr;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Clear
//
// Desc: Clears (resets) the logging file
//
// Args: [fWriteBannerAfterwards] -- after clearing, write the banner?
//
// Return: void
//
// Notes:
//
// History: 17-Jul-2000 SumitC Created
//
//-----------------------------------------------------------------------------
void
CmLogFile::Clear(BOOL fWriteBannerAfterwards)
{
HRESULT hr = S_OK;
BOOL fWasDisabled = FALSE;
if (NULL == m_hfile)
{
fWasDisabled = TRUE; // if called when logging is disabled, we still clear the log file
hr = OpenFile();
if (S_OK != hr)
{
goto Cleanup;
}
}
//
// make sure everything gets written out (ignore errors for this one)
//
FlushFileBuffers(m_hfile);
//
// clear the file (set fileptr to the start, then set EOF to that).
//
if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hfile, 0, NULL, FILE_BEGIN))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
if (FALSE == SetEndOfFile(m_hfile))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
m_dwSize = 0;
CMTRACE(TEXT("CmLogFile::Clear - cleared log file"));
//
// If this is NT and thus a Unicode file, we need to set the Byte order mark
//
if (OS_NT)
{
if ((INVALID_HANDLE_VALUE != m_hfile) && OS_NT)
{
//
// Set the Byte order mark on the file
//
DWORD cbActuallyWritten = 0;
WriteFile(m_hfile, &c_wchBOM, sizeof(c_wchBOM), &cbActuallyWritten, 0);
if (sizeof(c_wchBOM) != cbActuallyWritten)
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE(TEXT("CMLOG: Unable to set the Byte order mark while clearing the file"));
goto Cleanup;
}
m_dwSize += sizeof(c_wchBOM);
}
}
if (fWriteBannerAfterwards)
{
Banner();
}
if (fWasDisabled)
{
CloseFile();
}
Cleanup:
CMTRACEHR(TEXT("CmLogFile::Clear"), hr);
return;
}
//+----------------------------------------------------------------------------
//
// Func: CmLogFile::Banner
//
// Desc: Logs the banner heading for a Connection Manager log
//
// Args: none
//
// Return: void
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
void
CmLogFile::Banner()
{
HRESULT hr = S_OK;
LPTSTR psz = NULL;
if (NULL == m_hfile)
{
return;
}
//
// System information, Process, Time
//
OSVERSIONINFO VersionInfo;
LPTSTR pszPlatform = TEXT("NT");
ZeroMemory(&VersionInfo, sizeof(VersionInfo));
VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
GetVersionExU(&VersionInfo);
if (VER_PLATFORM_WIN32_WINDOWS == VersionInfo.dwPlatformId)
{
pszPlatform = TEXT("9x");
}
else if (VER_PLATFORM_WIN32_NT == VersionInfo.dwPlatformId)
{
pszPlatform = TEXT("NT");
}
else
{
CMASSERTMSG(0, TEXT("CmLogFile::Banner - platform ID is not Windows or NT"));
}
//
// Connection Manager version number (using cmdial32.dll)
//
DWORD dwCMVer = 0;
DWORD dwCMBuild = 0;
DWORD dwLCID = 0;
TCHAR szModulePath[MAX_PATH + 1];
UINT uRet = 0;
uRet = GetSystemDirectoryU(szModulePath, MAX_PATH);
if (0 == uRet)
{
CMTRACE1(TEXT("CmLogFile::Banner - GetSystemDirectoryU failed, GLE=%d"), GetLastError());
}
else
{
const LPTSTR c_pszCmdial32 = TEXT("\\cmdial32.dll");
if ((uRet + lstrlenU(c_pszCmdial32) + 1) <= MAX_PATH)
{
lstrcatU(szModulePath, c_pszCmdial32);
hr = GetModuleVersionAndLCID(szModulePath, &dwCMVer, &dwCMBuild, &dwLCID);
if (FAILED(hr))
{
CMTRACE1(TEXT("CmLogFile::Banner - couldn't get CM version, hr=%x"), hr);
}
}
}
//
// Date & Time
//
LPTSTR pszDate = NULL;
LPTSTR pszTime = NULL;
CmGetDateTime(&pszDate, &pszTime);
// strings can be NULL, but we handle that when using them (below)
LPTSTR pszFmt = CmLoadString(g_hInst, IDS_LOGFMT_BANNER);
LPTSTR pszUsers = CmLoadString(g_hInst,
m_fAllUser ? IDS_LOGSTR_ALLUSERS : IDS_LOGSTR_SINGLEUSER);
if (pszFmt && pszUsers)
{
UINT cch = lstrlenU(pszFmt) +
1 +
(3 * lstrlenU(c_szLineOfStars)) + // occurs thrice total
lstrlenU(pszPlatform) +
(6 * 10) + // how big can a DWORD get
lstrlenU(VersionInfo.szCSDVersion) +
lstrlenU(m_pszServiceName) +
lstrlenU(pszUsers) +
(pszDate ? lstrlenU(pszDate) : 0) +
(pszTime ? lstrlenU(pszTime) : 0) +
1;
psz = (LPTSTR) CmMalloc(cch * sizeof(TCHAR));
CMASSERTMSG(psz, TEXT("CmLogFile::Banner - couldn't log banner, malloc failed"));
if (psz)
{
//
// Unicode logfiles are marked as such using a byte order mark, which
// means that to check for an "empty" file we have to account for the
// presence of the BOM.
//
BOOL fFileIsEmpty = (m_dwSize == (OS_NT ? sizeof(c_wchBOM) : 0));
wsprintfU(psz, pszFmt,
fFileIsEmpty ? c_szEmpty : c_szNewLine, // don't start with a newline if the file is empty
c_szLineOfStars,
pszPlatform,
VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion, VersionInfo.szCSDVersion,
HIWORD(dwCMVer), LOWORD(dwCMVer), HIWORD(dwCMBuild), LOWORD(dwCMBuild),
m_pszServiceName,
pszUsers,
(pszDate ? pszDate : TEXT("")),
(pszTime ? pszTime : TEXT("")),
c_szLineOfStars,
c_szLineOfStars);
CMTRACE(TEXT("CmLogFile::Banner - wrote banner"));
}
}
CmFree(pszFmt);
CmFree(pszUsers);
CmFree(pszDate);
CmFree(pszTime);
//
// Write it out...
//
if (psz)
{
Write(psz);
CmFree(psz);
}
}
//+----------------------------------------------------------------------------
//
// Func: GetLogDesc
//
// Desc: Utility function, returns log item friendly name (desc)
//
// Args: [eItem] - the log item about which to return information
//
// Return: LPTSTR if found, or NULL if not
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
LPTSTR
GetLogDesc(_CMLOG_ITEM eItem)
{
CMASSERTMSG(VERIFY_CMLOG_ITEM_OK(eItem), TEXT("GetLogDesc - eItem must represent valid Log item"));
return CmLoadString(g_hInst, s_aCmLogItems[eItem - 1].idDesc);
}
//+----------------------------------------------------------------------------
//
// Func: GetLogFormat
//
// Desc: Utility function, returns log item Format
//
// Args: [eItem] - the log item about which to return information
// [fUnicode] - is the caller unicode?
//
// Return: LPTSTR if found, or NULL if not
//
// Notes:
//
// History: 30-Apr-2000 SumitC Created
//
//-----------------------------------------------------------------------------
LPTSTR
GetLogFormat(_CMLOG_ITEM eItem, BOOL fUnicode)
{
CMASSERTMSG(VERIFY_CMLOG_ITEM_OK(eItem), TEXT("GetLogFormat - eItem must represent valid Log item"));
CMASSERTMSG(fUnicode, TEXT("GetLogFormat - currently cmlog is only being compiled unicode"));
LPTSTR pszFmt = CmLoadString(g_hInst, s_aCmLogItems[eItem - 1].idFormat);
if (0 == lstrcmpU(TEXT(""), pszFmt))
{
// NOTE: CmLoadString has a rather broken implementation where it decides
// to return empty strings in case of failure. This is a problem
// because (a) it makes it impossible to detect an actual failure,
// as opposed to an empty string, and and (b) it uses an alloc within
// a return statement, so it can fail anyway. This 'if' block
// gives me back a NULL so that my code can work the way it should.
CmFree(pszFmt);
return NULL;
}
else if (pszFmt)
{
// If the module is compiled unicode, then fUnicode=false requires conversion.
// If the module is compiled ANSI, then fUnicode=true requires conversion.
#if 0 // since we're compiled Unicode for now
#ifdef UNICODE
if (!fUnicode)
{
if (FALSE == ConvertFormatString(pszFmt))
{
return NULL;
}
}
#else
if (fUnicode)
{
if (FALSE == ConvertFormatString(pszFmt))
{
return NULL;
}
}
#endif
#endif // 0
return pszFmt;
}
else
{
return NULL;
}
}
#undef CMLOG_IMPLEMENTATION