diff --git a/Dalamud/IoC/Internal/ServiceContainer.cs b/Dalamud/IoC/Internal/ServiceContainer.cs
index 7a0b4347d..06e2ff14d 100644
--- a/Dalamud/IoC/Internal/ServiceContainer.cs
+++ b/Dalamud/IoC/Internal/ServiceContainer.cs
@@ -84,15 +84,15 @@ internal class ServiceContainer : IServiceProvider, IServiceType
/// Scoped objects to be included in the constructor.
/// The scope to be used to create scoped services.
/// The created object.
- public async Task CreateAsync(Type objectType, object[] scopedObjects, IServiceScope? scope = null)
+ public async Task CreateAsync(Type objectType, object[] scopedObjects, IServiceScope? scope = null)
{
var scopeImpl = scope as ServiceScopeImpl;
var ctor = this.FindApplicableCtor(objectType, scopedObjects);
if (ctor == null)
{
- Log.Error("Failed to create {TypeName}, an eligible ctor with satisfiable services could not be found", objectType.FullName!);
- return null;
+ throw new InvalidOperationException(
+ $"Failed to create {objectType.FullName ?? objectType.Name}; an eligible ctor with satisfiable services could not be found");
}
// validate dependency versions (if they exist)
@@ -116,16 +116,16 @@ internal class ServiceContainer : IServiceProvider, IServiceType
var hasNull = resolvedParams.Any(p => p == null);
if (hasNull)
{
- Log.Error("Failed to create {TypeName}, a requested service type could not be satisfied", objectType.FullName!);
- return null;
+ throw new InvalidOperationException(
+ $"Failed to create {objectType.FullName ?? objectType.Name}; a requested service type could not be satisfied");
}
var instance = RuntimeHelpers.GetUninitializedObject(objectType);
if (!await this.InjectProperties(instance, scopedObjects, scope))
{
- Log.Error("Failed to create {TypeName}, a requested property service type could not be satisfied", objectType.FullName!);
- return null;
+ throw new InvalidOperationException(
+ $"Failed to create {objectType.FullName ?? objectType.Name}; a requested property service type could not be satisfied");
}
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
diff --git a/Dalamud/IoC/Internal/ServiceScope.cs b/Dalamud/IoC/Internal/ServiceScope.cs
index c243eca28..33a4d3f54 100644
--- a/Dalamud/IoC/Internal/ServiceScope.cs
+++ b/Dalamud/IoC/Internal/ServiceScope.cs
@@ -25,7 +25,7 @@ internal interface IServiceScope : IDisposable
/// The type of object to create.
/// Scoped objects to be included in the constructor.
/// The created object.
- public Task CreateAsync(Type objectType, params object[] scopedObjects);
+ public Task CreateAsync(Type objectType, params object[] scopedObjects);
///
/// Inject interfaces into public or static properties on the provided object.
@@ -45,7 +45,7 @@ internal class ServiceScopeImpl : IServiceScope
private readonly ServiceContainer container;
private readonly List privateScopedObjects = [];
- private readonly ConcurrentDictionary> scopeCreatedObjects = new();
+ private readonly ConcurrentDictionary> scopeCreatedObjects = new();
///
/// Initializes a new instance of the class.
@@ -63,7 +63,7 @@ internal class ServiceScopeImpl : IServiceScope
}
///
- public Task CreateAsync(Type objectType, params object[] scopedObjects)
+ public Task CreateAsync(Type objectType, params object[] scopedObjects)
{
return this.container.CreateAsync(objectType, scopedObjects, this);
}
@@ -80,7 +80,7 @@ internal class ServiceScopeImpl : IServiceScope
/// The type of object to create.
/// Additional scoped objects.
/// The created object, or null.
- public Task CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) =>
+ public Task CreatePrivateScopedObject(Type objectType, params object[] scopedObjects) =>
this.scopeCreatedObjects.GetOrAdd(
objectType,
static (objectType, p) => p.Scope.container.CreateAsync(
diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs
index bb92b6b0c..9fb73fbe1 100644
--- a/Dalamud/Plugin/DalamudPluginInterface.cs
+++ b/Dalamud/Plugin/DalamudPluginInterface.cs
@@ -5,6 +5,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Threading.Tasks;
using Dalamud.Configuration;
using Dalamud.Configuration.Internal;
@@ -26,6 +27,8 @@ using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal;
+using Serilog;
+
namespace Dalamud.Plugin;
///
@@ -458,21 +461,22 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
#region Dependency Injection
- ///
- /// Create a new object of the provided type using its default constructor, then inject objects and properties.
- ///
- /// Objects to inject additionally.
- /// The type to create.
- /// The created and initialized type.
+ ///
public T? Create(params object[] scopedObjects) where T : class
{
- var svcContainer = Service.Get();
+ var t = this.CreateAsync(scopedObjects);
+ t.Wait();
- return (T)this.plugin.ServiceScope!.CreateAsync(
- typeof(T),
- this.GetPublicIocScopes(scopedObjects)).GetAwaiter().GetResult();
+ if (t.Exception is { } e)
+ Log.Error(e, "{who}: Failed to initialize {what}", this.plugin.Name, typeof(T).FullName ?? typeof(T).Name);
+
+ return t.IsCompletedSuccessfully ? t.Result : null;
}
+ ///
+ public async Task CreateAsync(params object[] scopedObjects) where T : class =>
+ (T)await this.plugin.ServiceScope!.CreateAsync(typeof(T), this.GetPublicIocScopes(scopedObjects));
+
///
/// Inject services into properties on the provided object instance.
///
diff --git a/Dalamud/Plugin/IDalamudPluginInterface.cs b/Dalamud/Plugin/IDalamudPluginInterface.cs
index 6393dc5ab..b5257c033 100644
--- a/Dalamud/Plugin/IDalamudPluginInterface.cs
+++ b/Dalamud/Plugin/IDalamudPluginInterface.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Threading.Tasks;
using Dalamud.Configuration;
using Dalamud.Game.Text;
@@ -304,9 +305,17 @@ public interface IDalamudPluginInterface
///
/// Objects to inject additionally.
/// The type to create.
- /// The created and initialized type.
+ /// The created and initialized type, or null on failure.
T? Create(params object[] scopedObjects) where T : class;
+ ///
+ /// Create a new object of the provided type using its default constructor, then inject objects and properties.
+ ///
+ /// Objects to inject additionally.
+ /// The type to create.
+ /// A task representing the created and initialized type.
+ Task CreateAsync(params object[] scopedObjects) where T : class;
+
///
/// Inject services into properties on the provided object instance.
///
diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
index f7bb3495c..00fa9d243 100644
--- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
+++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs
@@ -417,24 +417,16 @@ internal class LocalPlugin : IDisposable
try
{
- if (this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1)
- {
- var newInstance = await framework.RunOnFrameworkThread(
- () => this.ServiceScope.CreateAsync(
- this.pluginType!,
- this.DalamudInterface!)).ConfigureAwait(false);
-
- this.instance = newInstance as IDalamudPlugin;
- }
- else
- {
- this.instance =
- await this.ServiceScope.CreateAsync(this.pluginType!, this.DalamudInterface!) as IDalamudPlugin;
- }
+ var forceFrameworkThread = this.manifest.LoadSync && this.manifest.LoadRequiredState is 0 or 1;
+ var newInstanceTask = forceFrameworkThread ? framework.RunOnFrameworkThread(Create) : Create();
+ this.instance = await newInstanceTask.ConfigureAwait(false);
+
+ async Task Create() =>
+ (IDalamudPlugin)await this.ServiceScope!.CreateAsync(this.pluginType!, this.DalamudInterface!);
}
catch (Exception ex)
{
- Log.Error(ex, "Exception in plugin constructor");
+ Log.Error(ex, "Exception during plugin initialization");
this.instance = null;
}