From 00244e3846cebc9050d3df372d3b12169940612d Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Mon, 18 Jul 2016 16:49:55 +0200 Subject: [PATCH] EventMonitor is functional now --- EventMonitor/App.cpp | 2 +- EventMonitor/ETWLog.cpp | 829 ++++++++++++++++++++++ EventMonitor/ETWLog.h | 136 ++++ EventMonitor/EventMonitor.vcxproj | 3 +- EventMonitor/EventMonitor.vcxproj.filters | 9 +- EventMonitor/LogPanel.cpp | 37 +- EventMonitor/Main.cpp | 562 --------------- EventMonitor/StdAfx.h | 25 +- EventMonitor/wxEventMonitor_UI.cpp | 4 +- EventMonitor/wxEventMonitor_UI.fbp | 4 +- EventMonitor/wxEventMonitor_UI.h | 4 +- 11 files changed, 1013 insertions(+), 602 deletions(-) create mode 100644 EventMonitor/ETWLog.cpp create mode 100644 EventMonitor/ETWLog.h delete mode 100644 EventMonitor/Main.cpp diff --git a/EventMonitor/App.cpp b/EventMonitor/App.cpp index 596981c..fec54d0 100644 --- a/EventMonitor/App.cpp +++ b/EventMonitor/App.cpp @@ -18,7 +18,7 @@ along with GÉANTLink. If not, see . */ -#include "stdafx.h" +#include "StdAfx.h" #if defined(__WXMSW__) #pragma comment(lib, "msi.lib") #endif diff --git a/EventMonitor/ETWLog.cpp b/EventMonitor/ETWLog.cpp new file mode 100644 index 0000000..b702925 --- /dev/null +++ b/EventMonitor/ETWLog.cpp @@ -0,0 +1,829 @@ +/* + Copyright 2015-2016 Amebis + Copyright 2016 GÉANT + + This file is part of GÉANTLink. + + GÉANTLink is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GÉANTLink is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GÉANTLink. If not, see . +*/ + +#include "StdAfx.h" + +#pragma comment(lib, "tdh.lib") +#pragma comment(lib, "Ws2_32.lib") + +using namespace std; +using namespace winstd; + + +////////////////////////////////////////////////////////////////////////// +// Local helper functions declarations +////////////////////////////////////////////////////////////////////////// + +static tstring MapToString(_In_ const EVENT_MAP_INFO *pMapInfo, _In_ LPCBYTE pData); +static tstring DataToString(_In_ USHORT InType, _In_ USHORT OutType, _In_count_(nDataSize) LPCBYTE pData, _In_ SIZE_T nDataSize, _In_ const EVENT_MAP_INFO *pMapInfo, _In_ BYTE nPtrSize); +static ULONG GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, ULONG i, ULONG *pulArraySize); +static tstring PropertyToString(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, ULONG ulPropIndex, LPWSTR pStructureName, ULONG ulStructIndex, BYTE nPtrSize); + + +////////////////////////////////////////////////////////////////////////// +// wxETWEvent +////////////////////////////////////////////////////////////////////////// + +const EVENT_RECORD wxETWEvent::s_record_null = {}; + + +wxETWEvent::wxETWEvent(wxEventType type, const EVENT_RECORD &record) : + m_record(record), + wxEvent(0, type) +{ + DoSetExtendedData(record.ExtendedDataCount, record.ExtendedData); + DoSetUserData(record.UserDataLength, record.UserData); +} + + +wxETWEvent::wxETWEvent(const wxETWEvent& event) : + m_record(event.m_record), + wxEvent(event) +{ + DoSetExtendedData(event.m_record.ExtendedDataCount, event.m_record.ExtendedData); + DoSetUserData(event.m_record.UserDataLength, event.m_record.UserData); +} + + +wxETWEvent::~wxETWEvent() +{ + if (m_record.ExtendedData) + delete (unsigned char*)m_record.ExtendedData; + + if (m_record.UserData) + delete (unsigned char*)m_record.UserData; +} + + +bool wxETWEvent::SetExtendedData(size_t extended_data_count, const EVENT_HEADER_EXTENDED_DATA_ITEM *extended_data) +{ + if (m_record.ExtendedData) + delete (unsigned char*)m_record.ExtendedData; + + return DoSetExtendedData(extended_data_count, extended_data); +} + + +bool wxETWEvent::SetUserData(size_t user_data_length, const void *user_data) +{ + if (m_record.UserData) + delete (unsigned char*)m_record.UserData; + + return DoSetUserData(user_data_length, user_data); +} + + +bool wxETWEvent::DoSetExtendedData(size_t extended_data_count, const EVENT_HEADER_EXTENDED_DATA_ITEM *extended_data) +{ + if (extended_data_count) { + wxASSERT_MSG(extended_data, wxT("extended data is NULL")); + + // Count the total required memory. + size_t data_size = 0; + for (size_t i = 0; i < extended_data_count; i++) + data_size += extended_data[i].DataSize; + + // Allocate memory for extended data. + m_record.ExtendedData = (EVENT_HEADER_EXTENDED_DATA_ITEM*)(new unsigned char[sizeof(EVENT_HEADER_EXTENDED_DATA_ITEM)*extended_data_count + data_size]); + wxCHECK_MSG(m_record.ExtendedData, false, wxT("extended data memory allocation failed")); + + // Bulk-copy extended data descriptors. + memcpy(m_record.ExtendedData, extended_data, sizeof(EVENT_HEADER_EXTENDED_DATA_ITEM) * extended_data_count); + + // Copy the data. + unsigned char *ptr = (unsigned char*)(m_record.ExtendedData + extended_data_count); + for (size_t i = 0; i < extended_data_count; i++) { + if (extended_data[i].DataSize) { + memcpy(ptr, (void*)(extended_data[i].DataPtr), extended_data[i].DataSize); + m_record.ExtendedData[i].DataPtr = (ULONGLONG)ptr; + ptr += extended_data[i].DataSize; + } else + m_record.ExtendedData[i].DataPtr = NULL; + } + } else + m_record.ExtendedData = NULL; + + m_record.ExtendedDataCount = extended_data_count; + + return true; +} + + +bool wxETWEvent::DoSetUserData(size_t user_data_length, const void *user_data) +{ + if (user_data_length) { + wxASSERT_MSG(user_data, wxT("user data is NULL")); + + // Allocate memory for user data. + m_record.UserData = new unsigned char[user_data_length]; + wxCHECK_MSG(m_record.UserData, false, wxT("user data memory allocation failed")); + + // Copy user data. + memcpy(m_record.UserData, user_data, user_data_length); + } else + m_record.UserData = NULL; + + m_record.UserDataLength = user_data_length; + + return true; +} + + +IMPLEMENT_DYNAMIC_CLASS(wxETWEvent, wxEvent) +wxDEFINE_EVENT(wxEVT_ETW_EVENT, wxETWEvent); + + +////////////////////////////////////////////////////////////////////////// +// wxEventTraceProcessorThread +////////////////////////////////////////////////////////////////////////// + +wxEventTraceProcessorThread::wxEventTraceProcessorThread(wxEvtHandler *parent, const wxArrayString &sessions) : + m_parent(parent), + wxThread(wxTHREAD_JOINABLE) +{ + EVENT_TRACE_LOGFILE tlf = {}; + tlf.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; + tlf.EventRecordCallback = EventRecordCallback; + tlf.Context = this; + + for (size_t i = 0, i_end = sessions.GetCount(); i < i_end; i++) { + // Open trace. + tlf.LoggerName = (LPTSTR)(LPCTSTR)(sessions[i]); + event_trace trace; + if (!trace.create(&tlf)) { + wxLogError(_("Error opening event trace (error %u)."), GetLastError()); + continue; + } + + // Save trace to the table. + m_traces.push_back(trace.detach()); + } +} + + +wxEventTraceProcessorThread::~wxEventTraceProcessorThread() +{ + for (vector::iterator trace = m_traces.begin(), trace_end = m_traces.end(); trace != trace_end; ++trace) { + TRACEHANDLE &h = *trace; + if (h) { + // Close trace. + CloseTrace(h); + } + } +} + + +void wxEventTraceProcessorThread::Abort() +{ + for (vector::iterator trace = m_traces.begin(), trace_end = m_traces.end(); trace != trace_end; ++trace) { + TRACEHANDLE &h = *trace; + if (h) { + // Close trace. + CloseTrace(h); + h = NULL; + } + } +} + + +wxThread::ExitCode wxEventTraceProcessorThread::Entry() +{ + // Process events. + ProcessTrace(m_traces.data(), (ULONG)m_traces.size(), NULL, NULL); + + return 0; +} + + +VOID WINAPI wxEventTraceProcessorThread::EventRecordCallback(_In_ PEVENT_RECORD pEvent) +{ + wxASSERT_MSG(pEvent, wxT("event is NULL")); + wxASSERT_MSG(pEvent->UserContext, wxT("thread is NULL")); + + wxEventTraceProcessorThread *_this = ((wxEventTraceProcessorThread*)pEvent->UserContext); + + if (_this->TestDestroy()) { + // Event processing is pending destruction. + return; + } + + _this->m_parent->QueueEvent(new wxETWEvent(wxEVT_ETW_EVENT, *pEvent)); +} + + +////////////////////////////////////////////////////////////////////////// +// wxETWListCtrl +////////////////////////////////////////////////////////////////////////// + +BEGIN_EVENT_TABLE(wxETWListCtrl, wxListCtrl) + EVT_ETW_EVENT(wxETWListCtrl::OnETWEvent) +END_EVENT_TABLE() + + +// {6EB8DB94-FE96-443F-A366-5FE0CEE7FB1C} +const GUID wxETWListCtrl::s_provider_eaphost = { 0X6EB8DB94, 0XFE96, 0X443F, { 0XA3, 0X66, 0X5F, 0XE0, 0XCE, 0XE7, 0XFB, 0X1C } }; + + +wxETWListCtrl::wxETWListCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) : + m_proc(NULL), + m_item_id(0), + wxListCtrl(parent, id, pos, size, style, validator, name) +{ + this->AppendColumn(_("Time" ), wxLIST_FORMAT_LEFT, 100); + this->AppendColumn(_("PID" ), wxLIST_FORMAT_LEFT, 50 ); + this->AppendColumn(_("TID" ), wxLIST_FORMAT_LEFT, 50 ); + this->AppendColumn(_("Source"), wxLIST_FORMAT_LEFT, 100); + this->AppendColumn(_("Event" ), wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE_USEHEADER); + + // Start a new session. + ULONG ulResult; + for (unsigned int i = 0; ; i++) { + //tstring log_file(tstring_printf(i ? _T("test.etl") : _T("test %u.etl"), i)); + tstring name(tstring_printf(i ? _T(PRODUCT_NAME_STR) _T(" Event Monitor Session %u") : _T(PRODUCT_NAME_STR) _T(" Event Monitor Session"), i)); + + // Allocate session properties. + ULONG + ulSizeName = (ULONG)((name .length() + 1)*sizeof(TCHAR)), + //ulSizeLogFile = (ULONG)((log_file.length() + 1)*sizeof(TCHAR)), + ulSize = sizeof(EVENT_TRACE_PROPERTIES) + ulSizeName /*+ ulSizeLogFile*/; + unique_ptr properties((EVENT_TRACE_PROPERTIES*)new char[ulSize]); + wxASSERT_MSG(properties, wxT("error allocating session properties memory")); + + // Initialize properties. + memset(properties.get(), 0, sizeof(EVENT_TRACE_PROPERTIES)); + properties->Wnode.BufferSize = ulSize; + properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + properties->Wnode.ClientContext = 1; //QPC clock resolution + CoCreateGuid(&(properties->Wnode.Guid)); + properties->LogFileMode = /*EVENT_TRACE_FILE_MODE_SEQUENTIAL |*/ EVENT_TRACE_REAL_TIME_MODE; + properties->MaximumFileSize = 1; // 1 MB + properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + //properties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + ulSizeName; + //memcpy((LPTSTR)((char*)properties.get() + properties->LogFileNameOffset), log_file.c_str(), ulSizeLogFile); + + if ((ulResult = m_session.create(name.c_str(), properties.get())) == ERROR_SUCCESS) { + break; + } else if (ulResult == ERROR_ACCESS_DENIED) { + wxLogError(_("Access denied creating event session: you need administrative privileges (Run As Administrator) or be a member of Performance Log Users group to start event tracing session.")); + return; + } else if (ulResult == ERROR_ALREADY_EXISTS) { + wxLogDebug(_("The %s event session already exists."), name.c_str()); + // Do not despair... Retry with a new session name and ID. + continue; + } else { + wxLogError(_("Error creating event session (error %u)."), ulResult); + return; + } + } + + // Enable event providers we are interested in to log events to our session. + if ((ulResult = EnableTraceEx( + &EAPMETHOD_TRACE_EVENT_PROVIDER, + &((const EVENT_TRACE_PROPERTIES*)m_session)->Wnode.Guid, + m_session, + EVENT_CONTROL_CODE_ENABLE_PROVIDER, + TRACE_LEVEL_VERBOSE, + 0, 0, + 0, + NULL)) != ERROR_SUCCESS) + { + wxLogError(_("Error enabling event provider (error %u)."), ulResult); + return; + } + if ((ulResult = EnableTraceEx( + &s_provider_eaphost, + &((const EVENT_TRACE_PROPERTIES*)m_session)->Wnode.Guid, + m_session, + EVENT_CONTROL_CODE_ENABLE_PROVIDER, + TRACE_LEVEL_VERBOSE, + 0, 0, + 0, + NULL)) != ERROR_SUCCESS) + { + // If the EAPHost trace provider failed to enable, do not despair. + wxLogDebug(_("Error enabling EAPHost event provider (error %u)."), ulResult); + } + + // Process events in separate thread, not to block wxWidgets' message pump. + wxArrayString sessions; + sessions.Add(m_session.name()); + m_proc = new wxEventTraceProcessorThread(this->GetEventHandler(), sessions); + wxASSERT_MSG(m_proc, wxT("error allocating thread memory")); + if (m_proc->Run() != wxTHREAD_NO_ERROR) { + wxFAIL_MSG("Can't create the thread!"); + delete m_proc; + m_proc = NULL; + } +} + + +wxETWListCtrl::~wxETWListCtrl() +{ + if (m_session) { + if (m_proc) { + m_proc->Abort(); + m_proc->Delete(); + delete m_proc; + } + + // Disable event providers. + EnableTraceEx( + &s_provider_eaphost, + &((const EVENT_TRACE_PROPERTIES*)m_session)->Wnode.Guid, + m_session, + EVENT_CONTROL_CODE_DISABLE_PROVIDER, + TRACE_LEVEL_VERBOSE, + 0, 0, + 0, + NULL); + + EnableTraceEx( + &EAPMETHOD_TRACE_EVENT_PROVIDER, + &((const EVENT_TRACE_PROPERTIES*)m_session)->Wnode.Guid, + m_session, + EVENT_CONTROL_CODE_DISABLE_PROVIDER, + TRACE_LEVEL_VERBOSE, + 0, 0, + 0, + NULL); + } +} + + +void wxETWListCtrl::OnETWEvent(wxETWEvent& event) +{ + EVENT_RECORD &rec = event.GetRecord(); + bool is_ours = IsEqualGUID(rec.EventHeader.ProviderId, EAPMETHOD_TRACE_EVENT_PROVIDER) ? true : false; + int column = 0; + + // Prepare item to insert into the list. + wxListItem item; + item.SetId(m_item_id++); + item.SetTextColour( + rec.EventHeader.EventDescriptor.Level >= TRACE_LEVEL_VERBOSE ? (is_ours ? 0x888888 : 0xaaaaaa) : + rec.EventHeader.EventDescriptor.Level >= TRACE_LEVEL_INFORMATION ? (is_ours ? 0x000000 : 0x555555) : + rec.EventHeader.EventDescriptor.Level >= TRACE_LEVEL_WARNING ? (is_ours ? 0x00aacc : 0x55ccdd) : + (is_ours ? 0x0000ff : 0x5555ff)); + item.SetBackgroundColour(0xffffff); + + { + // Output event time-stamp. + FILETIME ft; + ft.dwHighDateTime = rec.EventHeader.TimeStamp.HighPart; + ft.dwLowDateTime = rec.EventHeader.TimeStamp.LowPart; + + SYSTEMTIME st, st_local; + FileTimeToSystemTime(&ft, &st); + SystemTimeToTzSpecificLocalTime(NULL, &st, &st_local); + + ULONGLONG + ts = rec.EventHeader.TimeStamp.QuadPart, + nanosec = (ts % 10000000) * 100; + + item.SetColumn(column++); + item.SetText(tstring_printf(_T("%04d-%02d-%02d %02d:%02d:%02d.%09I64u"), + st_local.wYear, st_local.wMonth, st_local.wDay, st_local.wHour, st_local.wMinute, st_local.wSecond, nanosec)); + this->InsertItem(item); + } + + // Output process ID. + item.SetColumn(column++); + item.SetText(wxString::Format(wxT("%u"), rec.EventHeader.ProcessId)); + this->SetItem(item); + + // Output thread ID. + item.SetColumn(column++); + item.SetText(wxString::Format(wxT("%u"), rec.EventHeader.ThreadId)); + this->SetItem(item); + + // Output event source. + item.SetColumn(column++); + item.SetText(is_ours ? wxT(PRODUCT_NAME_STR) : wxT("EAPHost")); + this->SetItem(item); + + item.SetColumn(column++); + { + // Get event meta-info. + unique_ptr info; + ULONG ulResult; + if ((ulResult = TdhGetEventInformation(&rec, 0, NULL, info)) == ERROR_SUCCESS) { + if (info->DecodingSource != DecodingSourceWPP) { + if (rec.EventHeader.Flags & EVENT_HEADER_FLAG_STRING_ONLY) { + // This is a string-only event. Print it. + item.SetText((LPCWSTR)rec.UserData); + } else { + // This is not a string-only event. Prepare parameters. + + BYTE nPtrSize = (rec.EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER) ? 4 : 8; + vector props; + vector props_msg; + props.reserve(info->TopLevelPropertyCount); + props_msg.reserve(info->TopLevelPropertyCount); + for (ULONG i = 0; i < info->TopLevelPropertyCount; i++) { + props.push_back(std::move(PropertyToString(&rec, info.get(), i, NULL, 0, nPtrSize))); + props_msg.push_back((DWORD_PTR)props[i].c_str()); + } + + if (info->EventMessageOffset) { + // Format the message. + item.SetText(wstring_msg(0, (LPCTSTR)((LPCBYTE)info.get() + info->EventMessageOffset), props_msg.data()).c_str()); + } + } + } else if (info->EventMessageOffset) { + // This is a WPP event. + item.SetText((LPCWSTR)((LPCBYTE)info.get() + info->EventMessageOffset)); + } + } + } + this->SetItem(item); + + // Bring the record into view. + this->EnsureVisible(item.GetId()); +} + + +////////////////////////////////////////////////////////////////////////// +// Local helper functions +////////////////////////////////////////////////////////////////////////// + +static tstring MapToString(_In_ const EVENT_MAP_INFO *pMapInfo, _In_ LPCBYTE pData) +{ + if ( (pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP) || + ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP ) && (pMapInfo->Flag & ~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) != EVENTMAP_INFO_FLAG_WBEM_FLAG)) + { + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == EVENTMAP_INFO_FLAG_WBEM_NO_MAP) + return tstring_printf(_T("%ls"), (PBYTE)pMapInfo + pMapInfo->MapEntryArray[*(PULONG)pData].OutputOffset); + else { + for (ULONG i = 0; ; i++) { + if (i >= pMapInfo->EntryCount) + return tstring_printf(_T("%lu"), *(PULONG)pData); + else if (pMapInfo->MapEntryArray[i].Value == *(PULONG)pData) + return tstring_printf(_T("%ls"), (PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset); + } + } + } else if ( + (pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_BITMAP) || + (pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_BITMAP ) || + ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP ) && (pMapInfo->Flag & ~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == EVENTMAP_INFO_FLAG_WBEM_FLAG)) + { + tstring out; + + if (pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) { + for (ULONG i = 0; i < pMapInfo->EntryCount; i++) + if (*(PULONG)pData & (1 << i)) + out.append(tstring_printf(out.empty() ? _T("%ls") : _T(" | %ls"), (PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset)); + } else { + for (ULONG i = 0; i < pMapInfo->EntryCount; i++) + if ((pMapInfo->MapEntryArray[i].Value & *(PULONG)pData) == pMapInfo->MapEntryArray[i].Value) + out.append(tstring_printf(out.empty() ? _T("%ls") : _T(" | %ls"), (PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset)); + } + + return out.empty() ? tstring_printf(_T("%lu"), *(PULONG)pData) : out; + } + + return _T(""); +} + + +static tstring DataToString(_In_ USHORT InType, _In_ USHORT OutType, _In_count_(nDataSize) LPCBYTE pData, _In_ SIZE_T nDataSize, _In_ const EVENT_MAP_INFO *pMapInfo, _In_ BYTE nPtrSize) +{ + assert(pData || !nDataSize); + + switch (InType) { + case TDH_INTYPE_UNICODESTRING: + case TDH_INTYPE_NONNULLTERMINATEDSTRING: + case TDH_INTYPE_UNICODECHAR: + return tstring_printf(_T("%.*ls"), nDataSize/sizeof(WCHAR), pData); + + case TDH_INTYPE_ANSISTRING: + case TDH_INTYPE_NONNULLTERMINATEDANSISTRING: + case TDH_INTYPE_ANSICHAR: { + // Convert strings from ANSI code page, all others (JSON, XML etc.) from UTF-8 + wstring str; + MultiByteToWideChar(OutType == TDH_OUTTYPE_STRING ? CP_ACP : CP_UTF8, 0, (LPCSTR)pData, (int)nDataSize, str); + return tstring_printf(_T("%ls"), str.c_str()); + } + + case TDH_INTYPE_COUNTEDSTRING: + return DataToString(TDH_INTYPE_NONNULLTERMINATEDSTRING, OutType, (LPCBYTE)((PUSHORT)pData + 1), *(PUSHORT)pData, pMapInfo, nPtrSize); + + case TDH_INTYPE_COUNTEDANSISTRING: + return DataToString(TDH_INTYPE_NONNULLTERMINATEDANSISTRING, OutType, (LPCBYTE)((PUSHORT)pData + 1), *(PUSHORT)pData, pMapInfo, nPtrSize); + + case TDH_INTYPE_REVERSEDCOUNTEDSTRING: + return DataToString(TDH_INTYPE_NONNULLTERMINATEDSTRING, OutType, (LPCBYTE)((PUSHORT)pData + 1), MAKEWORD(HIBYTE(*(PUSHORT)pData), LOBYTE(*(PUSHORT)pData)), pMapInfo, nPtrSize); + + case TDH_INTYPE_REVERSEDCOUNTEDANSISTRING: + return DataToString(TDH_INTYPE_NONNULLTERMINATEDANSISTRING, OutType, (LPCBYTE)((PUSHORT)pData + 1), MAKEWORD(HIBYTE(*(PUSHORT)pData), LOBYTE(*(PUSHORT)pData)), pMapInfo, nPtrSize); + + case TDH_INTYPE_INT8: + assert(nDataSize >= sizeof(CHAR)); + switch (OutType) { + case TDH_OUTTYPE_STRING: return DataToString(TDH_INTYPE_ANSICHAR, TDH_OUTTYPE_NULL, pData, nDataSize, pMapInfo, nPtrSize); + default : return tstring_printf(_T("%hd"), *(PCHAR)pData); + } + + case TDH_INTYPE_UINT8: + assert(nDataSize >= sizeof(BYTE)); + switch (OutType) { + case TDH_OUTTYPE_STRING : return DataToString(TDH_INTYPE_ANSICHAR, TDH_OUTTYPE_NULL, pData, nDataSize, pMapInfo, nPtrSize); + case TDH_OUTTYPE_HEXINT8: return tstring_printf(_T("0x%x"), *(PBYTE)pData); + default : return tstring_printf(_T("%hu" ), *(PBYTE)pData); + } + + case TDH_INTYPE_INT16: + assert(nDataSize >= sizeof(SHORT)); + return tstring_printf(_T("%hd"), *(PSHORT)pData); + + case TDH_INTYPE_UINT16: + assert(nDataSize >= sizeof(USHORT)); + switch (OutType) { + case TDH_OUTTYPE_PORT : return tstring_printf(_T("%hu" ), ntohs(*(PUSHORT)pData)); + case TDH_OUTTYPE_HEXINT16: return tstring_printf(_T("0x%x"), *(PUSHORT)pData ); + case TDH_OUTTYPE_STRING : return tstring_printf(_T("%lc" ), *(PUSHORT)pData ); + default : return tstring_printf(_T("%hu" ), *(PUSHORT)pData ); + } + + case TDH_INTYPE_INT32: + assert(nDataSize >= sizeof(LONG)); + switch (OutType) { + case TDH_OUTTYPE_HRESULT: return tstring_printf(_T("0x%x"), *(PLONG)pData); + default : return tstring_printf(_T("%ld" ), *(PLONG)pData); + } + + case TDH_INTYPE_UINT32: + assert(nDataSize >= sizeof(ULONG)); + switch (OutType) { + case TDH_OUTTYPE_HRESULT : + case TDH_OUTTYPE_WIN32ERROR: + case TDH_OUTTYPE_NTSTATUS : + case TDH_OUTTYPE_HEXINT32 : return tstring_printf(_T("0x%x" ), *(PULONG)pData); + case TDH_OUTTYPE_IPV4 : return tstring_printf(_T("%d.%d.%d.%d"), (*(PULONG)pData >> 0) & 0xff, (*(PULONG)pData >> 8) & 0xff, (*(PULONG)pData >> 16) & 0xff, (*(PULONG)pData >> 24) & 0xff); + default: return pMapInfo ? MapToString(pMapInfo, pData) : tstring_printf(_T("%lu"), *(PULONG)pData); + } + + case TDH_INTYPE_HEXINT32: + return DataToString(TDH_INTYPE_UINT32, TDH_OUTTYPE_HEXINT32, pData, nDataSize, pMapInfo, nPtrSize); + + case TDH_INTYPE_INT64: + assert(nDataSize >= sizeof(LONGLONG)); + return tstring_printf(_T("%I64d"), *(PLONGLONG)pData); + + case TDH_INTYPE_UINT64: + assert(nDataSize >= sizeof(ULONGLONG)); + switch (OutType) { + case TDH_OUTTYPE_HEXINT64: return tstring_printf(_T("0x%I64x"), *(PULONGLONG)pData); + default : return tstring_printf(_T("%I64u" ), *(PULONGLONG)pData); + } + + case TDH_INTYPE_HEXINT64: + return DataToString(TDH_INTYPE_UINT64, TDH_OUTTYPE_HEXINT64, pData, nDataSize, pMapInfo, nPtrSize); + + case TDH_INTYPE_FLOAT: + assert(nDataSize >= sizeof(FLOAT)); + return tstring_printf(_T("%f"), *(PFLOAT)pData); + + case TDH_INTYPE_DOUBLE: + assert(nDataSize >= sizeof(DOUBLE)); + return tstring_printf(_T("%I64f"), *(DOUBLE*)pData); + + case TDH_INTYPE_BOOLEAN: + assert(nDataSize >= sizeof(ULONG)); // Yes, boolean is really 32-bit. + return *(PULONG)pData ? _T("true") : _T("false"); + + case TDH_INTYPE_BINARY: + switch (OutType) { + case TDH_OUTTYPE_IPV6: { + auto RtlIpv6AddressToString = (LPTSTR(NTAPI*)(const IN6_ADDR*, LPTSTR))GetProcAddress(GetModuleHandle(_T("ntdll.dll")), +#ifdef _UNICODE + "RtlIpv6AddressToStringW" +#else + "RtlIpv6AddressToStringA" +#endif + ); + if (RtlIpv6AddressToString) { + TCHAR szIPv6Addr[47]; + RtlIpv6AddressToString((IN6_ADDR*)pData, szIPv6Addr); + return tstring_printf(_T("%s"), szIPv6Addr); + } else + return _T(""); + } + default: { + tstring out; + for (SIZE_T i = 0; i < nDataSize; i++) + out.append(tstring_printf(i ? _T(" %02x") : _T("%02x"), pData[i])); + return out; + }} + + case TDH_INTYPE_HEXDUMP: + return DataToString(TDH_INTYPE_BINARY, TDH_OUTTYPE_NULL, pData, nDataSize, pMapInfo, nPtrSize); + + case TDH_INTYPE_GUID: { + assert(nDataSize >= sizeof(GUID)); + WCHAR szGuid[39]; + StringFromGUID2(*(GUID*)pData, szGuid, _countof(szGuid)); + return tstring_printf(_T("%ls"), szGuid); + } + + case TDH_INTYPE_POINTER: + assert(nDataSize >= nPtrSize); + switch (nPtrSize) { + case sizeof(ULONG ): return tstring_printf(_T("0x%08x" ), *(PULONG )pData); + case sizeof(ULONGLONG): return tstring_printf(_T("0x%016I64x"), *(PULONGLONG)pData); + default: // Unsupported pointer size. + assert(0); + return _T(""); + } + + case TDH_INTYPE_SIZET: + assert(nDataSize >= nPtrSize); + switch (nPtrSize) { + case sizeof(ULONG ): return tstring_printf(_T("%u" ), *(PULONG )pData); + case sizeof(ULONGLONG): return tstring_printf(_T("%I64u"), *(PULONGLONG)pData); + default: // Unsupported size_t size. + assert(0); + return _T(""); + } + + case TDH_INTYPE_FILETIME: { + assert(nDataSize >= sizeof(FILETIME)); + SYSTEMTIME st, st_local; + FileTimeToSystemTime((PFILETIME)pData, &st); + SystemTimeToTzSpecificLocalTime(NULL, &st, &st_local); + return DataToString(TDH_INTYPE_SYSTEMTIME, OutType, (LPCBYTE)&st_local, sizeof(st_local), pMapInfo, nPtrSize); + } + + case TDH_INTYPE_SYSTEMTIME: + assert(nDataSize >= sizeof(SYSTEMTIME)); + switch (OutType) { + case TDH_OUTTYPE_CULTURE_INSENSITIVE_DATETIME: return tstring_printf(_T("%04d-%02d-%02d %02d:%02d:%02d.%03u"), ((PSYSTEMTIME)pData)->wYear, ((PSYSTEMTIME)pData)->wMonth, ((PSYSTEMTIME)pData)->wDay, ((PSYSTEMTIME)pData)->wHour, ((PSYSTEMTIME)pData)->wMinute, ((PSYSTEMTIME)pData)->wSecond, ((PSYSTEMTIME)pData)->wMilliseconds); + default: { + tstring out; + return GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, (PSYSTEMTIME)pData, NULL, out) ? out : tstring(_T("