///////////////////////////////////////////////////////////////////////////// // AGCEventLogger.cpp : Implementation of CAGCEventLogger // #include "pch.h" #include <..\TCLib\AutoHandle.h> #include <..\TCLib\UtilImpl.h> #include <..\TCLib\WinApp.h> #include <..\TCAtl\SimpleStream.h> #include "AGCEventLogger.h" #include "AGCEventDef.h" ///////////////////////////////////////////////////////////////////////////// // CAGCEventLogger TC_OBJECT_EXTERN_IMPL(CAGCEventLogger) ///////////////////////////////////////////////////////////////////////////// // Construction / Destruction CAGCEventLogger::CAGCEventLogger() : m_bInitializing(true), m_bstrID(L"id"), m_bstrLogAsNTEvent(L"LogAsNTEvent"), m_bstrLogAsDBEvent(L"LogAsDBEvent"), m_bLoggingToNTEnabled(true), m_bLoggingToDBEnabled(true) { TCZeroMemory(&m_idTable); } ///////////////////////////////////////////////////////////////////////////// // Overrides IUnknown* CAGCEventLogger::OnGetUnknown() { return GetUnknown(); } TC_WorkItemRelProc CAGCEventLogger::OnGetWorkItemRelProc() { return ReleaseWorkItem; } void CAGCEventLogger::OnMessage(UINT idMsg, int cParams, LPARAM* rgParams) { switch (idMsg) { case e_LogEvent: { PRIVATE_ASSERTE(2 <= cParams); IAGCEvent* pEvent = reinterpret_cast(rgParams[0]); HANDLE hevt = reinterpret_cast(rgParams[1]); LogEvent(pEvent, !!hevt); break; }; case e_CloseNTEventLog: { PRIVATE_ASSERTE(2 <= cParams); HANDLE hevt = reinterpret_cast(rgParams[0]); HRESULT* phr = reinterpret_cast(rgParams[1]); *phr = CloseNTEventLog(); break; } case e_ChangeNTEventLog: { PRIVATE_ASSERTE(3 <= cParams); BSTR bstr = reinterpret_cast(rgParams[0]); HANDLE hevt = reinterpret_cast(rgParams[1]); HRESULT* phr = reinterpret_cast(rgParams[2]); *phr = ChangeNTEventLog(bstr); break; } case e_CloseDatabase: { PRIVATE_ASSERTE(2 <= cParams); HANDLE hevt = reinterpret_cast(rgParams[0]); HRESULT* phr = reinterpret_cast(rgParams[1]); *phr = CloseDatabase(); break; } case e_ChangeDatabase: { PRIVATE_ASSERTE(5 <= cParams); IDBInitialize* pInit = reinterpret_cast(rgParams[0]); DBID* pidTable = reinterpret_cast(rgParams[1]); CDBPropSet* pPropSet = reinterpret_cast(rgParams[2]); HANDLE hevt = reinterpret_cast(rgParams[3]); HRESULT* phr = reinterpret_cast(rgParams[4]); *phr = ChangeDatabase(pInit, pidTable, pPropSet); break; } } } void WINAPI CAGCEventLogger::ReleaseWorkItem(UINT idMsg, int cParams, LPARAM* rgParams) { switch (idMsg) { case e_LogEvent: { PRIVATE_ASSERTE(2 <= cParams); IAGCEvent* pEvent = reinterpret_cast(rgParams[0]); HANDLE hevt = reinterpret_cast(rgParams[1]); pEvent->Release(); if (hevt) ::SetEvent(hevt); break; }; case e_CloseNTEventLog: { PRIVATE_ASSERTE(2 <= cParams); HANDLE hevt = reinterpret_cast(rgParams[0]); HRESULT* phr = reinterpret_cast(rgParams[1]); SetEvent(hevt); break; } case e_ChangeNTEventLog: { PRIVATE_ASSERTE(3 <= cParams); BSTR bstr = reinterpret_cast(rgParams[0]); HANDLE hevt = reinterpret_cast(rgParams[1]); HRESULT* phr = reinterpret_cast(rgParams[2]); SetEvent(hevt); break; } case e_CloseDatabase: { PRIVATE_ASSERTE(2 <= cParams); HANDLE hevt = reinterpret_cast(rgParams[0]); HRESULT* phr = reinterpret_cast(rgParams[1]); SetEvent(hevt); break; } case e_ChangeDatabase: { PRIVATE_ASSERTE(5 <= cParams); IDBInitialize* pInit = reinterpret_cast(rgParams[0]); DBID* pidTable = reinterpret_cast(rgParams[1]); CDBPropSet* pPropSet = reinterpret_cast(rgParams[2]); HANDLE hevt = reinterpret_cast(rgParams[3]); HRESULT* phr = reinterpret_cast(rgParams[4]); pInit->Release(); SetEvent(hevt); break; } } } ///////////////////////////////////////////////////////////////////////////// // Implementation void DisplayOLEDBErrorRecords(HRESULT hrErr = S_OK) { CDBErrorInfo ErrorInfo; ULONG cRecords; HRESULT hr; ULONG i; CComBSTR bstrDesc, bstrHelpFile, bstrSource; GUID guid; DWORD dwHelpContext; WCHAR wszGuid[40]; USES_CONVERSION; TCERRLOG_BEGIN_SIZE(_MAX_PATH * 8); // If the user passed in an HRESULT then trace it if (hrErr != S_OK) { TCERRLOG_PART1("OLE DB Error Record dump for hr = 0x%x\n", hrErr); } LCID lcLocale = GetSystemDefaultLCID(); hr = ErrorInfo.GetErrorRecords(&cRecords); if (FAILED(hr) && ErrorInfo.m_spErrorInfo == NULL) { TCERRLOG_PART1("No OLE DB Error Information found: hr = 0x%x\n", hr); } else { for (i = 0; i < cRecords; i++) { hr = ErrorInfo.GetAllErrorInfo(i, lcLocale, &bstrDesc, &bstrSource, &guid, &dwHelpContext, &bstrHelpFile); if (FAILED(hr)) { TCERRLOG_PART1("OLE DB Error Record dump retrieval failed: hr = 0x%x\n", hr ); return; } StringFromGUID2(guid, wszGuid, sizeof(wszGuid) / sizeof(WCHAR)); TCERRLOG_PART5( "Source:\"%ls\"\nDescription:\"%ls\"\nHelp File:\"%ls\"\nHelp Context:%4d\nGUID:%ls\n", bstrSource, bstrDesc, bstrHelpFile, dwHelpContext, wszGuid); bstrSource.Empty(); bstrDesc.Empty(); bstrHelpFile.Empty(); } } TCERRLOG_END } void CAGCEventLogger::LogEvent(IAGCEvent* pEvent, bool bSynchronous) { PRIVATE_ASSERTE(pEvent); XLock lock(this); // Determine if we can log to either event log type bool bLogToNT = m_bLoggingToNTEnabled && !m_shEventLog.IsNull(); bool bLogToDB = m_bLoggingToDBEnabled && m_ds.m_spInit != NULL; // Get the event ID AGCEventID idEvent; PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_ID(&idEvent))); // Determine if the event is enabled for each event log type VARIANT_BOOL bEnabled; if (bLogToNT && NULL != m_spRangesNT && SUCCEEDED(m_spRangesNT->get_IntersectsWithValue(idEvent, &bEnabled))) bLogToNT = !!bEnabled; if (bLogToDB && NULL != m_spRangesDB && SUCCEEDED(m_spRangesDB->get_IntersectsWithValue(idEvent, &bEnabled))) bLogToDB = !!bEnabled; // Unlock the CritSec lock.Unlock(); // Do nothing if we're not logging this event anywhere if (!bLogToNT && !bLogToDB) return; // Find the event definition const CAGCEventDef::XEventDef* pEventDef = CAGCEventDef::find(idEvent); PRIVATE_ASSERTE(pEventDef); // Output the event to the debug monitor #if 0 && defined(_DEBUG) { CComBSTR bstrDescription, bstrSubjectName; PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_Description(&bstrDescription))); PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_SubjectName (&bstrSubjectName))); int cchDbg = bstrSubjectName.Length() + bstrDescription.Length() + 16; _TRACE_BEGIN_SIZE(cchDbg) if (bstrSubjectName.Length()) _TRACE_PART1("%ls:\n", (BSTR)bstrSubjectName); if (bstrDescription.Length()) _TRACE_PART1("%ls\n" , (BSTR)bstrDescription); _TRACE_END } #endif // defined(_DEBUG) // Log the event to the NT Event Log, if specified if (bLogToNT) { PRIVATE_ASSERTE(IsWinNT()); IAGCEventLoggerHookPtr spHook; get_HookForNTLogging(&spHook); if (NULL == spHook || S_FALSE == spHook->LogEvent(pEvent, VARBOOL(bSynchronous))) { XLock lockNT(this); if (m_bLoggingToNTEnabled && !m_shEventLog.IsNull()) { // Get the event severity WORD wSeverity = pEventDef->m_wSeverity; // Create the full event id (regarding the MC-generated id's) UINT nBits; switch (wSeverity) { case EVENTLOG_INFORMATION_TYPE: nBits = 0x01 << 30; break; case EVENTLOG_WARNING_TYPE : nBits = 0x02 << 30; break; case EVENTLOG_ERROR_TYPE : nBits = 0x03 << 30; break; case EVENTLOG_SUCCESS : nBits = 0x00 << 30; break; default : PRIVATE_ASSERTE(!"Bad severity code."); } DWORD dwEventID = nBits | idEvent; // Get the event replacement parameters CAGCEventDef::XParamStrings vecParamStrings; PRIVATE_VERIFYE(SUCCEEDED(CAGCEventDef::GetEventParameters(pEvent, vecParamStrings, pEventDef))); // Report the event to the NT Event log ReportEventW(m_shEventLog, wSeverity, 0, dwEventID, NULL, vecParamStrings.size(), 0, (LPCWSTR*)(vecParamStrings.begin()), NULL); // Free the replacement parameters BSTR's for (CAGCEventDef::XParamStrings::iterator it = vecParamStrings.begin(); it != vecParamStrings.end(); ++it) SysFreeString(*it); } } } // Log the event to the DB Event Log, if specified if (bLogToDB) { IAGCEventLoggerHookPtr spHook; get_HookForDBLogging(&spHook); if (NULL == spHook || S_FALSE == spHook->LogEvent(pEvent, VARBOOL(bSynchronous))) { XLock lockDB(this); if (m_bLoggingToDBEnabled && m_ds.m_spInit != NULL) { // Get the fields of the specified event object long idSubject; CComBSTR bstrComputerName, bstrSubjectName, bstrContext; PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_SubjectID (&idSubject ))); PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_ComputerName(&bstrComputerName))); PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_SubjectName (&bstrSubjectName ))); PRIVATE_VERIFYE(SUCCEEDED(pEvent->get_Context (&bstrContext ))); // Open the session and table, and insert the row HRESULT hr; if (SUCCEEDED(hr = m_session.Open(m_ds))) { if (SUCCEEDED(hr = m_table.Open(m_session, m_idTable, &m_propset))) { // Populate the fields of the new row USES_CONVERSION; // Event m_table.m_idEvent = idEvent; // Subject m_table.m_idSubject = idSubject; // ComputerName if (bstrComputerName.Length()) lstrcpyn(m_table.m_szComputerName, OLE2CT(bstrComputerName), sizeofArray(m_table.m_szComputerName)); else *m_table.m_szComputerName = TEXT('\0'); // SubjectName if (bstrSubjectName.Length()) lstrcpyn(m_table.m_szSubjectName, OLE2CT(bstrSubjectName), sizeofArray(m_table.m_szSubjectName)); else *m_table.m_szSubjectName = TEXT('\0'); // Context if (bstrContext.Length()) lstrcpyn(m_table.m_szContext, OLE2CT(bstrContext), sizeofArray(m_table.m_szContext)); else *m_table.m_szContext = TEXT('\0'); // Get the event object as a string PRIVATE_ASSERTE(IPersistStreamInitPtr(pEvent) != NULL || IPersistStreamPtr(pEvent) != NULL); PRIVATE_ASSERTE(IMarshalPtr(pEvent) != NULL); CComBSTR bstrTemp; PRIVATE_VERIFYE(SUCCEEDED(pEvent->SaveToString(&bstrTemp))); WideCharToMultiByte(CP_ACP, 0, bstrTemp, -1, m_table.m_szObjRef, sizeof(m_table.m_szObjRef), 0, 0); // Insert the row into the table hr = m_table.Insert(); } } // Report any errors if (FAILED(hr)) DisplayOLEDBErrorRecords(hr); // Close the table m_table.Close(); // Close the session m_session.Close(); } } } } HRESULT CAGCEventLogger::CloseDatabase() { XLock lock(this); // Close the previous database connection m_table.Close(); m_session.Close(); m_ds.Close(); // Indicate success return S_OK; } HRESULT CAGCEventLogger::ChangeDatabase(IDBInitialize* pInit, DBID* pidTable, CDBPropSet* pPropSet) { XLock lock(this); // Close the previous database connection CloseDatabase(); // Set the specified database connection m_ds.m_spInit = pInit; m_bstrTableName = pidTable->uName.pwszName; m_idTable.eKind = DBKIND_NAME; m_idTable.uName.pwszName = m_bstrTableName; m_propset = *pPropSet; // Test the database by opening it m_session.Open(m_ds); HRESULT hr = m_table.Open(m_session, m_idTable, &m_propset); if (FAILED(hr)) m_bstrTableName.Empty(); m_table.Close(); m_session.Close(); return hr; } HRESULT CAGCEventLogger::CloseNTEventLog() { XLock lock(this); // Close the previous event log m_shEventLog = NULL; // Indicate success return S_OK; } HRESULT CAGCEventLogger::ChangeNTEventLog(BSTR bstrComputer) { XLock lock(this); // Close the previous event log CloseNTEventLog(); // Open the NT event log on the specified computer BSTR bstr = BSTRLen(bstrComputer) ? bstrComputer : NULL; m_shEventLog = RegisterEventSourceW(bstr, m_bstrSourceApp); // Indicate success or failure return !m_shEventLog.IsNull() ? S_OK : HRESULT_FROM_WIN32(GetLastError()); } HKEY CAGCEventLogger::RootKeyFromString(BSTR bstrRegKey, DWORD* cchEaten) { // Define a static lookup table struct XRootKeys { LPCOLESTR pszName; DWORD cchName; HKEY hkey; }; const static XRootKeys s_table[] = { {L"HKCR\\" , 5, HKEY_CLASSES_ROOT }, {L"HKCU\\" , 5, HKEY_CURRENT_USER }, {L"HKLM\\" , 5, HKEY_LOCAL_MACHINE }, {L"HKCC\\" , 5, HKEY_CURRENT_CONFIG }, {L"HKPD\\" , 5, HKEY_PERFORMANCE_DATA}, {L"HKDD\\" , 5, HKEY_DYN_DATA }, {L"HKEY_CLASSES_ROOT\\" , 18, HKEY_CLASSES_ROOT }, {L"HKEY_CURRENT_USER\\" , 18, HKEY_CURRENT_USER }, {L"HKEY_LOCAL_MACHINE\\" , 19, HKEY_LOCAL_MACHINE }, {L"HKEY_CURRENT_CONFIG\\" , 20, HKEY_CURRENT_CONFIG }, {L"HKEY_PERFORMANCE_DATA\\", 22, HKEY_PERFORMANCE_DATA}, {L"HKEY_DYN_DATA\\" , 14, HKEY_DYN_DATA }, }; const static long s_cEntries = sizeofArray(s_table); // Search for an allowed root key name if (BSTRLen(bstrRegKey)) { for (long i = 0; i < s_cEntries; ++i) { if (!_wcsnicmp(s_table[i].pszName, bstrRegKey, s_table[i].cchName)) { *cchEaten = s_table[i].cchName; return s_table[i].hkey; } } } // Could not find the specified string *cchEaten = 0; return NULL; } HRESULT CAGCEventLogger::ReadStringValueFromRegistry(LPCTSTR pszValueName, CComBSTR& bstrOut) { // Initialize the [out] parameter bstrOut.Empty(); // Get the size of data from the specified value DWORD cbData = 0; long lr = RegQueryValueEx(m_key, pszValueName, NULL, NULL, NULL, &cbData); if (ERROR_SUCCESS != lr) return HRESULT_FROM_WIN32(lr); // Do nothing else if data size is zero if (!cbData) return S_OK; // Allocate an automatic block of memory LPTSTR pszValue = (LPTSTR)_alloca(cbData); // Attempt to read the specified value lr = m_key.QueryValue(pszValue, pszValueName, &cbData); // Save the value string to the [out] parameter bstrOut = pszValue; // Indicate success return S_OK; } HRESULT CAGCEventLogger::OpenWriteableRegKey(CRegKey& keyWrite) { // Ensure that the specified key is closed keyWrite.Close(); // Do nothing if we are initializing XLock lock(this); if (m_bInitializing) return S_FALSE; // Ensure that the main registry key is open if (!m_key.m_hKey) return E_UNEXPECTED; // Open/create the writeable registry key long lr = keyWrite.Create(m_key, TEXT("")); return (ERROR_SUCCESS == lr) ? S_OK : HRESULT_FROM_WIN32(lr); } void CAGCEventLogger::FireNTEventLogStopped() { // Fire an 'event log stopped' event if a log is open bool bLogIsOpen; { XLock lock(this); bLogIsOpen = !m_shEventLog.IsNull(); } if (bLogIsOpen) { // Fire an event log stopped event GetAGCGlobal()->TriggerEventSynchronous(NULL, EventID_NTEventLogStopped, NULL, NULL, -1, -1, -1, 0, NULL); } } void CAGCEventLogger::FireDBEventLogStopped() { // Fire an 'event log stopped' event if a log is open bool bLogIsOpen; { XLock lock(this); bLogIsOpen = m_ds.m_spInit != NULL; } if (bLogIsOpen) { // Fire an event log stopped event GetAGCGlobal()->TriggerEventSynchronous(NULL, EventID_DBEventLogStopped, NULL, NULL, -1, -1, -1, 0, NULL); } } HRESULT CAGCEventLogger::CopyChildNodes(IXMLDOMNode* pNode, CAGCEventLogger::XNodes& nodes) { // Get the list of child nodes IXMLDOMNodeListPtr spChildren; RETURN_FAILED(pNode->get_childNodes(&spChildren)); // Get the count of child nodes long cChildren; RETURN_FAILED(spChildren->get_length(&cChildren)); // Set the size of the array nodes.resize(cChildren); // Copy each child node to the array for (long nIndex = 0; nIndex < cChildren; ++nIndex) { IXMLDOMNodePtr spChild; RETURN_FAILED(spChildren->nextNode(&spChild)) assert(NULL != spChild); nodes[nIndex] = spChild; } // Indicate success return S_OK; } HRESULT CAGCEventLogger::RemoveStyleSheetPIs(IXMLDOMNode* pNode) { // Get an array of child nodes XNodes nodes; RETURN_FAILED(CopyChildNodes(pNode, nodes)); // Process each child node for (XNodeIt it = nodes.begin(); it != nodes.end(); ++it) { // Get the node's type DOMNodeType type; RETURN_FAILED((*it)->get_nodeType(&type)); if (NODE_PROCESSING_INSTRUCTION == type) { // Get the processing instruction's name CComBSTR bstrName; RETURN_FAILED((*it)->get_nodeName(&bstrName)); if (0 == wcscmp(bstrName, L"xml-stylesheet")) { // Remove it if it's a stylesheet PI IXMLDOMNodePtr spNodeRemoved; RETURN_FAILED(pNode->removeChild(*it, &spNodeRemoved)); } } } // Indicate success return S_OK; } HRESULT CAGCEventLogger::ProcessXMLNode(IXMLDOMNode* pNode, IXMLDOMNode* pNodeParent) { // Get an array of child nodes XNodes nodes; RETURN_FAILED(CopyChildNodes(pNode, nodes)); // Get the available ranges for the host application IAGCEventIDRangesPtr spRangesHost; GetAGCGlobal()->GetAvailableEventIDRanges(&spRangesHost); // Process each child node for (XNodeIt it = nodes.begin(); it != nodes.end(); ++it) { // Get the node as an element IXMLDOMElementPtr spElement(*it); if (spElement == NULL) { // Get the node as a comment IXMLDOMCommentPtr spComment(*it); if (spComment != NULL) { // Remove the comment IXMLDOMNodePtr spNodeRemoved; RETURN_FAILED(pNode->removeChild(spComment, &spNodeRemoved)); } // Leave the node alone continue; } // Get the element's tag name CComBSTR bstrTag; RETURN_FAILED(spElement->get_tagName(&bstrTag)); // Process depending on the tag name if (0 == wcscmp(bstrTag, L"Event")) { // Get the element's "id" attribute CComVariant varValue; RETURN_FAILED(spElement->getAttribute(m_bstrID, &varValue)); RETURN_FAILED(varValue.ChangeType(VT_I4)); AGCEventID idEvent = static_cast(V_I4(&varValue)); // Determine if the event id is available for the host application VARIANT_BOOL bInRange; ZSucceeded(spRangesHost->get_IntersectsWithValue(idEvent, &bInRange)); if (!bInRange) { IXMLDOMNodePtr spNodeRemoved; RETURN_FAILED(pNode->removeChild(spElement, &spNodeRemoved)); } else { // Determine if the event id is enabled for NT event logging ZSucceeded(m_spRangesNT->get_IntersectsWithValue(idEvent, &bInRange)); varValue = !!bInRange; RETURN_FAILED(spElement->setAttribute(m_bstrLogAsNTEvent, varValue)); // Determine if the event id is enabled for DB event logging ZSucceeded(m_spRangesDB->get_IntersectsWithValue(idEvent, &bInRange)); varValue = !!bInRange; RETURN_FAILED(spElement->setAttribute(m_bstrLogAsDBEvent, varValue)); } } else if (0 == wcscmp(bstrTag, L"EventGroup")) { // Recursively process the group RETURN_FAILED(ProcessXMLNode(*it, pNode)); } } // Possibly remove the node, unless parent is the document if (pNodeParent) { // Get the list of child nodes IXMLDOMNodeListPtr spChildren; RETURN_FAILED(pNode->get_childNodes(&spChildren)); // Get the count of child nodes long cChildren; RETURN_FAILED(spChildren->get_length(&cChildren)); // If the node has no remaining children, delete it from the parent if (!cChildren) { // Remove the node from its parent IXMLDOMNodePtr spNodeRemoved; RETURN_FAILED(pNodeParent->removeChild(pNode, &spNodeRemoved)); } } // Indicate success return S_OK; } ///////////////////////////////////////////////////////////////////////////// // IAGCEventSink Interface Methods STDMETHODIMP CAGCEventLogger::OnEventTriggered(IAGCEvent* pEvent) { pEvent->AddRef(); PostMessage(e_LogEvent, 2, pEvent, NULL); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // IAGCEventSinkSynchronous Interface Methods STDMETHODIMP CAGCEventLogger::OnEventTriggeredSynchronous(IAGCEvent* pEvent) { TCHandle shEvent = ::CreateEvent(NULL, false, false, NULL); pEvent->AddRef(); PostMessage(e_LogEvent, 2, pEvent, shEvent.GetHandle()); WaitForSingleObject(shEvent, INFINITE); return S_OK; } ///////////////////////////////////////////////////////////////////////////// // IAGCEventLogger Interface Methods STDMETHODIMP CAGCEventLogger::get_EventList(BSTR* pbstrEventListXML) { // Create an instance of the free-threaded MSXML component IXMLDOMDocumentPtr spXMLDoc; RETURN_FAILED(spXMLDoc.CreateInstance(L"Microsoft.FreeThreadedXMLDOM")); // Load the XML from the AGCEventsXML resource void* pXMLData = NULL; DWORD cbXMLData = 0; RETURN_FAILED(LockAndLoadResource(GetAGCGlobal()->GetResourceInstance(), TEXT("AGCEventsXML"), MAKEINTRESOURCE(1), &pXMLData, &cbXMLData)); // Create a simple stream on the resource memory block TCSimpleStream stm; stm.Init(cbXMLData, pXMLData); // Load the XML from the resource data IStream CComVariant varXML((IStream*)&stm); VARIANT_BOOL bLoaded; ZSucceeded(spXMLDoc->load(varXML, &bLoaded)); // Remove xml-stylesheet processing instructions, if any ZSucceeded(RemoveStyleSheetPIs(spXMLDoc)); // Get the root element IXMLDOMElementPtr spRoot; ZSucceeded(spXMLDoc->get_documentElement(&spRoot)); #ifdef _DEBUG { // Ensure that the root element tag is CComBSTR bstrRoot; ZSucceeded(spRoot->get_tagName(&bstrRoot)); assert(0 == wcscmp(bstrRoot, L"AGCEvents")); } #endif // _DEBUG // Recursively process the children of each group RETURN_FAILED(ProcessXMLNode(spRoot, NULL)); // Return the XML text as a string return spXMLDoc->get_xml(pbstrEventListXML); } STDMETHODIMP CAGCEventLogger::put_NTEventLog(BSTR bstrComputer) { // This property can only be set on WinNT machines if (!IsWinNT()) return Error(IDS_E_NTEVENTLOG_WIN9X, IID_IAGCEventLogger); // Open a writeable registry key, unless we're initializing CRegKey keyWrite; RETURN_FAILED(OpenWriteableRegKey(keyWrite)); // Fire an 'event log stopped' event if a log is open FireNTEventLogStopped(); // Save the new computer name to the registry, unless we're initializing if (keyWrite.m_hKey) { USES_CONVERSION; long lr = keyWrite.SetValue(OLE2CT(bstrComputer), TEXT("NTEventLog")); assert(ERROR_SUCCESS == lr); } // Create an event to be signaled when new NT event log is open TCHandle hevt = CreateEvent(NULL, false, false, NULL); assert(!hevt.IsNull()); // Open the new NT event log HRESULT hr = S_OK; PostMessage(e_ChangeNTEventLog, 3, bstrComputer, hevt, &hr); // Wait for the NT event log to be opened (or failure) WaitForSingleObject(hevt, INFINITE); RETURN_FAILED(hr); // Save the specified computer name name { XLock lock(this); m_bstrNTEventLog = bstrComputer; } // Get the AGC version information IAGCVersionInfoPtr spVersionInfo; ZSucceeded(spVersionInfo.CreateInstance("AGC.AGCVersionInfo")); // Get some product version information from the object WORD wProductBuildNumber; CComBSTR bstrProductVersion; ZSucceeded(spVersionInfo->get_ProductBuildNumber(&wProductBuildNumber)); ZSucceeded(spVersionInfo->get_ProductVersionString(&bstrProductVersion)); // Fire an event log started event _AGCModule.TriggerEvent(NULL, EventID_NTEventLogStarted, bstrProductVersion, wProductBuildNumber, -1, -1, 0); // Indicate success return S_OK; } STDMETHODIMP CAGCEventLogger::get_NTEventLog(BSTR* pbstrComputer) { CComBSTR bstr(m_bstrNTEventLog); CLEAROUT(pbstrComputer, (BSTR)bstr); bstr.Detach(); return S_OK; } STDMETHODIMP CAGCEventLogger::put_DBEventLog(IAGCDBParams* pDBParams) { USES_CONVERSION; // Open a writeable registry key, unless we're initializing CRegKey keyWrite; RETURN_FAILED(OpenWriteableRegKey(keyWrite)); // Get the specified connection string and table name, if any CComBSTR bstrConnectionString; CComBSTR bstrTableName; if (pDBParams) { // Get the specified connection string RETURN_FAILED(pDBParams->get_ConnectionString(&bstrConnectionString)); // Get the specified table name RETURN_FAILED(pDBParams->get_TableName(&bstrTableName)); } // Attemp to establish a connection CDataSource ds; CSession session; DBID idTable; CDBPropSet propset(DBPROPSET_ROWSET); if (bstrConnectionString.Length() && bstrTableName.Length()) { // Attempt to establish a connection to the specified data source RETURN_FAILED(ds.OpenFromInitializationString(bstrConnectionString)); // Attempt to open a session on the data source RETURN_FAILED(session.Open(ds)); // Setup the DBID parameter idTable.eKind = DBKIND_NAME; idTable.uName.pwszName = bstrTableName; // Setup the property set for allowing updates to the rowset propset.AddProperty(DBPROP_IRowsetChange, true); propset.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_INSERT); // Attempt to open the specified table CTable > table; RETURN_FAILED(table.Open(session, idTable, &propset)); table.Close(); session.Close(); // TODO: Check the specified table for the needed columns } // Fire an 'event log stopped' event if a log is open FireDBEventLogStopped(); // Save the new DB params to the registry, unless we're initializing if (keyWrite.m_hKey) { USES_CONVERSION; long lr = keyWrite.SetValue(OLE2CT(bstrConnectionString), TEXT("ConnectionString")); assert(ERROR_SUCCESS == lr); lr = keyWrite.SetValue(OLE2CT(bstrTableName), TEXT("TableName")); assert(ERROR_SUCCESS == lr); } // Create an event to be signaled when new DB is open TCHandle hevt = CreateEvent(NULL, false, false, NULL); assert(!hevt.IsNull()); // Establish new connection HRESULT hr = S_OK; if (bstrConnectionString.Length() && bstrTableName.Length()) PostMessage(e_ChangeDatabase, 5, ds.m_spInit.Detach(), &idTable, &propset, hevt, &hr); else PostMessage(e_CloseDatabase, 2, hevt, &hr); // Wait for the DB to be opened (or failure) WaitForSingleObject(hevt, INFINITE); RETURN_FAILED(hr); // Save the specified table name { XLock lock(this); m_bstrTableName = bstrTableName; } // Return now if no database was specified if (!bstrConnectionString.Length() || !bstrTableName.Length()) return S_OK; // Get the AGC version information IAGCVersionInfoPtr spVersionInfo; ZSucceeded(spVersionInfo.CreateInstance("AGC.AGCVersionInfo")); // Get some product version information from the object WORD wProductBuildNumber; CComBSTR bstrProductVersion; ZSucceeded(spVersionInfo->get_ProductBuildNumber(&wProductBuildNumber)); ZSucceeded(spVersionInfo->get_ProductVersionString(&bstrProductVersion)); // Fire an event log started event _AGCModule.TriggerEvent(NULL, EventID_DBEventLogStarted, bstrProductVersion, wProductBuildNumber, -1, -1, 0); // Indicate success return S_OK; } STDMETHODIMP CAGCEventLogger::get_DBEventLog(IAGCDBParams** ppDBParams) { IAGCDBParamsPtr spParams; RETURN_FAILED(spParams.CreateInstance("AGC.DBParams")); CComBSTR bstrConnectionString; XLock lock(this); if (m_ds.m_spInit != NULL) RETURN_FAILED(m_ds.GetInitializationString(&bstrConnectionString, true)) lock.Unlock(); RETURN_FAILED(spParams->put_ConnectionString(bstrConnectionString)); RETURN_FAILED(spParams->put_TableName(m_bstrTableName)); CLEAROUT(ppDBParams, (IAGCDBParams*)spParams); spParams.Detach(); return S_OK; } STDMETHODIMP CAGCEventLogger::put_EnabledNTEvents(IAGCEventIDRanges* pEvents) { // This property can only be set on WinNT machines if (!IsWinNT()) return Error(IDS_E_NTEVENTLOG_WIN9X, IID_IAGCEventLogger); // Open a writeable registry key, unless we're initializing XLock lock(this); CRegKey keyWrite; RETURN_FAILED(OpenWriteableRegKey(keyWrite)); // Revoke the current set of event ID ranges, if any if (NULL != m_spRangesNT) GetAGCGlobal()->RevokeEventRanges(m_spRangesNT, AGC_Any_Objects, static_cast(this)); // Use the default enabled events, if NULL was specified IAGCEventIDRangesPtr spRanges; if (!pEvents) { RETURN_FAILED(get_EnabledNTEvents(&spRanges)); pEvents = spRanges; } // Save the specified set of event ID ranges m_spRangesNT = pEvents; // Register the specified set of event ID ranges, if any CComBSTR bstrEventRanges; if (NULL != m_spRangesNT) { GetAGCGlobal()->RegisterEventRanges(m_spRangesNT, AGC_Any_Objects, static_cast(this)); // Persist the set of event ID ranges to a string ZSucceeded(m_spRangesNT->get_DisplayString(&bstrEventRanges)); } // Save the new set of event ID ranges to the registry, unless initializing if (keyWrite.m_hKey) { USES_CONVERSION; LPCTSTR pszValue = bstrEventRanges ? OLE2CT(bstrEventRanges) : TEXT(""); long lr = keyWrite.SetValue(pszValue, TEXT("EnabledNTEvents")); assert(ERROR_SUCCESS == lr); } // Indicate success return S_OK; } STDMETHODIMP CAGCEventLogger::get_EnabledNTEvents(IAGCEventIDRanges** ppEvents) { XLock lock(this); CLEAROUT(ppEvents, (IAGCEventIDRanges*)m_spRangesNT); (*ppEvents)->AddRef(); return S_OK; } STDMETHODIMP CAGCEventLogger::put_EnabledDBEvents(IAGCEventIDRanges* pEvents) { // Open a writeable registry key, unless we're initializing XLock lock(this); CRegKey keyWrite; RETURN_FAILED(OpenWriteableRegKey(keyWrite)); // Revoke the current set of event ID ranges, if any if (NULL != m_spRangesDB) GetAGCGlobal()->RevokeEventRanges(m_spRangesDB, AGC_Any_Objects, static_cast(this)); // Use the default enabled events, if NULL was specified IAGCEventIDRangesPtr spRanges; if (!pEvents) { RETURN_FAILED(get_EnabledDBEvents(&spRanges)); pEvents = spRanges; } // Save the specified set of event ID ranges m_spRangesDB = pEvents; // Register the specified set of event ID ranges, if any CComBSTR bstrEventRanges; if (NULL != m_spRangesDB) { GetAGCGlobal()->RegisterEventRanges(m_spRangesDB, AGC_Any_Objects, static_cast(this)); // Persist the set of event ID ranges to a string ZSucceeded(m_spRangesDB->get_DisplayString(&bstrEventRanges)); } // Save the new set of event ID ranges to the registry, unless initializing if (keyWrite.m_hKey) { USES_CONVERSION; LPCTSTR pszValue = bstrEventRanges ? OLE2CT(bstrEventRanges) : TEXT(""); long lr = keyWrite.SetValue(pszValue, TEXT("EnabledDBEvents")); assert(ERROR_SUCCESS == lr); } // Indicate success return S_OK; } STDMETHODIMP CAGCEventLogger::get_EnabledDBEvents(IAGCEventIDRanges** ppEvents) { XLock lock(this); CLEAROUT(ppEvents, (IAGCEventIDRanges*)m_spRangesDB); (*ppEvents)->AddRef(); return S_OK; } STDMETHODIMP CAGCEventLogger::get_DefaultEnabledNTEvents( IAGCEventIDRanges** ppEvents) { // Initialize the [out] parameter CLEAROUT(ppEvents, (IAGCEventIDRanges*)NULL); // Create an event ID ranges object IAGCEventIDRangesPtr spRanges; RETURN_FAILED(spRanges.CreateInstance("AGC.EventIDRanges")); // Get the available ranges IAGCEventIDRangesPtr spRangesHost; GetAGCGlobal()->GetAvailableEventIDRanges(&spRangesHost); // Loop through each event ID for (const CAGCEventDef::XEventDef* it = CAGCEventDef::begin(); it != CAGCEventDef::end(); ++it) { // Ignore group ID's and those not enabled by default if (0 == it->m_nIndent && it->m_bLogAsNTEvent) { VARIANT_BOOL bInRange; ZSucceeded(spRangesHost->get_IntersectsWithValue(it->m_id, &bInRange)); if (bInRange) ZSucceeded(spRanges->AddByValues(it->m_id, AGCEventID(it->m_id + 1))); } } // Detach the new object to the [out] parameter *ppEvents = spRanges.Detach(); // Indicate success return S_OK; } STDMETHODIMP CAGCEventLogger::get_DefaultEnabledDBEvents( IAGCEventIDRanges** ppEvents) { // Initialize the [out] parameter CLEAROUT(ppEvents, (IAGCEventIDRanges*)NULL); // Create an event ID ranges object IAGCEventIDRangesPtr spRanges; RETURN_FAILED(spRanges.CreateInstance("AGC.EventIDRanges")); // Get the available ranges IAGCEventIDRangesPtr spRangesHost; GetAGCGlobal()->GetAvailableEventIDRanges(&spRangesHost); // Loop through each event ID for (const CAGCEventDef::XEventDef* it = CAGCEventDef::begin(); it != CAGCEventDef::end(); ++it) { // Ignore group ID's and those not enabled by default if (0 == it->m_nIndent && it->m_bLogAsDBEvent) { VARIANT_BOOL bInRange; ZSucceeded(spRangesHost->get_IntersectsWithValue(it->m_id, &bInRange)); if (bInRange) ZSucceeded(spRanges->AddByValues(it->m_id, AGCEventID(it->m_id + 1))); } } // Detach the new object to the [out] parameter *ppEvents = spRanges.Detach(); // Indicate success return S_OK; } ///////////////////////////////////////////////////////////////////////////// // IAGCEventLoggerPrivate Interface Methods STDMETHODIMP CAGCEventLogger::Initialize(BSTR bstrSourceApp, BSTR bstrRegKey) { // Validate the specified parameters assert(BSTRLen(bstrSourceApp)); assert(BSTRLen(bstrRegKey)); // Interpret the first part of the specified registry key string DWORD cchEaten; HKEY hkeyRoot = RootKeyFromString(bstrRegKey, &cchEaten); if (!hkeyRoot) { ZError("CAGCEventLogger::Initialize(): Invalid registry key name specified."); return E_INVALIDARG; } // Attempt to open the specified registry key for read access USES_CONVERSION; LONG lr = m_key.Open(hkeyRoot, OLE2CT(bstrRegKey + cchEaten), KEY_READ); // Attempt to read the NT Event Logging properties from the registry CComBSTR bstrNTEventLog; IAGCEventIDRangesPtr spRangesNT; if (IsWinNT()) { // Attempt to read the NTEventLog computer name ReadStringValueFromRegistry(TEXT("NTEventLog"), bstrNTEventLog); // Attempt to read the EnabledNTEvents value CComBSTR bstrEnabledEvents; HRESULT hr = ReadStringValueFromRegistry(TEXT("EnabledNTEvents"), bstrEnabledEvents); if (SUCCEEDED(hr) && (!bstrEnabledEvents.Length() || _wcsicmp(bstrEnabledEvents, L"default"))) { // Create and populate the range object RETURN_FAILED(spRangesNT.CreateInstance("AGC.EventIDRanges")); RETURN_FAILED(spRangesNT->put_DisplayString(bstrEnabledEvents)); } else { // Get the default set of enabled events RETURN_FAILED(get_DefaultEnabledNTEvents(&spRangesNT)); } } // Attempt to read the DBEventLog connection string and table name CComBSTR bstrConnectionString; CComBSTR bstrTableName; IAGCEventIDRangesPtr spRangesDB; ReadStringValueFromRegistry(TEXT("ConnectionString"), bstrConnectionString); ReadStringValueFromRegistry(TEXT("TableName"), bstrTableName); // Create and populate the DBParams object IAGCDBParamsPtr spDBParams; RETURN_FAILED(spDBParams.CreateInstance("AGC.DBParams")); RETURN_FAILED(spDBParams->put_ConnectionString(bstrConnectionString)); RETURN_FAILED(spDBParams->put_TableName(bstrTableName)); // Attempt to read the EnabledDBEvents value CComBSTR bstrEnabledEvents; HRESULT hr = ReadStringValueFromRegistry(TEXT("EnabledDBEvents"), bstrEnabledEvents); if (SUCCEEDED(hr) && (!bstrEnabledEvents.Length() || _wcsicmp(bstrEnabledEvents, L"default"))) { // Create and populate the range object RETURN_FAILED(spRangesDB.CreateInstance("AGC.EventIDRanges")); RETURN_FAILED(spRangesDB->put_DisplayString(bstrEnabledEvents)); } else { // Get the default set of enabled events RETURN_FAILED(get_DefaultEnabledDBEvents(&spRangesDB)); } // Register as an event source if logging any events to NT event log if (IsWinNT()) { ZSucceeded(put_EnabledNTEvents(spRangesNT)); } // Register as an event source if logging any events to DB event log ZSucceeded(put_EnabledDBEvents(spRangesDB)); // Save the specified Event Source App name m_bstrSourceApp = bstrSourceApp; // Open the NT Event log specified in the registry if (IsWinNT()) { RETURN_FAILED(put_NTEventLog(bstrNTEventLog)); } // Open the DB Event log specified in the registry RETURN_FAILED(put_DBEventLog(spDBParams)); // Indicate success m_bInitializing = false; return S_OK; } STDMETHODIMP CAGCEventLogger::Terminate() { // Create an event to be signaled when event log is closed TCHandle hevt = CreateEvent(NULL, false, false, NULL); assert(!hevt.IsNull()); // Fire 'event log stopped' event if log is open FireDBEventLogStopped(); // Close the current NT event log HRESULT hr = S_OK; PostMessage(e_CloseDatabase, 2, hevt, &hr); // Wait for the DB event log to be closed (or failure) WaitForSingleObject(hevt, INFINITE); RETURN_FAILED(hr); // Fire 'event log stopped' event if log is open FireNTEventLogStopped(); // Close the current NT event log PostMessage(e_CloseNTEventLog, 2, hevt, &hr); // Wait for the NT event log to be closed (or failure) WaitForSingleObject(hevt, INFINITE); RETURN_FAILED(hr); // Keep a reference on ourself for the duration of this method IUnknownPtr spUnk(GetUnknown()); // Revoke all events for which we are registered { XLock lock(this); ZSucceeded(GetAGCGlobal()->RevokeAllEvents( static_cast(this))); } // Close the worker thread TCWorkerThread::Close(); // Indicate success return S_OK; } STDMETHODIMP CAGCEventLogger::put_LoggingToNTEnabled(VARIANT_BOOL bEnabled) { XLock lock(this); m_bLoggingToNTEnabled = !!bEnabled; return S_OK; } STDMETHODIMP CAGCEventLogger::get_LoggingToNTEnabled(VARIANT_BOOL* pbEnabled) { XLock lock(this); CLEAROUT(pbEnabled, VARBOOL(m_bLoggingToNTEnabled)); return S_OK; } STDMETHODIMP CAGCEventLogger::put_LoggingToDBEnabled(VARIANT_BOOL bEnabled) { XLock lock(this); m_bLoggingToDBEnabled = !!bEnabled; return S_OK; } STDMETHODIMP CAGCEventLogger::get_LoggingToDBEnabled(VARIANT_BOOL* pbEnabled) { XLock lock(this); CLEAROUT(pbEnabled, VARBOOL(m_bLoggingToDBEnabled)); return S_OK; } STDMETHODIMP CAGCEventLogger::put_HookForNTLogging(IAGCEventLoggerHook* pHook) { XLock lock(this); m_spHookNT = pHook; return S_OK; } STDMETHODIMP CAGCEventLogger::get_HookForNTLogging(IAGCEventLoggerHook** ppHook) { XLock lock(this); CLEAROUT(ppHook, (IAGCEventLoggerHook*)m_spHookNT); return S_OK; } STDMETHODIMP CAGCEventLogger::put_HookForDBLogging(IAGCEventLoggerHook* pHook) { XLock lock(this); m_spHookDB = pHook; return S_OK; } STDMETHODIMP CAGCEventLogger::get_HookForDBLogging(IAGCEventLoggerHook** ppHook) { XLock lock(this); CLEAROUT(ppHook, (IAGCEventLoggerHook*)m_spHookDB); return S_OK; }