From b90432c5805d2867c9ed75e1535d346904445fa1 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 11 Jun 2026 20:34:44 +0200 Subject: [PATCH 1/9] refactor: extract IProgressReporter from ILogger (#64) Separate progress reporting (Progress, EndProgress, Finish) from logging into a new IProgressReporter interface. This is the first step toward replacing the custom ILogger with Microsoft.Extensions.Logging + Serilog. - Add IProgressReporter interface and NullProgressReporter - Remove Progress, EndProgress, Finish from ILogger and all implementations - Remove dead BeginModule/EndModule from NullLogger - Add ProgressReporter property to ConfuserParameters and ConfuserContext - Replace PackerLogger (full ILogger decorator) with PackerProgressReporter - Update all WithProgress call sites to use context.ProgressReporter - Fix MSBuildLogger.Finish bug (was setting HasError=false on failure) - Seal NullLogger class --- Confuser.CLI/Program.cs | 25 +++++---- Confuser.Core/ConfuserContext.cs | 6 +++ Confuser.Core/ConfuserEngine.cs | 3 +- Confuser.Core/ConfuserParameters.cs | 14 +++++ Confuser.Core/ILogger.cs | 30 ----------- Confuser.Core/IProgressReporter.cs | 24 +++++++++ Confuser.Core/NullLogger.cs | 21 +------- Confuser.Core/NullProgressReporter.cs | 14 +++++ Confuser.Core/Packer.cs | 53 +++---------------- Confuser.Core/Utils.cs | 20 +++---- Confuser.MSBuild.Tasks/ConfuseTask.cs | 7 +-- Confuser.MSBuild.Tasks/MSBuildLogger.cs | 30 +++++------ Confuser.Protections/AntiTamper/JITMode.cs | 2 +- Confuser.Protections/Compress/Compressor.cs | 8 +-- Confuser.Protections/Constants/EncodePhase.cs | 6 +-- .../ControlFlow/ControlFlowPhase.cs | 2 +- .../ReferenceProxy/ReferenceProxyPhase.cs | 2 +- Confuser.Protections/Resources/MDPhase.cs | 4 +- .../TypeScrambler/AnalyzePhase.cs | 2 +- .../TypeScrambler/ScramblePhase.cs | 2 +- Confuser.Renamer/AnalyzePhase.cs | 4 +- Confuser.Renamer/RenamePhase.cs | 2 +- ConfuserEx/ViewModel/UI/ProtectTabVM.cs | 9 ++-- Tests/Confuser.UnitTest/TestBase.cs | 4 +- Tests/Confuser.UnitTest/XUnitLogger.cs | 8 +-- 25 files changed, 141 insertions(+), 161 deletions(-) create mode 100644 Confuser.Core/IProgressReporter.cs create mode 100644 Confuser.Core/NullProgressReporter.cs diff --git a/Confuser.CLI/Program.cs b/Confuser.CLI/Program.cs index aff59c47c..c56a57630 100644 --- a/Confuser.CLI/Program.cs +++ b/Confuser.CLI/Program.cs @@ -193,13 +193,14 @@ static void LoadTemplateProject(string templatePath, ConfuserProject proj, List< } static int RunProject(ConfuserParameters parameters) { - var logger = new ConsoleLogger(); - parameters.Logger = logger; + var progressReporter = new ConsoleProgressReporter(); + parameters.Logger = new ConsoleLogger(); + parameters.ProgressReporter = progressReporter; Console.Title = "ConfuserEx - Running..."; ConfuserEngine.Run(parameters).GetAwaiter().GetResult(); - return logger.ReturnValue; + return progressReporter.ReturnValue; } static bool NeedPause() { @@ -235,14 +236,6 @@ static void WriteLine() { } class ConsoleLogger : ILogger { - readonly DateTime begin; - - public ConsoleLogger() { - begin = DateTime.Now; - } - - public int ReturnValue { get; private set; } - public void Debug(string msg) { WriteLineWithColor(ConsoleColor.Gray, "[DEBUG] " + msg); } @@ -284,6 +277,16 @@ public void ErrorException(string msg, Exception ex) { WriteLineWithColor(ConsoleColor.Red, "[ERROR] " + msg); WriteLineWithColor(ConsoleColor.Red, "Exception: " + ex); } + } + + class ConsoleProgressReporter : IProgressReporter { + readonly DateTime begin; + + public ConsoleProgressReporter() { + begin = DateTime.Now; + } + + public int ReturnValue { get; private set; } public void Progress(int progress, int overall) { } diff --git a/Confuser.Core/ConfuserContext.cs b/Confuser.Core/ConfuserContext.cs index a8c7a1c76..d65bd2db8 100644 --- a/Confuser.Core/ConfuserContext.cs +++ b/Confuser.Core/ConfuserContext.cs @@ -20,6 +20,12 @@ public class ConfuserContext { /// The logger. public ILogger Logger { get; internal set; } + /// + /// Gets the progress reporter used for reporting protection progress. + /// + /// The progress reporter. + public IProgressReporter ProgressReporter { get; internal set; } + /// /// Gets the project being processed. /// diff --git a/Confuser.Core/ConfuserEngine.cs b/Confuser.Core/ConfuserEngine.cs index a4e737cce..f92c2967e 100644 --- a/Confuser.Core/ConfuserEngine.cs +++ b/Confuser.Core/ConfuserEngine.cs @@ -80,6 +80,7 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) // 1. Setup context var context = new ConfuserContext(); context.Logger = parameters.GetLogger(); + context.ProgressReporter = parameters.GetProgressReporter(); context.Project = parameters.Project.Clone(); context.PackerInitiated = parameters.PackerInitiated; context.token = token; @@ -218,7 +219,7 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) finally { if (context.Resolver != null) context.InternalResolver.Clear(); - context.Logger.Finish(ok); + context.ProgressReporter.Finish(ok); } } diff --git a/Confuser.Core/ConfuserParameters.cs b/Confuser.Core/ConfuserParameters.cs index f3e05a2c1..25162ae0e 100644 --- a/Confuser.Core/ConfuserParameters.cs +++ b/Confuser.Core/ConfuserParameters.cs @@ -18,6 +18,12 @@ public class ConfuserParameters { /// The logger, or null if logging is not needed. public ILogger Logger { get; set; } + /// + /// Gets or sets the progress reporter used to report protection progress. + /// + /// The progress reporter, or null if progress reporting is not needed. + public IProgressReporter ProgressReporter { get; set; } + internal bool PackerInitiated { get; set; } /// @@ -40,6 +46,14 @@ internal ILogger GetLogger() { return Logger ?? NullLogger.Instance; } + /// + /// Gets the actual non-null progress reporter. + /// + /// The progress reporter. + internal IProgressReporter GetProgressReporter() { + return ProgressReporter ?? NullProgressReporter.Instance; + } + /// /// Gets the actual non-null plugin discovery service. /// diff --git a/Confuser.Core/ILogger.cs b/Confuser.Core/ILogger.cs index ec55db448..d2aa9fdda 100644 --- a/Confuser.Core/ILogger.cs +++ b/Confuser.Core/ILogger.cs @@ -70,35 +70,5 @@ public interface ILogger { /// The message. /// The exception. void ErrorException(string msg, Exception ex); - - /// - /// Logs the progress of protection. - /// - /// - /// This method is intended to be used with . - /// - /// - /// - /// for (int i = 0; i < defs.Length; i++) { - /// logger.Progress(i + 1, defs.Length); - /// } - /// logger.EndProgress(); - /// - /// - /// The total work amount . - /// The amount of work done. - void Progress(int progress, int overall); - - /// - /// End the progress of protection. - /// - /// - void EndProgress(); - - /// - /// Logs the finish of protection. - /// - /// Indicated whether the protection process is successful. - void Finish(bool successful); } } diff --git a/Confuser.Core/IProgressReporter.cs b/Confuser.Core/IProgressReporter.cs new file mode 100644 index 000000000..eb1812e67 --- /dev/null +++ b/Confuser.Core/IProgressReporter.cs @@ -0,0 +1,24 @@ +namespace Confuser.Core { + /// + /// Reports progress and completion of a protection process. + /// + public interface IProgressReporter { + /// + /// Reports the progress of protection. + /// + /// The amount of work done. + /// The total work amount. + void Progress(int progress, int overall); + + /// + /// Signals the end of a progress sequence. + /// + void EndProgress(); + + /// + /// Signals the finish of the protection process. + /// + /// Whether the protection process succeeded. + void Finish(bool successful); + } +} diff --git a/Confuser.Core/NullLogger.cs b/Confuser.Core/NullLogger.cs index c40382274..c518d69fd 100644 --- a/Confuser.Core/NullLogger.cs +++ b/Confuser.Core/NullLogger.cs @@ -1,19 +1,15 @@ using System; -using dnlib.DotNet; namespace Confuser.Core { /// /// An implementation that doesn't actually do any logging. /// - internal class NullLogger : ILogger { + internal sealed class NullLogger : ILogger { /// /// The singleton instance of . /// public static readonly NullLogger Instance = new NullLogger(); - /// - /// Prevents a default instance of the class from being created. - /// NullLogger() { } /// @@ -45,20 +41,5 @@ public void ErrorFormat(string format, params object[] args) { } /// public void ErrorException(string msg, Exception ex) { } - - /// - public void Progress(int overall, int progress) { } - - /// - public void EndProgress() { } - - /// - public void Finish(bool successful) { } - - /// - public void BeginModule(ModuleDef module) { } - - /// - public void EndModule(ModuleDef module) { } } } diff --git a/Confuser.Core/NullProgressReporter.cs b/Confuser.Core/NullProgressReporter.cs new file mode 100644 index 000000000..53901f472 --- /dev/null +++ b/Confuser.Core/NullProgressReporter.cs @@ -0,0 +1,14 @@ +namespace Confuser.Core { + /// + /// An implementation that discards all progress reports. + /// + internal sealed class NullProgressReporter : IProgressReporter { + public static readonly NullProgressReporter Instance = new NullProgressReporter(); + + NullProgressReporter() { } + + public void Progress(int progress, int overall) { } + public void EndProgress() { } + public void Finish(bool successful) { } + } +} diff --git a/Confuser.Core/Packer.cs b/Confuser.Core/Packer.cs index e5e83b66f..b297e612c 100644 --- a/Confuser.Core/Packer.cs +++ b/Confuser.Core/Packer.cs @@ -73,7 +73,8 @@ protected void ProtectStub(ConfuserContext context, string fileName, byte[] modu ConfuserEngine .Run( new ConfuserParameters { - Logger = new PackerLogger(context.Logger), + Logger = context.Logger, + ProgressReporter = new PackerProgressReporter(context.ProgressReporter, context.Logger), PluginDiscovery = discovery, Marker = new PackerMarker(snKey, snPubKey, snDelaySig, snSigKey, snPubSigKey), Project = proj, @@ -101,59 +102,21 @@ protected void ProtectStub(ConfuserContext context, string fileName, byte[] modu } } - internal class PackerLogger : ILogger { + internal class PackerProgressReporter : IProgressReporter { + readonly IProgressReporter baseReporter; readonly ILogger baseLogger; - public PackerLogger(ILogger baseLogger) { + public PackerProgressReporter(IProgressReporter baseReporter, ILogger baseLogger) { + this.baseReporter = baseReporter; this.baseLogger = baseLogger; } - public void Debug(string msg) { - baseLogger.Debug(msg); - } - - public void DebugFormat(string format, params object[] args) { - baseLogger.DebugFormat(format, args); - } - - public void Info(string msg) { - baseLogger.Info(msg); - } - - public void InfoFormat(string format, params object[] args) { - baseLogger.InfoFormat(format, args); - } - - public void Warn(string msg) { - baseLogger.Warn(msg); - } - - public void WarnFormat(string format, params object[] args) { - baseLogger.WarnFormat(format, args); - } - - public void WarnException(string msg, Exception ex) { - baseLogger.WarnException(msg, ex); - } - - public void Error(string msg) { - baseLogger.Error(msg); - } - - public void ErrorFormat(string format, params object[] args) { - baseLogger.ErrorFormat(format, args); - } - - public void ErrorException(string msg, Exception ex) { - baseLogger.ErrorException(msg, ex); - } - public void Progress(int progress, int overall) { - baseLogger.Progress(progress, overall); + baseReporter.Progress(progress, overall); } public void EndProgress() { - baseLogger.EndProgress(); + baseReporter.EndProgress(); } public void Finish(bool successful) { diff --git a/Confuser.Core/Utils.cs b/Confuser.Core/Utils.cs index e941cc1e6..9df5c0d01 100644 --- a/Confuser.Core/Utils.cs +++ b/Confuser.Core/Utils.cs @@ -204,33 +204,33 @@ public static void RemoveWhere(this IList self, Predicate match) { } /// - /// Returns a that log the progress of iterating the specified list. + /// Returns a that reports the progress of iterating the specified list. /// /// The type of list element /// The list. - /// The logger. + /// The progress reporter. /// A wrapper of the list. - public static IEnumerable WithProgress(this IEnumerable enumerable, ILogger logger) { + public static IEnumerable WithProgress(this IEnumerable enumerable, IProgressReporter reporter) { switch (enumerable) { case IReadOnlyCollection readOnlyCollection: - return WithProgress(enumerable, readOnlyCollection.Count, logger); + return WithProgress(enumerable, readOnlyCollection.Count, reporter); case ICollection collection: - return WithProgress(enumerable, collection.Count, logger); + return WithProgress(enumerable, collection.Count, reporter); default: var buffered = enumerable.ToList(); - return WithProgress(buffered, buffered.Count, logger); + return WithProgress(buffered, buffered.Count, reporter); } } - public static IEnumerable WithProgress(this IEnumerable enumerable, int totalCount, ILogger logger) { + public static IEnumerable WithProgress(this IEnumerable enumerable, int totalCount, IProgressReporter reporter) { var counter = 0; foreach (var obj in enumerable) { - logger.Progress(counter, totalCount); + reporter.Progress(counter, totalCount); yield return obj; counter++; } - logger.Progress(totalCount, totalCount); - logger.EndProgress(); + reporter.Progress(totalCount, totalCount); + reporter.EndProgress(); } } } diff --git a/Confuser.MSBuild.Tasks/ConfuseTask.cs b/Confuser.MSBuild.Tasks/ConfuseTask.cs index f4526c910..8d4fdff90 100644 --- a/Confuser.MSBuild.Tasks/ConfuseTask.cs +++ b/Confuser.MSBuild.Tasks/ConfuseTask.cs @@ -24,17 +24,18 @@ public override bool Execute() { project.Load(xmlDoc); project.OutputDirectory = Path.GetDirectoryName(Path.GetFullPath(OutputAssembly.ItemSpec)); - var logger = new MSBuildLogger(Log); + var progressReporter = new MSBuildProgressReporter(); var parameters = new ConfuserParameters { Project = project, - Logger = logger + Logger = new MSBuildLogger(Log), + ProgressReporter = progressReporter }; ConfuserEngine.Run(parameters).Wait(); ConfusedFiles = project.Select(m => new TaskItem(Path.Combine(project.OutputDirectory, m.Path))).Cast().ToArray(); - return !logger.HasError; + return !progressReporter.HasError; } } } diff --git a/Confuser.MSBuild.Tasks/MSBuildLogger.cs b/Confuser.MSBuild.Tasks/MSBuildLogger.cs index fdcb2319b..bc92e200c 100644 --- a/Confuser.MSBuild.Tasks/MSBuildLogger.cs +++ b/Confuser.MSBuild.Tasks/MSBuildLogger.cs @@ -1,4 +1,5 @@ using System; +using Confuser.Core; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using ILogger = Confuser.Core.ILogger; @@ -7,8 +8,6 @@ namespace Confuser.MSBuild.Tasks { internal sealed class MSBuildLogger : ILogger { private readonly TaskLoggingHelper loggingHelper; - internal bool HasError { get; private set; } - internal MSBuildLogger(TaskLoggingHelper loggingHelper) => this.loggingHelper = loggingHelper ?? throw new ArgumentNullException(nameof(loggingHelper)); @@ -18,28 +17,17 @@ void ILogger.DebugFormat(string format, params object[] args) { loggingHelper.LogMessage(MessageImportance.Low, "[DEBUG] " + format, args); } - void ILogger.EndProgress() { } - void ILogger.Error(string msg) { loggingHelper.LogError(msg); - HasError = true; } void ILogger.ErrorException(string msg, Exception ex) { loggingHelper.LogError(msg); loggingHelper.LogErrorFromException(ex); - HasError = true; } void ILogger.ErrorFormat(string format, params object[] args) { loggingHelper.LogError(format, args); - HasError = true; - } - - void ILogger.Finish(bool successful) { - if (!successful) { - HasError = false; - } } void ILogger.Info(string msg) => loggingHelper.LogMessage(MessageImportance.Normal, msg); @@ -47,8 +35,6 @@ void ILogger.Finish(bool successful) { void ILogger.InfoFormat(string format, params object[] args) => loggingHelper.LogMessage(MessageImportance.Normal, format, args); - void ILogger.Progress(int progress, int overall) { } - void ILogger.Warn(string msg) => loggingHelper.LogWarning(msg); void ILogger.WarnException(string msg, Exception ex) { @@ -58,4 +44,18 @@ void ILogger.WarnException(string msg, Exception ex) { void ILogger.WarnFormat(string format, params object[] args) => loggingHelper.LogWarning(format, args); } + + internal sealed class MSBuildProgressReporter : IProgressReporter { + internal bool HasError { get; private set; } + + void IProgressReporter.Progress(int progress, int overall) { } + + void IProgressReporter.EndProgress() { } + + void IProgressReporter.Finish(bool successful) { + if (!successful) { + HasError = true; + } + } + } } diff --git a/Confuser.Protections/AntiTamper/JITMode.cs b/Confuser.Protections/AntiTamper/JITMode.cs index 23f41e90f..7c3a007d6 100644 --- a/Confuser.Protections/AntiTamper/JITMode.cs +++ b/Confuser.Protections/AntiTamper/JITMode.cs @@ -210,7 +210,7 @@ void CreateSection(ModuleWriterBase writer) { newSection.Add(bodyIndex, 0x10); // save methods - foreach (MethodDef method in methods.WithProgress(context.Logger)) { + foreach (MethodDef method in methods.WithProgress(context.ProgressReporter)) { if (!method.HasBody) continue; diff --git a/Confuser.Protections/Compress/Compressor.cs b/Confuser.Protections/Compress/Compressor.cs index 28e8ec5cd..83ade748b 100644 --- a/Confuser.Protections/Compress/Compressor.cs +++ b/Confuser.Protections/Compress/Compressor.cs @@ -164,7 +164,7 @@ void PackModules(ConfuserContext context, CompressorContext compCtx, ModuleDef s state = state * 0x5e3f1f + chr; byte[] encrypted = compCtx.Encrypt(comp, entry.Value, state, progress => { progress = (progress + moduleIndex) / modules.Count; - context.Logger.Progress((int)(progress * 10000), 10000); + context.ProgressReporter.Progress((int)(progress * 10000), 10000); }); context.CheckCancellation(); @@ -172,7 +172,7 @@ void PackModules(ConfuserContext context, CompressorContext compCtx, ModuleDef s stubModule.Resources.Add(resource); moduleIndex++; } - context.Logger.EndProgress(); + context.ProgressReporter.EndProgress(); } void InjectData(ConfuserContext context, ModuleDef stubModule, MethodDef method, byte[] data) { @@ -245,8 +245,8 @@ void InjectStub(ConfuserContext context, CompressorContext compCtx, ProtectionPa compCtx.OriginModule = context.OutputModules[compCtx.ModuleIndex]; byte[] encryptedModule = compCtx.Encrypt(comp, compCtx.OriginModule, seed, - progress => context.Logger.Progress((int)(progress * 10000), 10000)); - context.Logger.EndProgress(); + progress => context.ProgressReporter.Progress((int)(progress * 10000), 10000)); + context.ProgressReporter.EndProgress(); context.CheckCancellation(); compCtx.EncryptedModule = encryptedModule; diff --git a/Confuser.Protections/Constants/EncodePhase.cs b/Confuser.Protections/Constants/EncodePhase.cs index c108d14d4..b3db5bd68 100644 --- a/Confuser.Protections/Constants/EncodePhase.cs +++ b/Confuser.Protections/Constants/EncodePhase.cs @@ -38,12 +38,12 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa // Encode constants moduleCtx.ReferenceRepl = new Dictionary>>(); moduleCtx.EncodedBuffer = new List(); - foreach (var entry in ldInit.WithProgress(context.Logger)) // Ensure the array length haven't been encoded yet + foreach (var entry in ldInit.WithProgress(context.ProgressReporter)) // Ensure the array length haven't been encoded yet { EncodeInitializer(moduleCtx, entry.Key, entry.Value); context.CheckCancellation(); } - foreach (var entry in ldc.WithProgress(context.Logger)) { + foreach (var entry in ldc.WithProgress(context.ProgressReporter)) { if (entry.Key is string) { EncodeString(moduleCtx, (string)entry.Key, entry.Value); } @@ -234,7 +234,7 @@ void ExtractConstants( Dictionary>> ldInit) { var dataFields = new HashSet(); var fieldRefs = new HashSet(); - foreach (MethodDef method in parameters.Targets.OfType().WithProgress(context.Logger)) { + foreach (MethodDef method in parameters.Targets.OfType().WithProgress(context.ProgressReporter)) { if (!method.HasBody) continue; diff --git a/Confuser.Protections/ControlFlow/ControlFlowPhase.cs b/Confuser.Protections/ControlFlow/ControlFlowPhase.cs index 3f1b15e28..b154ea488 100644 --- a/Confuser.Protections/ControlFlow/ControlFlowPhase.cs +++ b/Confuser.Protections/ControlFlow/ControlFlowPhase.cs @@ -74,7 +74,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa bool disabledOpti = DisabledOptimization(context.CurrentModule); RandomGenerator random = context.Registry.GetService().GetRandomGenerator(ControlFlowProtection._FullId); - foreach (MethodDef method in parameters.Targets.OfType().WithProgress(context.Logger)) + foreach (MethodDef method in parameters.Targets.OfType().WithProgress(context.ProgressReporter)) if (method.HasBody && method.Body.Instructions.Count > 0) { ProcessMethod(method.Body, ParseParameters(method, context, parameters, random, disabledOpti)); context.CheckCancellation(); diff --git a/Confuser.Protections/ReferenceProxy/ReferenceProxyPhase.cs b/Confuser.Protections/ReferenceProxy/ReferenceProxyPhase.cs index 3e299f2fa..e08baf3d4 100644 --- a/Confuser.Protections/ReferenceProxy/ReferenceProxyPhase.cs +++ b/Confuser.Protections/ReferenceProxy/ReferenceProxyPhase.cs @@ -103,7 +103,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa var store = new RPStore { random = random }; - foreach (MethodDef method in parameters.Targets.OfType().WithProgress(context.Logger)) + foreach (MethodDef method in parameters.Targets.OfType().WithProgress(context.ProgressReporter)) if (method.HasBody && method.Body.Instructions.Count > 0) { ProcessMethod(ParseParameters(method, context, parameters, store)); context.CheckCancellation(); diff --git a/Confuser.Protections/Resources/MDPhase.cs b/Confuser.Protections/Resources/MDPhase.cs index 0f676cd26..056c75b41 100644 --- a/Confuser.Protections/Resources/MDPhase.cs +++ b/Confuser.Protections/Resources/MDPhase.cs @@ -68,8 +68,8 @@ void OnWriterEvent(object sender, ModuleWriterEventArgs e) { // compress moduleBuff = ctx.Context.Registry.GetService().Compress( moduleBuff, - progress => ctx.Context.Logger.Progress((int)(progress * 10000), 10000)); - ctx.Context.Logger.EndProgress(); + progress => ctx.Context.ProgressReporter.Progress((int)(progress * 10000), 10000)); + ctx.Context.ProgressReporter.EndProgress(); ctx.Context.CheckCancellation(); uint compressedLen = (uint)(moduleBuff.Length + 3) / 4; diff --git a/Confuser.Protections/TypeScrambler/AnalyzePhase.cs b/Confuser.Protections/TypeScrambler/AnalyzePhase.cs index fbec05899..5c8304208 100644 --- a/Confuser.Protections/TypeScrambler/AnalyzePhase.cs +++ b/Confuser.Protections/TypeScrambler/AnalyzePhase.cs @@ -19,7 +19,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa var typeService = context.Registry.GetService(); Debug.Assert(typeService != null, $"{nameof(typeService)} != null"); - foreach (var target in parameters.Targets.WithProgress(context.Logger)) { + foreach (var target in parameters.Targets.WithProgress(context.ProgressReporter)) { switch (target) { case TypeDef typeDef: typeService.AddScannedItem(new ScannedType(typeDef)); diff --git a/Confuser.Protections/TypeScrambler/ScramblePhase.cs b/Confuser.Protections/TypeScrambler/ScramblePhase.cs index 6fc86bfd0..1af58491f 100644 --- a/Confuser.Protections/TypeScrambler/ScramblePhase.cs +++ b/Confuser.Protections/TypeScrambler/ScramblePhase.cs @@ -27,7 +27,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa // In this stage the references to the scrambled types need to be fixed. This needs to be done for all // methods in the assembly, because all methods may contain references to the scrambled types and methods. - foreach (var def in context.CurrentModule.FindDefinitions().WithProgress(context.Logger)) { + foreach (var def in context.CurrentModule.FindDefinitions().WithProgress(context.ProgressReporter)) { switch (def) { case MethodDef md: if (md.HasReturnType) diff --git a/Confuser.Renamer/AnalyzePhase.cs b/Confuser.Renamer/AnalyzePhase.cs index 4d95e27d1..334ff93fd 100644 --- a/Confuser.Renamer/AnalyzePhase.cs +++ b/Confuser.Renamer/AnalyzePhase.cs @@ -39,7 +39,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa foreach (ModuleDef moduleDef in parameters.Targets.OfType()) moduleDef.EnableTypeDefFindCache = true; - foreach (IDnlibDef def in parameters.Targets.WithProgress(context.Logger)) { + foreach (IDnlibDef def in parameters.Targets.WithProgress(context.ProgressReporter)) { ParseParameters(def, context, service, parameters); if (def is ModuleDef module) { @@ -59,7 +59,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa context.Logger.Debug("Analyzing..."); RegisterRenamers(context, service); IList renamers = service.Renamers; - foreach (IDnlibDef def in parameters.Targets.WithProgress(context.Logger)) { + foreach (IDnlibDef def in parameters.Targets.WithProgress(context.ProgressReporter)) { Analyze(service, context, parameters, def, true); context.CheckCancellation(); } diff --git a/Confuser.Renamer/RenamePhase.cs b/Confuser.Renamer/RenamePhase.cs index a73630deb..39a58eaeb 100644 --- a/Confuser.Renamer/RenamePhase.cs +++ b/Confuser.Renamer/RenamePhase.cs @@ -32,7 +32,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa var targets = parameters.Targets.ToList(); service.GetRandom().Shuffle(targets); var pdbDocs = new HashSet(); - foreach (var def in GetTargetsWithDelay(targets, context, service).WithProgress(targets.Count, context.Logger)) { + foreach (var def in GetTargetsWithDelay(targets, context, service).WithProgress(targets.Count, context.ProgressReporter)) { if (def is ModuleDef moduleDef && parameters.GetParameter(context, moduleDef, "rickroll", false)) RickRoller.CommenceRickroll(context, moduleDef); diff --git a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs index a9f7088cc..0bcb75f45 100644 --- a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs +++ b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs @@ -10,7 +10,7 @@ using Confuser.Core.Project; namespace ConfuserEx.ViewModel { - internal class ProtectTabVM : TabViewModel, ILogger { + internal class ProtectTabVM : TabViewModel, ILogger, IProgressReporter { readonly Paragraph documentContent; CancellationTokenSource cancelSrc; double? progress = 0; @@ -49,6 +49,7 @@ void DoProtect() { if (File.Exists(App.FileName)) Environment.CurrentDirectory = Path.GetDirectoryName(App.FileName); parameters.Logger = this; + parameters.ProgressReporter = this; documentContent.Inlines.Clear(); cancelSrc = new CancellationTokenSource(); @@ -123,15 +124,15 @@ void ILogger.ErrorException(string msg, Exception ex) { AppendLine("Exception: {0}", Brushes.Red, ex); } - void ILogger.Progress(int progress, int overall) { + void IProgressReporter.Progress(int progress, int overall) { Progress = (double)progress / overall; } - void ILogger.EndProgress() { + void IProgressReporter.EndProgress() { Progress = null; } - void ILogger.Finish(bool successful) { + void IProgressReporter.Finish(bool successful) { DateTime now = DateTime.Now; string timeString = string.Format( "at {0}, {1}:{2:d2} elapsed.", diff --git a/Tests/Confuser.UnitTest/TestBase.cs b/Tests/Confuser.UnitTest/TestBase.cs index c23372ebc..006b8ae64 100644 --- a/Tests/Confuser.UnitTest/TestBase.cs +++ b/Tests/Confuser.UnitTest/TestBase.cs @@ -80,9 +80,11 @@ protected async Task Run(string[] inputFileNames, string[] expectedOutput, IEnum if (rule.Count > 0) proj.Rules.Add(rule); + var xunitLogger = new XunitLogger(outputHelper, outputAction); var parameters = new ConfuserParameters { Project = proj, - Logger = new XunitLogger(outputHelper, outputAction) + Logger = xunitLogger, + ProgressReporter = xunitLogger }; await ConfuserEngine.Run(parameters); diff --git a/Tests/Confuser.UnitTest/XUnitLogger.cs b/Tests/Confuser.UnitTest/XUnitLogger.cs index aa627b036..35bd31164 100644 --- a/Tests/Confuser.UnitTest/XUnitLogger.cs +++ b/Tests/Confuser.UnitTest/XUnitLogger.cs @@ -3,7 +3,7 @@ using Xunit.Abstractions; namespace Confuser.UnitTest { - public sealed class XunitLogger : ILogger { + public sealed class XunitLogger : ILogger, IProgressReporter { private readonly ITestOutputHelper _outputHelper; private readonly Action _outputAction; @@ -20,7 +20,7 @@ void ILogger.Debug(string msg) => void ILogger.DebugFormat(string format, params object[] args) => ProcessOutput("[DEBUG] " + format, args); - void ILogger.EndProgress() { } + void IProgressReporter.EndProgress() { } void ILogger.Error(string msg) => throw new Exception(msg); @@ -31,7 +31,7 @@ void ILogger.ErrorException(string msg, Exception ex) => void ILogger.ErrorFormat(string format, params object[] args) => throw new Exception(string.Format(format, args)); - void ILogger.Finish(bool successful) => + void IProgressReporter.Finish(bool successful) => ProcessOutput("[DONE]"); void ILogger.Info(string msg) => @@ -40,7 +40,7 @@ void ILogger.Info(string msg) => void ILogger.InfoFormat(string format, params object[] args) => ProcessOutput("[INFO] " + format, args); - void ILogger.Progress(int progress, int overall) { } + void IProgressReporter.Progress(int progress, int overall) { } void ILogger.Warn(string msg) => ProcessOutput("[WARN] " + msg); From fc8b8cbd4b592dec1cd167eef0be120e630d6f8d Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 11 Jun 2026 21:03:45 +0200 Subject: [PATCH 2/9] feature: add M.E.L abstraction and MelLoggerAdapter (#64) Add Microsoft.Extensions.Logging.Abstractions to Confuser.Core (netstandard2.0 compatible) and a MelLoggerAdapter that bridges M.E.L ILogger to the internal Confuser.Core.ILogger interface. This allows callers to pass a standard M.E.L logger (backed by Serilog or any other provider) into ConfuserEngine without changing any internal code yet. --- Confuser.Core/Confuser.Core.csproj | 1 + Confuser.Core/MelLoggerAdapter.cs | 44 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 Confuser.Core/MelLoggerAdapter.cs diff --git a/Confuser.Core/Confuser.Core.csproj b/Confuser.Core/Confuser.Core.csproj index 0e13ac77c..686e8f95b 100644 --- a/Confuser.Core/Confuser.Core.csproj +++ b/Confuser.Core/Confuser.Core.csproj @@ -17,6 +17,7 @@ + diff --git a/Confuser.Core/MelLoggerAdapter.cs b/Confuser.Core/MelLoggerAdapter.cs new file mode 100644 index 000000000..a60c6e878 --- /dev/null +++ b/Confuser.Core/MelLoggerAdapter.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.Extensions.Logging; +using MelILogger = Microsoft.Extensions.Logging.ILogger; +using MelLogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace Confuser.Core { + /// + /// Adapts a (Microsoft.Extensions.Logging) to the + /// interface used internally by Confuser. + /// + public sealed class MelLoggerAdapter : ILogger { + readonly MelILogger inner; + + public MelLoggerAdapter(MelILogger logger) { + inner = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public void Debug(string msg) => inner.Log(MelLogLevel.Debug, msg); + + public void DebugFormat(string format, params object[] args) => + inner.Log(MelLogLevel.Debug, format, args); + + public void Info(string msg) => inner.Log(MelLogLevel.Information, msg); + + public void InfoFormat(string format, params object[] args) => + inner.Log(MelLogLevel.Information, format, args); + + public void Warn(string msg) => inner.Log(MelLogLevel.Warning, msg); + + public void WarnFormat(string format, params object[] args) => + inner.Log(MelLogLevel.Warning, format, args); + + public void WarnException(string msg, Exception ex) => + inner.Log(MelLogLevel.Warning, ex, msg); + + public void Error(string msg) => inner.Log(MelLogLevel.Error, msg); + + public void ErrorFormat(string format, params object[] args) => + inner.Log(MelLogLevel.Error, format, args); + + public void ErrorException(string msg, Exception ex) => + inner.Log(MelLogLevel.Error, ex, msg); + } +} From 5ae36cefad651e7f7a94c93d2c1a20604495ed00 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 11 Jun 2026 21:06:47 +0200 Subject: [PATCH 3/9] feature: replace CLI ConsoleLogger with Serilog (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up Serilog as the logging provider in the CLI via Microsoft.Extensions.Logging and MelLoggerAdapter. - Add Serilog, Serilog.Extensions.Logging, Serilog.Sinks.Console - Delete custom ConsoleLogger — Serilog handles all console output - Add --verbose (-v, -vv, -vvv) and --quiet (-q) CLI flags - Default: Information level; -q: Warning; -v: Debug; -vv+: Verbose --- Confuser.CLI/Confuser.CLI.csproj | 3 ++ Confuser.CLI/Program.cs | 84 ++++++++++++++------------------ 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/Confuser.CLI/Confuser.CLI.csproj b/Confuser.CLI/Confuser.CLI.csproj index d087b4871..df0632989 100644 --- a/Confuser.CLI/Confuser.CLI.csproj +++ b/Confuser.CLI/Confuser.CLI.csproj @@ -16,6 +16,9 @@ + + + diff --git a/Confuser.CLI/Program.cs b/Confuser.CLI/Program.cs index c56a57630..6da8585ce 100644 --- a/Confuser.CLI/Program.cs +++ b/Confuser.CLI/Program.cs @@ -6,7 +6,10 @@ using System.Xml; using Confuser.Core; using Confuser.Core.Project; +using Microsoft.Extensions.Logging; using NDesk.Options; +using Serilog; +using Serilog.Events; namespace Confuser.CLI { internal class Program { @@ -21,6 +24,8 @@ static int Main(string[] args) { try { bool noPause = false; bool debug = false; + bool quiet = false; + int verbosity = 0; string outDir = null; string snKeyPath = null; string snKeyPass = null; @@ -48,6 +53,12 @@ static int Main(string[] args) { }, { "snkeypass=", "specifies strong name key password.", value => { snKeyPass = value; } + }, { + "v|verbose", "increase verbosity (repeat for more: -v, -vv, -vvv).", + value => { verbosity++; } + }, { + "q|quiet", "only show warnings and errors.", + value => { quiet = (value != null); } } }; @@ -130,7 +141,7 @@ static int Main(string[] args) { parameters.Project = proj; } - int retVal = RunProject(parameters); + int retVal = RunProject(parameters, quiet, verbosity); if (NeedPause() && !noPause) { Console.WriteLine("Press any key to continue..."); @@ -192,14 +203,33 @@ static void LoadTemplateProject(string templatePath, ConfuserProject proj, List< templateModules.Add(templateModule); } - static int RunProject(ConfuserParameters parameters) { + static int RunProject(ConfuserParameters parameters, bool quiet, int verbosity) { + var levelSwitch = quiet + ? LogEventLevel.Warning + : verbosity >= 3 ? LogEventLevel.Verbose + : verbosity >= 2 ? LogEventLevel.Verbose + : verbosity >= 1 ? LogEventLevel.Debug + : LogEventLevel.Information; + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Is(levelSwitch) + .WriteTo.Console( + outputTemplate: "[{Level:u4}] {Message:lj}{NewLine}{Exception}") + .CreateLogger(); + + using var loggerFactory = LoggerFactory.Create(builder => + builder.AddSerilog(dispose: false)); + var melLogger = loggerFactory.CreateLogger("ConfuserEx"); + var progressReporter = new ConsoleProgressReporter(); - parameters.Logger = new ConsoleLogger(); + parameters.Logger = new MelLoggerAdapter(melLogger); parameters.ProgressReporter = progressReporter; - Console.Title = "ConfuserEx - Running..."; + if (OperatingSystem.IsWindows()) + Console.Title = "ConfuserEx - Running..."; ConfuserEngine.Run(parameters).GetAwaiter().GetResult(); + Log.CloseAndFlush(); return progressReporter.ReturnValue; } @@ -218,6 +248,8 @@ static void PrintUsage() { WriteLine(" -debug : specifies debug symbol generation."); WriteLine(" -snkey : specifies strong name key file path."); WriteLine(" -snkeypass : specifies strong name key password."); + WriteLine(" -v|verbose : increase verbosity (-v debug, -vv trace)."); + WriteLine(" -q|quiet : only show warnings and errors."); } static void WriteLineWithColor(ConsoleColor color, string txt) { @@ -235,50 +267,6 @@ static void WriteLine() { Console.WriteLine(); } - class ConsoleLogger : ILogger { - public void Debug(string msg) { - WriteLineWithColor(ConsoleColor.Gray, "[DEBUG] " + msg); - } - - public void DebugFormat(string format, params object[] args) { - WriteLineWithColor(ConsoleColor.Gray, "[DEBUG] " + string.Format(format, args)); - } - - public void Info(string msg) { - WriteLineWithColor(ConsoleColor.White, " [INFO] " + msg); - } - - public void InfoFormat(string format, params object[] args) { - WriteLineWithColor(ConsoleColor.White, " [INFO] " + string.Format(format, args)); - } - - public void Warn(string msg) { - WriteLineWithColor(ConsoleColor.Yellow, " [WARN] " + msg); - } - - public void WarnFormat(string format, params object[] args) { - WriteLineWithColor(ConsoleColor.Yellow, " [WARN] " + string.Format(format, args)); - } - - public void WarnException(string msg, Exception ex) { - WriteLineWithColor(ConsoleColor.Yellow, " [WARN] " + msg); - WriteLineWithColor(ConsoleColor.Yellow, "Exception: " + ex); - } - - public void Error(string msg) { - WriteLineWithColor(ConsoleColor.Red, "[ERROR] " + msg); - } - - public void ErrorFormat(string format, params object[] args) { - WriteLineWithColor(ConsoleColor.Red, "[ERROR] " + string.Format(format, args)); - } - - public void ErrorException(string msg, Exception ex) { - WriteLineWithColor(ConsoleColor.Red, "[ERROR] " + msg); - WriteLineWithColor(ConsoleColor.Red, "Exception: " + ex); - } - } - class ConsoleProgressReporter : IProgressReporter { readonly DateTime begin; From cf631aea8ae5606c4887faadfadd5bcac239fb94 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 11 Jun 2026 21:38:51 +0200 Subject: [PATCH 4/9] feature: replace GUI logger with Serilog FlowDocument sink (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up Serilog in the WPF GUI via a custom FlowDocumentSink that renders color-coded log output to the protection log panel. - Add Serilog and Serilog.Extensions.Logging to ConfuserEx - Create FlowDocumentSink — custom Serilog sink for WPF Paragraph - Remove ILogger from ProtectTabVM — now uses MelLoggerAdapter - ProtectTabVM keeps only IProgressReporter (progress bar + finish) - Delete ~40 lines of manual ILogger boilerplate --- ConfuserEx/ConfuserEx.csproj | 2 + ConfuserEx/FlowDocumentSink.cs | 70 ++++++++++++++++++++ ConfuserEx/ViewModel/UI/ProtectTabVM.cs | 85 ++++++++----------------- 3 files changed, 100 insertions(+), 57 deletions(-) create mode 100644 ConfuserEx/FlowDocumentSink.cs diff --git a/ConfuserEx/ConfuserEx.csproj b/ConfuserEx/ConfuserEx.csproj index 5f11173c4..09231a04e 100644 --- a/ConfuserEx/ConfuserEx.csproj +++ b/ConfuserEx/ConfuserEx.csproj @@ -19,6 +19,8 @@ + + diff --git a/ConfuserEx/FlowDocumentSink.cs b/ConfuserEx/FlowDocumentSink.cs new file mode 100644 index 000000000..46a5dc70d --- /dev/null +++ b/ConfuserEx/FlowDocumentSink.cs @@ -0,0 +1,70 @@ +using System; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using Serilog.Core; +using Serilog.Events; + +namespace ConfuserEx { + /// + /// A Serilog sink that writes log events to a WPF + /// with color-coded output matching the original ConfuserEx console style. + /// + internal sealed class FlowDocumentSink : ILogEventSink { + readonly Paragraph paragraph; + + public FlowDocumentSink(Paragraph paragraph) { + this.paragraph = paragraph ?? throw new ArgumentNullException(nameof(paragraph)); + } + + public void Emit(LogEvent logEvent) { + var brush = GetBrush(logEvent.Level); + var prefix = GetPrefix(logEvent.Level); + var message = logEvent.RenderMessage(); + + Application.Current.Dispatcher.BeginInvoke(new Action(() => { + paragraph.Inlines.Add(new Run(prefix + message) { Foreground = brush }); + paragraph.Inlines.Add(new LineBreak()); + + if (logEvent.Exception != null) { + paragraph.Inlines.Add(new Run("Exception: " + logEvent.Exception) { Foreground = brush }); + paragraph.Inlines.Add(new LineBreak()); + } + })); + } + + static Brush GetBrush(LogEventLevel level) { + switch (level) { + case LogEventLevel.Verbose: + case LogEventLevel.Debug: + return Brushes.Gray; + case LogEventLevel.Information: + return Brushes.White; + case LogEventLevel.Warning: + return Brushes.Yellow; + case LogEventLevel.Error: + case LogEventLevel.Fatal: + return Brushes.Red; + default: + return Brushes.White; + } + } + + static string GetPrefix(LogEventLevel level) { + switch (level) { + case LogEventLevel.Verbose: + case LogEventLevel.Debug: + return "[DEBUG] "; + case LogEventLevel.Information: + return " [INFO] "; + case LogEventLevel.Warning: + return " [WARN] "; + case LogEventLevel.Error: + case LogEventLevel.Fatal: + return "[ERROR] "; + default: + return ""; + } + } + } +} diff --git a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs index 0bcb75f45..41532cc69 100644 --- a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs +++ b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs @@ -8,9 +8,11 @@ using CommunityToolkit.Mvvm.Input; using Confuser.Core; using Confuser.Core.Project; +using Microsoft.Extensions.Logging; +using Serilog; namespace ConfuserEx.ViewModel { - internal class ProtectTabVM : TabViewModel, ILogger, IProgressReporter { + internal class ProtectTabVM : TabViewModel, IProgressReporter { readonly Paragraph documentContent; CancellationTokenSource cancelSrc; double? progress = 0; @@ -48,10 +50,21 @@ void DoProtect() { parameters.Project = ((IViewModel)App.Project).Model; if (File.Exists(App.FileName)) Environment.CurrentDirectory = Path.GetDirectoryName(App.FileName); - parameters.Logger = this; - parameters.ProgressReporter = this; documentContent.Inlines.Clear(); + + var serilogLogger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Sink(new FlowDocumentSink(documentContent)) + .CreateLogger(); + + using var loggerFactory = LoggerFactory.Create(builder => + builder.AddSerilog(serilogLogger, dispose: true)); + var melLogger = loggerFactory.CreateLogger("ConfuserEx"); + + parameters.Logger = new MelLoggerAdapter(melLogger); + parameters.ProgressReporter = this; + cancelSrc = new CancellationTokenSource(); Result = null; Progress = null; @@ -71,59 +84,10 @@ void DoCancel() { cancelSrc.Cancel(); } - void AppendLine(string format, Brush foreground, params object[] args) { - Application.Current.Dispatcher.BeginInvoke(new Action(() => { - documentContent.Inlines.Add(new Run(string.Format(format, args)) { Foreground = foreground }); - documentContent.Inlines.Add(new LineBreak()); - })); - } - - #region Logger Impl + #region IProgressReporter DateTime begin; - void ILogger.Debug(string msg) { - AppendLine("[DEBUG] {0}", Brushes.Gray, msg); - } - - void ILogger.DebugFormat(string format, params object[] args) { - AppendLine("[DEBUG] {0}", Brushes.Gray, string.Format(format, args)); - } - - void ILogger.Info(string msg) { - AppendLine(" [INFO] {0}", Brushes.White, msg); - } - - void ILogger.InfoFormat(string format, params object[] args) { - AppendLine(" [INFO] {0}", Brushes.White, string.Format(format, args)); - } - - void ILogger.Warn(string msg) { - AppendLine(" [WARN] {0}", Brushes.Yellow, msg); - } - - void ILogger.WarnFormat(string format, params object[] args) { - AppendLine(" [WARN] {0}", Brushes.Yellow, string.Format(format, args)); - } - - void ILogger.WarnException(string msg, Exception ex) { - AppendLine(" [WARN] {0}", Brushes.Yellow, msg); - AppendLine("Exception: {0}", Brushes.Yellow, ex); - } - - void ILogger.Error(string msg) { - AppendLine("[ERROR] {0}", Brushes.Red, msg); - } - - void ILogger.ErrorFormat(string format, params object[] args) { - AppendLine("[ERROR] {0}", Brushes.Red, string.Format(format, args)); - } - - void ILogger.ErrorException(string msg, Exception ex) { - AppendLine("[ERROR] {0}", Brushes.Red, msg); - AppendLine("Exception: {0}", Brushes.Red, ex); - } - void IProgressReporter.Progress(int progress, int overall) { Progress = (double)progress / overall; } @@ -139,10 +103,17 @@ void IProgressReporter.Finish(bool successful) { now.ToShortTimeString(), (int)now.Subtract(begin).TotalMinutes, now.Subtract(begin).Seconds); - if (successful) - AppendLine("Finished {0}", Brushes.Lime, timeString); - else - AppendLine("Failed {0}", Brushes.Red, timeString); + + Application.Current.Dispatcher.BeginInvoke(new Action(() => { + if (successful) { + documentContent.Inlines.Add(new Run("Finished " + timeString) { Foreground = Brushes.Lime }); + } + else { + documentContent.Inlines.Add(new Run("Failed " + timeString) { Foreground = Brushes.Red }); + } + documentContent.Inlines.Add(new LineBreak()); + })); + Result = successful; } From 56574a20e7bf440f11ed8d53f3498a872c2065e4 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 11 Jun 2026 21:50:43 +0200 Subject: [PATCH 5/9] chore: add local-ci.sh script mirroring GitHub Actions pipeline Full local CI script that replicates lint.yml, ci.yml, and test.yml: - lint: whitespace, style, and analyzer checks via dotnet format - build: dotnet build for SDK projects + MSBuild.exe for C++/CLI - test: discovers all *.Test.csproj, runs with coverage, summary - package: creates CLI, GUI, and combined zip archives Usage: ./scripts/local-ci.sh [lint|build|test|package|all] --- scripts/local-ci.sh | 361 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 scripts/local-ci.sh diff --git a/scripts/local-ci.sh b/scripts/local-ci.sh new file mode 100644 index 000000000..cde77b79c --- /dev/null +++ b/scripts/local-ci.sh @@ -0,0 +1,361 @@ +#!/usr/bin/env bash +# ============================================================================= +# local-ci.sh — Run the full CI pipeline locally (mirrors GitHub Actions) +# +# Replicates: lint.yml, ci.yml (build + package), test.yml (test + coverage) +# +# Usage: +# ./scripts/local-ci.sh # run everything +# ./scripts/local-ci.sh lint # lint only +# ./scripts/local-ci.sh build # restore + build only +# ./scripts/local-ci.sh test # build + test only +# ./scripts/local-ci.sh package # build + package only +# ./scripts/local-ci.sh all # everything (default) +# ============================================================================= + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO_ROOT" + +CONFIGURATION="Release" +SLN="Confuser2.sln" +RESULTS_DIR="test-results" +COVERAGE_DIR="coverage" + +# --------------------------------------------------------------------------- +# Colors +# --------------------------------------------------------------------------- +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +step() { echo -e "\n${CYAN}${BOLD}==> $1${NC}"; } +success() { echo -e "${GREEN}✓ $1${NC}"; } +warn() { echo -e "${YELLOW}⚠ $1${NC}"; } +fail() { echo -e "${RED}✗ $1${NC}"; } + +# --------------------------------------------------------------------------- +# Find MSBuild via vswhere (CI uses microsoft/setup-msbuild@v2) +# --------------------------------------------------------------------------- +find_msbuild() { + # vswhere -latest finds the newest VS (2025 > 2022 > 2019). + # CI uses windows-2025 runners with VS 2025 / MSBuild 18. + local vswhere="/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" + if [ -f "$vswhere" ]; then + # Get installation path first, then construct MSBuild path + local vs_path + vs_path=$("$vswhere" -latest -requires Microsoft.Component.MSBuild \ + -property installationPath 2>/dev/null | head -1) + if [ -n "$vs_path" ]; then + # Convert Windows path to unix-style for bash + local unix_path + unix_path=$(cygpath -u "$vs_path" 2>/dev/null || echo "$vs_path") + MSBUILD="$unix_path/MSBuild/Current/Bin/MSBuild.exe" + if [ ! -f "$MSBUILD" ]; then + MSBUILD="" + fi + fi + fi + + if [ -z "${MSBUILD:-}" ]; then + warn "MSBuild not found — will use 'dotnet build' (C++/CLI project will be skipped)" + return 1 + fi + + local ver + ver=$("$MSBUILD" -version 2>/dev/null | tail -1 || echo "unknown") + echo " MSBuild: $MSBUILD (v$ver)" + return 0 +} + +# --------------------------------------------------------------------------- +# Phase: Lint (mirrors lint.yml) +# --------------------------------------------------------------------------- +do_lint() { + step "LINT — whitespace, style, analyzers" + + local any_failed=false + + echo " Restoring for lint..." + dotnet restore "$SLN" --verbosity quiet 2>/dev/null + + echo " Checking whitespace..." + if dotnet format whitespace "$SLN" --verify-no-changes --verbosity minimal 2>&1 | tail -5; then + success "Whitespace OK" + else + warn "Whitespace issues found" + any_failed=true + fi + + echo " Checking style..." + if dotnet format style "$SLN" --verify-no-changes --severity warn 2>&1 | tail -5; then + success "Style OK" + else + warn "Style issues found" + any_failed=true + fi + + echo " Checking analyzers..." + if dotnet format analyzers "$SLN" --verify-no-changes --severity warn 2>&1 | tail -5; then + success "Analyzers OK" + else + warn "Analyzer issues found" + any_failed=true + fi + + if [ "$any_failed" = true ]; then + warn "Lint issues found — run 'dotnet format $SLN' to fix" + return 1 + else + success "All lint checks passed" + fi +} + +# --------------------------------------------------------------------------- +# Phase: Restore + Build (mirrors ci.yml) +# --------------------------------------------------------------------------- +do_build() { + step "BUILD — restore + compile ($CONFIGURATION)" + + local has_msbuild=false + find_msbuild && has_msbuild=true + + # Use 'dotnet build' for all SDK-style projects (handles .NET SDK resolution). + # Then use MSBuild.exe for the C++/CLI project if available. + echo " Restoring..." + dotnet restore "$SLN" --verbosity minimal + + echo " Building (dotnet)..." + dotnet build "$SLN" -c "$CONFIGURATION" --no-restore 2>&1 \ + | grep -v "244_ClrProtection" || true + + # C++/CLI project requires MSBuild.exe (not dotnet build) + if [ "$has_msbuild" = true ]; then + local vcxproj="Tests/244_ClrProtection/244_ClrProtection.vcxproj" + if [ -f "$vcxproj" ]; then + echo " Building C++/CLI test project (msbuild)..." + "$MSBUILD" "$vcxproj" -p:Configuration="$CONFIGURATION" -verbosity:minimal 2>&1 \ + | tail -3 || warn "C++/CLI project build failed (non-critical)" + fi + else + warn "Skipping C++/CLI project (no MSBuild.exe found)" + fi + + # Verify key outputs exist + local cli_dll="Confuser.CLI/bin/$CONFIGURATION/net10.0/Confuser.CLI.dll" + local gui_dll="ConfuserEx/bin/$CONFIGURATION/net10.0-windows/ConfuserEx.dll" + local core48="Confuser.Core/bin/$CONFIGURATION/net48/Confuser.Core.dll" + local corenstd="Confuser.Core/bin/$CONFIGURATION/netstandard2.0/Confuser.Core.dll" + + local all_ok=true + for f in "$cli_dll" "$gui_dll" "$core48" "$corenstd"; do + if [ -f "$f" ]; then + success "$(basename "$f") ($(dirname "$f" | sed "s|.*/bin/$CONFIGURATION/||"))" + else + fail "Missing: $f" + all_ok=false + fi + done + + if [ "$all_ok" = false ]; then + fail "Build produced missing outputs" + return 1 + fi + + success "Build complete" +} + +# --------------------------------------------------------------------------- +# Phase: Test (mirrors test.yml) +# --------------------------------------------------------------------------- +do_test() { + step "TEST — run all test projects with coverage" + + rm -rf "$RESULTS_DIR" 2>/dev/null || true + mkdir -p "$RESULTS_DIR" + + local total_passed=0 + local total_failed=0 + local total_skipped=0 + local any_failed=false + + # Find all *.Test.csproj (same as CI: Get-ChildItem -Filter '*.Test.csproj' -Recurse) + while IFS= read -r proj; do + local name + name=$(basename "$proj" .csproj) + echo -e "\n ${CYAN}Testing $name...${NC}" + + local output + output=$(dotnet test "$proj" -c "$CONFIGURATION" --no-build --verbosity minimal \ + --collect:"XPlat Code Coverage" \ + --logger "trx;LogFileName=$name.trx" \ + --results-directory "$RESULTS_DIR/$name" 2>&1) || true + + # Parse summary line: "Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3" + local summary + summary=$(echo "$output" | grep -E "^(Passed!|Failed!)" | tail -1) + + if [ -n "$summary" ]; then + local p f s + p=$(echo "$summary" | grep -oP 'Passed:\s+\K\d+' || echo 0) + f=$(echo "$summary" | grep -oP 'Failed:\s+\K\d+' || echo 0) + s=$(echo "$summary" | grep -oP 'Skipped:\s+\K\d+' || echo 0) + total_passed=$((total_passed + p)) + total_failed=$((total_failed + f)) + total_skipped=$((total_skipped + s)) + + if echo "$summary" | grep -q "^Failed!"; then + fail "$name — $summary" + any_failed=true + else + success "$name — Passed: $p, Failed: $f, Skipped: $s" + fi + else + # No tests discovered + local no_test + no_test=$(echo "$output" | grep -c "No test is available" || true) + if [ "$no_test" -gt 0 ]; then + warn "$name — no tests discovered (missing Microsoft.NET.Test.Sdk?)" + else + warn "$name — no test output" + fi + fi + done < <(find Tests -name "*.Test.csproj" -type f | sort) + + echo "" + echo " ─────────────────────────────────────" + echo -e " ${BOLD}Total: Passed=$total_passed Failed=$total_failed Skipped=$total_skipped${NC}" + echo " ─────────────────────────────────────" + + # Generate coverage report if reportgenerator is available + local reports + reports=$(find "$RESULTS_DIR" -name "coverage.cobertura.xml" 2>/dev/null | tr '\n' ';') + if [ -n "$reports" ] && command -v reportgenerator &>/dev/null; then + step "COVERAGE — generating report" + mkdir -p "$COVERAGE_DIR/report" + reportgenerator "-reports:$reports" \ + "-targetdir:$COVERAGE_DIR/report" \ + "-reporttypes:TextSummary" 2>/dev/null + cat "$COVERAGE_DIR/report/Summary.txt" 2>/dev/null || true + elif [ -n "$reports" ]; then + warn "Install reportgenerator for coverage reports: dotnet tool install -g dotnet-reportgenerator-globaltool" + fi + + if [ "$any_failed" = true ]; then + fail "Some tests failed" + return 1 + fi + + success "All tests passed" +} + +# --------------------------------------------------------------------------- +# Phase: Package (mirrors ci.yml packaging steps) +# --------------------------------------------------------------------------- +do_package() { + step "PACKAGE — create release archives" + + local cli_dir="Confuser.CLI/bin/$CONFIGURATION/net10.0" + local gui_dir="ConfuserEx/bin/$CONFIGURATION/net10.0-windows" + + # CLI zip + if [ -d "$cli_dir" ]; then + rm -f ConfuserEx-CLI.zip 2>/dev/null || true + (cd "$cli_dir" && find . -not -name '*.pdb' -not -name '*.xml' -not -path './runtimes/*/native/*' \ + -type f | sort | zip -q "$REPO_ROOT/ConfuserEx-CLI.zip" -@) + local size + size=$(du -h ConfuserEx-CLI.zip | cut -f1) + success "ConfuserEx-CLI.zip ($size)" + else + fail "CLI output not found at $cli_dir — run build first" + fi + + # GUI zip + if [ -d "$gui_dir" ]; then + rm -f ConfuserEx-GUI.zip 2>/dev/null || true + (cd "$gui_dir" && find . -not -name '*.pdb' -not -name '*.xml' \ + -type f | sort | zip -q "$REPO_ROOT/ConfuserEx-GUI.zip" -@) + size=$(du -h ConfuserEx-GUI.zip | cut -f1) + success "ConfuserEx-GUI.zip ($size)" + else + fail "GUI output not found at $gui_dir — run build first" + fi + + # Combined zip + rm -rf combined 2>/dev/null || true + mkdir -p combined + cp "$cli_dir"/* combined/ 2>/dev/null || true + cp "$gui_dir"/* combined/ 2>/dev/null || true + rm -f combined/*.pdb combined/*.xml 2>/dev/null || true + if [ "$(ls -A combined 2>/dev/null)" ]; then + rm -f ConfuserEx.zip 2>/dev/null || true + (cd combined && find . -type f | sort | zip -q "$REPO_ROOT/ConfuserEx.zip" -@) + size=$(du -h ConfuserEx.zip | cut -f1) + success "ConfuserEx.zip ($size)" + fi + rm -rf combined + + # NuGet package + local nupkg + nupkg=$(find Confuser.MSBuild.Tasks/bin/$CONFIGURATION -name "*.nupkg" 2>/dev/null | head -1) + if [ -n "$nupkg" ]; then + success "$(basename "$nupkg")" + else + warn "No .nupkg found (MSBuild-only build may be required)" + fi + + success "Packaging complete" +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +MODE="${1:-all}" + +echo -e "${BOLD}╔══════════════════════════════════════╗${NC}" +echo -e "${BOLD}║ ConfuserEx Local CI Pipeline ║${NC}" +echo -e "${BOLD}╚══════════════════════════════════════╝${NC}" +echo " Mode: $MODE" +echo " Config: $CONFIGURATION" +echo " Root: $REPO_ROOT" + +ERRORS=0 + +case "$MODE" in + lint) + do_lint || ERRORS=$((ERRORS + 1)) + ;; + build) + do_build || ERRORS=$((ERRORS + 1)) + ;; + test) + do_build || ERRORS=$((ERRORS + 1)) + do_test || ERRORS=$((ERRORS + 1)) + ;; + package) + do_build || ERRORS=$((ERRORS + 1)) + do_package || ERRORS=$((ERRORS + 1)) + ;; + all) + do_lint || ERRORS=$((ERRORS + 1)) + do_build || ERRORS=$((ERRORS + 1)) + do_test || ERRORS=$((ERRORS + 1)) + do_package || ERRORS=$((ERRORS + 1)) + ;; + *) + echo "Usage: $0 [lint|build|test|package|all]" + exit 1 + ;; +esac + +echo "" +if [ "$ERRORS" -gt 0 ]; then + fail "Pipeline finished with $ERRORS failed phase(s)" + exit 1 +else + success "Pipeline complete — all phases passed" +fi From e72ade7f6cafe7bacfb2a82c60a97cde3b2277f0 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 11 Jun 2026 22:23:00 +0200 Subject: [PATCH 6/9] refactor: replace Confuser.Core.ILogger with Microsoft.Extensions.Logging.ILogger (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete migration from the custom 13-method ILogger interface to the standard M.E.L ILogger abstraction across all projects. - Change ConfuserContext.Logger and ConfuserParameters.Logger to M.E.L ILogger - Convert all ~80 call sites: Debug→LogDebug, Info→LogInformation, Warn→LogWarning, Error→LogError, *Exception→swap parameter order - Rewrite MSBuildLogger as MSBuildMelLogger implementing M.E.L ILogger - Rewrite XUnitLogger implementing M.E.L ILogger + IProgressReporter - Remove MelLoggerAdapter (no longer needed — M.E.L is the native type) - Delete Confuser.Core.ILogger, NullLogger (replaced by M.E.L NullLogger) - Add test-results/ and coverage/ to .gitignore --- .gitignore | 6 +- Confuser.CLI/Program.cs | 2 +- Confuser.Core/ConfuserContext.cs | 3 +- Confuser.Core/ConfuserEngine.cs | 83 ++++++++++--------- Confuser.Core/ConfuserParameters.cs | 7 +- Confuser.Core/DotNetCorePathResolver.cs | 23 ++--- Confuser.Core/ILogger.cs | 74 ----------------- Confuser.Core/Marker.cs | 15 ++-- Confuser.Core/MelLoggerAdapter.cs | 44 ---------- Confuser.Core/NullLogger.cs | 45 ---------- Confuser.Core/ObfAttrMarker.cs | 17 ++-- Confuser.Core/Packer.cs | 11 +-- Confuser.Core/PluginDiscovery.cs | 15 ++-- Confuser.Core/ProtectionPipeline.cs | 7 +- Confuser.Core/WatermarkingProtection.cs | 3 +- Confuser.MSBuild.Tasks/ConfuseTask.cs | 2 +- Confuser.MSBuild.Tasks/MSBuildLogger.cs | 73 ++++++++-------- Confuser.Protections/AntiTamper/JITMode.cs | 5 +- Confuser.Protections/Compress/Compressor.cs | 5 +- Confuser.Protections/Compress/ExtractPhase.cs | 3 +- .../ControlFlow/ControlFlowPhase.cs | 3 +- Confuser.Protections/HardeningPhase.cs | 3 +- Confuser.Protections/Resources/InjectPhase.cs | 3 +- Confuser.Protections/Resources/MDPhase.cs | 3 +- Confuser.Renamer/AnalyzePhase.cs | 17 ++-- .../Analyzers/CallSiteAnalyzer.cs | 3 +- .../Analyzers/ReflectionAnalyzer.cs | 6 +- .../Analyzers/ResourceAnalyzer.cs | 7 +- .../Analyzers/TypeBlobAnalyzer.cs | 5 +- Confuser.Renamer/Analyzers/WPFAnalyzer.cs | 21 ++--- .../Analyzers/WinFormsAnalyzer.cs | 17 ++-- Confuser.Renamer/BAML/BAMLAnalyzer.cs | 7 +- Confuser.Renamer/RenamePhase.cs | 7 +- Confuser.Renamer/VTable.cs | 16 ++-- ConfuserEx/ViewModel/UI/ProtectTabVM.cs | 2 +- .../Analyzers/ReflectionAnalyzerTest.cs | 4 +- Tests/Confuser.UnitTest/XUnitLogger.cs | 47 ++++------- 37 files changed, 227 insertions(+), 387 deletions(-) delete mode 100644 Confuser.Core/ILogger.cs delete mode 100644 Confuser.Core/MelLoggerAdapter.cs delete mode 100644 Confuser.Core/NullLogger.cs diff --git a/.gitignore b/.gitignore index 9ee6a61e2..557af9475 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,8 @@ packages/ gh-pages/ .idea/ -**/*out* \ No newline at end of file +**/*out* + +# Local CI artifacts +test-results/ +coverage/ \ No newline at end of file diff --git a/Confuser.CLI/Program.cs b/Confuser.CLI/Program.cs index 6da8585ce..47da3ca8e 100644 --- a/Confuser.CLI/Program.cs +++ b/Confuser.CLI/Program.cs @@ -222,7 +222,7 @@ static int RunProject(ConfuserParameters parameters, bool quiet, int verbosity) var melLogger = loggerFactory.CreateLogger("ConfuserEx"); var progressReporter = new ConsoleProgressReporter(); - parameters.Logger = new MelLoggerAdapter(melLogger); + parameters.Logger = melLogger; parameters.ProgressReporter = progressReporter; if (OperatingSystem.IsWindows()) diff --git a/Confuser.Core/ConfuserContext.cs b/Confuser.Core/ConfuserContext.cs index d65bd2db8..493643745 100644 --- a/Confuser.Core/ConfuserContext.cs +++ b/Confuser.Core/ConfuserContext.cs @@ -4,6 +4,7 @@ using Confuser.Core.Project; using dnlib.DotNet; using dnlib.DotNet.Writer; +using Microsoft.Extensions.Logging; namespace Confuser.Core { /// @@ -18,7 +19,7 @@ public class ConfuserContext { /// Gets the logger used for logging events. /// /// The logger. - public ILogger Logger { get; internal set; } + public Microsoft.Extensions.Logging.ILogger Logger { get; internal set; } /// /// Gets the progress reporter used for reporting protection progress. diff --git a/Confuser.Core/ConfuserEngine.cs b/Confuser.Core/ConfuserEngine.cs index f92c2967e..68ac9c482 100644 --- a/Confuser.Core/ConfuserEngine.cs +++ b/Confuser.Core/ConfuserEngine.cs @@ -10,6 +10,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.Writer; +using Microsoft.Extensions.Logging; using Microsoft.Win32; using CopyrightAttribute = System.Reflection.AssemblyCopyrightAttribute; using InformationalAttribute = System.Reflection.AssemblyInformationalVersionAttribute; @@ -108,7 +109,7 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) var modulePath = Path.Combine(context.BaseDirectory, firstModule.Path); foreach (var runtimePath in DotNetCorePathResolver.ResolveRuntimePaths(modulePath, context.Logger)) { asmResolver.PostSearchPaths.Add(runtimePath); - context.Logger.DebugFormat("Auto-detected .NET runtime path: {0}", runtimePath); + context.Logger.LogDebug("Auto-detected .NET runtime path: {0}", runtimePath); } } @@ -117,25 +118,25 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) Marker marker = parameters.GetMarker(); // 2. Discover plugins - context.Logger.Debug("Discovering plugins..."); + context.Logger.LogDebug("Discovering plugins..."); IList prots; IList packers; IList components; parameters.GetPluginDiscovery().GetPlugins(context, out prots, out packers, out components); - context.Logger.InfoFormat("Discovered {0} protections, {1} packers.", prots.Count, packers.Count); + context.Logger.LogInformation("Discovered {0} protections, {1} packers.", prots.Count, packers.Count); context.CheckCancellation(); // 3. Resolve dependency - context.Logger.Debug("Resolving component dependency..."); + context.Logger.LogDebug("Resolving component dependency..."); try { var resolver = new DependencyResolver(prots); prots = resolver.SortDependency(); } catch (CircularDependencyException ex) { - context.Logger.ErrorException("", ex); + context.Logger.LogError(ex, ""); throw new ConfuserException(ex); } @@ -148,7 +149,7 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) context.CheckCancellation(); // 4. Load modules - context.Logger.Info("Loading input modules..."); + context.Logger.LogInformation("Loading input modules..."); marker.Initialize(prots, packers); MarkerResult markings = marker.MarkProject(context.Project, context); context.Modules = new ModuleSorter(markings.Modules).Sort().ToList().AsReadOnly(); @@ -163,13 +164,13 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) context.CheckCancellation(); // 5. Initialize components - context.Logger.Info("Initializing..."); + context.Logger.LogInformation("Initializing..."); foreach (ConfuserComponent comp in components) { try { comp.Initialize(context); } catch (Exception ex) { - context.Logger.ErrorException("Error occurred during initialization of '" + comp.Name + "'.", ex); + context.Logger.LogError(ex, "Error occurred during initialization of '" + comp.Name + "'."); throw new ConfuserException(ex); } context.CheckCancellation(); @@ -178,7 +179,7 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) context.CheckCancellation(); // 6. Build pipeline - context.Logger.Debug("Building pipeline..."); + context.Logger.LogDebug("Building pipeline..."); var pipeline = new ProtectionPipeline(); context.Pipeline = pipeline; foreach (ConfuserComponent comp in components) { @@ -193,28 +194,28 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token) ok = true; } catch (AssemblyResolveException ex) { - context.Logger.ErrorException("Failed to resolve an assembly, check if all dependencies are present in the correct version.", ex); + context.Logger.LogError(ex, "Failed to resolve an assembly, check if all dependencies are present in the correct version."); PrintEnvironmentInfo(context); } catch (TypeResolveException ex) { - context.Logger.ErrorException("Failed to resolve a type, check if all dependencies are present in the correct version.", ex); + context.Logger.LogError(ex, "Failed to resolve a type, check if all dependencies are present in the correct version."); PrintEnvironmentInfo(context); } catch (MemberRefResolveException ex) { - context.Logger.ErrorException("Failed to resolve a member, check if all dependencies are present in the correct version.", ex); + context.Logger.LogError(ex, "Failed to resolve a member, check if all dependencies are present in the correct version."); PrintEnvironmentInfo(context); } catch (IOException ex) { - context.Logger.ErrorException("An IO error occurred, check if all input/output locations are readable/writable.", ex); + context.Logger.LogError(ex, "An IO error occurred, check if all input/output locations are readable/writable."); } catch (OperationCanceledException) { - context.Logger.Error("Operation cancelled."); + context.Logger.LogError("Operation cancelled."); } catch (ConfuserException) { // Exception is already handled/logged, so just ignore and report failure } catch (Exception ex) { - context.Logger.ErrorException("Unknown error occurred.", ex); + context.Logger.LogError(ex, "Unknown error occurred."); } finally { if (context.Resolver != null) @@ -269,27 +270,27 @@ static void RunPipeline(ProtectionPipeline pipeline, ConfuserContext context) { pipeline.ExecuteStage(PipelineStage.SaveModules, SaveModules, () => getAllDefs(), context); if (!context.PackerInitiated) - context.Logger.Info("Done."); + context.Logger.LogInformation("Done."); } static void Inspection(ConfuserContext context) { - context.Logger.Info("Resolving dependencies..."); + context.Logger.LogInformation("Resolving dependencies..."); foreach (var dependency in context.Modules .SelectMany(module => module.GetAssemblyRefs().Select(asmRef => Tuple.Create(asmRef, module)))) { var resolved = context.Resolver.Resolve(dependency.Item1, dependency.Item2); if (resolved == null) - context.Logger.WarnFormat("Failed to resolve dependency '{0}' of '{1}'. Some protections may not work correctly.", + context.Logger.LogWarning("Failed to resolve dependency '{0}' of '{1}'. Some protections may not work correctly.", dependency.Item1.FullName, dependency.Item2.Name); } - context.Logger.Debug("Checking Strong Name..."); + context.Logger.LogDebug("Checking Strong Name..."); foreach (var module in context.Modules) { CheckStrongName(context, module); } var marker = context.Registry.GetService(); - context.Logger.Debug("Creating global .cctors..."); + context.Logger.LogDebug("Creating global .cctors..."); foreach (ModuleDefMD module in context.Modules) { TypeDef modType = module.GlobalType; if (modType == null) { @@ -317,12 +318,12 @@ static void CheckStrongName(ConfuserContext context, ModuleDef module) { bool isKeyProvided = snKey != null || (snDelaySign && snPubKeyBytes != null); if (!isKeyProvided && moduleIsSignedOrDelayedSigned) - context.Logger.WarnFormat("[{0}] SN Key or SN public Key is not provided for a signed module, the output may not be working.", module.Name); + context.Logger.LogWarning("[{0}] SN Key or SN public Key is not provided for a signed module, the output may not be working.", module.Name); else if (isKeyProvided && !moduleIsSignedOrDelayedSigned) - context.Logger.WarnFormat("[{0}] SN Key or SN public Key is provided for an unsigned module, the output may not be working.", module.Name); + context.Logger.LogWarning("[{0}] SN Key or SN public Key is provided for an unsigned module, the output may not be working.", module.Name); else if (snPubKeyBytes != null && moduleIsSignedOrDelayedSigned && !module.Assembly.PublicKey.Data.SequenceEqual(snPubKeyBytes)) - context.Logger.WarnFormat("[{0}] Provided SN public Key and signed module's public key do not match, the output may not be working.", + context.Logger.LogWarning("[{0}] Provided SN public Key and signed module's public key do not match, the output may not be working.", module.Name); } @@ -339,7 +340,7 @@ static void CopyPEHeaders(PEHeadersOptions writerOptions, ModuleDefMD module) { } static void BeginModule(ConfuserContext context) { - context.Logger.InfoFormat("Processing module '{0}'...", context.CurrentModule.Name); + context.Logger.LogInformation("Processing module '{0}'...", context.CurrentModule.Name); context.CurrentModuleWriterOptions = new ModuleWriterOptions(context.CurrentModule); CopyPEHeaders(context.CurrentModuleWriterOptions.PEHeadersOptions, context.CurrentModule); @@ -394,7 +395,7 @@ static void EndModule(ConfuserContext context) { output = Path.Combine(context.BaseDirectory, output); string relativeOutput = Utils.GetRelativePath(output, context.BaseDirectory); if (relativeOutput is null) { - context.Logger.WarnFormat("Input file is not inside the base directory. Relative path can't be created. Placing file into output root." + + context.Logger.LogWarning("Input file is not inside the base directory. Relative path can't be created. Placing file into output root." + Environment.NewLine + "Responsible file is: {0}", output); output = Path.GetFileName(output); } @@ -409,7 +410,7 @@ static void EndModule(ConfuserContext context) { } static void WriteModule(ConfuserContext context) { - context.Logger.InfoFormat("Writing module '{0}'...", context.CurrentModule.Name); + context.Logger.LogInformation("Writing module '{0}'...", context.CurrentModule.Name); MemoryStream pdb = null, output = new MemoryStream(); @@ -431,7 +432,7 @@ static void WriteModule(ConfuserContext context) { } static void Debug(ConfuserContext context) { - context.Logger.Info("Finalizing..."); + context.Logger.LogInformation("Finalizing..."); if (!context.Project.Debug) return; for (int i = 0; i < context.OutputModules.Count; i++) { @@ -447,7 +448,7 @@ static void Debug(ConfuserContext context) { static void Pack(ConfuserContext context) { if (context.Packer != null) { - context.Logger.Info("Packing..."); + context.Logger.LogInformation("Packing..."); context.Packer.Pack(context, new ProtectionParameters(context.Packer, context.Modules.OfType().ToList())); } } @@ -459,7 +460,7 @@ static void SaveModules(ConfuserContext context) { string dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); - context.Logger.DebugFormat("Saving to '{0}'...", path); + context.Logger.LogDebug("Saving to '{0}'...", path); File.WriteAllBytes(path, context.OutputModules[i]); } } @@ -470,13 +471,13 @@ static void SaveModules(ConfuserContext context) { /// The working context. static void PrintInfo(ConfuserContext context) { if (context.PackerInitiated) { - context.Logger.Info("Protecting packer stub..."); + context.Logger.LogInformation("Protecting packer stub..."); } else { - context.Logger.InfoFormat("{0} {1}", Version, Copyright); + context.Logger.LogInformation("{0} {1}", Version, Copyright); Type mono = Type.GetType("Mono.Runtime"); - context.Logger.InfoFormat("Running on {0}, {1}, {2} bits", + context.Logger.LogInformation("Running on {0}, {1}, {2} bits", Environment.OSVersion, mono == null ? ".NET Framework v" + Environment.Version : @@ -543,27 +544,27 @@ static void PrintEnvironmentInfo(ConfuserContext context) { if (context.PackerInitiated) return; - context.Logger.Error("---BEGIN DEBUG INFO---"); + context.Logger.LogError("---BEGIN DEBUG INFO---"); - context.Logger.Error("Installed Framework Versions:"); + context.Logger.LogError("Installed Framework Versions:"); foreach (string ver in GetFrameworkVersions()) { - context.Logger.ErrorFormat(" {0}", ver.Trim()); + context.Logger.LogError(" {0}", ver.Trim()); } - context.Logger.Error(""); + context.Logger.LogError(""); if (context.Resolver != null) { - context.Logger.Error("Cached assemblies:"); + context.Logger.LogError("Cached assemblies:"); foreach (AssemblyDef asm in context.InternalResolver.GetCachedAssemblies()) { if (string.IsNullOrEmpty(asm.ManifestModule.Location)) - context.Logger.ErrorFormat(" {0}", asm.FullName); + context.Logger.LogError(" {0}", asm.FullName); else - context.Logger.ErrorFormat(" {0} ({1})", asm.FullName, asm.ManifestModule.Location); + context.Logger.LogError(" {0} ({1})", asm.FullName, asm.ManifestModule.Location); foreach (var reference in asm.Modules.OfType().SelectMany(m => m.GetAssemblyRefs())) - context.Logger.ErrorFormat(" {0}", reference.FullName); + context.Logger.LogError(" {0}", reference.FullName); } } - context.Logger.Error("---END DEBUG INFO---"); + context.Logger.LogError("---END DEBUG INFO---"); } } } diff --git a/Confuser.Core/ConfuserParameters.cs b/Confuser.Core/ConfuserParameters.cs index 25162ae0e..5f9d00ba7 100644 --- a/Confuser.Core/ConfuserParameters.cs +++ b/Confuser.Core/ConfuserParameters.cs @@ -1,5 +1,6 @@ using System; using Confuser.Core.Project; +using Microsoft.Extensions.Logging; namespace Confuser.Core { /// @@ -16,7 +17,7 @@ public class ConfuserParameters { /// Gets or sets the logger that used to log the protection process. /// /// The logger, or null if logging is not needed. - public ILogger Logger { get; set; } + public Microsoft.Extensions.Logging.ILogger Logger { get; set; } /// /// Gets or sets the progress reporter used to report protection progress. @@ -42,8 +43,8 @@ public class ConfuserParameters { /// Gets the actual non-null logger. /// /// The logger. - internal ILogger GetLogger() { - return Logger ?? NullLogger.Instance; + internal Microsoft.Extensions.Logging.ILogger GetLogger() { + return Logger ?? Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance; } /// diff --git a/Confuser.Core/DotNetCorePathResolver.cs b/Confuser.Core/DotNetCorePathResolver.cs index e056613d6..b35503ce4 100644 --- a/Confuser.Core/DotNetCorePathResolver.cs +++ b/Confuser.Core/DotNetCorePathResolver.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.Extensions.Logging; namespace Confuser.Core { /// @@ -13,7 +14,7 @@ internal static class DotNetCorePathResolver { /// Resolves runtime assembly paths for a given module by parsing its runtimeconfig.json /// or probing standard dotnet installation directories. /// - public static IEnumerable ResolveRuntimePaths(string modulePath, ILogger logger) { + public static IEnumerable ResolveRuntimePaths(string modulePath, Microsoft.Extensions.Logging.ILogger logger) { // 1. Try runtimeconfig.json next to the module (target framework gets priority) var runtimeConfigPaths = Enumerable.Empty(); var runtimeConfig = FindRuntimeConfig(modulePath); @@ -39,36 +40,36 @@ static string FindRuntimeConfig(string modulePath) { return File.Exists(configPath) ? configPath : null; } - static IEnumerable GetPathsFromRuntimeConfig(string configPath, ILogger logger) { + static IEnumerable GetPathsFromRuntimeConfig(string configPath, Microsoft.Extensions.Logging.ILogger logger) { string content; try { content = File.ReadAllText(configPath); } catch (IOException ex) { - logger.WarnFormat("Failed to read runtime config '{0}': {1}", configPath, ex.Message); + logger.LogWarning("Failed to read runtime config '{0}': {1}", configPath, ex.Message); yield break; } catch (UnauthorizedAccessException ex) { - logger.WarnFormat("Access denied reading runtime config '{0}': {1}", configPath, ex.Message); + logger.LogWarning("Access denied reading runtime config '{0}': {1}", configPath, ex.Message); yield break; } var frameworks = ParseFrameworks(content); if (frameworks.Count == 0) { - logger.DebugFormat("No framework references found in '{0}'.", configPath); + logger.LogDebug("No framework references found in '{0}'.", configPath); yield break; } var dotnetRoot = GetDotNetRoot(); if (dotnetRoot == null) { - logger.Warn("Could not locate .NET installation directory. Set DOTNET_ROOT environment variable if installed in a non-standard location."); + logger.LogWarning("Could not locate .NET installation directory. Set DOTNET_ROOT environment variable if installed in a non-standard location."); yield break; } foreach (var fw in frameworks) { var sharedDir = Path.Combine(dotnetRoot, "shared", fw.Name); if (!Directory.Exists(sharedDir)) { - logger.DebugFormat("Framework directory not found: {0}", sharedDir); + logger.LogDebug("Framework directory not found: {0}", sharedDir); continue; } @@ -88,7 +89,7 @@ static IEnumerable GetPathsFromRuntimeConfig(string configPath, ILogger if (best != null) yield return best; else - logger.WarnFormat("No installed runtime found matching {0} {1} in {2}", fw.Name, fw.Version, sharedDir); + logger.LogWarning("No installed runtime found matching {0} {1} in {2}", fw.Name, fw.Version, sharedDir); } } @@ -137,16 +138,16 @@ static string GetMajorMinor(string version) { return parts.Length >= 2 ? parts[0] + "." + parts[1] : version; } - static IEnumerable ProbeAllInstalledRuntimes(ILogger logger) { + static IEnumerable ProbeAllInstalledRuntimes(Microsoft.Extensions.Logging.ILogger logger) { var dotnetRoot = GetDotNetRoot(); if (dotnetRoot == null) { - logger.Warn("Could not locate .NET installation directory for runtime probing."); + logger.LogWarning("Could not locate .NET installation directory for runtime probing."); yield break; } var sharedDir = Path.Combine(dotnetRoot, "shared"); if (!Directory.Exists(sharedDir)) { - logger.WarnFormat("Shared framework directory not found: {0}", sharedDir); + logger.LogWarning("Shared framework directory not found: {0}", sharedDir); yield break; } diff --git a/Confuser.Core/ILogger.cs b/Confuser.Core/ILogger.cs deleted file mode 100644 index d2aa9fdda..000000000 --- a/Confuser.Core/ILogger.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; - -namespace Confuser.Core { - /// - /// Defines a logger used to log Confuser events - /// - public interface ILogger { - /// - /// Logs a message at DEBUG level. - /// - /// The message. - void Debug(string msg); - - /// - /// Logs a message at DEBUG level with specified parameters. - /// - /// The format. - /// The arguments. - void DebugFormat(string format, params object[] args); - - /// - /// Logs a message at INFO level. - /// - /// The message. - void Info(string msg); - - /// - /// Logs a message at INFO level with specified parameters. - /// - /// The format. - /// The arguments. - void InfoFormat(string format, params object[] args); - - /// - /// Logs a message at WARN level. - /// - /// The message. - void Warn(string msg); - - /// - /// Logs a message at WARN level with specified parameters. - /// - /// The format. - /// The arguments. - void WarnFormat(string format, params object[] args); - - /// - /// Logs a message at WARN level with specified exception. - /// - /// The message. - /// The exception. - void WarnException(string msg, Exception ex); - - /// - /// Logs a message at ERROR level. - /// - /// The message. - void Error(string msg); - - /// - /// Logs a message at ERROR level with specified parameters. - /// - /// The format. - /// The arguments. - void ErrorFormat(string format, params object[] args); - - /// - /// Logs a message at ERROR level with specified exception. - /// - /// The message. - /// The exception. - void ErrorException(string msg, Exception ex); - } -} diff --git a/Confuser.Core/Marker.cs b/Confuser.Core/Marker.cs index 889ae036d..95709a058 100644 --- a/Confuser.Core/Marker.cs +++ b/Confuser.Core/Marker.cs @@ -7,6 +7,7 @@ using Confuser.Core.Project; using Confuser.Core.Project.Patterns; using dnlib.DotNet; +using Microsoft.Extensions.Logging; namespace Confuser.Core { using Rules = Dictionary; @@ -83,7 +84,7 @@ public static StrongNamePublicKey LoadSNPubKey(ConfuserContext context, string p return new StrongNamePublicKey(path); } catch (Exception ex) { - context.Logger.ErrorException("Cannot load the Strong Name Public Key located at: " + path, ex); + context.Logger.LogError(ex, "Cannot load the Strong Name Public Key located at: " + path); throw new ConfuserException(ex); } } @@ -117,7 +118,7 @@ public static StrongNameKey LoadSNKey(ConfuserContext context, string path, stri return new StrongNameKey(path); } catch (Exception ex) { - context.Logger.ErrorException("Cannot load the Strong Name Key located at: " + path, ex); + context.Logger.LogError(ex, "Cannot load the Strong Name Key located at: " + path); throw new ConfuserException(ex); } } @@ -134,11 +135,11 @@ protected internal virtual MarkerResult MarkProject(ConfuserProject proj, Confus if (proj.Packer != null) { if (!packers.ContainsKey(proj.Packer.Id)) { - context.Logger.ErrorFormat("Cannot find packer with ID '{0}'.", proj.Packer.Id); + context.Logger.LogError("Cannot find packer with ID '{0}'.", proj.Packer.Id); throw new ConfuserException(null); } if (proj.Debug) - context.Logger.Warn("Generated Debug symbols might not be usable with packers!"); + context.Logger.LogWarning("Generated Debug symbols might not be usable with packers!"); packer = packers[proj.Packer.Id]; packerParams = new Dictionary(proj.Packer, StringComparer.OrdinalIgnoreCase); @@ -167,7 +168,7 @@ protected internal virtual MarkerResult MarkProject(ConfuserProject proj, Confus } foreach (var module in modules) { - context.Logger.InfoFormat("Loading '{0}'...", module.Item1.Path); + context.Logger.LogInformation("Loading '{0}'...", module.Item1.Path); Rules rules = ParseRules(proj, module.Item1, context); context.Annotations.Set(module.Item2, SNKey, LoadSNKey(context, module.Item1.SNKeyPath == null ? null : Path.Combine(proj.BaseDirectory, module.Item1.SNKeyPath), module.Item1.SNKeyPassword)); @@ -219,12 +220,12 @@ protected Rules ParseRules(ConfuserProject proj, ProjectModule module, ConfuserC ret.Add(rule, parser.Parse(rule.Pattern)); } catch (InvalidPatternException ex) { - context.Logger.ErrorFormat("Invalid rule pattern: " + rule.Pattern + ": {0}", ex.Message); + context.Logger.LogError("Invalid rule pattern: " + rule.Pattern + ": {0}", ex.Message); throw new ConfuserException(ex); } foreach (var setting in rule) { if (!protections.ContainsKey(setting.Id)) { - context.Logger.ErrorFormat("Cannot find protection with ID '{0}'.", setting.Id); + context.Logger.LogError("Cannot find protection with ID '{0}'.", setting.Id); throw new ConfuserException(null); } } diff --git a/Confuser.Core/MelLoggerAdapter.cs b/Confuser.Core/MelLoggerAdapter.cs deleted file mode 100644 index a60c6e878..000000000 --- a/Confuser.Core/MelLoggerAdapter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using MelILogger = Microsoft.Extensions.Logging.ILogger; -using MelLogLevel = Microsoft.Extensions.Logging.LogLevel; - -namespace Confuser.Core { - /// - /// Adapts a (Microsoft.Extensions.Logging) to the - /// interface used internally by Confuser. - /// - public sealed class MelLoggerAdapter : ILogger { - readonly MelILogger inner; - - public MelLoggerAdapter(MelILogger logger) { - inner = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - public void Debug(string msg) => inner.Log(MelLogLevel.Debug, msg); - - public void DebugFormat(string format, params object[] args) => - inner.Log(MelLogLevel.Debug, format, args); - - public void Info(string msg) => inner.Log(MelLogLevel.Information, msg); - - public void InfoFormat(string format, params object[] args) => - inner.Log(MelLogLevel.Information, format, args); - - public void Warn(string msg) => inner.Log(MelLogLevel.Warning, msg); - - public void WarnFormat(string format, params object[] args) => - inner.Log(MelLogLevel.Warning, format, args); - - public void WarnException(string msg, Exception ex) => - inner.Log(MelLogLevel.Warning, ex, msg); - - public void Error(string msg) => inner.Log(MelLogLevel.Error, msg); - - public void ErrorFormat(string format, params object[] args) => - inner.Log(MelLogLevel.Error, format, args); - - public void ErrorException(string msg, Exception ex) => - inner.Log(MelLogLevel.Error, ex, msg); - } -} diff --git a/Confuser.Core/NullLogger.cs b/Confuser.Core/NullLogger.cs deleted file mode 100644 index c518d69fd..000000000 --- a/Confuser.Core/NullLogger.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; - -namespace Confuser.Core { - /// - /// An implementation that doesn't actually do any logging. - /// - internal sealed class NullLogger : ILogger { - /// - /// The singleton instance of . - /// - public static readonly NullLogger Instance = new NullLogger(); - - NullLogger() { } - - /// - public void Debug(string msg) { } - - /// - public void DebugFormat(string format, params object[] args) { } - - /// - public void Info(string msg) { } - - /// - public void InfoFormat(string format, params object[] args) { } - - /// - public void Warn(string msg) { } - - /// - public void WarnFormat(string format, params object[] args) { } - - /// - public void WarnException(string msg, Exception ex) { } - - /// - public void Error(string msg) { } - - /// - public void ErrorFormat(string format, params object[] args) { } - - /// - public void ErrorException(string msg, Exception ex) { } - } -} diff --git a/Confuser.Core/ObfAttrMarker.cs b/Confuser.Core/ObfAttrMarker.cs index 1c8cb068c..8dbe5ecca 100644 --- a/Confuser.Core/ObfAttrMarker.cs +++ b/Confuser.Core/ObfAttrMarker.cs @@ -8,6 +8,7 @@ using Confuser.Core.Project; using Confuser.Core.Project.Patterns; using dnlib.DotNet; +using Microsoft.Extensions.Logging; namespace Confuser.Core { using Rules = Dictionary; @@ -221,7 +222,7 @@ bool ToInfo(ObfuscationAttributeInfo attr, out ProtectionSettingsInfo info) { } if (!ok) { - context.Logger.WarnFormat("Ignoring rule '{0}' in {1}.", info.Settings, attr.Owner); + context.Logger.LogWarning("Ignoring rule '{0}' in {1}.", info.Settings, attr.Owner); return false; } @@ -299,7 +300,7 @@ protected internal override MarkerResult MarkProject(ConfuserProject proj, Confu if (proj.Packer != null) { if (!packers.ContainsKey(proj.Packer.Id)) { - context.Logger.ErrorFormat("Cannot find packer with ID '{0}'.", proj.Packer.Id); + context.Logger.LogError("Cannot find packer with ID '{0}'.", proj.Packer.Id); throw new ConfuserException(null); } @@ -325,20 +326,20 @@ protected internal override MarkerResult MarkProject(ConfuserProject proj, Confu modules.Add(Tuple.Create(module, modDef)); } catch (BadImageFormatException ex) { - context.Logger.ErrorFormat("Failed to load \"{0}\" - Assembly does not appear to be a .NET assembly: \"{1}\".", module.Path, ex.Message); + context.Logger.LogError("Failed to load \"{0}\" - Assembly does not appear to be a .NET assembly: \"{1}\".", module.Path, ex.Message); if (module.Path.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { var dllPath = Path.ChangeExtension(module.Path, ".dll"); var fullDllPath = Path.Combine(proj.BaseDirectory, dllPath); if (File.Exists(fullDllPath)) - context.Logger.ErrorFormat("Hint: .NET 6+ apps use a native host .exe — try obfuscating \"{0}\" instead.", dllPath); + context.Logger.LogError("Hint: .NET 6+ apps use a native host .exe — try obfuscating \"{0}\" instead.", dllPath); else - context.Logger.Error("Hint: For .NET 6+ apps, obfuscate the .dll file, not the .exe (which is a native host stub)."); + context.Logger.LogError("Hint: For .NET 6+ apps, obfuscate the .dll file, not the .exe (which is a native host stub)."); } throw new ConfuserException(ex); } } foreach (var module in modules) { - context.Logger.InfoFormat("Loading '{0}'...", module.Item1.Path); + context.Logger.LogInformation("Loading '{0}'...", module.Item1.Path); Rules rules = ParseRules(proj, module.Item1, context); MarkModule(module.Item1, module.Item2, rules, module == modules[0]); @@ -351,7 +352,7 @@ protected internal override MarkerResult MarkProject(ConfuserProject proj, Confu } if (proj.Debug && proj.Packer != null) - context.Logger.Warn("Generated Debug symbols might not be usable with packers!"); + context.Logger.LogWarning("Generated Debug symbols might not be usable with packers!"); return new MarkerResult(modules.Select(module => module.Item2).ToList(), packer, extModules); } @@ -384,7 +385,7 @@ ProtectionSettingsInfo AddRule(ObfuscationAttributeInfo attr, List @@ -82,7 +83,7 @@ protected void ProtectStub(ConfuserContext context, string fileName, byte[] modu }, context.token).Wait(); } catch (AggregateException ex) { - context.Logger.Error("Failed to protect packer stub."); + context.Logger.LogError("Failed to protect packer stub."); throw new ConfuserException(ex); } @@ -96,7 +97,7 @@ protected void ProtectStub(ConfuserContext context, string fileName, byte[] modu } } catch (IOException ex) { - context.Logger.WarnException("Failed to remove temporary files of packer.", ex); + context.Logger.LogWarning(ex, "Failed to remove temporary files of packer."); } } } @@ -104,9 +105,9 @@ protected void ProtectStub(ConfuserContext context, string fileName, byte[] modu internal class PackerProgressReporter : IProgressReporter { readonly IProgressReporter baseReporter; - readonly ILogger baseLogger; + readonly Microsoft.Extensions.Logging.ILogger baseLogger; - public PackerProgressReporter(IProgressReporter baseReporter, ILogger baseLogger) { + public PackerProgressReporter(IProgressReporter baseReporter, Microsoft.Extensions.Logging.ILogger baseLogger) { this.baseReporter = baseReporter; this.baseLogger = baseLogger; } @@ -122,7 +123,7 @@ public void EndProgress() { public void Finish(bool successful) { if (!successful) throw new ConfuserException(null); - baseLogger.Info("Finish protecting packer stub."); + baseLogger.LogInformation("Finish protecting packer stub."); } } diff --git a/Confuser.Core/PluginDiscovery.cs b/Confuser.Core/PluginDiscovery.cs index 642dafcf8..c42de92d6 100644 --- a/Confuser.Core/PluginDiscovery.cs +++ b/Confuser.Core/PluginDiscovery.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using Microsoft.Extensions.Logging; namespace Confuser.Core { /// @@ -64,7 +65,7 @@ protected static void AddPlugins( protections.Add((Protection)Activator.CreateInstance(i)); } catch (Exception ex) { - context.Logger.ErrorException("Failed to instantiate protection '" + i.Name + "'.", ex); + context.Logger.LogError(ex, "Failed to instantiate protection '" + i.Name + "'."); } } else if (typeof(Packer).IsAssignableFrom(i)) { @@ -72,7 +73,7 @@ protected static void AddPlugins( packers.Add((Packer)Activator.CreateInstance(i)); } catch (Exception ex) { - context.Logger.ErrorException("Failed to instantiate packer '" + i.Name + "'.", ex); + context.Logger.LogError(ex, "Failed to instantiate packer '" + i.Name + "'."); } } else if (typeof(ConfuserComponent).IsAssignableFrom(i)) { @@ -80,7 +81,7 @@ protected static void AddPlugins( components.Add((ConfuserComponent)Activator.CreateInstance(i)); } catch (Exception ex) { - context.Logger.ErrorException("Failed to instantiate component '" + i.Name + "'.", ex); + context.Logger.LogError(ex, "Failed to instantiate component '" + i.Name + "'."); } } } @@ -103,7 +104,7 @@ protected virtual void GetPluginsInternal( AddPlugins(context, protections, packers, components, protAsm); } catch (Exception ex) { - context.Logger.WarnException("Failed to load built-in protections.", ex); + context.Logger.LogWarning(ex, "Failed to load built-in protections."); } try { @@ -111,7 +112,7 @@ protected virtual void GetPluginsInternal( AddPlugins(context, protections, packers, components, renameAsm); } catch (Exception ex) { - context.Logger.WarnException("Failed to load renamer.", ex); + context.Logger.LogWarning(ex, "Failed to load renamer."); } try { @@ -119,7 +120,7 @@ protected virtual void GetPluginsInternal( AddPlugins(context, protections, packers, components, renameAsm); } catch (Exception ex) { - context.Logger.WarnException("Failed to load dynamic cipher library.", ex); + context.Logger.LogWarning(ex, "Failed to load dynamic cipher library."); } foreach (string pluginPath in context.Project.PluginPaths) { @@ -129,7 +130,7 @@ protected virtual void GetPluginsInternal( AddPlugins(context, protections, packers, components, plugin); } catch (Exception ex) { - context.Logger.WarnException("Failed to load plugin '" + pluginPath + "'.", ex); + context.Logger.LogWarning(ex, "Failed to load plugin '" + pluginPath + "'."); } } } diff --git a/Confuser.Core/ProtectionPipeline.cs b/Confuser.Core/ProtectionPipeline.cs index 79da655dc..ba798f559 100644 --- a/Confuser.Core/ProtectionPipeline.cs +++ b/Confuser.Core/ProtectionPipeline.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using dnlib.DotNet; +using Microsoft.Extensions.Logging; namespace Confuser.Core { /// @@ -127,14 +128,14 @@ public T FindPhase() where T : ProtectionPhase { internal void ExecuteStage(PipelineStage stage, Action func, Func> targets, ConfuserContext context) { foreach (ProtectionPhase pre in preStage[stage]) { context.CheckCancellation(); - context.Logger.DebugFormat("Executing '{0}' phase...", pre.Name); + context.Logger.LogDebug("Executing '{0}' phase...", pre.Name); pre.Execute(context, new ProtectionParameters(pre.Parent, Filter(context, targets(), pre))); } context.CheckCancellation(); func(context); context.CheckCancellation(); foreach (ProtectionPhase post in postStage[stage]) { - context.Logger.DebugFormat("Executing '{0}' phase...", post.Name); + context.Logger.LogDebug("Executing '{0}' phase...", post.Name); post.Execute(context, new ProtectionParameters(post.Parent, Filter(context, targets(), post))); context.CheckCancellation(); } @@ -170,7 +171,7 @@ static IList Filter(ConfuserContext context, IList targets ProtectionSettings parameters = ProtectionParameters.GetParameters(context, def); Debug.Assert(parameters != null); if (parameters == null) { - context.Logger.ErrorFormat("'{0}' not marked for obfuscation, possibly a bug.", def); + context.Logger.LogError("'{0}' not marked for obfuscation, possibly a bug.", def); throw new ConfuserException(null); } return parameters.ContainsKey(phase.Parent); diff --git a/Confuser.Core/WatermarkingProtection.cs b/Confuser.Core/WatermarkingProtection.cs index 1b1cdb967..7fff32205 100644 --- a/Confuser.Core/WatermarkingProtection.cs +++ b/Confuser.Core/WatermarkingProtection.cs @@ -2,6 +2,7 @@ using Confuser.Core.Services; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Microsoft.Extensions.Logging; namespace Confuser.Core { public sealed class WatermarkingProtection : Protection { @@ -45,7 +46,7 @@ public WatermarkingPhase(ConfuserComponent parent) : base(parent) { } protected internal override void Execute(ConfuserContext context, ProtectionParameters parameters) { var marker = context.Registry.GetService(); - context.Logger.Debug("Watermarking..."); + context.Logger.LogDebug("Watermarking..."); foreach (var module in parameters.Targets.OfType()) { var attrRef = module.CorLibTypes.GetTypeRef("System", "Attribute"); var attrType = module.FindNormal("ConfusedByAttribute"); diff --git a/Confuser.MSBuild.Tasks/ConfuseTask.cs b/Confuser.MSBuild.Tasks/ConfuseTask.cs index 8d4fdff90..02986a3f0 100644 --- a/Confuser.MSBuild.Tasks/ConfuseTask.cs +++ b/Confuser.MSBuild.Tasks/ConfuseTask.cs @@ -27,7 +27,7 @@ public override bool Execute() { var progressReporter = new MSBuildProgressReporter(); var parameters = new ConfuserParameters { Project = project, - Logger = new MSBuildLogger(Log), + Logger = new MSBuildMelLogger(Log), ProgressReporter = progressReporter }; diff --git a/Confuser.MSBuild.Tasks/MSBuildLogger.cs b/Confuser.MSBuild.Tasks/MSBuildLogger.cs index bc92e200c..8f1f7c81f 100644 --- a/Confuser.MSBuild.Tasks/MSBuildLogger.cs +++ b/Confuser.MSBuild.Tasks/MSBuildLogger.cs @@ -1,58 +1,51 @@ -using System; -using Confuser.Core; +using System; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using ILogger = Confuser.Core.ILogger; +using Microsoft.Extensions.Logging; namespace Confuser.MSBuild.Tasks { - internal sealed class MSBuildLogger : ILogger { + internal sealed class MSBuildMelLogger : Microsoft.Extensions.Logging.ILogger { private readonly TaskLoggingHelper loggingHelper; - internal MSBuildLogger(TaskLoggingHelper loggingHelper) => + internal MSBuildMelLogger(TaskLoggingHelper loggingHelper) => this.loggingHelper = loggingHelper ?? throw new ArgumentNullException(nameof(loggingHelper)); - void ILogger.Debug(string msg) => loggingHelper.LogMessage(MessageImportance.Low, "[DEBUG] " + msg); - - void ILogger.DebugFormat(string format, params object[] args) { - loggingHelper.LogMessage(MessageImportance.Low, "[DEBUG] " + format, args); - } - - void ILogger.Error(string msg) { - loggingHelper.LogError(msg); - } - - void ILogger.ErrorException(string msg, Exception ex) { - loggingHelper.LogError(msg); - loggingHelper.LogErrorFromException(ex); - } - - void ILogger.ErrorFormat(string format, params object[] args) { - loggingHelper.LogError(format, args); - } - - void ILogger.Info(string msg) => loggingHelper.LogMessage(MessageImportance.Normal, msg); - - void ILogger.InfoFormat(string format, params object[] args) => - loggingHelper.LogMessage(MessageImportance.Normal, format, args); - - void ILogger.Warn(string msg) => loggingHelper.LogWarning(msg); - - void ILogger.WarnException(string msg, Exception ex) { - loggingHelper.LogWarning(msg); - loggingHelper.LogWarningFromException(ex); + public IDisposable BeginScope(TState state) => null; + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, + Exception exception, Func formatter) { + var message = formatter(state, exception); + switch (logLevel) { + case LogLevel.Trace: + case LogLevel.Debug: + loggingHelper.LogMessage(MessageImportance.Low, message); + break; + case LogLevel.Information: + loggingHelper.LogMessage(MessageImportance.Normal, message); + break; + case LogLevel.Warning: + loggingHelper.LogWarning(message); + if (exception != null) loggingHelper.LogWarningFromException(exception); + break; + case LogLevel.Error: + case LogLevel.Critical: + loggingHelper.LogError(message); + if (exception != null) loggingHelper.LogErrorFromException(exception); + break; + } } - - void ILogger.WarnFormat(string format, params object[] args) => loggingHelper.LogWarning(format, args); } - internal sealed class MSBuildProgressReporter : IProgressReporter { + internal sealed class MSBuildProgressReporter : Confuser.Core.IProgressReporter { internal bool HasError { get; private set; } - void IProgressReporter.Progress(int progress, int overall) { } + void Confuser.Core.IProgressReporter.Progress(int progress, int overall) { } - void IProgressReporter.EndProgress() { } + void Confuser.Core.IProgressReporter.EndProgress() { } - void IProgressReporter.Finish(bool successful) { + void Confuser.Core.IProgressReporter.Finish(bool successful) { if (!successful) { HasError = true; } diff --git a/Confuser.Protections/AntiTamper/JITMode.cs b/Confuser.Protections/AntiTamper/JITMode.cs index 7c3a007d6..86a919805 100644 --- a/Confuser.Protections/AntiTamper/JITMode.cs +++ b/Confuser.Protections/AntiTamper/JITMode.cs @@ -8,6 +8,7 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.Renamer; +using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; @@ -154,11 +155,11 @@ public void HandleMD(AntiTamperProtection parent, ConfuserContext context, Prote void OnWriterEvent(object sender, ModuleWriterEventArgs e) { var writer = (ModuleWriterBase)sender; if (e.Event == ModuleWriterEvent.MDBeginWriteMethodBodies) { - context.Logger.Debug("Extracting method bodies..."); + context.Logger.LogDebug("Extracting method bodies..."); CreateSection(writer); } else if (e.Event == ModuleWriterEvent.BeginStrongNameSign) { - context.Logger.Debug("Encrypting method section..."); + context.Logger.LogDebug("Encrypting method section..."); EncryptSection(writer); } } diff --git a/Confuser.Protections/Compress/Compressor.cs b/Confuser.Protections/Compress/Compressor.cs index 83ade748b..ad6b7d5a1 100644 --- a/Confuser.Protections/Compress/Compressor.cs +++ b/Confuser.Protections/Compress/Compressor.cs @@ -10,6 +10,7 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.Protections.Compress; +using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; @@ -50,7 +51,7 @@ protected override void PopulatePipeline(ProtectionPipeline pipeline) { protected override void Pack(ConfuserContext context, ProtectionParameters parameters) { var ctx = context.Annotations.Get(context, ContextKey); if (ctx == null) { - context.Logger.Error("No executable module!"); + context.Logger.LogError("No executable module!"); throw new ConfuserException(null); } @@ -222,7 +223,7 @@ void InjectStub(ConfuserContext context, CompressorContext compCtx, ProtectionPa } compCtx.Deriver.Init(context, random); - context.Logger.Debug("Encrypting modules..."); + context.Logger.LogDebug("Encrypting modules..."); // Main MethodDef entryPoint = defs.OfType().Single(method => method.Name == "Main"); diff --git a/Confuser.Protections/Compress/ExtractPhase.cs b/Confuser.Protections/Compress/ExtractPhase.cs index 24fd835b0..54e50cab1 100644 --- a/Confuser.Protections/Compress/ExtractPhase.cs +++ b/Confuser.Protections/Compress/ExtractPhase.cs @@ -5,6 +5,7 @@ using System.Text; using Confuser.Core; using dnlib.DotNet; +using Microsoft.Extensions.Logging; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; @@ -29,7 +30,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa if (context.Annotations.Get(context, Compressor.ContextKey) != null) { if (isExe) { - context.Logger.Error("Too many executable modules!"); + context.Logger.LogError("Too many executable modules!"); throw new ConfuserException(null); } return; diff --git a/Confuser.Protections/ControlFlow/ControlFlowPhase.cs b/Confuser.Protections/ControlFlow/ControlFlowPhase.cs index b154ea488..5c42b0934 100644 --- a/Confuser.Protections/ControlFlow/ControlFlowPhase.cs +++ b/Confuser.Protections/ControlFlow/ControlFlowPhase.cs @@ -4,6 +4,7 @@ using Confuser.Core; using Confuser.Core.Services; using Confuser.DynCipher; +using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; @@ -90,7 +91,7 @@ static ManglerBase GetMangler(CFType type) { void ProcessMethod(CilBody body, CFContext ctx) { uint maxStack; if (!MaxStackCalculator.GetMaxStack(body.Instructions, body.ExceptionHandlers, out maxStack)) { - ctx.Context.Logger.Error("Failed to calcuate maxstack."); + ctx.Context.Logger.LogError("Failed to calcuate maxstack."); throw new ConfuserException(null); } body.MaxStack = (ushort)maxStack; diff --git a/Confuser.Protections/HardeningPhase.cs b/Confuser.Protections/HardeningPhase.cs index 17dabdfe4..0e5defa8c 100644 --- a/Confuser.Protections/HardeningPhase.cs +++ b/Confuser.Protections/HardeningPhase.cs @@ -4,6 +4,7 @@ using Confuser.Core.Services; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Microsoft.Extensions.Logging; namespace Confuser.Protections { internal sealed class HardeningPhase : ProtectionPhase { @@ -31,7 +32,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa private static void HardenMethod(ConfuserContext context, ModuleDef module) { var cctor = module.GlobalType.FindStaticConstructor(); if (cctor == null) { - context.Logger.Debug("No .cctor containing protection code found. Nothing to do."); + context.Logger.LogDebug("No .cctor containing protection code found. Nothing to do."); return; } diff --git a/Confuser.Protections/Resources/InjectPhase.cs b/Confuser.Protections/Resources/InjectPhase.cs index 3d086568b..cc73c83c5 100644 --- a/Confuser.Protections/Resources/InjectPhase.cs +++ b/Confuser.Protections/Resources/InjectPhase.cs @@ -7,6 +7,7 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.DynCipher; +using Microsoft.Extensions.Logging; using Confuser.Renamer; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -27,7 +28,7 @@ public override string Name { protected override void Execute(ConfuserContext context, ProtectionParameters parameters) { if (parameters.Targets.Any()) { if (!UTF8String.IsNullOrEmpty(context.CurrentModule.Assembly.Culture)) { - context.Logger.DebugFormat("Skipping resource encryption for satellite assembly '{0}'.", + context.Logger.LogDebug("Skipping resource encryption for satellite assembly '{0}'.", context.CurrentModule.Assembly.FullName); return; } diff --git a/Confuser.Protections/Resources/MDPhase.cs b/Confuser.Protections/Resources/MDPhase.cs index 056c75b41..49786d0aa 100644 --- a/Confuser.Protections/Resources/MDPhase.cs +++ b/Confuser.Protections/Resources/MDPhase.cs @@ -7,6 +7,7 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.Renamer; +using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; @@ -28,7 +29,7 @@ void OnWriterEvent(object sender, ModuleWriterEventArgs e) { var writer = (ModuleWriterBase)sender; if (e.Event == ModuleWriterEvent.MDBeginAddResources) { ctx.Context.CheckCancellation(); - ctx.Context.Logger.Debug("Encrypting resources..."); + ctx.Context.Logger.LogDebug("Encrypting resources..."); bool hasPacker = ctx.Context.Packer != null; List resources = ctx.Module.Resources.OfType().ToList(); diff --git a/Confuser.Renamer/AnalyzePhase.cs b/Confuser.Renamer/AnalyzePhase.cs index 334ff93fd..94e9f7753 100644 --- a/Confuser.Renamer/AnalyzePhase.cs +++ b/Confuser.Renamer/AnalyzePhase.cs @@ -6,6 +6,7 @@ using Confuser.Core.Services; using Confuser.Renamer.Analyzers; using dnlib.DotNet; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer { internal class AnalyzePhase : ProtectionPhase { @@ -34,7 +35,7 @@ void ParseParameters(IDnlibDef def, ConfuserContext context, NameService service protected override void Execute(ConfuserContext context, ProtectionParameters parameters) { var service = (NameService)context.Registry.GetService(); - context.Logger.Debug("Building VTables & identifier list..."); + context.Logger.LogDebug("Building VTables & identifier list..."); foreach (ModuleDef moduleDef in parameters.Targets.OfType()) moduleDef.EnableTypeDefFindCache = true; @@ -56,7 +57,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa context.CheckCancellation(); } - context.Logger.Debug("Analyzing..."); + context.Logger.LogDebug("Analyzing..."); RegisterRenamers(context, service); IList renamers = service.Renamers; foreach (IDnlibDef def in parameters.Targets.WithProgress(context.ProgressReporter)) { @@ -106,35 +107,35 @@ void RegisterRenamers(ConfuserContext context, NameService service) { if (wpf) { var wpfAnalyzer = new WPFAnalyzer(); - context.Logger.Debug("WPF found, enabling compatibility."); + context.Logger.LogDebug("WPF found, enabling compatibility."); service.Renamers.Add(wpfAnalyzer); if (caliburn) { - context.Logger.Debug("Caliburn.Micro found, enabling compatibility."); + context.Logger.LogDebug("Caliburn.Micro found, enabling compatibility."); service.Renamers.Add(new CaliburnAnalyzer(wpfAnalyzer)); } } if (winforms) { var winformsAnalyzer = new WinFormsAnalyzer(); - context.Logger.Debug("WinForms found, enabling compatibility."); + context.Logger.LogDebug("WinForms found, enabling compatibility."); service.Renamers.Add(winformsAnalyzer); } if (json) { var jsonAnalyzer = new JsonAnalyzer(); - context.Logger.Debug("Newtonsoft.Json found, enabling compatibility."); + context.Logger.LogDebug("Newtonsoft.Json found, enabling compatibility."); service.Renamers.Add(jsonAnalyzer); } if (visualBasic) { var vbAnalyzer = new VisualBasicRuntimeAnalyzer(); - context.Logger.Debug("Visual Basic Embedded Runtime found, enabling compatibility."); + context.Logger.LogDebug("Visual Basic Embedded Runtime found, enabling compatibility."); service.Renamers.Add(vbAnalyzer); } if (vsComposition) { var analyzer = new VsCompositionAnalyzer(); - context.Logger.Debug("Visual Studio Composition found, enabling compatibility."); + context.Logger.LogDebug("Visual Studio Composition found, enabling compatibility."); service.Renamers.Add(analyzer); } } diff --git a/Confuser.Renamer/Analyzers/CallSiteAnalyzer.cs b/Confuser.Renamer/Analyzers/CallSiteAnalyzer.cs index b4cf6f007..0a7cca3fa 100644 --- a/Confuser.Renamer/Analyzers/CallSiteAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/CallSiteAnalyzer.cs @@ -4,6 +4,7 @@ using Confuser.Renamer.References; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.Analyzers { internal sealed class CallSiteAnalyzer : IRenamer { @@ -61,7 +62,7 @@ private static void HandleBinderInvokeMember(ConfuserContext context, MethodDef BuildMemberReferences(context, typeDefOrRef, boundMemberName, nameInstruction); } else { - context.Logger.WarnFormat( + context.Logger.LogWarning( "Failed to resolve type for dynamic invoke member in {0} - blocking all members with name {1} from renaming.", method, boundMemberName); diff --git a/Confuser.Renamer/Analyzers/ReflectionAnalyzer.cs b/Confuser.Renamer/Analyzers/ReflectionAnalyzer.cs index 5deeeaa4a..f6bbc267c 100644 --- a/Confuser.Renamer/Analyzers/ReflectionAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/ReflectionAnalyzer.cs @@ -7,7 +7,7 @@ using Confuser.Renamer.References; using dnlib.DotNet; using dnlib.DotNet.Emit; -using ILogger = Confuser.Core.ILogger; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.Analyzers { /// @@ -20,7 +20,7 @@ void IRenamer.Analyze(ConfuserContext context, INameService service, ProtectionP Analyze(service, context.Registry.GetService(), context.Modules.Cast().ToArray(), context.Logger, method); } - public void Analyze(INameService nameService, ITraceService traceService, IReadOnlyList moduleDefs, ILogger logger, MethodDef method) { + public void Analyze(INameService nameService, ITraceService traceService, IReadOnlyList moduleDefs, Microsoft.Extensions.Logging.ILogger logger, MethodDef method) { if (!method.HasBody) return; MethodTrace methodTrace = null; @@ -49,7 +49,7 @@ MethodTrace GetMethodTrace() { var trace = GetMethodTrace(); var arguments = trace.TraceArguments(instr); if (arguments == null) { - logger.WarnFormat(Resources.ReflectionAnalyzer_Analyze_TracingArgumentsFailed, calledMethod.FullName, method.FullName); + logger.LogWarning(Resources.ReflectionAnalyzer_Analyze_TracingArgumentsFailed, calledMethod.FullName, method.FullName); } else if (arguments.Length >= 2) { var types = GetReferencedTypes(method.Body.Instructions[arguments[0]], method, trace); diff --git a/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs b/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs index 9f58d66ce..230ddb5eb 100644 --- a/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using Confuser.Core; using Confuser.Renamer.Properties; +using Microsoft.Extensions.Logging; using Confuser.Renamer.References; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -23,7 +24,7 @@ public void Analyze(ConfuserContext context, INameService service, ProtectionPar string nameAsmName = asmName.Substring(0, asmName.Length - ".resources".Length); ModuleDef mainModule = context.Modules.SingleOrDefault(mod => mod.Assembly.Name == nameAsmName); if (mainModule == null) { - context.Logger.ErrorFormat("Could not find main assembly of satellite assembly '{0}'.", module.Assembly.FullName); + context.Logger.LogError("Could not find main assembly of satellite assembly '{0}'.", module.Assembly.FullName); throw new ConfuserException(null); } @@ -35,7 +36,7 @@ public void Analyze(ConfuserContext context, INameService service, ProtectionPar TypeDef type = mainModule.FindReflection(typeName); if (type == null) { - context.Logger.WarnFormat(Resources.ResourceAnalyzer_Analyze_CouldNotFindResourceType, typeName); + context.Logger.LogWarning(Resources.ResourceAnalyzer_Analyze_CouldNotFindResourceType, typeName); continue; } string format = $"{{0}}.{culture}.resources"; @@ -68,7 +69,7 @@ public void Analyze(ConfuserContext context, INameService service, ProtectionPar } if (type == null) { - context.Logger.WarnFormat(Resources.ResourceAnalyzer_Analyze_CouldNotFindResourceType, typeName); + context.Logger.LogWarning(Resources.ResourceAnalyzer_Analyze_CouldNotFindResourceType, typeName); continue; } service.ReduceRenameMode(type, RenameMode.Reflection); diff --git a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs index 15a3e94f1..4fa369a79 100644 --- a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs @@ -5,6 +5,7 @@ using Confuser.Core; using Confuser.Renamer.References; using dnlib.DotNet; +using Microsoft.Extensions.Logging; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; @@ -16,7 +17,7 @@ void IRenamer.Analyze(ConfuserContext context, INameService service, ProtectionP Analyze(service, context.Modules, context.Logger, moduleDef); } - public static void Analyze(INameService service, ICollection modules, Core.ILogger logger, ModuleDefMD module) { + public static void Analyze(INameService service, ICollection modules, Microsoft.Extensions.Logging.ILogger logger, ModuleDefMD module) { // MemberRef var table = module.TablesStream.Get(Table.Method); var len = table.Rows; @@ -75,7 +76,7 @@ public static void Analyze(INameService service, ICollection module foreach (var arg in attr.NamedArguments) { var memberDef = FindArgumentMemberDef(arg, attrType); if (memberDef == null) - logger.WarnFormat( + logger.LogWarning( arg.IsField ? "Failed to resolve CA field '{0}::{1} : {2}'." : "Failed to resolve CA property '{0}::{1} : {2}'.", attrType, arg.Name, arg.Type); else diff --git a/Confuser.Renamer/Analyzers/WPFAnalyzer.cs b/Confuser.Renamer/Analyzers/WPFAnalyzer.cs index ebf49dceb..51840bfd9 100644 --- a/Confuser.Renamer/Analyzers/WPFAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/WPFAnalyzer.cs @@ -14,6 +14,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.IO; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.Analyzers { internal class WPFAnalyzer : IRenamer { @@ -47,7 +48,7 @@ public void PreRename(ConfuserContext context, INameService service, ProtectionP var renameMode = parameters.GetParameter(context, def, "renXamlMode", RenameMode.Letters); if (renameMode < RenameMode.Letters) { var illegalValues = Enum.GetValues(typeof(RenameMode)).Cast().Where(m => m < RenameMode.Letters); - context.Logger.Warn("The renaming modes " + String.Join(", ", illegalValues) + " are not allowed for XAML resources. Letters mode will be used."); + context.Logger.LogWarning("The renaming modes " + String.Join(", ", illegalValues) + " are not allowed for XAML resources. Letters mode will be used."); renameMode = RenameMode.Letters; } @@ -82,7 +83,7 @@ public void PreRename(ConfuserContext context, INameService service, ProtectionP string decodedNewName = decodedDirectory + fileName; string encodedNewName = encodedDirectory + fileName; - context.Logger.Debug(String.Format("Preserving virtual paths. Replaced {0} with {1}", decodedName, decodedNewName)); + context.Logger.LogDebug(String.Format("Preserving virtual paths. Replaced {0} with {1}", decodedName, decodedNewName)); bool renameOk = references.All(r => r.CanRename(module, decodedName, decodedNewName) || r.CanRename(module, encodedName, encodedNewName)); @@ -199,7 +200,7 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth operand = match.Groups[2].Value; } else if (operand.Contains("/")) - context.Logger.WarnFormat("Fail to extract XAML name from '{0}'.", instr.Operand); + context.Logger.LogWarning("Fail to extract XAML name from '{0}'.", instr.Operand); var reference = new BAMLStringReference(refModule, instr); operand = WebUtility.UrlDecode(operand.TrimStart('/')); @@ -222,14 +223,14 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth int[] args = trace.TraceArguments(instrInfo.Item2); if (args == null) { if (!erred) - context.Logger.WarnFormat("Failed to extract dependency property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract dependency property name in '{0}'.", method.FullName); erred = true; continue; } Instruction ldstr = method.Body.Instructions[args[0]]; if (ldstr.OpCode.Code != Code.Ldstr) { if (!erred) - context.Logger.WarnFormat("Failed to extract dependency property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract dependency property name in '{0}'.", method.FullName); erred = true; continue; } @@ -270,10 +271,10 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth } if (!found) { if (instrInfo.Item1) - context.Logger.WarnFormat("Failed to find the accessors of attached dependency property '{0}' in type '{1}'.", + context.Logger.LogWarning("Failed to find the accessors of attached dependency property '{0}' in type '{1}'.", name, declType.FullName); else - context.Logger.WarnFormat("Failed to find the CLR property of normal dependency property '{0}' in type '{1}'.", + context.Logger.LogWarning("Failed to find the CLR property of normal dependency property '{0}' in type '{1}'.", name, declType.FullName); } } @@ -283,14 +284,14 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth int[] args = trace.TraceArguments(instr); if (args == null) { if (!erred) - context.Logger.WarnFormat("Failed to extract routed event name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract routed event name in '{0}'.", method.FullName); erred = true; continue; } Instruction ldstr = method.Body.Instructions[args[0]]; if (ldstr.OpCode.Code != Code.Ldstr) { if (!erred) - context.Logger.WarnFormat("Failed to extract routed event name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract routed event name in '{0}'.", method.FullName); erred = true; continue; } @@ -300,7 +301,7 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth EventDef eventDef = null; if ((eventDef = declType.FindEvent(name)) == null) { - context.Logger.WarnFormat("Failed to find the CLR event of routed event '{0}' in type '{1}'.", + context.Logger.LogWarning("Failed to find the CLR event of routed event '{0}' in type '{1}'.", name, declType.FullName); continue; } diff --git a/Confuser.Renamer/Analyzers/WinFormsAnalyzer.cs b/Confuser.Renamer/Analyzers/WinFormsAnalyzer.cs index c8f78e92c..9a33714c7 100644 --- a/Confuser.Renamer/Analyzers/WinFormsAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/WinFormsAnalyzer.cs @@ -6,6 +6,7 @@ using Confuser.Core.Services; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.Analyzers { public class WinFormsAnalyzer : IRenamer { @@ -66,7 +67,7 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth int[] args = trace.TraceArguments(instrInfo.Item2); if (args == null) { if (!erred) - context.Logger.WarnFormat("Failed to extract binding property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract binding property name in '{0}'.", method.FullName); erred = true; continue; } @@ -75,14 +76,14 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth var propertyName = ResolveNameInstruction(method, args, ref argumentIndex); if (propertyName.OpCode.Code != Code.Ldstr) { if (!erred) - context.Logger.WarnFormat("Failed to extract binding property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract binding property name in '{0}'.", method.FullName); erred = true; } else { List props; if (!properties.TryGetValue((string)propertyName.Operand, out props)) { if (!erred) - context.Logger.WarnFormat("Failed to extract target property in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract target property in '{0}'.", method.FullName); erred = true; } else { @@ -95,14 +96,14 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth var dataMember = ResolveNameInstruction(method, args, ref argumentIndex); if (dataMember.OpCode.Code != Code.Ldstr) { if (!erred) - context.Logger.WarnFormat("Failed to extract binding property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract binding property name in '{0}'.", method.FullName); erred = true; } else { List props; if (!properties.TryGetValue((string)dataMember.Operand, out props)) { if (!erred) - context.Logger.WarnFormat("Failed to extract target property in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract target property in '{0}'.", method.FullName); erred = true; } else { @@ -116,7 +117,7 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth int[] args = trace.TraceArguments(instrInfo); if (args == null) { if (!erred) - context.Logger.WarnFormat("Failed to extract binding property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract binding property name in '{0}'.", method.FullName); erred = true; continue; } @@ -125,13 +126,13 @@ void AnalyzeMethod(ConfuserContext context, INameService service, MethodDef meth var propertyName = ResolveNameInstruction(method, args, ref argumentIndex); if (propertyName.OpCode.Code != Code.Ldstr) { if (!erred) - context.Logger.WarnFormat("Failed to extract binding property name in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract binding property name in '{0}'.", method.FullName); erred = true; } else { if (!properties.TryGetValue((string)propertyName.Operand, out var props)) { if (!erred) - context.Logger.WarnFormat("Failed to extract target property in '{0}'.", method.FullName); + context.Logger.LogWarning("Failed to extract target property in '{0}'.", method.FullName); erred = true; } else { diff --git a/Confuser.Renamer/BAML/BAMLAnalyzer.cs b/Confuser.Renamer/BAML/BAMLAnalyzer.cs index dec64a2e7..f4569a595 100644 --- a/Confuser.Renamer/BAML/BAMLAnalyzer.cs +++ b/Confuser.Renamer/BAML/BAMLAnalyzer.cs @@ -9,6 +9,7 @@ using Confuser.Renamer.Analyzers; using Confuser.Renamer.References; using dnlib.DotNet; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.BAML { internal class BAMLAnalyzer { @@ -338,7 +339,7 @@ void ProcessElementBody(BamlElement root, BamlElement elem) { if (attrInfo.Item1 is EventDef) { MethodDef method = root.Type.FindMethod(propRec.Value); if (method == null) - context.Logger.WarnFormat("Cannot resolve method '{0}' in '{1}'.", root.Type.FullName, propRec.Value); + context.Logger.LogWarning("Cannot resolve method '{0}' in '{1}'.", root.Type.FullName, propRec.Value); else { var reference = new BAMLAttributeReference(method, propRec); service.AddReference(method, reference); @@ -472,7 +473,7 @@ void ProcessConverter(PropertyWithConverterRecord rec, TypeDef type) { AddDefReference(field, reference); } if (property == null && field == null) - context.Logger.WarnFormat("Could not resolve command '{0}' in '{1}'.", cmd, CurrentBAMLName); + context.Logger.LogWarning("Could not resolve command '{0}' in '{1}'.", cmd, CurrentBAMLName); } } } @@ -540,7 +541,7 @@ void ProcessConverter(PropertyWithConverterRecord rec, TypeDef type) { src = match.Groups[2].Value; } else if (rec.Value.Contains("/")) - context.Logger.WarnFormat("Fail to extract XAML name from '{0}'.", rec.Value); + context.Logger.LogWarning("Fail to extract XAML name from '{0}'.", rec.Value); if (!src.StartsWith(packScheme, StringComparison.OrdinalIgnoreCase)) { var rel = new Uri(new Uri(packScheme + "application:,,,/" + CurrentBAMLName), src); diff --git a/Confuser.Renamer/RenamePhase.cs b/Confuser.Renamer/RenamePhase.cs index 39a58eaeb..2302ddd74 100644 --- a/Confuser.Renamer/RenamePhase.cs +++ b/Confuser.Renamer/RenamePhase.cs @@ -5,6 +5,7 @@ using Confuser.Core; using dnlib.DotNet; using dnlib.DotNet.Pdb; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer { class RenamePhase : ProtectionPhase { @@ -22,7 +23,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa var service = (NameService)context.Registry.GetService(); bool overloadConfusion = parameters.GetParameter(context, context.CurrentModule, "overload", false); - context.Logger.Debug("Renaming..."); + context.Logger.LogDebug("Renaming..."); foreach (var renamer in service.Renamers) { foreach (var def in parameters.Targets) renamer.PreRename(context, service, parameters, def); @@ -115,7 +116,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa foreach (var reference in updatedReferenceList) { errorBuilder.Append(" - ").AppendLine(reference.ToString(service)); } - context.Logger.Error(errorBuilder.ToString().Trim()); + context.Logger.LogError(errorBuilder.ToString().Trim()); throw new ConfuserException(); } context.CheckCancellation(); @@ -189,7 +190,7 @@ static IEnumerable GetTargetsWithDelay(IList definitions, foreach (var def in delayedItems) { errorBuilder.Append("• ").AppendDescription(def, service).AppendLine(); } - context.Logger.Warn(errorBuilder.ToString().Trim()); + context.Logger.LogWarning(errorBuilder.ToString().Trim()); yield break; } lastCount = delayedItems.Count; diff --git a/Confuser.Renamer/VTable.cs b/Confuser.Renamer/VTable.cs index cca4effa4..7a62802c7 100644 --- a/Confuser.Renamer/VTable.cs +++ b/Confuser.Renamer/VTable.cs @@ -4,7 +4,7 @@ using System.Linq; using Confuser.Core; using dnlib.DotNet; -using ILogger = Confuser.Core.ILogger; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer { public class VTableSignature { @@ -314,31 +314,31 @@ static void Inherits(VTableConstruction vTbl, VTable baseVTbl) { [Conditional("DEBUG")] static void CheckKeyExist(VTableStorage storage, IDictionary dictionary, TKey key, string name) { if (!dictionary.ContainsKey(key)) { - storage.GetLogger().ErrorFormat("{0} not found: {1}", name, key); + storage.GetLogger().LogError("{0} not found: {1}", name, key); foreach (var k in dictionary.Values) - storage.GetLogger().ErrorFormat(" {0}", k); + storage.GetLogger().LogError(" {0}", k); } } [Conditional("DEBUG")] static void CheckKeyExist(VTableStorage storage, ILookup lookup, TKey key, string name) { if (!lookup.Contains(key)) { - storage.GetLogger().ErrorFormat("{0} not found: {1}", name, key); + storage.GetLogger().LogError("{0} not found: {1}", name, key); foreach (var k in lookup.Select(g => g.Key)) - storage.GetLogger().ErrorFormat(" {0}", k); + storage.GetLogger().LogError(" {0}", k); } } } public class VTableStorage { Dictionary storage = new Dictionary(); - ILogger logger; + Microsoft.Extensions.Logging.ILogger logger; - public VTableStorage(ILogger logger) { + public VTableStorage(Microsoft.Extensions.Logging.ILogger logger) { this.logger = logger; } - public ILogger GetLogger() { + public Microsoft.Extensions.Logging.ILogger GetLogger() { return logger; } diff --git a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs index 41532cc69..9a95654d3 100644 --- a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs +++ b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs @@ -62,7 +62,7 @@ void DoProtect() { builder.AddSerilog(serilogLogger, dispose: true)); var melLogger = loggerFactory.CreateLogger("ConfuserEx"); - parameters.Logger = new MelLoggerAdapter(melLogger); + parameters.Logger = melLogger; parameters.ProgressReporter = this; cancelSrc = new CancellationTokenSource(); diff --git a/Tests/Confuser.Renamer.Test/Analyzers/ReflectionAnalyzerTest.cs b/Tests/Confuser.Renamer.Test/Analyzers/ReflectionAnalyzerTest.cs index fe2ff1be0..c1ff62411 100644 --- a/Tests/Confuser.Renamer.Test/Analyzers/ReflectionAnalyzerTest.cs +++ b/Tests/Confuser.Renamer.Test/Analyzers/ReflectionAnalyzerTest.cs @@ -6,10 +6,10 @@ using Confuser.Renamer.Analyzers; using Confuser.UnitTest; using dnlib.DotNet; +using Microsoft.Extensions.Logging; using Moq; using Xunit; using Xunit.Abstractions; -using ILogger = Confuser.Core.ILogger; namespace Confuser.Renamer.Test.Analyzers { public sealed class ReflectionAnalyzerTest { @@ -21,7 +21,7 @@ public sealed class ReflectionAnalyzerTest { public ReflectionAnalyzerTest(ITestOutputHelper outputHelper) => _outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper)); - private ILogger CreateLogger() => new XunitLogger(_outputHelper); + private Microsoft.Extensions.Logging.ILogger CreateLogger() => new XunitLogger(_outputHelper); private string ReferenceProperty { get; } diff --git a/Tests/Confuser.UnitTest/XUnitLogger.cs b/Tests/Confuser.UnitTest/XUnitLogger.cs index 35bd31164..a7d8cddd3 100644 --- a/Tests/Confuser.UnitTest/XUnitLogger.cs +++ b/Tests/Confuser.UnitTest/XUnitLogger.cs @@ -1,9 +1,10 @@ -using System; +using System; using Confuser.Core; +using Microsoft.Extensions.Logging; using Xunit.Abstractions; namespace Confuser.UnitTest { - public sealed class XunitLogger : ILogger, IProgressReporter { + public sealed class XunitLogger : Microsoft.Extensions.Logging.ILogger, IProgressReporter { private readonly ITestOutputHelper _outputHelper; private readonly Action _outputAction; @@ -14,45 +15,25 @@ public XunitLogger(ITestOutputHelper outputHelper, Action outputAction) _outputAction = outputAction; } - void ILogger.Debug(string msg) => - ProcessOutput("[DEBUG] " + msg); + public IDisposable BeginScope(TState state) => null; - void ILogger.DebugFormat(string format, params object[] args) => - ProcessOutput("[DEBUG] " + format, args); + public bool IsEnabled(LogLevel logLevel) => true; - void IProgressReporter.EndProgress() { } - - void ILogger.Error(string msg) => - throw new Exception(msg); - - void ILogger.ErrorException(string msg, Exception ex) => - throw new Exception(msg, ex); - - void ILogger.ErrorFormat(string format, params object[] args) => - throw new Exception(string.Format(format, args)); - - void IProgressReporter.Finish(bool successful) => - ProcessOutput("[DONE]"); + public void Log(LogLevel logLevel, EventId eventId, TState state, + Exception exception, Func formatter) { + var message = formatter(state, exception); - void ILogger.Info(string msg) => - ProcessOutput("[INFO] " + msg); + if (logLevel >= LogLevel.Error) + throw new Exception(message, exception); - void ILogger.InfoFormat(string format, params object[] args) => - ProcessOutput("[INFO] " + format, args); + ProcessOutput(message); + } void IProgressReporter.Progress(int progress, int overall) { } - void ILogger.Warn(string msg) => - ProcessOutput("[WARN] " + msg); - - void ILogger.WarnException(string msg, Exception ex) => - ProcessOutput("[WARN] " + msg + Environment.NewLine + ex.ToString()); - - void ILogger.WarnFormat(string format, params object[] args) => - ProcessOutput("[WARN] " + format, args); + void IProgressReporter.EndProgress() { } - private void ProcessOutput(string format, params object[] args) => - ProcessOutput(string.Format(format, args)); + void IProgressReporter.Finish(bool successful) => ProcessOutput("[DONE]"); private void ProcessOutput(string message) { _outputAction?.Invoke(message); From a3c07b2a2a0fbece24471cb9a7c263bb2a648c6f Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Fri, 12 Jun 2026 00:04:57 +0200 Subject: [PATCH 7/9] chore: fix import ordering to pass CI lint (#64) --- Confuser.Protections/AntiTamper/JITMode.cs | 2 +- Confuser.Protections/Compress/Compressor.cs | 2 +- Confuser.Protections/Compress/ExtractPhase.cs | 2 +- Confuser.Protections/ControlFlow/ControlFlowPhase.cs | 2 +- Confuser.Protections/Resources/InjectPhase.cs | 2 +- Confuser.Protections/Resources/MDPhase.cs | 2 +- Confuser.Renamer/Analyzers/ResourceAnalyzer.cs | 2 +- Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Confuser.Protections/AntiTamper/JITMode.cs b/Confuser.Protections/AntiTamper/JITMode.cs index 86a919805..074c54b40 100644 --- a/Confuser.Protections/AntiTamper/JITMode.cs +++ b/Confuser.Protections/AntiTamper/JITMode.cs @@ -8,11 +8,11 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.Renamer; -using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; +using Microsoft.Extensions.Logging; namespace Confuser.Protections.AntiTamper { internal class JITMode : IModeHandler { diff --git a/Confuser.Protections/Compress/Compressor.cs b/Confuser.Protections/Compress/Compressor.cs index ad6b7d5a1..ca81339db 100644 --- a/Confuser.Protections/Compress/Compressor.cs +++ b/Confuser.Protections/Compress/Compressor.cs @@ -10,12 +10,12 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.Protections.Compress; -using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; using dnlib.PE; +using Microsoft.Extensions.Logging; using FileAttributes = dnlib.DotNet.FileAttributes; using SR = System.Reflection; diff --git a/Confuser.Protections/Compress/ExtractPhase.cs b/Confuser.Protections/Compress/ExtractPhase.cs index 54e50cab1..6fc5e39aa 100644 --- a/Confuser.Protections/Compress/ExtractPhase.cs +++ b/Confuser.Protections/Compress/ExtractPhase.cs @@ -5,9 +5,9 @@ using System.Text; using Confuser.Core; using dnlib.DotNet; -using Microsoft.Extensions.Logging; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; +using Microsoft.Extensions.Logging; namespace Confuser.Protections.Compress { internal class ExtractPhase : ProtectionPhase { diff --git a/Confuser.Protections/ControlFlow/ControlFlowPhase.cs b/Confuser.Protections/ControlFlow/ControlFlowPhase.cs index 5c42b0934..addeb6257 100644 --- a/Confuser.Protections/ControlFlow/ControlFlowPhase.cs +++ b/Confuser.Protections/ControlFlow/ControlFlowPhase.cs @@ -4,12 +4,12 @@ using Confuser.Core; using Confuser.Core.Services; using Confuser.DynCipher; -using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; using dnlib.DotNet.Pdb; using dnlib.DotNet.Writer; +using Microsoft.Extensions.Logging; namespace Confuser.Protections.ControlFlow { internal class ControlFlowPhase : ProtectionPhase { diff --git a/Confuser.Protections/Resources/InjectPhase.cs b/Confuser.Protections/Resources/InjectPhase.cs index cc73c83c5..de803fd58 100644 --- a/Confuser.Protections/Resources/InjectPhase.cs +++ b/Confuser.Protections/Resources/InjectPhase.cs @@ -7,10 +7,10 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.DynCipher; -using Microsoft.Extensions.Logging; using Confuser.Renamer; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Microsoft.Extensions.Logging; namespace Confuser.Protections.Resources { internal class InjectPhase : ProtectionPhase { diff --git a/Confuser.Protections/Resources/MDPhase.cs b/Confuser.Protections/Resources/MDPhase.cs index 49786d0aa..741ebbf32 100644 --- a/Confuser.Protections/Resources/MDPhase.cs +++ b/Confuser.Protections/Resources/MDPhase.cs @@ -7,10 +7,10 @@ using Confuser.Core.Helpers; using Confuser.Core.Services; using Confuser.Renamer; -using Microsoft.Extensions.Logging; using dnlib.DotNet; using dnlib.DotNet.MD; using dnlib.DotNet.Writer; +using Microsoft.Extensions.Logging; namespace Confuser.Protections.Resources { internal class MDPhase { diff --git a/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs b/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs index 230ddb5eb..fccfc9d40 100644 --- a/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/ResourceAnalyzer.cs @@ -3,10 +3,10 @@ using System.Text.RegularExpressions; using Confuser.Core; using Confuser.Renamer.Properties; -using Microsoft.Extensions.Logging; using Confuser.Renamer.References; using dnlib.DotNet; using dnlib.DotNet.Emit; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.Analyzers { internal class ResourceAnalyzer : IRenamer { diff --git a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs index 4fa369a79..5b755ab59 100644 --- a/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs +++ b/Confuser.Renamer/Analyzers/TypeBlobAnalyzer.cs @@ -5,9 +5,9 @@ using Confuser.Core; using Confuser.Renamer.References; using dnlib.DotNet; -using Microsoft.Extensions.Logging; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; +using Microsoft.Extensions.Logging; namespace Confuser.Renamer.Analyzers { public sealed class TypeBlobAnalyzer : IRenamer { From d67f926b289c47a5fc33d7949779d676acfddcc8 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 2 Jul 2026 20:40:14 +0200 Subject: [PATCH 8/9] fix: dispose GUI logger factory after async protection completes (#64) DoProtect used 'using var loggerFactory' which disposed the factory (and the Serilog logger via dispose:true) as soon as DoProtect returned. Since ConfuserEngine.Run executes asynchronously on a background thread, this disposed the logger before the protection actually used it, silently dropping log output mid-run. Move disposal into the ContinueWith continuation so the factory lives for the full protection lifetime. --- ConfuserEx/ViewModel/UI/ProtectTabVM.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs index 9a95654d3..3f7936c41 100644 --- a/ConfuserEx/ViewModel/UI/ProtectTabVM.cs +++ b/ConfuserEx/ViewModel/UI/ProtectTabVM.cs @@ -58,7 +58,10 @@ void DoProtect() { .WriteTo.Sink(new FlowDocumentSink(documentContent)) .CreateLogger(); - using var loggerFactory = LoggerFactory.Create(builder => + // The logger factory must outlive the async protection run — ConfuserEngine.Run + // executes on a background thread, so we dispose it in the continuation below + // rather than with a method-scoped 'using' (which would dispose it too early). + var loggerFactory = LoggerFactory.Create(builder => builder.AddSerilog(serilogLogger, dispose: true)); var melLogger = loggerFactory.CreateLogger("ConfuserEx"); @@ -72,12 +75,14 @@ void DoProtect() { App.NavigationDisabled = true; ConfuserEngine.Run(parameters, cancelSrc.Token) - .ContinueWith(_ => - Application.Current.Dispatcher.BeginInvoke(new Action(() => { - Progress = 0; - App.NavigationDisabled = false; - CommandManager.InvalidateRequerySuggested(); - }))); + .ContinueWith(_ => { + loggerFactory.Dispose(); + Application.Current.Dispatcher.BeginInvoke(new Action(() => { + Progress = 0; + App.NavigationDisabled = false; + CommandManager.InvalidateRequerySuggested(); + })); + }); } void DoCancel() { From 9130673c3bc597a546f8dc475eaa06f03124f180 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Thu, 2 Jul 2026 20:48:36 +0200 Subject: [PATCH 9/9] =?UTF-8?q?test:=20fix=20flaky=20GUI=20protect=20test?= =?UTF-8?q?=20=E2=80=94=20deterministic=20tab=20navigation=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gui_ProtectSampleApp_ShowsSuccess intermittently failed at the Protect! button lookup (~1 in 3 runs). Two root causes: 1. ByText("Protect!") ambiguously matched both the tab header and the Protect! button (they share the caption), so the wrong element could be clicked and the tab never actually got selected. 2. WPF virtualizes inactive tab content — the Protect! button does not enter the UIA tree until the tab is selected AND rendered. The 5s button-find timeout was too short under load. Fix: match the tab by TabItem control type + name, Select() it and wait for IsSelected, then find the button with a 15s timeout. 5/5 consecutive full-suite runs now pass 3/3 (previously ~1/3 failed). --- Tests/Confuser.GUI.Test/GuiSmokeTest.cs | 29 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Tests/Confuser.GUI.Test/GuiSmokeTest.cs b/Tests/Confuser.GUI.Test/GuiSmokeTest.cs index ac6f04a64..50e3e85e1 100644 --- a/Tests/Confuser.GUI.Test/GuiSmokeTest.cs +++ b/Tests/Confuser.GUI.Test/GuiSmokeTest.cs @@ -150,15 +150,28 @@ public void Gui_ProtectSampleApp_ShowsSuccess() { LaunchGui($"\"{crprojPath}\""); var mainWindow = WaitForMainWindow(app); - // Navigate to the Protect! tab + // Navigate to the Protect! tab. Match by TabItem control type + name — + // NOT ByText, which ambiguously matches both the tab header and the + // Protect! button (they share the caption "Protect!"). var protectTab = Retry.WhileNull( - () => mainWindow.FindFirstDescendant(cf => cf.ByText("Protect!")), - TimeSpan.FromSeconds(5), - TimeSpan.FromMilliseconds(500)).Result; + () => mainWindow.FindFirstDescendant(cf => + cf.ByControlType(FlaUI.Core.Definitions.ControlType.TabItem) + .And(cf.ByName("Protect!"))), + TimeSpan.FromSeconds(10), + TimeSpan.FromMilliseconds(300)).Result; Assert.NotNull(protectTab); - protectTab.Click(); - // Find and click the Protect! button + // Select the tab and wait until it is actually selected. WPF virtualizes + // inactive tab content, so the Protect! button does not enter the UIA tree + // until the tab is selected and its content has rendered. + var tabItem = protectTab.AsTabItem(); + tabItem.Select(); + Retry.WhileFalse( + () => tabItem.IsSelected, + TimeSpan.FromSeconds(5), + TimeSpan.FromMilliseconds(200)); + + // Find and click the Protect! button (only present once the tab is rendered). var protectButton = Retry.WhileNull( () => { var buttons = mainWindow.FindAllDescendants(cf => cf.ByControlType(FlaUI.Core.Definitions.ControlType.Button)); @@ -167,8 +180,8 @@ public void Gui_ProtectSampleApp_ShowsSuccess() { } return null; }, - TimeSpan.FromSeconds(5), - TimeSpan.FromMilliseconds(500)).Result; + TimeSpan.FromSeconds(15), + TimeSpan.FromMilliseconds(300)).Result; Assert.NotNull(protectButton); output.WriteLine("Clicking Protect! button...");