Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jameswestgate/b4eb35df19d74747bbac8eaa50ebed2e to your computer and use it in GitHub Desktop.
Save jameswestgate/b4eb35df19d74747bbac8eaa50ebed2e to your computer and use it in GitHub Desktop.
EventLogRecord Harmony Patch
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Diagnostics.Eventing.Reader;
using HarmonyLib;
namespace Authlogics.ReportingService
{
//This patch fixes the internal .net System.InvalidOperationException: We do not have 18 variants given for the UnsafeNativeMethods.EvtRenderFlags.EvtRenderEventValues flag
//It works by replacing the PrepareSystemData method, passing the number of expected parameters (16)
//to NativeWrapper.EvtRenderBufferWithContextSystem which copies the values across from the internal data structure
//If this fails with an InvalidOperationException, then we try the initial value (18)
//Values are accessed via reflection and static caching is used as much as possible to reduce impact on performance
//Because the Lib.Harmony package is not signed, this patch also relies of the Brutal.Dev.StrongNameSigner nuget package.
//Another nuget that could be considered is https://github.com/dsplaisted/strongnamer
[HarmonyPatch(typeof(EventLogRecord))]
[HarmonyPatch("PrepareSystemData")]
internal class EventLogRecordPatch
{
static FieldInfo _systemPropertiesFieldInfo;
static FieldInfo _filledFieldInfo;
static FieldInfo _sessionFieldInfo;
static MethodInfo _setupSystemContextMethodInfo;
static FieldInfo _syncObjectFieldInfo;
static Assembly _systemCoreAssembly;
static Type _nativeWrapperType;
static MethodInfo _evtRenderBufferWithContextSystemMethodInfo;
static FieldInfo _renderContextHandleSystemFieldInfo;
static PropertyInfo _handlePropertyInfo;
const int SERVER_SYSTEM_PROPERTY_COUNT = 16;
const int SYSTEM_PROPERTY_COUNT = 18;
enum EvtRenderFlags
{
EvtRenderEventValues = 0, // Variants
EvtRenderEventXml = 1, // XML
EvtRenderBookmark = 2 // Bookmark
}
//Only setup reflection fields, properties and methods once
static EventLogRecordPatch()
{
_systemPropertiesFieldInfo = typeof(EventLogRecord).GetField("systemProperties", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
_filledFieldInfo = _systemPropertiesFieldInfo.FieldType.GetField("filled");
_sessionFieldInfo = typeof(EventLogRecord).GetField("session", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
_setupSystemContextMethodInfo = _sessionFieldInfo.FieldType.GetMethod("SetupSystemContext", BindingFlags.Instance | BindingFlags.NonPublic);
_syncObjectFieldInfo = typeof(EventLogRecord).GetField("syncObject", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
_systemCoreAssembly = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(assembly => assembly.GetName().Name == "System.Core");
_nativeWrapperType = _systemCoreAssembly.GetType("System.Diagnostics.Eventing.Reader.NativeWrapper");
_evtRenderBufferWithContextSystemMethodInfo = _nativeWrapperType.GetMethod("EvtRenderBufferWithContextSystem", BindingFlags.Static | BindingFlags.Public);
_renderContextHandleSystemFieldInfo = _sessionFieldInfo.FieldType.GetField("renderContextHandleSystem", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
_handlePropertyInfo = typeof(EventLogRecord).GetProperty("Handle", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty);
}
static bool Prefix(EventLogRecord __instance)
{
//if (_systemProperties.filled) return;
var systemProperties = _systemPropertiesFieldInfo.GetValue(__instance);
//Return false so the original method doesnt run
if ((bool)_filledFieldInfo.GetValue(systemProperties)) return false;
//_session.SetupSystemContext();
var session = _sessionFieldInfo.GetValue(__instance);
_setupSystemContextMethodInfo.Invoke(session, null);
//lock (_syncObject)
var syncObject = _syncObjectFieldInfo.GetValue(__instance);
lock (syncObject)
{
//if (_systemProperties.filled == false)
if (!(bool)_filledFieldInfo.GetValue(systemProperties))
{
//NativeWrapper.EvtRenderBufferWithContextSystem(_session.renderContextHandleSystem, Handle, UnsafeNativeMethods.EvtRenderFlags.EvtRenderEventValues, _systemProperties, SERVER_SYSTEM_PROPERTY_COUNT);
var renderContextHandleSystem = _renderContextHandleSystemFieldInfo.GetValue(session);
var handle = _handlePropertyInfo.GetValue(__instance);
//We try with both the 16 and 18 property count version
try
{
_evtRenderBufferWithContextSystemMethodInfo.Invoke(session, new object[] { renderContextHandleSystem, handle, EvtRenderFlags.EvtRenderEventValues, systemProperties, SERVER_SYSTEM_PROPERTY_COUNT });
}
catch (TargetInvocationException ex) when (ex?.InnerException is InvalidOperationException)
{
_evtRenderBufferWithContextSystemMethodInfo.Invoke(session, new object[] { renderContextHandleSystem, handle, EvtRenderFlags.EvtRenderEventValues, systemProperties, SYSTEM_PROPERTY_COUNT });
}
//_systemProperties.filled = true;
_filledFieldInfo.SetValue(systemProperties, true);
}
}
//It doesnt matter now if the original runs because we have set filled to true, but there is no need
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment