///////////////////////////////////////////////////////////////////////////////
/*  File: eventlog.cpp

    Description: Implements a subset of the NT event log APIs as a C++ class.
        CEventLog is intended only to provide a convenient method for writing
        NT event log messages.  No reading of event log entries is supported.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    02/14/98    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h"
#pragma hdrstop

#include "eventlog.h"
#include "registry.h"

TCHAR CEventLog::m_szFmtDec[] = TEXT("%1!d!");
TCHAR CEventLog::m_szFmtHex[] = TEXT("0x%1!X!");


CEventLog::~CEventLog(
    void
    )
{
    DBGTRACE((DM_EVENTLOG, DL_MID, TEXT("CEventLog::~CEventLog")));
    Close();
}

//
// Register the specified event source.
// Note that the registry entries must already exist.
// HKLM\System\CurrentControlSet\Services\EventLog\Application\<pszEventSource>
//     Requires values "EventMessageFile" and "TypesSupported".
//
HRESULT
CEventLog::Initialize(
    LPCTSTR pszEventSource
    )
{
    DBGTRACE((DM_EVENTLOG, DL_MID, TEXT("CEventLog::Initialize")));
    if (NULL != m_hLog)
    {
        return S_FALSE;
    }

    HRESULT hr = NOERROR;
    m_hLog = RegisterEventSource(NULL, pszEventSource);
    if (NULL == m_hLog)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        DBGERROR((TEXT("Error 0x%08X registering event source \"%s\""), hr, pszEventSource));
        DBGERROR((TEXT("Run regsvr32 on dskquota.dll")));
    }

    return hr;
}

//
// Deregister the event source.
//
void
CEventLog::Close(
    void
    )
{
    DBGTRACE((DM_EVENTLOG, DL_MID, TEXT("CEventLog::Close")));

    if (NULL != m_hLog)
    {
        DeregisterEventSource(m_hLog);
        m_hLog = NULL;
    }
}


//
// Report an event.  No replaceable parameters explicitly specified.
// If msg string contains replaceable parameters, use Push() to
// build list of replacement strings.
//
HRESULT
CEventLog::ReportEvent(
    WORD wType,
    WORD wCategory,
    DWORD dwEventID,
    PSID lpUserSid,    // [optional]
    LPVOID pvRawData,  // [optional]
    DWORD cbRawData    // [optional]
    )
{
    DBGTRACE((DM_EVENTLOG, DL_MID, TEXT("CEventLog::ReportEvent")));

    if (NULL == m_hLog)
    {
        DBGERROR((TEXT("Event log not initialized")));
        return E_FAIL;
    }

    BOOL bResult = false;
    HRESULT hr = NOERROR;
    if (0 < m_rgstrText.Count())
    {
        bResult = ReportEvent(wType,
                              wCategory,
                              dwEventID,
                              m_rgstrText,
                              lpUserSid,
                              pvRawData,
                              cbRawData);
        m_rgstrText.Clear();
    }
    else
    {
        bResult = ::ReportEvent(m_hLog,
                                wType,
                                wCategory,
                                dwEventID,
                                lpUserSid,
                                0,
                                cbRawData,
                                NULL,
                                pvRawData);
    }
    if (!bResult)
    {
        //
        // Special-case ERROR_IO_PENDING.  ::ReportEvent will fail with
        // this error code even when it succeeds.  Don't know exactly why
        // but it does.  Treat this as success so we don't get unnecessary
        // debugger output.
        //
        DWORD dwError = GetLastError();
        if (ERROR_IO_PENDING != dwError)
        {
            hr = HRESULT_FROM_WIN32(dwError);
            DBGERROR((TEXT("Error 0x%08X reporting event"), hr));
        }
    }
    return hr;
}


//
// Report an event.  Replacement strings are explicitly specified
// through an array of CString objects.
//
HRESULT
CEventLog::ReportEvent(
    WORD wType,
    WORD wCategory,
    DWORD dwEventID,
    const CArray<CString>& rgstr,
    PSID lpUserSid,
    LPVOID pvRawData,
    DWORD cbRawData
    )
{
    DBGTRACE((DM_EVENTLOG, DL_MID, TEXT("CEventLog::ReportEvent [ with strings ]")));

    if (NULL == m_hLog)
    {
        DBGERROR((TEXT("Event log not initialized")));
        return E_FAIL;
    }

    HRESULT hr = NOERROR;
    int cStrings = rgstr.Count();
    array_autoptr<LPCTSTR> rgpsz;
    if (0 < cStrings)
    {
        rgpsz = new LPCTSTR[cStrings];
        for (int i = 0; i < cStrings; i++)
        {
            rgpsz[i] = rgstr[i].Cstr();
        }
    }
    if (!::ReportEvent(m_hLog,
                       wType,
                       wCategory,
                       dwEventID,
                       lpUserSid,
                       (WORD)cStrings,
                       cbRawData,
                       rgpsz.get(),
                       pvRawData))
    {
        //
        // Special-case ERROR_IO_PENDING.  ::ReportEvent will fail with
        // this error code even when it succeeds.  Don't know exactly why
        // but it does.  Treat this as success so we don't get unnecessary
        // debugger output.
        //
        DWORD dwError = GetLastError();
        if (ERROR_IO_PENDING != dwError)
        {
            hr = HRESULT_FROM_WIN32(dwError);
            DBGERROR((TEXT("Error 0x%08X reporting event"), hr));
        }
    }
    return hr;
}


//
// Push an HRESULT value onto the stack of replacment strings.
//
void
CEventLog::Push(
    HRESULT hr,
    eFmt fmt
    )
{
    DBGTRACE((DM_EVENTLOG, DL_LOW, TEXT("CEventLog::Push [ integer ]")));

    LPTSTR pszBuffer = NULL;
    CString s;
    try
    {
        if (eFmtSysErr == fmt)
        {
            int cchLoaded = ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                                            FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                            NULL,
                                            HRESULT_CODE(hr),
                                            0,
                                            (LPTSTR)&pszBuffer,
                                            1,
                                            NULL);

            if (NULL != pszBuffer && 0 != cchLoaded)
            {
                s = pszBuffer;
            }
        }
        else
        {
            s.Format(eFmtDec == fmt ? m_szFmtDec : m_szFmtHex, hr);
        }
        m_rgstrText.Append(s);
    }
    catch(CAllocException& e)
    {
        DBGERROR((TEXT("Out of memory error in CEventLog::Push")));
    }
    LocalFree(pszBuffer);
}

//
// Push a string onto the stack of replacement strings.
//
void
CEventLog::Push(
    LPCTSTR psz
    )
{
    DBGTRACE((DM_EVENTLOG, DL_LOW, TEXT("CEventLog::Push [ string ]")));
    try
    {
        m_rgstrText.Append(CString(psz));
    }
    catch(CAllocException& e)
    {
        DBGERROR((TEXT("Out of memory error in CEventLog::Push")));
    }
}



