EDKEVENT.C

// --edkevent.c----------------------------------------------------------------- 
//
// Routines to do event logging for EDK.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------

#include "edk.h"
#include <stdarg.h>
#include "edkevent.chk"


static EDKEVENTCOUNTsEventCount= {0};
static HANDLEhEventSource= NULL;
static BOOLfPreviouslyOpenedHandle= FALSE;


static VOID LogEventCommon(
IN BOOL fIsUnicode,
IN DWORD dwEvent,
IN DWORD cStrings,
IN va_list pArgList);


//$--HrEventOpenLog----------------------------------------------------------
// Initialize event logging for the EDK.
// -----------------------------------------------------------------------------
HRESULT HrEventOpenLog(// RETURNS: HRESULT
IN LPSTR pszApplicationName,// name of this application
IN LPSTR pszExecutableName,// name of executable
IN LPSTR pszEventMessageFile, // name of event message file
IN LPSTR pszParameterMessageFile, // name of parameter message file
IN LPSTR pszCategoryMessageFile, // name of category message file
OUT LPHANDLE phEventSourceOut)// [returns event logging handle]
{
HRESULT hr = NOERROR;
DWORDdwError= ERROR_SUCCESS;
CHARszKeyName[MAX_PATH+1] = {0};
HKEYhKey= NULL;
DWORDdwDisposition= 0;
HMODULEhinstModule= NULL;
CHARszExecutableBuffer[MAX_PATH+1] = {0};
DWORDcch= 0;

LPSTR pszMessageFile = NULL;

DEBUGPUBLIC("HrEventOpenLog()\n");

// Check the parameters.

hr = CHK_HrEventOpenLog(
pszApplicationName,
pszExecutableName,
pszEventMessageFile,
pszParameterMessageFile,
pszCategoryMessageFile,
phEventSourceOut);

if(FAILED(hr))
RETURN(hr);

// Make sure the event log isn't already open.

if (hEventSource != NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Flag that this is not a previously opened handle, and should be closed
// when HrEventCloseLog() is called.

fPreviouslyOpenedHandle = FALSE;

// If they didn't pass in an executable name then figure out the name
// of the executable currently executing and use that.

if (pszExecutableName == NULL)
{
hinstModule = GetModuleHandle(NULL);
if (hinstModule == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cch = GetModuleFileName(
hinstModule,
szExecutableBuffer,
sizeof(szExecutableBuffer) - 1);
if (cch == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

pszExecutableName = szExecutableBuffer;
}

// Create the name of the registry key for this application.

lstrcpy(szKeyName,
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
lstrcat(szKeyName, pszApplicationName);

// Open or create the registry key. This registry key contains/will
// contain values that are required by the Windows NT function
// ReportEvent().

dwError = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
szKeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisposition);
if (dwError != ERROR_SUCCESS)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Add the message file names to the key.

if(pszEventMessageFile == NULL)
{
pszMessageFile = pszExecutableName;
}
else
{
pszMessageFile = pszEventMessageFile;
}

hr = _HrWriteRegistrySZ(
hKey,
"EventMessageFile",
pszMessageFile);

if (FAILED(hr))
{
goto cleanup;
}

if(pszParameterMessageFile == NULL)
{
pszMessageFile = "%SystemRoot%\\System32\\kernel32.dll";
}
else
{
pszMessageFile = pszParameterMessageFile;
}

hr = _HrWriteRegistrySZ(
hKey,
"ParameterMessageFile",
pszMessageFile);

if(FAILED(hr))
{
goto cleanup;
}

if(pszCategoryMessageFile == NULL)
{
pszMessageFile = pszExecutableName;
}
else
{
pszMessageFile = pszCategoryMessageFile;
}

hr = _HrWriteRegistrySZ(
hKey,
"CategoryMessageFile",
pszMessageFile);

if(FAILED(hr))
{
goto cleanup;
}

hr = _HrWriteRegistryDWORD(
hKey,
"TypesSupported",
EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE);

if(FAILED(hr))
{
goto cleanup;
}

// Register the event source.

hEventSource = RegisterEventSource(NULL, pszApplicationName);

if (hEventSource == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Reset the event type counters.

sEventCount.cError = 0;
sEventCount.cWarning = 0;
sEventCount.cInformation = 0;

// Return the handle if they requested it.

if (phEventSourceOut)
{
*phEventSourceOut = hEventSource;
}

cleanup:
if (hKey != NULL)
{
dwError = RegCloseKey(hKey);

if (dwError != ERROR_SUCCESS)
{
hr = HR_LOG(E_FAIL);
}

hKey = NULL;
}

RETURN(hr);
}


//$--HrEventUseExisting---------------------------------------------------
// Initialize event logging for the EDK by connecting to an already open
// event log handle. This allows EventLogMsg() to log events to a handle
// that was opened elsewhere. Calling HrEventCloseLog() after calling
// this routine will do internal cleanup but will not close the event log
// handle. One example of where this routine is useful is within a DLL
// that is called by EDK code in which event logging has already been
// initialized.
// -----------------------------------------------------------------------------
HRESULT HrEventUseExisting(// RETURNS: HRESULT
IN HANDLE hExistingEventSource)// previously opened event log handle
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("HrEventUseExisting()\n");

// Check the parameters.

hr = CHK_HrEventUseExisting(
hExistingEventSource);
if (FAILED(hr))
RETURN(hr);

// Make sure the event log isn't already open.

if (hEventSource != NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Use the existing event log handle.

hEventSource = hExistingEventSource;

cleanup:
RETURN(hr);
}


//$--EventLogMsg----------------------------------------------------------------
//
// EventLogMsgA -- byte string version
// EventLogMsgW -- word string version
//
// Log an event to the event log, and optionally, log the original error(s)
// that caused the event. It has the following parameters:
//
// DWORD dwEvent
// DWORD cStrings
// [LPSTR pszString1]
// [LPSTR pszString2]
// [...................]
// [LPSTR pszStringN]
// DWORD cErrorCodes
// [DWORD dwErrorCode1]
// [DWORD dwErrorCode2]
// [.....................]
// [DWORD dwErrorCodeN]
//
// Each of the above strings and error codes are used as parameters to the
// message in the order they appear. This means that in event messages,
// all of the error message replacement parameters must have higher numbers
// than all of the string replacement parameters. For example:
//
// EventLogMsg(
// MYAPP_CANNOT_COPY_FILE,
// 2, pszSourceFile, pszDestFile,
// 1, dwError);
//
// And the message would be defined as:
//
// MessageId=
// Severity=Error
// Facility=Application
// SymbolicName=MYAPP_CANNOT_COPY_FILE
// Language=English
// Cannot copy file from %1 to %2 due to the following error:%n%3.
// .
//
// Note: This routine preserves the last error value returned by
// GetLastError().
//
// -----------------------------------------------------------------------------

//$--EventLogMsgA---------------------------------------------------------------
// Byte string version of EventLogMsg().
//
// IMPORTANT!!! The error code count [and error code list] is REQUIRED after
// the text string count [and text string list]. Failure to include the
// error code argument(s) may cause unexpected results.
// -----------------------------------------------------------------------------
VOID EventLogMsgA(// RETURNS: nothing
IN DWORD dwEvent,// error code of event to log
IN DWORD cStrings,// number of text string parameters
IN ...// text string parameters
//IN DWORD cErrors,// number of error code parameters
//IN ...// error code parameters
)
{
DWORD dwSaveLastError= 0;
va_listpArgList = {0};

dwSaveLastError = GetLastError();

va_start(pArgList, cStrings);
LogEventCommon(FALSE, dwEvent, cStrings, pArgList);
va_end(pArgList);

SetLastError(dwSaveLastError);
}

//$--EventLogMsgW---------------------------------------------------------------
// Word string version of EventLogMsg().
//
// IMPORTANT!!! The error code count [and error code list] is REQUIRED after
// the text string count [and text string list]. Failure to include the
// error code argument(s) may cause unexpected results.
// -----------------------------------------------------------------------------
VOID EventLogMsgW(// RETURNS: nothing
IN DWORD dwEvent,// error code of event to log
IN DWORD cStrings,// number of text string parameters
IN ...// text string parameters
//IN DWORD cErrors,// number of error code parameters
//IN ...// error code parameters
)
{
DWORDdwSaveLastError= 0;
va_listpArgList = {0};

dwSaveLastError = GetLastError();

va_start(pArgList, cStrings);
LogEventCommon(TRUE, dwEvent, cStrings, pArgList);
va_end(pArgList);

SetLastError(dwSaveLastError);
}

// Maximum total number of string and error parameters allowed by EventLogMsg().
#define_MAX_BUILD_STRINGS20

// Size of buffer to write error number string into (must be big enough to hold
// a string of the form: %%<decimal#> in either multi-byte or UNICODE.
#define_MAX_ERROR_STRING_BYTES40

//$--LogEventCommon-------------------------------------------------------------
// Common event logging code for both multi-byte and UNICODE strings.
// -----------------------------------------------------------------------------
static VOID LogEventCommon(// RETURNS: nothing
IN BOOL fIsUnicode,// flag set if text strings are UNICODE
IN DWORD dwEvent,// error code of event to log
IN DWORD cStrings,// number of text strings that follow
IN va_list pArgList)// argument strings/error codes
{
HRESULThr= NOERROR;
BOOLfItWorked= TRUE;
DWORDiArg= 0;
DWORDcBuildStrings= 0;
WORDwEventType= 0;

// This array can contain either all LPSTR's or all LPWSTR's.
LPVOIDrgpvBuildStrings[_MAX_BUILD_STRINGS] = {NULL};

// This array will be used to build either WCHAR strings or CHAR strings.
BYTErgrgbBuildErrorStrings[_MAX_BUILD_STRINGS]
[_MAX_ERROR_STRING_BYTES] = {0};

DEBUGPRIVATE("LogEventCommon()\n");

// Make sure event log is open.

if (hEventSource == NULL)
{
OutputDebugString("Event log is not open in LogEventCommon()\n");
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Get the number of string arguments and make sure it is within limits.

cBuildStrings = cStrings;

if (cBuildStrings > _MAX_BUILD_STRINGS)
{
OutputDebugString("Too many string parameters in LogEventCommon()\n");
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

// Get the string arguments and make sure they are valid string pointers.

for (iArg = 0; iArg < cBuildStrings; iArg++)
{
BOOL fIsValidString = TRUE;

if (fIsUnicode)
{
rgpvBuildStrings[iArg] = va_arg(pArgList, LPWSTR);
fIsValidString = TEST_STRINGW_PTR(rgpvBuildStrings[iArg]);
}
else
{
rgpvBuildStrings[iArg] = va_arg(pArgList, LPSTR);
fIsValidString = TEST_STRINGA_PTR(rgpvBuildStrings[iArg]);
}

if (!fIsValidString)
{
OutputDebugString("Invalid string parameter in LogEventCommon()\n");
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}

// Get the number of error code arguments and make sure it is within limits.

cBuildStrings += va_arg(pArgList, DWORD);

if (cBuildStrings > _MAX_BUILD_STRINGS)
{
OutputDebugString("Too many error code parameters in "
"LogEventCommon()\n");
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

// Get the error code arguments and convert them to strings of the form:
// %%<Number>.

for ( ; iArg < cBuildStrings; iArg++)
{
rgpvBuildStrings[iArg] = rgrgbBuildErrorStrings[iArg];

if (fIsUnicode)
{
swprintf(rgpvBuildStrings[iArg], L"%%%%%ld",
va_arg(pArgList, DWORD));
}
else
{
sprintf(rgpvBuildStrings[iArg], "%%%%%ld",
va_arg(pArgList, DWORD));
}
}

// Compute the event type for this event.

switch (dwEvent & 0xC0000000)
{
case 0xC0000000:
wEventType = EVENTLOG_ERROR_TYPE;
break;
case 0x80000000:
wEventType = EVENTLOG_WARNING_TYPE;
break;
default:
wEventType = EVENTLOG_INFORMATION_TYPE;
break;
}

// Log the event.

if (fIsUnicode)
{
fItWorked = ReportEventW(
hEventSource,
wEventType,
0,
dwEvent,
NULL,
(WORD)cBuildStrings,
0,
(LPWSTR *)rgpvBuildStrings,
NULL);
}
else
{
fItWorked = ReportEventA(
hEventSource,
wEventType,
0,
dwEvent,
NULL,
(WORD)cBuildStrings,
0,
(LPSTR *)rgpvBuildStrings,
NULL);
}

if (!fItWorked)
{
OutputDebugString("ReportEvent() failed in LogEventCommon()\n");
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Bump up the counter for the right type of event.

switch (wEventType)
{
case EVENTLOG_ERROR_TYPE:
sEventCount.cError++;
break;
case EVENTLOG_WARNING_TYPE:
sEventCount.cWarning++;
break;
case EVENTLOG_INFORMATION_TYPE:
sEventCount.cInformation++;
break;
default:
OutputDebugString(
"Cannot increment event type counter for "
"unknown event type.\n");
break;
}

cleanup:
// If we couldn't log it to the event log, then output a message to the
// error log and debug log.

if (FAILED(hr))
{
CHAR szDescription[100] = {0};
sprintf(szDescription, "Unable to log event 0x%08lx\n", dwEvent);
OutputDebugString(szDescription);
}
}


//$--HrEventGetCounts--------------------------------------------------------
// Returns the number of Error, Warning, and Information events logged (by the
// current executable).
// -----------------------------------------------------------------------------
HRESULT HrEventGetCounts(// RETURNS: HRESULT
OUT LPEDKEVENTCOUNT lpsEventCount)// structure to return event counts
{
HRESULThr= NOERROR;

DEBUGPUBLIC("HrEventGetCounts(lpsEventCount)\n");

hr = CHK_HrEventGetCounts(
lpsEventCount);
if (FAILED(hr))
RETURN(hr);

// Return the values.

MoveMemory(lpsEventCount, &sEventCount, sizeof(sEventCount));

RETURN(hr);
}


//$--HrEventCloseLog---------------------------------------------------------
// Shut down event logging for the EDK.
// -----------------------------------------------------------------------------
HRESULT HrEventCloseLog()// RETURNS: HRESULT
{
HRESULThr= NOERROR;
BOOLfItWorked= FALSE;

DEBUGPUBLIC("HrEventCloseLog()\n");

if (hEventSource && !fPreviouslyOpenedHandle)
{
fItWorked = CloseEventLog(hEventSource);
if (!fItWorked)
{
hr = HR_LOG(E_FAIL);
}
}

hEventSource = NULL;

RETURN(hr);
}

// $--HrEventGetHandle------------------------------------------------------
//
// DESCRIPTION: Retrieve event handle for this executable.
//
// OUTPUT: phEventLog -- event log handle pointer
//
// RETURNS: HRESULT -- NOERROR if successful,
// E_INVALIDARG if bad input,
// E_FAIL otherwise.
//
// Notes:
//
// 1) The event handle returned will be NULL if there is
// no open event log.
//
// 2) DLLs may not call this function to retrieve the event handle
// which their parent executable set. If the parent executable sets
// an event handle, then it must pass the event handle to the DLL.
//
// ----------------------------------------------------------------------------
HRESULT HrEventGetHandle(
IN HANDLE * phEventLog) // event log handle pointer
{
HRESULT hr = NOERROR;

DEBUGPRIVATE("HrEventGetHandle");

// check input parameters
hr = CHK_HrEventGetHandle(phEventLog);

if ( FAILED(hr) )
{
RETURN(hr);
}

// Return current event log handle. Note that it
// may be NULL.
*phEventLog = hEventSource;

RETURN(hr);

}