Created
May 31, 2022 20:11
-
-
Save jameswestgate/b4eb35df19d74747bbac8eaa50ebed2e to your computer and use it in GitHub Desktop.
EventLogRecord Harmony Patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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