Use EnumerateInvocationList instead of GetInvocationList (#2303)

This commit is contained in:
srkizer 2025-06-24 05:09:48 +09:00 committed by GitHub
parent 13306e24ba
commit 03e728e129
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 402 additions and 294 deletions

View file

@ -650,6 +650,16 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
} }
}); });
this.DalamudConfigurationSaved?.Invoke(this); foreach (var action in Delegate.EnumerateInvocationList(this.DalamudConfigurationSaved))
{
try
{
action(this);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
}
} }
} }

View file

@ -184,8 +184,18 @@ internal partial class ConsoleManager : IServiceType
/// <returns>Whether the command was successfully processed.</returns> /// <returns>Whether the command was successfully processed.</returns>
public bool ProcessCommand(string command) public bool ProcessCommand(string command)
{ {
if (this.Invoke?.Invoke(command) == true) foreach (var action in Delegate.EnumerateInvocationList(this.Invoke))
return true; {
try
{
if (action(command))
return true;
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
}
var matches = GetCommandParsingRegex().Matches(command); var matches = GetCommandParsingRegex().Matches(command);
if (matches.Count == 0) if (matches.Count == 0)

View file

@ -42,10 +42,10 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
[ServiceManager.ServiceDependency] [ServiceManager.ServiceDependency]
private readonly Framework framework = Service<Framework>.Get(); private readonly Framework framework = Service<Framework>.Get();
[ServiceManager.ServiceDependency] [ServiceManager.ServiceDependency]
private readonly NetworkHandlers networkHandlers = Service<NetworkHandlers>.Get(); private readonly NetworkHandlers networkHandlers = Service<NetworkHandlers>.Get();
private bool lastConditionNone = true; private bool lastConditionNone = true;
[ServiceManager.ServiceConstructor] [ServiceManager.ServiceConstructor]
@ -176,7 +176,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
this.uiModuleHandlePacketHook.Dispose(); this.uiModuleHandlePacketHook.Dispose();
this.onLogoutHook.Dispose(); this.onLogoutHook.Dispose();
this.framework.Update -= this.FrameworkOnOnUpdateEvent; this.framework.Update -= this.FrameworkOnOnUpdateEvent;
this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop; this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop;
} }
@ -211,50 +211,51 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
this.setupTerritoryTypeHook.Original(eventFramework, territoryType); this.setupTerritoryTypeHook.Original(eventFramework, territoryType);
} }
private unsafe void UIModuleHandlePacketDetour(UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet) private unsafe void UIModuleHandlePacketDetour(
UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet)
{ {
this.uiModuleHandlePacketHook.Original(thisPtr, type, uintParam, packet); this.uiModuleHandlePacketHook.Original(thisPtr, type, uintParam, packet);
switch (type) switch (type)
{ {
case UIModulePacketType.ClassJobChange when this.ClassJobChanged is { } callback: case UIModulePacketType.ClassJobChange:
{
var classJobId = uintParam;
foreach (var action in Delegate.EnumerateInvocationList(this.ClassJobChanged))
{ {
var classJobId = uintParam; try
foreach (var action in callback.GetInvocationList().Cast<IClientState.ClassJobChangeDelegate>())
{ {
try action(classJobId);
{ }
action(classJobId); catch (Exception ex)
} {
catch (Exception ex) Log.Error(ex, "Exception during raise of {handler}", action.Method);
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
break;
} }
case UIModulePacketType.LevelChange when this.LevelChanged is { } callback: break;
}
case UIModulePacketType.LevelChange:
{
var classJobId = *(uint*)packet;
var level = *(ushort*)((nint)packet + 4);
foreach (var action in Delegate.EnumerateInvocationList(this.LevelChanged))
{ {
var classJobId = *(uint*)packet; try
var level = *(ushort*)((nint)packet + 4);
foreach (var action in callback.GetInvocationList().Cast<IClientState.LevelChangeDelegate>())
{ {
try action(classJobId, level);
{ }
action(classJobId, level); catch (Exception ex)
} {
catch (Exception ex) Log.Error(ex, "Exception during raise of {handler}", action.Method);
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
break;
} }
break;
}
} }
} }
@ -291,18 +292,15 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
Log.Debug("Logout: Type {type}, Code {code}", type, code); Log.Debug("Logout: Type {type}, Code {code}", type, code);
if (this.Logout is { } callback) foreach (var action in Delegate.EnumerateInvocationList(this.Logout))
{ {
foreach (var action in callback.GetInvocationList().Cast<IClientState.LogoutDelegate>()) try
{ {
try action(type, code);
{ }
action(type, code); catch (Exception ex)
} {
catch (Exception ex) Log.Error(ex, "Exception during raise of {handler}", action.Method);
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }

View file

@ -22,7 +22,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition
[ServiceManager.ServiceDependency] [ServiceManager.ServiceDependency]
private readonly Framework framework = Service<Framework>.Get(); private readonly Framework framework = Service<Framework>.Get();
private readonly bool[] cache = new bool[MaxConditionEntries]; private readonly bool[] cache = new bool[MaxConditionEntries];
private bool isDisposed; private bool isDisposed;
@ -38,7 +38,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition
this.framework.Update += this.FrameworkUpdate; this.framework.Update += this.FrameworkUpdate;
} }
/// <summary>Finalizes an instance of the <see cref="Condition" /> class.</summary> /// <summary>Finalizes an instance of the <see cref="Condition" /> class.</summary>
~Condition() => this.Dispose(false); ~Condition() => this.Dispose(false);
@ -69,12 +69,12 @@ internal sealed class Condition : IInternalDisposableService, ICondition
/// <inheritdoc/> /// <inheritdoc/>
void IInternalDisposableService.DisposeService() => this.Dispose(true); void IInternalDisposableService.DisposeService() => this.Dispose(true);
/// <inheritdoc/> /// <inheritdoc/>
public IReadOnlySet<ConditionFlag> AsReadOnlySet() public IReadOnlySet<ConditionFlag> AsReadOnlySet()
{ {
var result = new HashSet<ConditionFlag>(); var result = new HashSet<ConditionFlag>();
for (var i = 0; i < MaxConditionEntries; i++) for (var i = 0; i < MaxConditionEntries; i++)
{ {
if (this[i]) if (this[i])
@ -99,14 +99,14 @@ internal sealed class Condition : IInternalDisposableService, ICondition
return false; return false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Any(params ConditionFlag[] flags) public bool Any(params ConditionFlag[] flags)
{ {
foreach (var flag in flags) foreach (var flag in flags)
{ {
// this[i] performs range checking, so no need to check here // this[i] performs range checking, so no need to check here
if (this[flag]) if (this[flag])
{ {
return true; return true;
} }
@ -114,7 +114,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition
return false; return false;
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool AnyExcept(params ConditionFlag[] excluded) public bool AnyExcept(params ConditionFlag[] excluded)
{ {
@ -157,13 +157,16 @@ internal sealed class Condition : IInternalDisposableService, ICondition
{ {
this.cache[i] = value; this.cache[i] = value;
try foreach (var d in Delegate.EnumerateInvocationList(this.ConditionChange))
{ {
this.ConditionChange?.Invoke((ConditionFlag)i, value); try
} {
catch (Exception ex) d((ConditionFlag)i, value);
{ }
Log.Error(ex, $"While invoking {nameof(this.ConditionChange)}, an exception was thrown."); catch (Exception ex)
{
Log.Error(ex, $"While invoking {d.Method.Name}, an exception was thrown.");
}
} }
} }
} }
@ -190,7 +193,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
{ {
this.conditionService.ConditionChange += this.ConditionChangedForward; this.conditionService.ConditionChange += this.ConditionChangedForward;
} }
/// <inheritdoc/> /// <inheritdoc/>
public event ICondition.ConditionChangeDelegate? ConditionChange; public event ICondition.ConditionChangeDelegate? ConditionChange;
@ -202,7 +205,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
/// <inheritdoc/> /// <inheritdoc/>
public bool this[int flag] => this.conditionService[flag]; public bool this[int flag] => this.conditionService[flag];
/// <inheritdoc/> /// <inheritdoc/>
void IInternalDisposableService.DisposeService() void IInternalDisposableService.DisposeService()
{ {
@ -210,7 +213,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
this.ConditionChange = null; this.ConditionChange = null;
} }
/// <inheritdoc/> /// <inheritdoc/>
public IReadOnlySet<ConditionFlag> AsReadOnlySet() => this.conditionService.AsReadOnlySet(); public IReadOnlySet<ConditionFlag> AsReadOnlySet() => this.conditionService.AsReadOnlySet();
@ -222,10 +225,10 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
/// <inheritdoc/> /// <inheritdoc/>
public bool AnyExcept(params ConditionFlag[] except) => this.conditionService.AnyExcept(except); public bool AnyExcept(params ConditionFlag[] except) => this.conditionService.AnyExcept(except);
/// <inheritdoc/> /// <inheritdoc/>
public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other); public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other);
/// <inheritdoc/> /// <inheritdoc/>
public bool EqualTo(params ConditionFlag[] other) => this.conditionService.EqualTo(other); public bool EqualTo(params ConditionFlag[] other) => this.conditionService.EqualTo(other);

View file

@ -132,7 +132,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
return false; return false;
} }
this.CommandAdded?.Invoke(this, new CommandEventArgs this.CommandAdded?.InvokeSafely(this, new CommandEventArgs
{ {
Command = command, Command = command,
CommandInfo = info, CommandInfo = info,
@ -160,7 +160,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
return false; return false;
} }
this.CommandAdded?.Invoke(this, new CommandEventArgs this.CommandAdded?.InvokeSafely(this, new CommandEventArgs
{ {
Command = command, Command = command,
CommandInfo = info, CommandInfo = info,
@ -180,7 +180,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
var removed = this.commandMap.Remove(command, out var info); var removed = this.commandMap.Remove(command, out var info);
if (removed) if (removed)
{ {
this.CommandRemoved?.Invoke(this, new CommandEventArgs this.CommandRemoved?.InvokeSafely(this, new CommandEventArgs
{ {
Command = command, Command = command,
CommandInfo = info, CommandInfo = info,

View file

@ -350,17 +350,13 @@ internal sealed class Framework : IInternalDisposableService, IFramework
/// <param name="frameworkInstance">The Framework Instance to pass to delegate.</param> /// <param name="frameworkInstance">The Framework Instance to pass to delegate.</param>
internal void ProfileAndInvoke(IFramework.OnUpdateDelegate? eventDelegate, IFramework frameworkInstance) internal void ProfileAndInvoke(IFramework.OnUpdateDelegate? eventDelegate, IFramework frameworkInstance)
{ {
if (eventDelegate is null) return;
var invokeList = eventDelegate.GetInvocationList();
// Individually invoke OnUpdate handlers and time them. // Individually invoke OnUpdate handlers and time them.
foreach (var d in invokeList) foreach (var d in Delegate.EnumerateInvocationList(eventDelegate))
{ {
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
try try
{ {
d.Method.Invoke(d.Target, new object[] { frameworkInstance }); d(frameworkInstance);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -370,8 +366,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework
stopwatch.Stop(); stopwatch.Stop();
var key = $"{d.Target}::{d.Method.Name}"; var key = $"{d.Target}::{d.Method.Name}";
if (this.NonUpdatedSubDelegates.Contains(key)) this.NonUpdatedSubDelegates.Remove(key);
this.NonUpdatedSubDelegates.Remove(key);
AddToStats(key, stopwatch.Elapsed.TotalMilliseconds); AddToStats(key, stopwatch.Elapsed.TotalMilliseconds);
} }

View file

@ -346,24 +346,21 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
// Call events // Call events
var isHandled = false; var isHandled = false;
if (this.CheckMessageHandled is { } handledCallback) foreach (var action in Delegate.EnumerateInvocationList(this.CheckMessageHandled))
{ {
foreach (var action in handledCallback.GetInvocationList().Cast<IChatGui.OnCheckMessageHandledDelegate>()) try
{ {
try action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled);
{ }
action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); catch (Exception e)
} {
catch (Exception e) Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method);
{
Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method);
}
} }
} }
if (!isHandled && this.ChatMessage is { } callback) if (!isHandled)
{ {
foreach (var action in callback.GetInvocationList().Cast<IChatGui.OnMessageDelegate>()) foreach (var action in Delegate.EnumerateInvocationList(this.ChatMessage))
{ {
try try
{ {
@ -394,12 +391,14 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
// Print the original chat if it's handled. // Print the original chat if it's handled.
if (isHandled) if (isHandled)
{ {
this.ChatMessageHandled?.Invoke(chatType, timestamp, parsedSender, parsedMessage); foreach (var d in Delegate.EnumerateInvocationList(this.ChatMessageHandled))
d(chatType, timestamp, parsedSender, parsedMessage);
} }
else else
{ {
messageId = this.printMessageHook.Original(manager, chatType, sender, message, timestamp, silent); messageId = this.printMessageHook.Original(manager, chatType, sender, message, timestamp, silent);
this.ChatMessageUnhandled?.Invoke(chatType, timestamp, parsedSender, parsedMessage); foreach (var d in Delegate.EnumerateInvocationList(this.ChatMessageUnhandled))
d(chatType, timestamp, parsedSender, parsedMessage);
} }
} }
catch (Exception ex) catch (Exception ex)

View file

@ -75,7 +75,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui
strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false); strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false);
strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false); strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false);
flytext->AddFlyText(actorIndex, 1, numArray, numOffset, 10, strArray, strOffset, 2, 0); flytext->AddFlyText(actorIndex, 1, numArray, numOffset, 10, strArray, strOffset, 2, 0);
} }
@ -116,17 +116,27 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui
$"text1({(nint)text1:X}, \"{tmpText1}\") text2({(nint)text2:X}, \"{tmpText2}\") " + $"text1({(nint)text1:X}, \"{tmpText1}\") text2({(nint)text2:X}, \"{tmpText2}\") " +
$"color({color:X}) icon({icon}) yOffset({yOffset})"); $"color({color:X}) icon({icon}) yOffset({yOffset})");
Log.Verbose("[FlyText] Calling flytext events!"); Log.Verbose("[FlyText] Calling flytext events!");
this.FlyTextCreated?.Invoke( foreach (var d in Delegate.EnumerateInvocationList(this.FlyTextCreated))
ref tmpKind, {
ref tmpVal1, try
ref tmpVal2, {
ref tmpText1, d(
ref tmpText2, ref tmpKind,
ref tmpColor, ref tmpVal1,
ref tmpIcon, ref tmpVal2,
ref tmpDamageTypeIcon, ref tmpText1,
ref tmpYOffset, ref tmpText2,
ref handled); ref tmpColor,
ref tmpIcon,
ref tmpDamageTypeIcon,
ref tmpYOffset,
ref handled);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
// If handled, ignore the original call // If handled, ignore the original call
if (handled) if (handled)

View file

@ -6,6 +6,7 @@ using Dalamud.Hooking;
using Dalamud.IoC; using Dalamud.IoC;
using Dalamud.IoC.Internal; using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
@ -169,8 +170,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
handler.ResetState(); handler.ResetState();
} }
this.OnDataUpdate?.Invoke(this.context, activeHandlers); this.OnDataUpdate?.InvokeSafely(this.context, activeHandlers);
this.OnNamePlateUpdate?.Invoke(this.context, activeHandlers); this.OnNamePlateUpdate?.InvokeSafely(this.context, activeHandlers);
if (this.context.HasParts) if (this.context.HasParts)
this.ApplyBuilders(activeHandlers); this.ApplyBuilders(activeHandlers);
@ -185,8 +186,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate."); Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate.");
} }
this.OnPostNamePlateUpdate?.Invoke(this.context, activeHandlers); this.OnPostNamePlateUpdate?.InvokeSafely(this.context, activeHandlers);
this.OnPostDataUpdate?.Invoke(this.context, activeHandlers); this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers);
} }
else else
{ {
@ -200,8 +201,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
if (this.OnDataUpdate is not null) if (this.OnDataUpdate is not null)
{ {
this.OnDataUpdate?.Invoke(this.context, activeHandlers); this.OnDataUpdate?.InvokeSafely(this.context, activeHandlers);
this.OnNamePlateUpdate?.Invoke(this.context, updatedHandlers); this.OnNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
if (this.context.HasParts) if (this.context.HasParts)
this.ApplyBuilders(activeHandlers); this.ApplyBuilders(activeHandlers);
@ -216,12 +217,12 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate."); Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate.");
} }
this.OnPostNamePlateUpdate?.Invoke(this.context, updatedHandlers); this.OnPostNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
this.OnPostDataUpdate?.Invoke(this.context, activeHandlers); this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers);
} }
else if (updatedHandlers.Count != 0) else if (updatedHandlers.Count != 0)
{ {
this.OnNamePlateUpdate?.Invoke(this.context, updatedHandlers); this.OnNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
if (this.context.HasParts) if (this.context.HasParts)
this.ApplyBuilders(updatedHandlers); this.ApplyBuilders(updatedHandlers);
@ -236,8 +237,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate."); Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate.");
} }
this.OnPostNamePlateUpdate?.Invoke(this.context, updatedHandlers); this.OnPostNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
this.OnPostDataUpdate?.Invoke(this.context, activeHandlers); this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers);
} }
} }
} }

View file

@ -89,7 +89,17 @@ internal sealed unsafe class PartyFinderGui : IInternalDisposableService, IParty
var listing = new PartyFinderListing(packet.Listings[i]); var listing = new PartyFinderListing(packet.Listings[i]);
var args = new PartyFinderListingEventArgs(packet.BatchNumber); var args = new PartyFinderListingEventArgs(packet.BatchNumber);
this.ReceiveListing?.Invoke(listing, args); foreach (var d in Delegate.EnumerateInvocationList(this.ReceiveListing))
{
try
{
d(listing, args);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
if (args.Visible) if (args.Visible)
{ {

View file

@ -10,6 +10,8 @@ using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using Serilog;
namespace Dalamud.Game.Gui.Toast; namespace Dalamud.Game.Gui.Toast;
/// <summary> /// <summary>
@ -112,7 +114,7 @@ internal sealed partial class ToastGui
options ??= new ToastOptions(); options ??= new ToastOptions();
this.normalQueue.Enqueue((Encoding.UTF8.GetBytes(message), options)); this.normalQueue.Enqueue((Encoding.UTF8.GetBytes(message), options));
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ShowNormal(SeString message, ToastOptions? options = null) public void ShowNormal(SeString message, ToastOptions? options = null)
{ {
@ -150,7 +152,17 @@ internal sealed partial class ToastGui
Speed = (ToastSpeed)isFast, Speed = (ToastSpeed)isFast,
}; };
this.Toast?.Invoke(ref str, ref options, ref isHandled); foreach (var d in Delegate.EnumerateInvocationList(this.Toast))
{
try
{
d.Invoke(ref str, ref options, ref isHandled);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
// do nothing if handled // do nothing if handled
if (isHandled) if (isHandled)
@ -180,7 +192,7 @@ internal sealed partial class ToastGui
options ??= new QuestToastOptions(); options ??= new QuestToastOptions();
this.questQueue.Enqueue((Encoding.UTF8.GetBytes(message), options)); this.questQueue.Enqueue((Encoding.UTF8.GetBytes(message), options));
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ShowQuest(SeString message, QuestToastOptions? options = null) public void ShowQuest(SeString message, QuestToastOptions? options = null)
{ {
@ -223,7 +235,17 @@ internal sealed partial class ToastGui
PlaySound = playSound == 1, PlaySound = playSound == 1,
}; };
this.QuestToast?.Invoke(ref str, ref options, ref isHandled); foreach (var d in Delegate.EnumerateInvocationList(this.QuestToast))
{
try
{
d.Invoke(ref str, ref options, ref isHandled);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
// do nothing if handled // do nothing if handled
if (isHandled) if (isHandled)
@ -286,7 +308,17 @@ internal sealed partial class ToastGui
var isHandled = false; var isHandled = false;
var str = SeString.Parse(text); var str = SeString.Parse(text);
this.ErrorToast?.Invoke(ref str, ref isHandled); foreach (var d in Delegate.EnumerateInvocationList(this.ErrorToast))
{
try
{
d.Invoke(ref str, ref isHandled);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
// do nothing if handled // do nothing if handled
if (isHandled) if (isHandled)
@ -321,16 +353,16 @@ internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui
this.toastGuiService.QuestToast += this.QuestToastForward; this.toastGuiService.QuestToast += this.QuestToastForward;
this.toastGuiService.ErrorToast += this.ErrorToastForward; this.toastGuiService.ErrorToast += this.ErrorToastForward;
} }
/// <inheritdoc/> /// <inheritdoc/>
public event IToastGui.OnNormalToastDelegate? Toast; public event IToastGui.OnNormalToastDelegate? Toast;
/// <inheritdoc/> /// <inheritdoc/>
public event IToastGui.OnQuestToastDelegate? QuestToast; public event IToastGui.OnQuestToastDelegate? QuestToast;
/// <inheritdoc/> /// <inheritdoc/>
public event IToastGui.OnErrorToastDelegate? ErrorToast; public event IToastGui.OnErrorToastDelegate? ErrorToast;
/// <inheritdoc/> /// <inheritdoc/>
void IInternalDisposableService.DisposeService() void IInternalDisposableService.DisposeService()
{ {
@ -342,7 +374,7 @@ internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui
this.QuestToast = null; this.QuestToast = null;
this.ErrorToast = null; this.ErrorToast = null;
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ShowNormal(string message, ToastOptions? options = null) => this.toastGuiService.ShowNormal(message, options); public void ShowNormal(string message, ToastOptions? options = null) => this.toastGuiService.ShowNormal(message, options);

View file

@ -150,9 +150,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
private void OnHistoryReceived(IMarketBoardHistory history) private void OnHistoryReceived(IMarketBoardHistory history)
{ {
if (this.HistoryReceived == null) return; foreach (var action in Delegate.EnumerateInvocationList(this.HistoryReceived))
foreach (var action in this.HistoryReceived.GetInvocationList().Cast<HistoryReceivedDelegate>())
{ {
try try
{ {
@ -167,9 +165,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
private void OnItemPurchased(IMarketBoardPurchase purchase) private void OnItemPurchased(IMarketBoardPurchase purchase)
{ {
if (this.ItemPurchased == null) return; foreach (var action in Delegate.EnumerateInvocationList(this.ItemPurchased))
foreach (var action in this.ItemPurchased.GetInvocationList().Cast<ItemPurchasedDelegate>())
{ {
try try
{ {
@ -184,10 +180,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
private void OnOfferingsReceived(IMarketBoardCurrentOfferings currentOfferings) private void OnOfferingsReceived(IMarketBoardCurrentOfferings currentOfferings)
{ {
if (this.OfferingsReceived == null) return; foreach (var action in Delegate.EnumerateInvocationList(this.OfferingsReceived))
foreach (var action in this.OfferingsReceived.GetInvocationList()
.Cast<OfferingsReceivedDelegate>())
{ {
try try
{ {
@ -202,9 +195,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
private void OnPurchaseRequested(IMarketBoardPurchaseHandler purchaseHandler) private void OnPurchaseRequested(IMarketBoardPurchaseHandler purchaseHandler)
{ {
if (this.PurchaseRequested == null) return; foreach (var action in Delegate.EnumerateInvocationList(this.PurchaseRequested))
foreach (var action in this.PurchaseRequested.GetInvocationList().Cast<PurchaseRequestedDelegate>())
{ {
try try
{ {
@ -219,9 +210,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
private void OnTaxRatesReceived(IMarketTaxRates taxRates) private void OnTaxRatesReceived(IMarketTaxRates taxRates)
{ {
if (this.TaxRatesReceived == null) return; foreach (var action in Delegate.EnumerateInvocationList(this.TaxRatesReceived))
foreach (var action in this.TaxRatesReceived.GetInvocationList().Cast<TaxRatesReceivedDelegate>())
{ {
try try
{ {

View file

@ -28,7 +28,7 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
[ServiceManager.ServiceDependency] [ServiceManager.ServiceDependency]
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get(); private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
[ServiceManager.ServiceConstructor] [ServiceManager.ServiceConstructor]
private unsafe GameNetwork(TargetSigScanner sigScanner) private unsafe GameNetwork(TargetSigScanner sigScanner)
{ {
@ -71,32 +71,36 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
// Go back 0x10 to get back to the start of the packet header // Go back 0x10 to get back to the start of the packet header
dataPtr -= 0x10; dataPtr -= 0x10;
try foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage))
{ {
// Call events
this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr, 0x12), 0, targetId, NetworkMessageDirection.ZoneDown);
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
}
catch (Exception ex)
{
string header;
try try
{ {
var data = new byte[32]; d.Invoke(
Marshal.Copy(dataPtr, data, 0, 32); dataPtr + 0x20,
header = BitConverter.ToString(data); (ushort)Marshal.ReadInt16(dataPtr, 0x12),
0,
targetId,
NetworkMessageDirection.ZoneDown);
} }
catch (Exception) catch (Exception ex)
{ {
header = "failed"; string header;
try
{
var data = new byte[32];
Marshal.Copy(dataPtr, data, 0, 32);
header = BitConverter.ToString(data);
}
catch (Exception)
{
header = "failed";
}
Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header);
} }
Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header);
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
} }
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
this.hitchDetectorDown.Stop(); this.hitchDetectorDown.Stop();
} }
@ -153,7 +157,7 @@ internal class GameNetworkPluginScoped : IInternalDisposableService, IGameNetwor
{ {
this.gameNetworkService.NetworkMessage += this.NetworkMessageForward; this.gameNetworkService.NetworkMessage += this.NetworkMessageForward;
} }
/// <inheritdoc/> /// <inheritdoc/>
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage; public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;

View file

@ -36,7 +36,7 @@ internal sealed class WndProcHookManager : IInternalDisposableService
this.dispatchMessageWHook.Enable(); this.dispatchMessageWHook.Enable();
// Capture the game main window handle, // Capture the game main window handle,
// so that no guarantees would have to be made on the service dispose order. // so that no guarantees would have to be made on the service dispose order.
Service<InterfaceManager.InterfaceManagerWithScene> Service<InterfaceManager.InterfaceManagerWithScene>
.GetAsync() .GetAsync()
.ContinueWith(r => this.mainWindowHwnd = (HWND)r.Result.Manager.GameWindowHandle); .ContinueWith(r => this.mainWindowHwnd = (HWND)r.Result.Manager.GameWindowHandle);
@ -82,13 +82,16 @@ internal sealed class WndProcHookManager : IInternalDisposableService
/// <param name="args">The arguments.</param> /// <param name="args">The arguments.</param>
internal void InvokePreWndProc(WndProcEventArgs args) internal void InvokePreWndProc(WndProcEventArgs args)
{ {
try foreach (var d in Delegate.EnumerateInvocationList(this.PreWndProc))
{ {
this.PreWndProc?.Invoke(args); try
} {
catch (Exception e) d(args);
{ }
Log.Error(e, $"{nameof(this.PreWndProc)} error"); catch (Exception e)
{
Log.Error(e, $"{nameof(this.PreWndProc)} error calling {d.Method.Name}");
}
} }
} }
@ -98,13 +101,16 @@ internal sealed class WndProcHookManager : IInternalDisposableService
/// <param name="args">The arguments.</param> /// <param name="args">The arguments.</param>
internal void InvokePostWndProc(WndProcEventArgs args) internal void InvokePostWndProc(WndProcEventArgs args)
{ {
try foreach (var d in Delegate.EnumerateInvocationList(this.PostWndProc))
{ {
this.PostWndProc?.Invoke(args); try
} {
catch (Exception e) d(args);
{ }
Log.Error(e, $"{nameof(this.PostWndProc)} error"); catch (Exception e)
{
Log.Error(e, $"{nameof(this.PostWndProc)} error calling {d.Method.Name}");
}
} }
} }

View file

@ -278,7 +278,7 @@ internal sealed partial class ActiveNotification : IActiveNotification
if (@delegate is null) if (@delegate is null)
return null; return null;
foreach (var il in @delegate.GetInvocationList()) foreach (var il in Delegate.EnumerateInvocationList(@delegate))
{ {
if (il.Target is { } target && !IsOwnedByDalamud(target.GetType())) if (il.Target is { } target && !IsOwnedByDalamud(target.GetType()))
@delegate = (T)Delegate.Remove(@delegate, il); @delegate = (T)Delegate.Remove(@delegate, il);

View file

@ -3,6 +3,7 @@
using Dalamud.Configuration.Internal; using Dalamud.Configuration.Internal;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
@ -69,14 +70,14 @@ internal class NotificationPositionChooser
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right)) if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
{ {
this.SelectionMade?.Invoke(); this.SelectionMade.InvokeSafely();
} }
else if (ImGui.IsMouseClicked(ImGuiMouseButton.Left)) else if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
{ {
this.configuration.NotificationAnchorPosition = this.currentAnchorPosition; this.configuration.NotificationAnchorPosition = this.currentAnchorPosition;
this.configuration.QueueSave(); this.configuration.QueueSave();
this.SelectionMade?.Invoke(); this.SelectionMade.InvokeSafely();
} }
// In the middle of the screen, draw some instructions // In the middle of the screen, draw some instructions

View file

@ -832,7 +832,7 @@ internal partial class InterfaceManager : IInternalDisposableService
this.defaultFontResourceLock = fontLocked; this.defaultFontResourceLock = fontLocked;
// Broadcast to auto-rebuilding instances. // Broadcast to auto-rebuilding instances.
this.AfterBuildFonts?.Invoke(); this.AfterBuildFonts.InvokeSafely();
}); });
}; };
} }

View file

@ -66,7 +66,7 @@ internal sealed class DelegateFontHandle : FontHandle
var key = new DelegateFontHandle(this, buildStepDelegate); var key = new DelegateFontHandle(this, buildStepDelegate);
lock (this.syncRoot) lock (this.syncRoot)
this.handles.Add(key); this.handles.Add(key);
this.RebuildRecommend?.Invoke(); this.RebuildRecommend.InvokeSafely();
return key; return key;
} }
@ -259,7 +259,7 @@ internal sealed class DelegateFontHandle : FontHandle
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public void OnPreBuildCleanup(IFontAtlasBuildToolkitPreBuild toolkitPreBuild) public void OnPreBuildCleanup(IFontAtlasBuildToolkitPreBuild toolkitPreBuild)
{ {
// irrelevant // irrelevant

View file

@ -386,7 +386,7 @@ internal sealed partial class FontAtlasFactory
if (this.disposed) if (this.disposed)
return; return;
this.BeforeDispose?.InvokeSafely(this); this.BeforeDispose.InvokeSafely(this);
try try
{ {
@ -400,25 +400,11 @@ internal sealed partial class FontAtlasFactory
this.disposables.Dispose(); this.disposables.Dispose();
} }
try this.AfterDispose.InvokeSafely(this, null);
{
this.AfterDispose?.Invoke(this, null);
}
catch
{
// ignore
}
} }
catch (Exception e) catch (Exception e)
{ {
try this.AfterDispose.InvokeSafely(this, e);
{
this.AfterDispose?.Invoke(this, e);
}
catch
{
// ignore
}
} }
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
@ -664,7 +650,7 @@ internal sealed partial class FontAtlasFactory
{ {
res = new(this, scale); res = new(this, scale);
foreach (var fhm in this.fontHandleManagers) foreach (var fhm in this.fontHandleManagers)
res.InitialAddSubstance(fhm.NewSubstance(res)); res.InitialAddSubstance(fhm.NewSubstance(res));
unsafe unsafe
{ {
atlasPtr = (nint)res.Atlas.NativePtr; atlasPtr = (nint)res.Atlas.NativePtr;
@ -699,7 +685,7 @@ internal sealed partial class FontAtlasFactory
res = new(this, scale); res = new(this, scale);
foreach (var fhm in this.fontHandleManagers) foreach (var fhm in this.fontHandleManagers)
res.InitialAddSubstance(fhm.NewSubstance(res)); res.InitialAddSubstance(fhm.NewSubstance(res));
unsafe unsafe
{ {
atlasPtr = (nint)res.Atlas.NativePtr; atlasPtr = (nint)res.Atlas.NativePtr;
@ -828,7 +814,7 @@ internal sealed partial class FontAtlasFactory
this.factory.Framework.RunOnFrameworkThread( this.factory.Framework.RunOnFrameworkThread(
() => () =>
{ {
this.RebuildRecommend?.InvokeSafely(); this.RebuildRecommend.InvokeSafely();
switch (this.AutoRebuildMode) switch (this.AutoRebuildMode)
{ {

View file

@ -79,13 +79,16 @@ internal abstract class FontHandle : IFontHandle
/// <param name="font">The font, locked during the call of <see cref="ImFontChanged"/>.</param> /// <param name="font">The font, locked during the call of <see cref="ImFontChanged"/>.</param>
public void InvokeImFontChanged(ILockedImFont font) public void InvokeImFontChanged(ILockedImFont font)
{ {
try foreach (var d in Delegate.EnumerateInvocationList(this.ImFontChanged))
{ {
this.ImFontChanged?.Invoke(this, font); try
} {
catch (Exception e) d(this, font);
{ }
Log.Error(e, $"{nameof(this.InvokeImFontChanged)}: error"); catch (Exception e)
{
Log.Error(e, $"{nameof(this.InvokeImFontChanged)}: error calling {d.Method.Name}");
}
} }
} }

View file

@ -151,7 +151,7 @@ internal class GamePrebakedFontHandle : FontHandle
} }
if (suggestRebuild) if (suggestRebuild)
this.RebuildRecommend?.Invoke(); this.RebuildRecommend.InvokeSafely();
return handle; return handle;
} }
@ -641,7 +641,7 @@ internal class GamePrebakedFontHandle : FontHandle
glyphIndex = (ushort)glyphs.Length; glyphIndex = (ushort)glyphs.Length;
glyphs.Add(default); glyphs.Add(default);
} }
ref var g = ref glyphs[glyphIndex]; ref var g = ref glyphs[glyphIndex];
g = sourceGlyph; g = sourceGlyph;
if (fontScaleMode == FontScaleMode.SkipHandling) if (fontScaleMode == FontScaleMode.SkipHandling)
@ -807,7 +807,7 @@ internal class GamePrebakedFontHandle : FontHandle
.GetCustomRectByIndex(rectId) .GetCustomRectByIndex(rectId)
.NativePtr; .NativePtr;
var widthAdjustment = this.BaseStyle.CalculateBaseWidthAdjustment(fdtFontHeader, fdtGlyph); var widthAdjustment = this.BaseStyle.CalculateBaseWidthAdjustment(fdtFontHeader, fdtGlyph);
// Glyph is scaled at this point; undo that. // Glyph is scaled at this point; undo that.
ref var glyph = ref glyphs[lookups[rc.GlyphId]]; ref var glyph = ref glyphs[lookups[rc.GlyphId]];
glyph.X0 = this.BaseAttr.HorizontalOffset; glyph.X0 = this.BaseAttr.HorizontalOffset;
@ -822,7 +822,7 @@ internal class GamePrebakedFontHandle : FontHandle
this.gftp.GetTexFile(this.BaseAttr.TexPathFormat, fdtGlyph.TextureFileIndex); this.gftp.GetTexFile(this.BaseAttr.TexPathFormat, fdtGlyph.TextureFileIndex);
var sourceBuffer = texFiles[fdtGlyph.TextureFileIndex].ImageData; var sourceBuffer = texFiles[fdtGlyph.TextureFileIndex].ImageData;
var sourceBufferDelta = fdtGlyph.TextureChannelByteIndex; var sourceBufferDelta = fdtGlyph.TextureChannelByteIndex;
for (var y = 0; y < fdtGlyph.BoundingHeight; y++) for (var y = 0; y < fdtGlyph.BoundingHeight; y++)
{ {
var sourcePixelIndex = var sourcePixelIndex =
@ -830,11 +830,11 @@ internal class GamePrebakedFontHandle : FontHandle
sourcePixelIndex *= 4; sourcePixelIndex *= 4;
sourcePixelIndex += sourceBufferDelta; sourcePixelIndex += sourceBufferDelta;
var blend1 = horzBlend[fdtGlyph.CurrentOffsetY + y]; var blend1 = horzBlend[fdtGlyph.CurrentOffsetY + y];
var targetOffset = ((rc.Y + y) * width) + rc.X; var targetOffset = ((rc.Y + y) * width) + rc.X;
for (var x = 0; x < rc.Width; x++) for (var x = 0; x < rc.Width; x++)
pixels8[targetOffset + x] = 0; pixels8[targetOffset + x] = 0;
targetOffset += horzShift[fdtGlyph.CurrentOffsetY + y]; targetOffset += horzShift[fdtGlyph.CurrentOffsetY + y];
if (blend1 == 0) if (blend1 == 0)
{ {

View file

@ -117,7 +117,18 @@ public class Localization : IServiceType
public void SetupWithFallbacks() public void SetupWithFallbacks()
{ {
this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture; this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture;
this.LocalizationChanged?.Invoke(FallbackLangCode); foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged))
{
try
{
d(FallbackLangCode);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
Loc.SetupWithFallbacks(this.assembly); Loc.SetupWithFallbacks(this.assembly);
} }
@ -134,7 +145,17 @@ public class Localization : IServiceType
} }
this.DalamudLanguageCultureInfo = GetCultureInfoFromLangCode(langCode); this.DalamudLanguageCultureInfo = GetCultureInfoFromLangCode(langCode);
this.LocalizationChanged?.Invoke(langCode); foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged))
{
try
{
d(langCode);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", d.Method);
}
}
try try
{ {

View file

@ -527,18 +527,15 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
/// <param name="affectedThisPlugin">If this plugin was affected by the change.</param> /// <param name="affectedThisPlugin">If this plugin was affected by the change.</param>
internal void NotifyActivePluginsChanged(PluginListInvalidationKind kind, bool affectedThisPlugin) internal void NotifyActivePluginsChanged(PluginListInvalidationKind kind, bool affectedThisPlugin)
{ {
if (this.ActivePluginsChanged is { } callback) foreach (var action in Delegate.EnumerateInvocationList(this.ActivePluginsChanged))
{ {
foreach (var action in callback.GetInvocationList().Cast<IDalamudPluginInterface.ActivePluginsChangedDelegate>()) try
{ {
try action(kind, affectedThisPlugin);
{ }
action(kind, affectedThisPlugin); catch (Exception ex)
} {
catch (Exception ex) Log.Error(ex, "Exception during raise of {handler}", action.Method);
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }
} }
@ -547,18 +544,15 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
{ {
this.UiLanguage = langCode; this.UiLanguage = langCode;
if (this.LanguageChanged is { } callback) foreach (var action in Delegate.EnumerateInvocationList(this.LanguageChanged))
{ {
foreach (var action in callback.GetInvocationList().Cast<IDalamudPluginInterface.LanguageChangedDelegate>()) try
{ {
try action(langCode);
{ }
action(langCode); catch (Exception ex)
} {
catch (Exception ex) Log.Error(ex, "Exception during raise of {handler}", action.Method);
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }
} }

View file

@ -1,7 +1,8 @@
using System.Linq; using System.Collections.Generic;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.Gui.ContextMenu; using Dalamud.Game.Gui.ContextMenu;
using Dalamud.Game.Gui.NamePlate;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Serilog; using Serilog;
@ -14,25 +15,29 @@ internal static class EventHandlerExtensions
{ {
/// <summary> /// <summary>
/// Replacement for Invoke() on EventHandlers to catch exceptions that stop event propagation in case /// Replacement for Invoke() on EventHandlers to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation. /// of a thrown Exception inside an invocation.
/// </summary> /// </summary>
/// <param name="eh">The EventHandler in question.</param> /// <param name="eh">The EventHandler in question.</param>
/// <param name="sender">Default sender for Invoke equivalent.</param> /// <param name="sender">Default sender for Invoke equivalent.</param>
/// <param name="e">Default EventArgs for Invoke equivalent.</param> /// <param name="e">Default EventArgs for Invoke equivalent.</param>
public static void InvokeSafely(this EventHandler? eh, object sender, EventArgs e) public static void InvokeSafely(this EventHandler? eh, object sender, EventArgs e)
{ {
if (eh == null) foreach (var handler in Delegate.EnumerateInvocationList(eh))
return;
foreach (var handler in eh.GetInvocationList().Cast<EventHandler>())
{ {
HandleInvoke(() => handler(sender, e)); try
{
handler(sender, e);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", handler.Method);
}
} }
} }
/// <summary> /// <summary>
/// Replacement for Invoke() on generic EventHandlers to catch exceptions that stop event propagation in case /// Replacement for Invoke() on generic EventHandlers to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation. /// of a thrown Exception inside an invocation.
/// </summary> /// </summary>
/// <param name="eh">The EventHandler in question.</param> /// <param name="eh">The EventHandler in question.</param>
/// <param name="sender">Default sender for Invoke equivalent.</param> /// <param name="sender">Default sender for Invoke equivalent.</param>
@ -40,104 +45,135 @@ internal static class EventHandlerExtensions
/// <typeparam name="T">Type of EventArgs.</typeparam> /// <typeparam name="T">Type of EventArgs.</typeparam>
public static void InvokeSafely<T>(this EventHandler<T>? eh, object sender, T e) public static void InvokeSafely<T>(this EventHandler<T>? eh, object sender, T e)
{ {
if (eh == null) foreach (var handler in Delegate.EnumerateInvocationList(eh))
return;
foreach (var handler in eh.GetInvocationList().Cast<EventHandler<T>>())
{ {
HandleInvoke(() => handler(sender, e)); try
{
handler(sender, e);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", handler.Method);
}
} }
} }
/// <summary> /// <summary>
/// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case /// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation. /// of a thrown Exception inside an invocation.
/// </summary> /// </summary>
/// <param name="act">The Action in question.</param> /// <param name="act">The Action in question.</param>
public static void InvokeSafely(this Action? act) public static void InvokeSafely(this Action? act)
{ {
if (act == null) foreach (var action in Delegate.EnumerateInvocationList(act))
return;
foreach (var action in act.GetInvocationList().Cast<Action>())
{ {
HandleInvoke(action); try
{
action();
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }
/// <summary> /// <inheritdoc cref="InvokeSafely(Action)"/>
/// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation.
/// </summary>
/// <param name="act">The Action in question.</param>
/// <param name="argument">Templated argument for Action.</param>
/// <typeparam name="T">Type of Action args.</typeparam>
public static void InvokeSafely<T>(this Action<T>? act, T argument) public static void InvokeSafely<T>(this Action<T>? act, T argument)
{ {
if (act == null) foreach (var action in Delegate.EnumerateInvocationList(act))
return;
foreach (var action in act.GetInvocationList().Cast<Action<T>>())
{ {
HandleInvoke(action, argument); try
{
action(argument);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
}
}
/// <inheritdoc cref="InvokeSafely(Action)"/>
public static void InvokeSafely<T1, T2>(this Action<T1, T2>? act, T1 arg1, T2 arg2)
{
foreach (var action in Delegate.EnumerateInvocationList(act))
{
try
{
action(arg1, arg2);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }
/// <summary> /// <summary>
/// Replacement for Invoke() on OnUpdateDelegate to catch exceptions that stop event propagation in case /// Replacement for Invoke() on OnUpdateDelegate to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation. /// of a thrown Exception inside an invocation.
/// </summary> /// </summary>
/// <param name="updateDelegate">The OnUpdateDelegate in question.</param> /// <param name="updateDelegate">The OnUpdateDelegate in question.</param>
/// <param name="framework">Framework to be passed on to OnUpdateDelegate.</param> /// <param name="framework">Framework to be passed on to OnUpdateDelegate.</param>
public static void InvokeSafely(this IFramework.OnUpdateDelegate? updateDelegate, Framework framework) public static void InvokeSafely(this IFramework.OnUpdateDelegate? updateDelegate, Framework framework)
{ {
if (updateDelegate == null) foreach (var action in Delegate.EnumerateInvocationList(updateDelegate))
return;
foreach (var action in updateDelegate.GetInvocationList().Cast<IFramework.OnUpdateDelegate>())
{ {
HandleInvoke(() => action(framework)); try
{
action(framework);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }
/// <summary> /// <summary>
/// Replacement for Invoke() on OnMenuOpenedDelegate to catch exceptions that stop event propagation in case /// Replacement for Invoke() on OnMenuOpenedDelegate to catch exceptions that stop event propagation in case
/// of a thrown Exception inside of an invocation. /// of a thrown Exception inside an invocation.
/// </summary> /// </summary>
/// <param name="openedDelegate">The OnMenuOpenedDelegate in question.</param> /// <param name="openedDelegate">The OnMenuOpenedDelegate in question.</param>
/// <param name="argument">Templated argument for Action.</param> /// <param name="argument">Templated argument for Action.</param>
public static void InvokeSafely(this IContextMenu.OnMenuOpenedDelegate? openedDelegate, MenuOpenedArgs argument) public static void InvokeSafely(this IContextMenu.OnMenuOpenedDelegate? openedDelegate, MenuOpenedArgs argument)
{ {
if (openedDelegate == null) foreach (var action in Delegate.EnumerateInvocationList(openedDelegate))
return;
foreach (var action in openedDelegate.GetInvocationList().Cast<IContextMenu.OnMenuOpenedDelegate>())
{ {
HandleInvoke(() => action(argument)); try
{
action(argument);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
} }
} }
private static void HandleInvoke(Action act) /// <summary>
/// Replacement for Invoke() on OnMenuOpenedDelegate to catch exceptions that stop event propagation in case
/// of a thrown Exception inside an invocation.
/// </summary>
/// <param name="updatedDelegate">The OnMenuOpenedDelegate in question.</param>
/// <param name="context">An object containing information about the pending data update.</param>
/// <param name="handlers>">A list of handlers used for updating nameplate data.</param>
public static void InvokeSafely(
this INamePlateGui.OnPlateUpdateDelegate? updatedDelegate,
INamePlateUpdateContext context,
IReadOnlyList<INamePlateUpdateHandler> handlers)
{ {
try foreach (var action in Delegate.EnumerateInvocationList(updatedDelegate))
{ {
act(); try
} {
catch (Exception ex) action(context, handlers);
{ }
Log.Error(ex, "Exception during raise of {handler}", act.Method); catch (Exception ex)
} {
} Log.Error(ex, "Exception during raise of {handler}", action.Method);
}
private static void HandleInvoke<T>(Action<T> act, T argument)
{
try
{
act(argument);
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", act.Method);
} }
} }
} }