mirror of https://github.com/lianthony/NT4.0
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.
914 lines
26 KiB
914 lines
26 KiB
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <pdh.h>
|
|
#include <pdhmsg.h>
|
|
#include <common.h>
|
|
#include "pdlsvc.h"
|
|
#include "logutils.h"
|
|
|
|
|
|
// local definitions
|
|
typedef struct _LOG_INFO {
|
|
DWORD dwLength; // the size of this structure
|
|
LPWSTR szLogFileName; // full file name for this log file
|
|
HANDLE hLogFileHandle; // handle to open log file
|
|
|
|
HANDLE hMappedLogFile; // handle for memory mapped files
|
|
LPVOID lpMappedFileBase; // starting address for mapped log file
|
|
FILE *StreamFile; // stream pointer for text files
|
|
DWORD dwLastRecordRead; // index of last record read from the file
|
|
|
|
LPSTR szLastRecordRead; // pointer to buffer containing the last record
|
|
LPWSTR szCatFileName; // catalog file name
|
|
HANDLE hCatFileHandle; // handle to the open catalog file
|
|
HQUERY hQuery; // query handle associated with the log
|
|
|
|
DWORD dwMaxRecords; // max size of a circular log file
|
|
DWORD dwLogFormat; // log type and access flags
|
|
} LOG_INFO, *PLOG_INFO;
|
|
|
|
// note that when the text format headers are written
|
|
// they will be prefixed with a double quote character
|
|
// the binary header will not. That's why there's a space
|
|
// in the binary string so the offset in the file will be
|
|
// the same.
|
|
|
|
#define VALUE_BUFFER_SIZE 32
|
|
//
|
|
// local static variables
|
|
//
|
|
static LOG_INFO LogEntry = {0,NULL,NULL,
|
|
NULL,NULL,NULL,0,
|
|
NULL,NULL,NULL,NULL,
|
|
0,0};
|
|
|
|
#define TAB_DELIMITER '\t'
|
|
#define COMMA_DELIMITER ','
|
|
#define DOUBLE_QUOTE '\"'
|
|
#define VALUE_BUFFER_SIZE 32
|
|
|
|
const CHAR szFmtTimeStamp[] = {"\"%2.2d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d.%3.3d\""};
|
|
const CHAR szFmtRealValue[] = {"%c\"%.20g\""};
|
|
const CHAR szRecordTerminator[] = {"\r\n"};
|
|
const DWORD dwRecordTerminatorLength = 2;
|
|
const CHAR szTimeStampLabel[] = {" Sample Time\""};
|
|
const DWORD dwTimeStampLabelLength = 13;
|
|
|
|
#define TEXTLOG_TYPE_ID_RECORD 1
|
|
#define TEXTLOG_HEADER_RECORD 1
|
|
#define TEXTLOG_FIRST_DATA_RECORD 2
|
|
|
|
#define TIME_FIELD_COUNT 7
|
|
#define TIME_FIELD_BUFF_SIZE 24
|
|
DWORD dwTimeFieldOffsetList[TIME_FIELD_COUNT] = {2, 5, 10, 13, 16, 19, 23};
|
|
|
|
BOOL
|
|
GetLocalFileTime (
|
|
SYSTEMTIME *pST,
|
|
LONGLONG *pFileTime
|
|
)
|
|
{
|
|
BOOL bResult;
|
|
GetLocalTime (pST);
|
|
if (pFileTime != NULL) {
|
|
bResult = SystemTimeToFileTime (pST, (LPFILETIME)pFileTime);
|
|
} else {
|
|
bResult = TRUE;
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
DateStringToFileTimeA (
|
|
IN LPSTR szDateTimeString,
|
|
IN LPFILETIME pFileTime
|
|
)
|
|
{
|
|
CHAR mszTimeFields[TIME_FIELD_BUFF_SIZE];
|
|
DWORD dwThisField;
|
|
LONG lValue;
|
|
SYSTEMTIME st;
|
|
|
|
// make string into msz
|
|
lstrcpynA (mszTimeFields, szDateTimeString, TIME_FIELD_BUFF_SIZE);
|
|
for (dwThisField = 0; dwThisField < TIME_FIELD_COUNT; dwThisField++) {
|
|
mszTimeFields[dwTimeFieldOffsetList[dwThisField]] = 0;
|
|
}
|
|
|
|
// read string into system time structure
|
|
dwThisField = 0;
|
|
st.wDayOfWeek = 0;
|
|
lValue = atol(&mszTimeFields[0]);
|
|
st.wMonth = LOWORD(lValue);
|
|
lValue = atol(&mszTimeFields[dwTimeFieldOffsetList[dwThisField++]+1]);
|
|
st.wDay = LOWORD(lValue);
|
|
lValue = atol(&mszTimeFields[dwTimeFieldOffsetList[dwThisField++]+1]);
|
|
st.wYear = LOWORD(lValue);
|
|
lValue = atol(&mszTimeFields[dwTimeFieldOffsetList[dwThisField++]+1]);
|
|
st.wHour = LOWORD(lValue);
|
|
lValue = atol(&mszTimeFields[dwTimeFieldOffsetList[dwThisField++]+1]);
|
|
st.wMinute = LOWORD(lValue);
|
|
lValue = atol(&mszTimeFields[dwTimeFieldOffsetList[dwThisField++]+1]);
|
|
st.wSecond = LOWORD(lValue);
|
|
lValue = atol(&mszTimeFields[dwTimeFieldOffsetList[dwThisField++]+1]);
|
|
st.wMilliseconds = LOWORD(lValue);
|
|
|
|
return SystemTimeToFileTime (&st, pFileTime);
|
|
}
|
|
|
|
#define GSFDL_REMOVE_QUOTES 0x00000001
|
|
static
|
|
DWORD
|
|
GetStringFromDelimitedListA (
|
|
IN LPSTR szInputString,
|
|
IN DWORD dwItemIndex,
|
|
IN CHAR cDelimiter,
|
|
IN DWORD dwFlags,
|
|
IN LPSTR szOutputString,
|
|
IN DWORD cchBufferLength
|
|
)
|
|
{
|
|
DWORD dwCurrentIndex = 0;
|
|
LPSTR szCurrentItem;
|
|
LPSTR szSrcPtr, szDestPtr;
|
|
DWORD dwReturn = 0;
|
|
|
|
// go to desired entry in string, 0 = first entry
|
|
szCurrentItem = szInputString;
|
|
|
|
while (dwCurrentIndex < dwItemIndex) {
|
|
// goto next delimiter or terminator
|
|
while (*szCurrentItem++ != cDelimiter) {
|
|
if (*szCurrentItem == 0) break;
|
|
}
|
|
dwCurrentIndex++;
|
|
}
|
|
if (*szCurrentItem != 0) {
|
|
// then copy to the user's buffer, as long as it fits
|
|
szSrcPtr = szCurrentItem;
|
|
szDestPtr = szOutputString;
|
|
dwReturn = 0;
|
|
while (((*szSrcPtr != cDelimiter) && (*szSrcPtr != 0)) &&
|
|
(dwReturn < cchBufferLength)) {
|
|
if (dwFlags & GSFDL_REMOVE_QUOTES) {
|
|
if (*szSrcPtr == '\"') {
|
|
// skip the quote
|
|
szSrcPtr++;
|
|
continue;
|
|
}
|
|
}
|
|
*szDestPtr++ = *szSrcPtr++; // copy character
|
|
dwReturn++; // increment length
|
|
}
|
|
if (dwReturn > 0) {
|
|
*szDestPtr = 0; // add terminator char
|
|
}
|
|
}
|
|
return dwReturn;
|
|
}
|
|
|
|
static
|
|
DWORD
|
|
AddUniqueStringToMultiSz (
|
|
IN LPVOID mszDest,
|
|
IN LPSTR szSource,
|
|
IN BOOL bUnicodeDest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
searches the Multi-SZ list, mszDest for szSource and appends it
|
|
to mszDest if it wasn't found
|
|
|
|
Arguments:
|
|
|
|
OUT LPVOID mszDest Multi-SZ list to get new string
|
|
IN LPSTR szSource string to add if it's not already in list
|
|
|
|
ReturnValue:
|
|
|
|
The new length of the destination string including both
|
|
trailing NULL characters if the string was added, or 0 if the
|
|
string is already in the list.
|
|
|
|
--*/
|
|
{
|
|
LPVOID szDestElem;
|
|
DWORD dwReturnLength;
|
|
LPWSTR wszSource = NULL;
|
|
DWORD dwLength;
|
|
|
|
// check arguments
|
|
|
|
if ((mszDest == NULL) || (szSource == NULL)) return 0; // invalid buffers
|
|
if (*szSource == '\0') return 0; // no source string to add
|
|
|
|
// if unicode list, make a unicode copy of the string to compare
|
|
// and ultimately copy if it's not already in the list
|
|
|
|
if (bUnicodeDest) {
|
|
dwLength = lstrlenA(szSource) + 1;
|
|
wszSource = G_ALLOC (dwLength * sizeof(WCHAR));
|
|
if (wszSource != NULL) {
|
|
dwReturnLength = mbstowcs (wszSource, szSource, dwLength);
|
|
} else {
|
|
// unable to allocate memory for the temp string
|
|
dwReturnLength = 0;
|
|
}
|
|
} else {
|
|
// just use the ANSI version of the source file name
|
|
dwReturnLength = 1;
|
|
}
|
|
|
|
if (dwReturnLength > 0) {
|
|
// go to end of dest string
|
|
//
|
|
for (szDestElem = mszDest;
|
|
(bUnicodeDest ? (*(LPWSTR)szDestElem != 0) :
|
|
(*(LPSTR)szDestElem != 0));
|
|
) {
|
|
if (bUnicodeDest) {
|
|
// bail out if string already in lsit
|
|
if (lstrcmpiW((LPCWSTR)szDestElem, wszSource) == 0) {
|
|
return 0;
|
|
} else {
|
|
// goto the next item
|
|
szDestElem = (LPVOID)((LPWSTR)szDestElem +
|
|
(lstrlenW((LPCWSTR)szDestElem)+1));
|
|
}
|
|
} else {
|
|
// bail out if string already in lsit
|
|
if (lstrcmpiA((LPSTR)szDestElem, szSource) == 0) {
|
|
return 0;
|
|
} else {
|
|
// goto the next item
|
|
szDestElem = (LPVOID)((LPSTR)szDestElem +
|
|
(lstrlenA((LPCSTR)szDestElem)+1));
|
|
}
|
|
}
|
|
}
|
|
|
|
// if here, then add string
|
|
// szDestElem is at end of list
|
|
|
|
if (bUnicodeDest) {
|
|
lstrcpyW ((LPWSTR)szDestElem, wszSource);
|
|
szDestElem = (LPVOID)((LPWSTR)szDestElem + lstrlenW(wszSource) + 1);
|
|
*((LPWSTR)szDestElem)++ = L'\0';
|
|
dwReturnLength = (DWORD)((LPWSTR)szDestElem - (LPWSTR)mszDest);
|
|
} else {
|
|
lstrcpyA ((LPSTR)szDestElem, szSource);
|
|
szDestElem = (LPVOID)((LPSTR)szDestElem + lstrlenA(szDestElem) + 1);
|
|
*((LPSTR)szDestElem)++ = '\0'; // add second NULL
|
|
dwReturnLength = (DWORD)((LPSTR)szDestElem - (LPSTR)mszDest);
|
|
}
|
|
}
|
|
|
|
return dwReturnLength;
|
|
}
|
|
|
|
PDH_FUNCTION
|
|
OpenOutputTextLog (
|
|
IN PLOG_INFO pLog
|
|
)
|
|
{
|
|
LONG pdhStatus;
|
|
|
|
pLog->StreamFile = (FILE *)-1;
|
|
pdhStatus = ERROR_SUCCESS;
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
PDH_FUNCTION
|
|
CloseTextLog (
|
|
IN PLOG_INFO pLog
|
|
)
|
|
{
|
|
LONG pdhStatus;
|
|
|
|
if (pLog->StreamFile != (FILE *)-1) {
|
|
fclose (pLog->StreamFile);
|
|
}
|
|
pdhStatus = ERROR_SUCCESS;
|
|
return pdhStatus;
|
|
}
|
|
|
|
PDH_FUNCTION
|
|
WriteTextLogHeader (
|
|
IN PLOG_INFO pLog
|
|
)
|
|
{
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
PLOG_COUNTER_INFO pThisCounter;
|
|
CHAR cDelim;
|
|
CHAR szLeadDelim[4];
|
|
DWORD dwLeadSize;
|
|
CHAR szTrailDelim[4];
|
|
DWORD dwTrailSize;
|
|
DWORD dwBytesWritten;
|
|
PPDH_COUNTER_INFO_A pCtrInfo;
|
|
DWORD dwCtrInfoSize;
|
|
BOOL bResult;
|
|
|
|
pCtrInfo = G_ALLOC (8192);
|
|
|
|
if (pCtrInfo == NULL) {
|
|
return PDH_MEMORY_ALLOCATION_FAILURE;
|
|
}
|
|
|
|
cDelim = (LOWORD(pLog->dwLogFormat) == OPD_CSV_FILE) ? COMMA_DELIMITER :
|
|
TAB_DELIMITER;
|
|
|
|
szLeadDelim[0] = cDelim;
|
|
szLeadDelim[1] = DOUBLE_QUOTE;
|
|
szLeadDelim[2] = 0;
|
|
szLeadDelim[3] = 0;
|
|
dwLeadSize = 2;
|
|
|
|
szTrailDelim[0] = DOUBLE_QUOTE;
|
|
szTrailDelim[1] = 0;
|
|
szTrailDelim[2] = 0;
|
|
szTrailDelim[3] = 0;
|
|
dwTrailSize = 1;
|
|
|
|
|
|
// write the logfile header record
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)&szTrailDelim[0],
|
|
1,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// write the time stamp title
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)szTimeStampLabel,
|
|
dwTimeStampLabelLength,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
}
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// check each counter in the list of counters for this query and
|
|
// write them to the file
|
|
|
|
// output the path names
|
|
pThisCounter = pFirstCounter;
|
|
|
|
if (pThisCounter != NULL) {
|
|
do {
|
|
// write the leading delimiter
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)szLeadDelim,
|
|
dwLeadSize,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
break; // out of the Do Loop
|
|
}
|
|
|
|
// get the counter path information from the counter
|
|
dwCtrInfoSize = 8192;
|
|
pdhStatus = PdhGetCounterInfoA (
|
|
pThisCounter->hCounter,
|
|
FALSE,
|
|
&dwCtrInfoSize,
|
|
pCtrInfo);
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// write the counter name
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)pCtrInfo->szFullPath,
|
|
lstrlen(pCtrInfo->szFullPath),
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
break; // out of the Do Loop
|
|
}
|
|
} else {
|
|
// unable to get counter information so bail here
|
|
break;
|
|
}
|
|
|
|
// write the trailing delimiter
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)szTrailDelim,
|
|
dwTrailSize,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
break; // out of the Do Loop
|
|
}
|
|
|
|
pThisCounter = pThisCounter->next;
|
|
} while (pThisCounter != NULL);
|
|
}
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// write the record terminator
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)szRecordTerminator,
|
|
dwRecordTerminatorLength,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
}
|
|
}
|
|
|
|
G_FREE (pCtrInfo);
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
PDH_FUNCTION
|
|
WriteTextLogRecord (
|
|
IN PLOG_INFO pLog,
|
|
IN SYSTEMTIME *stTimeStamp
|
|
)
|
|
{
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
PLOG_COUNTER_INFO pThisCounter;
|
|
CHAR cDelim;
|
|
DWORD dwBytesWritten;
|
|
DWORD dwBufferSize;
|
|
CHAR szValueBuffer[VALUE_BUFFER_SIZE];
|
|
PDH_FMT_COUNTERVALUE pdhValue;
|
|
BOOL bResult;
|
|
|
|
cDelim = (LOWORD(pLog->dwLogFormat) == OPD_CSV_FILE) ? COMMA_DELIMITER :
|
|
TAB_DELIMITER;
|
|
|
|
// format and write the time stamp title
|
|
|
|
dwBufferSize = sprintf (szValueBuffer, szFmtTimeStamp,
|
|
stTimeStamp->wMonth, stTimeStamp->wDay, stTimeStamp->wYear,
|
|
stTimeStamp->wHour, stTimeStamp->wMinute, stTimeStamp->wSecond, stTimeStamp->wMilliseconds);
|
|
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
szValueBuffer,
|
|
dwBufferSize,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
} else {
|
|
// check each counter in the list of counters for this query and
|
|
// write them to the file
|
|
|
|
pThisCounter = pFirstCounter;
|
|
|
|
if (pThisCounter != NULL) {
|
|
do {
|
|
// get the formatted value from the counter
|
|
|
|
// compute and format current value
|
|
pdhStatus = PdhGetFormattedCounterValue (
|
|
pThisCounter->hCounter,
|
|
PDH_FMT_DOUBLE,
|
|
NULL,
|
|
&pdhValue);
|
|
|
|
if ((pdhStatus == ERROR_SUCCESS) &&
|
|
((pdhValue.CStatus == PDH_CSTATUS_VALID_DATA) ||
|
|
(pdhValue.CStatus == PDH_CSTATUS_NEW_DATA))) {
|
|
// then this is a valid data value so print it
|
|
dwBufferSize = sprintf (szValueBuffer,
|
|
szFmtRealValue, cDelim, pdhValue.doubleValue);
|
|
} else {
|
|
// invalid data so show a blank
|
|
dwBufferSize = sprintf (szValueBuffer, " ");
|
|
// reset error value
|
|
pdhStatus = ERROR_SUCCESS;
|
|
}
|
|
|
|
// write this value to the file
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
szValueBuffer,
|
|
dwBufferSize,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
break; // out of the loop
|
|
}
|
|
|
|
// goto the next counter in the list
|
|
pThisCounter = pThisCounter->next;
|
|
} while (pThisCounter != NULL);
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// write the record terminator
|
|
bResult = WriteFile (pLog->hLogFileHandle,
|
|
(LPCVOID)szRecordTerminator,
|
|
dwRecordTerminatorLength,
|
|
&dwBytesWritten,
|
|
NULL);
|
|
|
|
if (!bResult) {
|
|
pdhStatus = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
static
|
|
LONG
|
|
CreateNewLogEntry (
|
|
IN LPCWSTR szLogFileName,
|
|
IN DWORD dwLogFileNameSize, // in chars, including term char
|
|
IN HQUERY hQuery,
|
|
IN DWORD dwMaxRecords
|
|
)
|
|
/*++
|
|
creates a new log entry and inserts it in the list of open log files
|
|
|
|
--*/
|
|
{
|
|
PLOG_INFO pNewLog;
|
|
DWORD dwSize;
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
|
|
dwSize = dwLogFileNameSize;
|
|
dwSize *= sizeof (WCHAR);
|
|
dwSize *= 2; // double to make room for cat file name
|
|
dwSize = DWORD_MULTIPLE (dwSize); // ... rounded to the next DWORD
|
|
dwSize += sizeof (LOG_INFO); // + room for the data block
|
|
|
|
pNewLog = &LogEntry;
|
|
|
|
// set length field (this is used more for validation
|
|
// than anything else
|
|
pNewLog->dwLength = sizeof (LOG_INFO);
|
|
// append filename strings immediately after this block
|
|
pNewLog->szLogFileName = (LPWSTR)(&pNewLog[1]);
|
|
lstrcpyW (pNewLog->szLogFileName, szLogFileName);
|
|
// locate catalog name immediately after log file name
|
|
pNewLog->szCatFileName = pNewLog->szLogFileName + dwLogFileNameSize;
|
|
// FIXFIX: for now they are the same, later the log file extension
|
|
// will be replaced with the catalog file extenstion
|
|
lstrcpyW (pNewLog->szCatFileName, szLogFileName);
|
|
// initialize the file handles
|
|
pNewLog->hLogFileHandle = INVALID_HANDLE_VALUE;
|
|
pNewLog->hCatFileHandle = INVALID_HANDLE_VALUE;
|
|
|
|
// assign the query
|
|
pNewLog->hQuery = hQuery;
|
|
|
|
pNewLog->dwMaxRecords = dwMaxRecords;
|
|
|
|
pNewLog->dwLogFormat = 0; // for now
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
static
|
|
LONG
|
|
OpenOutputLogFile (
|
|
IN PLOG_INFO pLog,
|
|
IN DWORD dwAccessFlags,
|
|
IN LPDWORD lpdwLogType
|
|
)
|
|
{
|
|
LONG Win32Error;
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
DWORD dwFileCreate;
|
|
|
|
// open file for output based on the specified access flags
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
switch (dwAccessFlags & LOG_CREATE_MASK) {
|
|
case LOG_CREATE_NEW:
|
|
dwFileCreate = CREATE_NEW;
|
|
break;
|
|
|
|
case LOG_CREATE_ALWAYS:
|
|
dwFileCreate = CREATE_ALWAYS;
|
|
break;
|
|
|
|
case LOG_OPEN_EXISTING:
|
|
dwFileCreate = OPEN_EXISTING;
|
|
break;
|
|
|
|
case LOG_OPEN_ALWAYS:
|
|
dwFileCreate = OPEN_ALWAYS;
|
|
break;
|
|
|
|
default:
|
|
// unrecognized value
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
pLog->hLogFileHandle = CreateFileW (
|
|
pLog->szLogFileName,
|
|
GENERIC_WRITE, // write access for output
|
|
FILE_SHARE_READ, // allow read sharing
|
|
NULL, // default security
|
|
dwFileCreate,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL); // no template file
|
|
|
|
if (pLog->hLogFileHandle == INVALID_HANDLE_VALUE) {
|
|
Win32Error = GetLastError();
|
|
}
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// the file opened successfully so update the data structure
|
|
// this assumes the access flags are in the HIWORD and the...
|
|
pLog->dwLogFormat = dwAccessFlags & LOG_ACCESS_MASK;
|
|
// the type id is in the LOWORD
|
|
pLog->dwLogFormat |= *lpdwLogType;
|
|
|
|
// call any type-specific open functions
|
|
switch (LOWORD(pLog->dwLogFormat)) {
|
|
case OPD_CSV_FILE:
|
|
case OPD_TSV_FILE:
|
|
pdhStatus = OpenOutputTextLog(pLog);
|
|
break;
|
|
|
|
default:
|
|
pdhStatus = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
}
|
|
return pdhStatus;
|
|
}
|
|
|
|
static
|
|
LONG
|
|
WriteLogHeader (
|
|
IN PLOG_INFO pLog
|
|
)
|
|
{
|
|
LONG pdhStatus;
|
|
|
|
switch (LOWORD(pLog->dwLogFormat)) {
|
|
case OPD_CSV_FILE:
|
|
case OPD_TSV_FILE:
|
|
pdhStatus = WriteTextLogHeader (pLog);
|
|
break;
|
|
|
|
default:
|
|
pdhStatus = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
return pdhStatus;
|
|
|
|
}
|
|
|
|
static
|
|
LONG
|
|
CloseAndDeleteLogEntry (
|
|
IN PLOG_INFO pLog,
|
|
IN DWORD dwFlags
|
|
)
|
|
{
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
|
|
// call any type-specific open functions
|
|
switch (LOWORD(pLog->dwLogFormat)) {
|
|
case OPD_CSV_FILE:
|
|
case OPD_TSV_FILE:
|
|
pdhStatus = CloseTextLog(pLog);
|
|
break;
|
|
|
|
default:
|
|
pdhStatus = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
if (pLog->lpMappedFileBase != NULL) {
|
|
UnmapViewOfFile (pLog->lpMappedFileBase);
|
|
}
|
|
|
|
if (pLog->hMappedLogFile != NULL) {
|
|
CloseHandle (pLog->hMappedLogFile);
|
|
}
|
|
|
|
if (pLog->hLogFileHandle != INVALID_HANDLE_VALUE) {
|
|
CloseHandle (pLog->hLogFileHandle);
|
|
}
|
|
|
|
if (pLog->hCatFileHandle != INVALID_HANDLE_VALUE) {
|
|
|
|
CloseHandle (pLog->hCatFileHandle);
|
|
}
|
|
|
|
if ((dwFlags & FLAGS_CLOSE_QUERY) == FLAGS_CLOSE_QUERY) {
|
|
pdhStatus = PdhCloseQuery (pLog->hQuery);
|
|
}
|
|
}
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
//
|
|
// Local utility functions
|
|
//
|
|
|
|
LONG __stdcall
|
|
OpenLogW (
|
|
IN LPCWSTR szLogFileName,
|
|
IN DWORD dwAccessFlags,
|
|
IN LPDWORD lpdwLogType,
|
|
IN HQUERY hQuery,
|
|
IN DWORD dwMaxRecords
|
|
)
|
|
{
|
|
DWORD dwFileNameSize;
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
DWORD dwLocalLogType;
|
|
PLOG_INFO pLog = &LogEntry;
|
|
|
|
try {
|
|
// test the parameters before continuing
|
|
if (szLogFileName != NULL) {
|
|
dwFileNameSize = lstrlenW (szLogFileName) + 1;
|
|
if (dwFileNameSize > 1) {
|
|
if (lpdwLogType != NULL) {
|
|
dwLocalLogType = *lpdwLogType; // test read
|
|
*lpdwLogType = 0; // test write to buffer
|
|
*lpdwLogType = dwLocalLogType; // restore value
|
|
} else {
|
|
// required parameter is missing
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
}
|
|
} else {
|
|
// empty file name
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
}
|
|
} else {
|
|
// required parameter is missing
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
// something failed so give up here
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
}
|
|
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// create a log entry
|
|
pdhStatus = CreateNewLogEntry (
|
|
szLogFileName,
|
|
dwFileNameSize,
|
|
hQuery,
|
|
dwMaxRecords);
|
|
|
|
// open the file
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// dispatch based on read/write attribute
|
|
if ((dwAccessFlags & LOG_READ_ACCESS) ==
|
|
LOG_READ_ACCESS) {
|
|
pdhStatus = ERROR_NOT_SUPPORTED;
|
|
} else if ((dwAccessFlags & LOG_WRITE_ACCESS) ==
|
|
LOG_WRITE_ACCESS) {
|
|
pdhStatus = OpenOutputLogFile (pLog,
|
|
dwAccessFlags, &dwLocalLogType);
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
pdhStatus = WriteLogHeader (pLog);
|
|
}
|
|
} else {
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
}
|
|
if (pdhStatus == ERROR_SUCCESS) {
|
|
// return handle to caller
|
|
*lpdwLogType = dwLocalLogType;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
LONG __stdcall
|
|
UpdateLog (
|
|
IN LPDWORD pdwSampleTime)
|
|
{
|
|
LONG pdhStatus;
|
|
SYSTEMTIME st;
|
|
LONGLONG llStartTime = 0;
|
|
LONGLONG llFinishTime = 0;
|
|
PLOG_INFO pLog = &LogEntry;
|
|
|
|
*pdwSampleTime = 0;
|
|
|
|
if (TRUE) {
|
|
// get the timestamp and update the log's query, then write the data
|
|
// to the log file in the appropriate format
|
|
GetLocalFileTime (&st, &llStartTime);
|
|
|
|
// update data samples
|
|
pdhStatus = PdhCollectQueryData (pLog->hQuery);
|
|
|
|
// write data to log file
|
|
pdhStatus = WriteTextLogRecord (pLog, &st);
|
|
|
|
GetLocalFileTime (&st, &llFinishTime);
|
|
|
|
*pdwSampleTime = (DWORD)((llFinishTime - llStartTime) / 10000L);
|
|
}
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
LONG __stdcall
|
|
CloseLog(
|
|
IN DWORD dwFlags
|
|
)
|
|
{
|
|
LONG pdhStatus;
|
|
PLOG_INFO pLog;
|
|
|
|
pLog = &LogEntry;
|
|
pdhStatus = CloseAndDeleteLogEntry (pLog, dwFlags);
|
|
memset (pLog, 0, sizeof(LOG_INFO));
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
LONG __stdcall
|
|
GetLogFileSize (
|
|
IN LONGLONG *llSize
|
|
)
|
|
{
|
|
LONG pdhStatus = ERROR_SUCCESS;
|
|
PLOG_INFO pLog;
|
|
UINT nErrorMode;
|
|
DWORD dwFileSizeLow = 0;
|
|
DWORD dwFileSizeHigh = 0;
|
|
LONGLONG llFileLength;
|
|
DWORD dwError;
|
|
|
|
// test return argument
|
|
try {
|
|
// test access to the user's buffer.
|
|
llFileLength = *llSize;
|
|
*llSize = 0;
|
|
*llSize = llFileLength;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
pdhStatus = PDH_INVALID_ARGUMENT;
|
|
return pdhStatus;
|
|
}
|
|
|
|
if (TRUE) {
|
|
pLog = &LogEntry;
|
|
// disable windows error message popup
|
|
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|
if (pLog->hLogFileHandle != INVALID_HANDLE_VALUE) {
|
|
dwFileSizeLow = GetFileSize (pLog->hLogFileHandle, &dwFileSizeHigh);
|
|
// test for error
|
|
if ((dwFileSizeLow == 0xFFFFFFFF) &&
|
|
((dwError = GetLastError()) != NO_ERROR)) {
|
|
// then we couldn't get the file size
|
|
pdhStatus = ERROR_OPEN_FAILED;
|
|
} else {
|
|
if (dwFileSizeHigh > 0) {
|
|
llFileLength = dwFileSizeHigh << (sizeof(DWORD) * 8);
|
|
} else {
|
|
llFileLength = 0;
|
|
}
|
|
llFileLength += dwFileSizeLow;
|
|
// write to the caller' buffer
|
|
*llSize = llFileLength;
|
|
}
|
|
} else {
|
|
pdhStatus = ERROR_OPEN_FAILED;
|
|
}
|
|
SetErrorMode (nErrorMode); // restore old error mode
|
|
} else {
|
|
pdhStatus = PDH_INVALID_HANDLE;
|
|
}
|
|
|
|
return pdhStatus;
|
|
}
|
|
|
|
|