TraceFix.cs 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. using System;
  2. using System.Diagnostics;
  3. using System.Linq;
  4. using System.Reflection;
  5. using HarmonyLib;
  6. namespace BepInEx.Preloader.RuntimeFixes
  7. {
  8. /// <summary>
  9. /// This exists because the Mono implementation of <see cref="Trace"/> is/was broken, and would call Write directly instead of calling TraceEvent. This class fixes that with a <see cref="BepInEx.Harmony"/> hook.
  10. /// </summary>
  11. internal static class TraceFix
  12. {
  13. private static Type TraceImplType;
  14. private static object ListenersSyncRoot;
  15. private static TraceListenerCollection Listeners;
  16. private static PropertyInfo prop_AutoFlush;
  17. private static bool AutoFlush => (bool)prop_AutoFlush.GetValue(null, null);
  18. public static void ApplyFix()
  19. {
  20. TraceImplType = AppDomain.CurrentDomain.GetAssemblies()
  21. .First(x => x.GetName().Name == "System")
  22. .GetTypes()
  23. .First(x => x.Name == "TraceImpl");
  24. ListenersSyncRoot = AccessTools.Property(TraceImplType, "ListenersSyncRoot").GetValue(null, null);
  25. Listeners = (TraceListenerCollection)AccessTools.Property(TraceImplType, "Listeners").GetValue(null, null);
  26. prop_AutoFlush = AccessTools.Property(TraceImplType, "AutoFlush");
  27. HarmonyLib.Harmony instance = new HarmonyLib.Harmony("com.bepis.bepinex.tracefix");
  28. instance.Patch(
  29. typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic),
  30. prefix: new HarmonyMethod(typeof(TraceFix).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.NonPublic)));
  31. }
  32. private static bool DoTraceReplacement(string kind, Assembly report, string message)
  33. {
  34. string arg = string.Empty;
  35. try
  36. {
  37. arg = report.GetName().Name;
  38. }
  39. catch (MethodAccessException) { }
  40. TraceEventType type = (TraceEventType)Enum.Parse(typeof(TraceEventType), kind);
  41. lock (ListenersSyncRoot)
  42. {
  43. foreach (object obj in Listeners)
  44. {
  45. TraceListener traceListener = (TraceListener)obj;
  46. traceListener.TraceEvent(new TraceEventCache(), arg, type, 0, message);
  47. if (AutoFlush)
  48. {
  49. traceListener.Flush();
  50. }
  51. }
  52. }
  53. return false;
  54. }
  55. }
  56. }