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"); var instance = new Harmony("com.bepis.bepinex.tracefix"); instance.Patch( typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic), new HarmonyMethod(typeof(TraceFix).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.NonPublic))); } private static bool DoTraceReplacement(string kind, Assembly report, string message) { var arg = string.Empty; try { arg = report.GetName().Name; } catch (MethodAccessException) { } var type = (TraceEventType) Enum.Parse(typeof(TraceEventType), kind); lock (ListenersSyncRoot) { foreach (var obj in Listeners) { var traceListener = (TraceListener) obj; traceListener.TraceEvent(new TraceEventCache(), arg, type, 0, message); if (AutoFlush) traceListener.Flush(); } } return false; } }