From 0fe66d2c3ca2b9dbed4f9d1dcd8ee5b5efe302c1 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Sun, 17 May 2026 08:20:02 +0200 Subject: [PATCH] fix(di): use factory lambdas for internal-ctor services C3 bootstrap throws "A suitable constructor for type HellionChat.Ipc.ExtraChat could not be located" because Microsoft.Extensions.DependencyInjection's ActivatorUtilities only binds to PUBLIC constructors via reflection. ExtraChat is a public class with an internal ctor; Commands and StatusBar are internal classes whose implicit default ctor inherits class accessibility (internal); every IHostedService adapter is `internal sealed class X(deps)` with a primary ctor that is also internal. The fix routes all eight singletons and all seven hosted-service adapters through factory lambdas. `new T(...)` inside the PluginHostFactory namespace sees the internal surface, so the container never has to reflect over internal ctors. --- HellionChat/PluginHostFactory.cs | 56 +++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/HellionChat/PluginHostFactory.cs b/HellionChat/PluginHostFactory.cs index f1cec76..a4c3786 100644 --- a/HellionChat/PluginHostFactory.cs +++ b/HellionChat/PluginHostFactory.cs @@ -86,14 +86,22 @@ internal static class PluginHostFactory // constraint; ctors that need a Plugin backref go through a factory // lambda that resolves Plugin from the container. // ----------------------------------------------------------------- - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + // Factory lambdas across the board: Microsoft.Extensions.DependencyInjection's + // ActivatorUtilities only inspects PUBLIC constructors via reflection, + // and several HellionChat classes are `internal sealed` with implicit- + // internal default ctors (Commands, StatusBar) or explicitly `internal` + // ctors on public classes (ExtraChat). The lambda body compiles inside + // the HellionChat namespace, so `new T()` sees the internal surface. + services.AddSingleton(_ => new DalamudPlatformUtil()); + services.AddSingleton(sp => new DalamudPluginLogProxy( + sp.GetRequiredService() + )); + services.AddSingleton(_ => new FileDialogManager()); + services.AddSingleton(_ => new Commands()); + services.AddSingleton(_ => new FontManager()); + services.AddSingleton(_ => new StatusBar()); + services.AddSingleton(_ => new IpcManager()); + services.AddSingleton(_ => new ExtraChat()); services.AddSingleton(sp => new ThemeRegistry( Path.Combine( @@ -149,13 +157,31 @@ internal static class PluginHostFactory // making the services themselves implement IHostedService (Lightless' // pattern) — DI-2a leaves service classes untouched. // ----------------------------------------------------------------- - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); - services.AddHostedService(); + // Same internal-ctor pitfall as the singletons above - the adapter + // classes are `internal sealed` with primary constructors, so the + // direct AddHostedService() overload's ActivatorUtilities fails. + services.AddHostedService(sp => new FontManagerInitHostedService( + sp.GetRequiredService() + )); + services.AddHostedService(sp => new ThemeRegistryInitHostedService( + sp.GetRequiredService() + )); + services.AddHostedService(sp => new IpcManagerInitHostedService( + sp.GetRequiredService() + )); + services.AddHostedService(sp => new TypingIpcInitHostedService( + sp.GetRequiredService() + )); + services.AddHostedService(sp => new ExtraChatInitHostedService( + sp.GetRequiredService() + )); + services.AddHostedService(sp => new MessageManagerInitHostedService( + sp.GetRequiredService(), + sp.GetRequiredService() + )); + services.AddHostedService(sp => new AutoTellTabsServiceInitHostedService( + sp.GetRequiredService() + )); } }