using System; using System.Diagnostics; using System.Linq; using System.Reflection; using HarmonyLib; namespace BepInEx.Preloader.RuntimeFixes { /// /// This exists because the Mono implementation of is/was broken, and would call Write directly instead of calling TraceEvent. /// internal static class TraceFix { private static Type TraceImplType; private static object ListenersSyncRoot; private static TraceListenerCollection Listeners; private static PropertyInfo prop_AutoFlush; private static bool AutoFlush => (bool)prop_AutoFlush.GetValue(null, null); public static void ApplyFix() { TraceImplType = AppDomain.CurrentDomain.GetAssemblies() .First(x => x.GetName().Name == "System") .GetTypes() .FirstOrDefault(x => x.Name == "TraceImpl"); // assembly that has already fixed this if (TraceImplType == null) return; ListenersSyncRoot = AccessTools.Property(TraceImplType, "ListenersSyncRoot").GetValue(null, null); Listeners = (TraceListenerCollection)AccessTools.Property(TraceImplType, "Listeners").GetValue(null, null); prop_AutoFlush = AccessTools.Property(TraceImplType, "AutoFlush"); HarmonyLib.Harmony instance = new HarmonyLib.Harmony("com.bepis.bepinex.tracefix"); instance.Patch( typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic), prefix: new HarmonyMethod(typeof(TraceFix).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.NonPublic))); } private static bool DoTraceReplacement(string kind, Assembly report, string message) { string arg = string.Empty; try { arg = report.GetName().Name; } catch (MethodAccessException) { } TraceEventType type = (TraceEventType)Enum.Parse(typeof(TraceEventType), kind); lock (ListenersSyncRoot) { foreach (object obj in Listeners) { TraceListener traceListener = (TraceListener)obj; traceListener.TraceEvent(new TraceEventCache(), arg, type, 0, message); if (AutoFlush) { traceListener.Flush(); } } } return false; } } }