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.
 
 
 
 
 
 

901 lines
26 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
LogThred.c
Abstract:
module containing logging thread functions
Author:
Bob Watson (a-robw) 10 Apr 96
Revision History:
--*/
#ifndef UNICODE
#define UNICODE 1
#endif
#ifndef _UNICODE
#define _UNICODE 1
#endif
//
// Windows Include files
//
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <pdh.h>
#include <pdhmsg.h>
#include "pdlsvc.h"
//#include "logutils.h"
#include "pdlmsg.h"
static
long
JulianDateFromSystemTime(
SYSTEMTIME *pST
)
{
static WORD wDaysInRegularMonth[] = {
31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
static WORD wDaysInLeapYearMonth[] = {
31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
};
long JDate = 0;
// Check for leap year.
if (pST->wMonth > 1) {
if( pST->wYear % 100 != 0 && pST->wYear % 4 == 0 ) {
// this is a leap year
JDate += wDaysInLeapYearMonth[pST->wMonth - 1];
} else {
// this is not a leap year
JDate += wDaysInRegularMonth[pST->wMonth - 1];
}
}
// Add in days for this month.
JDate += pST->wDay;
// Add in year.
JDate += (pST->wYear % 100) * 10000;
return JDate;
}
static
BOOL
GetLocalFileTime (
SYSTEMTIME *pST,
LONGLONG *pFileTime
)
{
BOOL bResult;
GetLocalTime (pST);
if (pFileTime != NULL) {
bResult = SystemTimeToFileTime (pST, (LPFILETIME)pFileTime);
} else {
bResult = TRUE;
}
return bResult;
}
static
DWORD
GetSamplesInRenameInterval(
IN DWORD dwSampleInterval, // in seconds
IN DWORD dwRenameIntervalCount, // in units
IN DWORD dwRenameIntervalUnits) // for "count" arg
{
DWORD dwRenameIntervalSeconds;
// convert rename interval to seconds and account for the
// first (or zero-th) sample in the log
dwRenameIntervalSeconds = dwRenameIntervalCount;
switch (dwRenameIntervalUnits) {
case OPD_RENAME_HOURS:
dwRenameIntervalSeconds *= SECONDS_IN_HOUR;
break;
case OPD_RENAME_DAYS:
default:
dwRenameIntervalSeconds *= SECONDS_IN_DAY;
break;
case OPD_RENAME_MONTHS:
dwRenameIntervalSeconds *= SECONDS_IN_DAY * 30;
break;
case OPD_RENAME_KBYTES:
case OPD_RENAME_MBYTES:
// these don't use a rename counter
return (DWORD)0;
break;
}
dwRenameIntervalSeconds /= dwSampleInterval;
dwRenameIntervalSeconds += 1; // add in the "zero-th" sample
return (dwRenameIntervalSeconds);
}
static
LONG
BuildCurrentLogFileName (
IN LPCTSTR szBaseFileName,
IN LPCTSTR szDefaultDir,
IN LPTSTR szOutFileBuffer,
IN LPDWORD lpdwSerialNumber,
IN DWORD dwDateFormat,
IN DWORD dwLogFormat
)
// presumes OutFileBuffer is large enough (i.e. >= MAX_PATH)
{
SYSTEMTIME st;
BOOL bUseCurrentDir = FALSE;
TCHAR szAuto[MAX_PATH];
LPTSTR szExt;
if (szDefaultDir != NULL) {
if (*szDefaultDir == 0) {
bUseCurrentDir = TRUE;
}
} else {
bUseCurrentDir = TRUE;
}
if (bUseCurrentDir) {
GetCurrentDirectory (MAX_PATH, szOutFileBuffer);
} else {
lstrcpy (szOutFileBuffer, szDefaultDir);
}
// add a backslash to the path name if it doesn't have one already
if (szOutFileBuffer[lstrlen(szOutFileBuffer)-1] != TEXT('\\')) {
lstrcat (szOutFileBuffer, TEXT("\\"));
}
// add the base filename
lstrcat (szOutFileBuffer, szBaseFileName);
// add the auto name part
// get date/time/serial integer format
GetLocalTime(&st);
switch (dwDateFormat) {
case OPD_NAME_NNNNNN:
_stprintf (szAuto, TEXT("_%6.6d"), *lpdwSerialNumber);
(*lpdwSerialNumber)++; // increment
if (*lpdwSerialNumber >= 1000000) {
// roll over to 0
*lpdwSerialNumber = 0;
}
break;
case OPD_NAME_YYDDD:
_stprintf (szAuto, TEXT("_%5.5d"),
JulianDateFromSystemTime(&st));
break;
case OPD_NAME_YYMM:
_stprintf (szAuto, TEXT("_%2.2d%2.2d"),
st.wYear % 100, st.wMonth);
break;
case OPD_NAME_YYMMDDHH:
_stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d%2.2d"),
(st.wYear % 100), st.wMonth, st.wDay, st.wHour);
break;
case OPD_NAME_MMDDHH:
_stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d"),
st.wMonth, st.wDay, st.wHour);
break;
case OPD_NAME_YYMMDD:
default:
_stprintf (szAuto, TEXT("_%2.2d%2.2d%2.2d"),
st.wYear % 100, st.wMonth, st.wDay);
break;
}
lstrcat (szOutFileBuffer, szAuto);
// get file type
switch (dwLogFormat) {
case PDH_LOG_TYPE_TSV:
szExt = TEXT(".tsv");
break;
case PDH_LOG_TYPE_BINARY:
szExt = TEXT(".blg");
break;
case PDH_LOG_TYPE_CSV:
default:
szExt = TEXT(".csv");
break;
}
lstrcat (szOutFileBuffer, szExt);
return ERROR_SUCCESS;
}
static
BOOL
LoadDataFromRegistry (
IN LPLOG_THREAD_DATA pArg,
IN LPTSTR szDefaultDir,
IN LPTSTR szBaseName,
IN LPTSTR szCurrentLogFile
)
{
LONG lStatus;
DWORD dwType;
DWORD dwSize;
DWORD dwData;
LPTSTR szStringArray[2];
// get size of buffer required by counter list,
// then allocate the buffer and retrieve the counter list
dwType = 0;
dwData = 0;
dwSize = 0;
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Counter List"),
NULL,
&dwType,
(LPBYTE)NULL,
&dwSize);
pArg->mszCounterList = (LPTSTR)G_ALLOC(dwSize);
if (pArg->mszCounterList != NULL) {
dwType = 0;
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Counter List"),
NULL,
&dwType,
(LPBYTE)pArg->mszCounterList,
&dwSize);
if ((lStatus != ERROR_SUCCESS) || (dwSize == 0)) {
// no counter list retrieved so there's not much
// point in continuing
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFLOG_UNABLE_READ_COUNTER_LIST,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&lStatus);
return FALSE;
}
} else {
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFLOG_UNABLE_ALLOC_COUNTER_LIST,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&lStatus);
return FALSE;
}
dwType = 0;
dwData = 0;
dwSize = sizeof(DWORD);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Auto Name Interval"),
NULL,
&dwType,
(LPBYTE)&dwData,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
dwData = 0; // default is no autonaming
} else if (dwType != REG_DWORD) {
dwData = 0; // default is no autonaming
} // else assume success
pArg->dwRenameIntervalCount = dwData;
dwType = 0;
dwData = 0;
dwSize = sizeof(DWORD);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Auto Rename Units"),
NULL,
&dwType,
(LPBYTE)&dwData,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
dwData = OPD_RENAME_DAYS; // default is days
} else if (dwType != REG_DWORD) {
dwData = OPD_RENAME_DAYS; // default is days
} // else assume success
pArg->dwRenameIntervalUnits = dwData;
dwType = 0;
dwData = 0;
dwSize = sizeof(DWORD);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Log File Auto Format"),
NULL,
&dwType,
(LPBYTE)&dwData,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
dwData = OPD_NAME_NNNNNN; // default is a serial number
} else if (dwType != REG_DWORD) {
dwData = OPD_NAME_NNNNNN; // default is a serial number
} // else assume success
pArg->dwAutoNameFormat = dwData;
dwType = 0;
dwData = 0;
dwSize = sizeof(DWORD);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Log File Type"),
NULL,
&dwType,
(LPBYTE)&dwData,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
dwData = OPD_CSV_FILE; // default is a CSV file
} else if (dwType != REG_DWORD) {
dwData = OPD_CSV_FILE; // default is a CSV file
} // else assume success
// convert from OPD to PDH constant
switch (dwData) {
case OPD_TSV_FILE:
pArg->dwLogType = PDH_LOG_TYPE_TSV;
break;
case OPD_BIN_FILE:
pArg->dwLogType = PDH_LOG_TYPE_BINARY;
break;
case OPD_CSV_FILE:
default:
pArg->dwLogType = PDH_LOG_TYPE_CSV;
break;
}
dwType = 0;
dwData = 0;
dwSize = sizeof(DWORD);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Sample Interval"),
NULL,
&dwType,
(LPBYTE)&dwData,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
dwData = SECONDS_IN_MINUTE; // default is 1 Minute samples
} else if (dwType != REG_DWORD) {
dwData = SECONDS_IN_MINUTE; // default is 1 Minute samples
} // else assume success
pArg->dwTimeInterval = dwData;
// get filename or components if auto name
if (pArg->dwRenameIntervalCount > 0) {
// this is an autoname file so get components
dwType = 0;
*szDefaultDir = 0;
dwSize = MAX_PATH * sizeof(TCHAR);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Log Default Directory"),
NULL,
&dwType,
(LPBYTE)&szDefaultDir[0],
&dwSize);
if (lStatus != ERROR_SUCCESS) {
*szDefaultDir = 0;
} // else assume success
dwType = 0;
*szBaseName = 0;
dwSize = MAX_PATH * sizeof(TCHAR);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Base Filename"),
NULL,
&dwType,
(LPBYTE)&szBaseName[0],
&dwSize);
if (lStatus != ERROR_SUCCESS) {
// apply default
lstrcpy (szBaseName, TEXT("perfdata"));
} // else assume success
dwType = 0;
dwSize = 0;
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Command File"),
NULL,
&dwType,
NULL,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
// assume no command filname
pArg->szCmdFileName = NULL;
} else {
// allocate a buffer for this field and collect data
pArg->szCmdFileName = (LPTSTR)G_ALLOC(dwSize);
if (pArg->szCmdFileName != NULL) {
// get command filename
dwType = 0;
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Command File"),
NULL,
&dwType,
(LPBYTE)pArg->szCmdFileName,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
// apply default
pArg->szCmdFileName = NULL;
} // else assume success
}
if (pArg->szCmdFileName == NULL) {
// log error message
// no command file could be read so issue
// warning and continue
szStringArray[0] = pArg->szQueryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFLOG_ALLOC_CMDFILE_BUFFER,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&lStatus);
}
}
} else {
// this is a manual name file so read name
dwType = 0;
*szCurrentLogFile = 0;
dwSize = MAX_PATH * sizeof(TCHAR);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Log Filename"),
NULL,
&dwType,
(LPBYTE)&szCurrentLogFile[0],
&dwSize);
if (lStatus != ERROR_SUCCESS) {
// apply default
lstrcpy (szCurrentLogFile, TEXT("c:\\perfdata.log"));
} // else assume success
}
dwType = 0;
dwData = 0;
dwSize = sizeof(DWORD);
lStatus = RegQueryValueEx (
pArg->hKeyQuery,
TEXT("Log File Serial Number"),
NULL,
&dwType,
(LPBYTE)&dwData,
&dwSize);
if (lStatus != ERROR_SUCCESS) {
dwData = 1; // default is to start at 1
} else if (dwType != REG_DWORD) {
dwData = 1; // default is to start at 1
} // else assume success
pArg->dwCurrentSerialNumber = dwData;
return TRUE;
}
static
LONG
DoCommandFile (
IN LPLOG_THREAD_DATA pArg,
IN LPTSTR szLogFileName,
IN BOOL bStillRunning
)
{
LONG lStatus;
BOOL bStatus;
LPTSTR szCommandString;
LONG nErrorMode;
TCHAR TempBuffer [ 5 * MAX_PATH] ;
DWORD StringLen;
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
szCommandString = (LPTSTR)G_ALLOC(4096 * sizeof(TCHAR));
if (szCommandString != NULL) {
// build command line arguments
szCommandString[0] = _T('\"');
lstrcpy (&szCommandString[1], szLogFileName);
lstrcat (szCommandString, TEXT("\" "));
lstrcat (szCommandString,
(bStillRunning ? TEXT("1") : TEXT("0")));
// initialize Startup Info block
memset (&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW ;
si.wShowWindow = SW_SHOWNOACTIVATE ;
memset (&pi, 0, sizeof(pi));
// supress pop-ups inf the detached process
nErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
lstrcpy (TempBuffer, pArg->szCmdFileName) ;
// see if this is a CMD or a BAT file
// if it is then create a process with a console window, otherwise
// assume it's an executable file that will create it's own window
// or console if necessary
//
_tcslwr (TempBuffer);
if ((_tcsstr(TempBuffer, TEXT(".bat")) != NULL) ||
(_tcsstr(TempBuffer, TEXT(".cmd")) != NULL)){
dwCreationFlags |= CREATE_NEW_CONSOLE;
} else {
dwCreationFlags |= DETACHED_PROCESS;
}
// recopy the image name to the temp buffer since it was modified
// (i.e. lowercased) for the previous comparison.
lstrcpy (TempBuffer, pArg->szCmdFileName) ;
StringLen = lstrlen (TempBuffer) ;
// now add on the alert text preceded with a space char
TempBuffer [StringLen] = TEXT(' ') ;
StringLen++ ;
lstrcpy (&TempBuffer[StringLen], szCommandString) ;
bStatus = CreateProcess (
NULL,
TempBuffer,
NULL, NULL, FALSE,
dwCreationFlags,
NULL,
NULL,
&si,
&pi);
SetErrorMode(nErrorMode);
if (bStatus) {
lStatus = ERROR_SUCCESS;
} else {
lStatus = GetLastError();
}
} else {
lStatus = ERROR_OUTOFMEMORY;
}
return lStatus;
}
BOOL
LoggingProc (
IN LPLOG_THREAD_DATA pArg
)
{
HQUERY hQuery;
HCOUNTER hThisCounter;
HLOG hLog;
DWORD dwDelay;
DWORD dwSampleInterval, dwSampleTime=-1;
PDH_STATUS pdhStatus;
DWORD dwNumCounters;
LONG lStatus;
TCHAR szDefaultDir[MAX_PATH];
TCHAR szBaseName[MAX_PATH];
LPTSTR szThisPath;
DWORD dwLogType = PDH_LOG_TYPE_CSV;
BOOL bRun = FALSE;
DWORD dwSamplesUntilNewFile;
TCHAR szCurrentLogFile[MAX_PATH];
LONG lWaitStatus;
LPTSTR szStringArray[4];
DWORD dwFileSizeLimit;
LONGLONG llFileSizeLimit;
LONGLONG llFileSize;
PLOG_COUNTER_INFO pCtrInfo;
SYSTEMTIME st;
LONGLONG llStartTime = 0;
LONGLONG llFinishTime = 0;
// read registry values
if (!LoadDataFromRegistry (pArg, szDefaultDir, szBaseName, szCurrentLogFile)) {
// unable to initialize the query from the registry
return FALSE;
}
// convert to milliseconds for use in timeouts
dwSampleInterval = pArg->dwTimeInterval * 1000L;
// open query and add counters from info file
pdhStatus = PdhOpenQuery (NULL, 0, &hQuery); // from current activity
if (pdhStatus == ERROR_SUCCESS) {
dwNumCounters = 0;
for (szThisPath = pArg->mszCounterList;
*szThisPath != 0;
szThisPath += lstrlen(szThisPath) + 1) {
pdhStatus = PdhAddCounter (hQuery,
(LPTSTR)szThisPath, dwNumCounters++, &hThisCounter);
if (pdhStatus == ERROR_SUCCESS) {
// then add this handle to the list
pCtrInfo = G_ALLOC (sizeof (LOG_COUNTER_INFO));
if (pCtrInfo != NULL) {
// insert at front of list since the order isn't
// important and this is simpler than walking the
// list each time.
pCtrInfo->hCounter = hThisCounter;
pCtrInfo->next = pFirstCounter;
pFirstCounter = pCtrInfo;
}
}
}
// to make sure we get to log the data
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
bRun = TRUE;
while (bRun) {
// Get the current Log filename
if (pArg->dwRenameIntervalCount != 0) {
// then this is an autonamed file
// so make current name
BuildCurrentLogFileName (
szBaseName,
szDefaultDir,
szCurrentLogFile,
&pArg->dwCurrentSerialNumber,
pArg->dwAutoNameFormat,
pArg->dwLogType);
// reset loop counter
switch (pArg->dwRenameIntervalUnits) {
case OPD_RENAME_KBYTES:
dwFileSizeLimit = pArg->dwRenameIntervalCount * 1024;
dwSamplesUntilNewFile = 0;
break;
case OPD_RENAME_MBYTES:
dwFileSizeLimit = pArg->dwRenameIntervalCount * 1024 * 1024;
dwSamplesUntilNewFile = 0;
break;
case OPD_RENAME_HOURS:
case OPD_RENAME_DAYS:
case OPD_RENAME_MONTHS:
default:
dwSamplesUntilNewFile = GetSamplesInRenameInterval(
pArg->dwTimeInterval,
pArg->dwRenameIntervalCount,
pArg->dwRenameIntervalUnits);
dwFileSizeLimit = 0;
break;
}
} else {
// filename is left as read from the registry
dwSamplesUntilNewFile = 0;
dwFileSizeLimit = 0;
}
llFileSizeLimit = dwFileSizeLimit;
// open log file using this query
dwLogType = pArg->dwLogType;
pdhStatus = PdhOpenLog (
szCurrentLogFile,
PDH_LOG_WRITE_ACCESS |
PDH_LOG_CREATE_ALWAYS,
&dwLogType,
hQuery,
0,
NULL,
&hLog);
if (pdhStatus == ERROR_SUCCESS) {
szStringArray[0] = pArg->szQueryName;
szStringArray[1] = szCurrentLogFile;
ReportEvent (hEventLog,
EVENTLOG_INFORMATION_TYPE,
0,
PERFLOG_LOGGING_QUERY,
NULL,
2,
0,
szStringArray,
NULL);
// start sampling immediately
dwDelay = 0;
while ((lWaitStatus = WaitForSingleObject (pArg->hExitEvent, dwDelay)) == WAIT_TIMEOUT) {
// the event flag will be set when the sampling should exit. if
// the wait times out, then that means it's time to collect and
// log another sample of data.
// time the call to adjust the wait time
GetLocalFileTime (&st, &llStartTime);
pdhStatus = PdhUpdateLog (hLog, NULL);
if (pdhStatus == ERROR_SUCCESS) {
// see if it's time to rename the file
if (dwSamplesUntilNewFile) {
if (!--dwSamplesUntilNewFile) break;
} else if (llFileSizeLimit) {
// see if the file is too big
pdhStatus = PdhGetLogFileSize (hLog, &llFileSize);
if (pdhStatus == ERROR_SUCCESS) {
if (llFileSizeLimit <= llFileSize) break;
}
}
// compute new timeout value
if (dwSampleTime < dwSampleInterval) {
dwDelay = dwSampleInterval - dwSampleTime;
} else {
dwDelay = 0;
}
} else {
// unable to update the log so log event and exit
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFLOG_UNABLE_UPDATE_LOG,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&pdhStatus);
bRun = FALSE;
break;
}
GetLocalFileTime (&st, &llFinishTime);
// compute difference and convert to milliseconds
dwSampleTime = (DWORD)((llFinishTime - llStartTime) / 10000L);
} // end while wait keeps timing out
if (lWaitStatus == WAIT_OBJECT_0) {
// then the loop was terminated by the Exit event
// so clear the "run" flag to exit the loop & thread
bRun = FALSE;
}
// close log file, but keep query open
PdhCloseLog (hLog, 0);
if (pArg->szCmdFileName != NULL) {
DoCommandFile (pArg, szCurrentLogFile, bRun);
}
} else {
// unable to open log file so log event log message
szStringArray[0] = szCurrentLogFile;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFLOG_UNABLE_OPEN_LOG_FILE,
NULL,
1,
sizeof(DWORD),
szStringArray,
(LPVOID)&pdhStatus);
bRun = FALSE; // exit now
}
} // end while (bRun)
PdhCloseQuery (hQuery);
// update log serial number if necssary
if (pArg->dwAutoNameFormat == OPD_NAME_NNNNNN) {
lStatus = RegSetValueEx (
pArg->hKeyQuery,
TEXT("Log File Serial Number"),
0L,
REG_DWORD,
(LPBYTE)&pArg->dwCurrentSerialNumber,
sizeof(DWORD));
}
} else {
// unable to open query so write event log message
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFLOG_UNABLE_OPEN_LOG_QUERY,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&pdhStatus);
}
// free allocated buffers
if (pArg->mszCounterList != NULL) {
G_FREE(pArg->mszCounterList);
pArg->mszCounterList = NULL;
}
if (pArg->szCmdFileName != NULL) {
G_FREE(pArg->szCmdFileName);
pArg->szCmdFileName = NULL;
}
return bRun;
}
DWORD
LoggingThreadProc (
IN LPVOID lpThreadArg
)
{
LPLOG_THREAD_DATA pThreadData = (LPLOG_THREAD_DATA)lpThreadArg;
DWORD dwStatus = ERROR_SUCCESS;
BOOL bContinue = TRUE;
if (pThreadData != NULL) {
// read config from registry
do {
// read config from registry
// expand counter paths as necessary
// call Logging function
bContinue = LoggingProc (pThreadData);
// see if this thread was paused for reloading
// or stopped to terminate
if (pThreadData->bReloadNewConfig) {
bContinue = TRUE;
} // else bContinue is always returned as FALSE
// so that will terminate this loop
} while (bContinue);
dwStatus = ERROR_SUCCESS;
} else {
// unable to find data block so return
dwStatus = ERROR_INVALID_PARAMETER;
}
return dwStatus;
}