291 lines
11 KiB
C++
291 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CTSLog.h - Thread safe logger class
|
|
//
|
|
// JVP 2/23/2000 added g_szLogFileName for use as logger file name
|
|
// Added:
|
|
// AddULAsString(GetCurrentProcessId(), pszOut, LOG_STRING_LEN);
|
|
// AddULAsString(GetCurrentThreadId(), pszOut, LOG_STRING_LEN);
|
|
// to AddDateTimeAndLog, so that the processid and thread id
|
|
// get prepended to the string to be logged
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef _TSLog_H_
|
|
#define _TSLog_H_
|
|
|
|
#include <limits.h>
|
|
#include <queue>
|
|
#include <time.h>
|
|
#include <process.h> /* _beginthread, _endthread */
|
|
|
|
//some helper functions
|
|
void AddStringToString(char *szIn, char * szStr, long lMaxLen);
|
|
void AddBSTRAsString(BSTR bsIn, char * szStr, long lMaxLen);
|
|
void AddDoubleAsString(double dIn, char * szStr, long lMaxLen);
|
|
void AddLongAsString(long lIn, char * szStr, long lMaxLen);
|
|
void AddULAsString(unsigned long lIn, char * szStr, long lMaxLen);
|
|
void AddVariantBoolAsString(VARIANT_BOOL vIn, char * szStr, long lMaxLen);
|
|
void AddVariantAsString(VARIANT vIn, char * szStr, long lMaxLen);
|
|
|
|
|
|
#define PASSPORT_VERBOSE_MODE_ON
|
|
|
|
|
|
#define NUM_POOL_STRINGS 1024
|
|
#define LOG_STRING_LEN 512
|
|
|
|
//NOTE: The string result produced by asctime contains exactly 26 characters
|
|
// and we need to allow space for the user string to be appended to the
|
|
// end. 512 - 26 - 1 = 486 user characters - should be plenty
|
|
|
|
//NOTE: Each instance of the CTSLog class will need storage for
|
|
// NUM_POOL_STRINGS * LOG_STRING_LEN, just for strings
|
|
|
|
const _TCHAR kszLogFileName[] = _T("C:\\PASSPORTLOG.LOG");
|
|
extern _TCHAR g_szLogFileName[MAX_PATH];
|
|
|
|
//kszFileSemaphoreName needs to be a unique name on the system
|
|
const _TCHAR kszFileSemaphoreName[] = _T("MS_Passport_LogFile_Semaphore_Name_7771");
|
|
const DWORD kdwNoFileShare = 0 ;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CTSLog
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// N O T E S:
|
|
//
|
|
//1/28/2000 - JVP
|
|
//
|
|
//The way this class is designed to work is the following:
|
|
//We have a "pool" of strings that the class uses to pass data to a thread
|
|
//that is in charge of logging data (string) to a file. This pool is an STL
|
|
//queue. We have another STL queue that we push strings onto to be logged
|
|
//by the "child" thread. There is a mutex for each queue, that synchronizes
|
|
//access to the queue (thus making it "thread safe"). There is a semaphore
|
|
//for each queue. There is also a semaphore to synchronize access to the file
|
|
//For the log queue, the log semaphore is used to signal the
|
|
//thread that there is data to be logged (the thread blocks waiting for the
|
|
//semaphore to become signaled). When the semaphore becomes signaled, the
|
|
//thread wakes up and uses the mutex to gain access to the log queue. It then
|
|
//gets the first item from the queue, deletes it from the queue, waits for
|
|
//the file semaphore, opens the file, logs the string and goes back to
|
|
//waiting for the log queue semaphore.
|
|
//
|
|
//The reason for having a pool of strings rather than allocate them on the
|
|
//fly is two fold; First we would "churn" memory if we had to allocate and
|
|
//de-allocate strings all of the time. The second is performance. Memory
|
|
//allocation takes time. There is also the issue of one thread allocating
|
|
//a string and the other de-allocatting the string. (I'm not sure how Windows
|
|
//handles this and it may be prone to error).
|
|
//
|
|
//Anyway, for a relativly small amount of memory we get some real advantages.
|
|
//If ,however, we allocate to many strings we are wasting memory and if we
|
|
//don't allocate enough, we are waiting for the logger thread to log the
|
|
//data to often.
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CTSLog
|
|
{
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CTSLog P U B L I C M E M B E R F U N C T I O N S
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
public:
|
|
CTSLog( void );
|
|
~CTSLog( void );
|
|
|
|
BOOL Init(_TCHAR * pFileName, int nPriority );
|
|
|
|
void Close();
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Function name : AddDateTimeAndLog
|
|
// Description : This function starts the loggin process for a string.
|
|
// We get a string from our pool of strings, then we add
|
|
// some information to the front of it. As of this
|
|
// writing, we add the value from GetTickCount and
|
|
// asctime to the begining, then we add the string the
|
|
// caller gave us to the end of our pool string, then
|
|
// we push this string onto the end of our log queue
|
|
// Return type : BOOL
|
|
// Argument : char * pStr
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
BOOL AddDateTimeAndLog( char * pStr )
|
|
{
|
|
if ( FALSE == m_bVerboseModeOn )
|
|
return FALSE;
|
|
|
|
if ( FALSE == m_bInit )
|
|
return FALSE;
|
|
|
|
//first, we must get a string from our pool of strings
|
|
char * pszOut = popPool();
|
|
|
|
//we must have valid strings
|
|
if ( (NULL != pszOut) && (NULL != pszOut) )
|
|
{
|
|
//get the time to add to the front of the string
|
|
//The string result produced by asctime contains exactly 26 characters
|
|
time_t ltime;
|
|
struct tm *now;
|
|
time( <ime );
|
|
now = localtime( <ime );
|
|
//OK. we'll add the value returned from GetTickCount to the front of the string
|
|
sprintf( pszOut, "%ld, %s", GetTickCount(), asctime( now ) );
|
|
|
|
//asctime appends a '\n' character to the end of the string
|
|
//so replace it with a blank
|
|
//Note: start searching from the end of the string backwards
|
|
char * pEnd = strrchr( pszOut , '\n');
|
|
if ( NULL != pEnd )
|
|
*pEnd = ' ';
|
|
|
|
//we add processid and thread id to the end of the string
|
|
AddULAsString(GetCurrentProcessId(), pszOut, LOG_STRING_LEN);
|
|
AddULAsString(GetCurrentThreadId(), pszOut, LOG_STRING_LEN);
|
|
//now we add the given string to the end of the pool string
|
|
AddStringToString(pStr, pszOut, LOG_STRING_LEN);
|
|
|
|
pushLogQueue ( pszOut );
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CTSLog P R I V A T E M E M B E R F U N C T I O N S
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
private:
|
|
|
|
//NOTE: There is no popLogQueue because this is done in the
|
|
// logger thread directly.
|
|
|
|
//push an item onto the log queue
|
|
void pushLogQueue( char * pStr )
|
|
{
|
|
//synchronize access to the queue
|
|
EnterCriticalSection( &m_outMutex );
|
|
m_outQueue.push( pStr );
|
|
LeaveCriticalSection( &m_outMutex );//we are done with the queue
|
|
//signal the semaphore that there is one more item in the queue
|
|
ReleaseSemaphore( m_hQSemaphore, 1, NULL );
|
|
};
|
|
|
|
//push a string onto the end of the pool of strings
|
|
void pushPool( char * pStr )
|
|
{
|
|
//synchronize access to the queue
|
|
EnterCriticalSection( &m_poolMutex );
|
|
m_stringPool.push( pStr );
|
|
LeaveCriticalSection( &m_poolMutex );//we are done with the queue
|
|
//signal the semaphore that there is one more item in the queue
|
|
ReleaseSemaphore( m_hPoolSemaphore, 1, NULL );
|
|
};
|
|
|
|
//get the first string from the pool, wait for one if there isn't one
|
|
//available
|
|
char * popPool( DWORD dwMilliseconds = -1)
|
|
{
|
|
DWORD dwRet;
|
|
char * pStr = NULL;
|
|
|
|
//wait for there to be a string in the pool
|
|
if ( dwMilliseconds <= 0 )//wait forever
|
|
{
|
|
dwRet = WaitForSingleObject( m_hPoolSemaphore, INFINITE );
|
|
}
|
|
else
|
|
{//wait for the given number of milliseconds
|
|
dwRet = WaitForSingleObject( m_hPoolSemaphore, dwMilliseconds );
|
|
}
|
|
|
|
if (WAIT_OBJECT_0 == dwRet)
|
|
{
|
|
//synchronize access to the queue
|
|
EnterCriticalSection( &m_poolMutex );
|
|
pStr = m_stringPool.front();
|
|
m_stringPool.pop();
|
|
LeaveCriticalSection( &m_poolMutex );
|
|
}
|
|
return pStr;
|
|
};
|
|
|
|
//function to check if the verbose mode is on in the registry
|
|
BOOL IsRegistryVerboseSet()
|
|
{
|
|
HKEY hKey;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx( (HKEY) HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Passport"), 0, KEY_READ, &hKey ))
|
|
{
|
|
DWORD bufSize;
|
|
DWORD type;
|
|
BYTE buffer[128];
|
|
bufSize = sizeof(buffer);
|
|
if (ERROR_SUCCESS == RegQueryValueEx( hKey, _T("Verbose"), 0, &type, buffer, &bufSize ))
|
|
{
|
|
if ( 1 == buffer[0] )
|
|
bRet = TRUE;
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
return bRet;
|
|
};
|
|
|
|
void GetLogfileName()
|
|
{
|
|
HKEY hKey;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx( (HKEY) HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Passport"), 0, KEY_READ, &hKey ))
|
|
{
|
|
DWORD bufSize;
|
|
DWORD type;
|
|
BYTE buffer[MAX_PATH];
|
|
bufSize = sizeof(buffer);
|
|
if (ERROR_SUCCESS == RegQueryValueEx( hKey, _T("InstallDir"), 0, &type, buffer, &bufSize ))
|
|
{
|
|
//we are going to try the new name, so put it in a temp variable first
|
|
//and then try to open/create the file, if that fails, we don't set the name
|
|
_TCHAR sLocalName[MAX_PATH];
|
|
wsprintf(sLocalName,_T("%s\\\\PASSPORTLOG.LOG"), buffer);
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
DWORD dwRetVal = WaitForSingleObject( m_hFileSemaphore, INFINITE );
|
|
hFile = CreateFile(sLocalName, GENERIC_WRITE, kdwNoFileShare, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL /*| FILE_FLAG_WRITE_THROUGH*/ , NULL);
|
|
if ( INVALID_HANDLE_VALUE != hFile )
|
|
{
|
|
wsprintf(g_szLogFileName,_T("%s"), sLocalName);
|
|
CloseHandle(hFile);
|
|
}
|
|
ReleaseSemaphore( m_hFileSemaphore, 1, NULL );
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
//the logger thread
|
|
static unsigned int __stdcall LoggerThread(void * pArg);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CTSLog P R I V A T E M E M B E R V A R I A B L E S
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
private:
|
|
|
|
HANDLE m_hPoolSemaphore; //used to signal when the pool is empty
|
|
HANDLE m_hQSemaphore; //used to signal when something is in the queue
|
|
HANDLE m_hFileSemaphore; //used to synchronize access to the file
|
|
CRITICAL_SECTION m_outMutex; //mutex used to synchronize access to the logg queue
|
|
CRITICAL_SECTION m_poolMutex; //mutex used to synchronize access to the string pool
|
|
PtStlQueue<char *> m_outQueue; //the queue that holds strings to be logged to the file
|
|
PtStlQueue<char *> m_stringPool; //the "pool" that holds the strings we use
|
|
BOOL m_bQuit; //used to tell the child thread when to quit
|
|
BOOL m_bInit; //have we been initialized
|
|
BOOL m_bVerboseModeOn; //is the registry entry set to ON
|
|
HANDLE m_ulChildThread; //a handle to the child thread
|
|
unsigned int m_uThreadAddr; //the thread ID returned by beginthreadex
|
|
unsigned long m_ulNumPoolStrings; //the number of strings actually in the pool
|
|
};
|
|
|
|
#endif // _TSLog_H_
|