diff --git a/src/HotAvalonia/HotAvalonia.csproj b/src/HotAvalonia/HotAvalonia.csproj index 56fc55a..4635a8e 100644 --- a/src/HotAvalonia/HotAvalonia.csproj +++ b/src/HotAvalonia/HotAvalonia.csproj @@ -16,14 +16,16 @@ + + - - + + diff --git a/src/HotAvalonia/Reflection/Inject/MethodInjector.cs b/src/HotAvalonia/Reflection/Inject/MethodInjector.cs index 6730c9b..47585f0 100644 --- a/src/HotAvalonia/Reflection/Inject/MethodInjector.cs +++ b/src/HotAvalonia/Reflection/Inject/MethodInjector.cs @@ -34,7 +34,7 @@ internal static class MethodInjector /// public static IInjection Inject(MethodBase source, MethodInfo replacement) => InjectionType switch { - InjectionType.Native => new NativeInjection(source, replacement), + InjectionType.Native => NativeInjection.Create(source, replacement), _ => ThrowNotSupportedException(), }; @@ -72,60 +72,105 @@ private static InjectionType DetectSupportedInjectionType() if (IsDisabled()) return InjectionType.None; - try - { - // Enable dynamic code generation, which is required for MonoMod to function. - using IDisposable context = AssemblyHelper.ForceAllowDynamicCode(); - - // `PlatformTriple.Current` may throw exceptions such as: - // - NotImplementedException - // - PlatformNotSupportedException - // - etc. - // This happens if the current environment is not (yet) supported. - if (PlatformTriple.Current is not null) - return InjectionType.Native; - } - catch { } - - return InjectionType.None; + return NativeInjection.IsSupported ? InjectionType.Native : InjectionType.None; } } /// /// Provides functionality to inject a replacement method using native code hooks. /// -file sealed class NativeInjection : IInjection +file static class NativeInjection { /// - /// The hook used for the method injection. + /// Injects a replacement method implementation for the specified source method. /// - private readonly Hook _hook; + /// The method to be replaced. + /// The replacement method implementation. + /// An instance representing the method injection. + public static IInjection Create(MethodBase source, MethodInfo replacement) + => new MonoModInjection(source, replacement); /// - /// Initializes a new instance of the class. + /// Indicates whether native method injections are supported in the current runtime environment. /// - /// The method to be replaced. - /// The replacement method implementation. - public NativeInjection(MethodBase source, MethodInfo replacement) + public static bool IsSupported { - // Enable dynamic code generation, which is required for MonoMod to function. - // Note that we cannot enable it forcefully just once and call it a day, - // because this only affects the current thread. - _ = AssemblyHelper.ForceAllowDynamicCode(); - - _hook = new(source, replacement, applyByDefault: true); + get + { + try + { + // If `MonoMod.RuntimeDetour` is not present, + // this will result in `TypeLoadException`, + // that's why we need this wrapper. + return MonoModInjection.IsSupported; + } + catch + { + return false; + } + } } /// - /// Applies the method injection. + /// Represents a MonoMod-based injection. /// - public void Apply() => _hook.Apply(); + private sealed class MonoModInjection : IInjection + { + /// + /// The hook used for the method injection. + /// + private readonly Hook _hook; + + /// + /// Initializes a new instance of the class. + /// + /// The method to be replaced. + /// The replacement method implementation. + public MonoModInjection(MethodBase source, MethodInfo replacement) + { + // Enable dynamic code generation, which is required for MonoMod to function. + // Note that we cannot enable it forcefully just once and call it a day, + // because this only affects the current thread. + _ = AssemblyHelper.ForceAllowDynamicCode(); - /// - /// Reverts all the effects caused by the method injection. - /// - public void Undo() => _hook.Undo(); + _hook = new(source, replacement, applyByDefault: true); + } - /// - public void Dispose() => _hook.Dispose(); + /// + /// Indicates whether MonoMod is supported in the current environment. + /// + /// + /// + /// + /// + /// + public static bool IsSupported + { + get + { + // Enable dynamic code generation, which is required for MonoMod to function. + using IDisposable context = AssemblyHelper.ForceAllowDynamicCode(); + + // `PlatformTriple.Current` may throw exceptions such as: + // - NotImplementedException + // - PlatformNotSupportedException + // - etc. + // This happens if the current environment is not (yet) supported. + return PlatformTriple.Current is not null; + } + } + + /// + /// Applies the method injection. + /// + public void Apply() => _hook.Apply(); + + /// + /// Reverts all the effects caused by the method injection. + /// + public void Undo() => _hook.Undo(); + + /// + public void Dispose() => _hook.Dispose(); + } }