Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/ElectronNET.API/ElectronAppLifetimeEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace ElectronNET
{
using System;
using System.Threading.Tasks;

/// <summary>
/// Represents callbacks that are invoked during different phases of the Electron application
/// lifetime. These callbacks allow consumers to hook into the startup sequence more
/// granularly than the existing single callback. Callbacks return <see cref="Task"/>
/// enabling asynchronous work to be awaited before the next phase of the Electron runtime
/// commences.
/// </summary>
public class ElectronAppLifetimeEvents
{
/// <summary>
/// Gets or sets the callback that is invoked once the Electron process and socket bridge
/// have been established but before the Electron <c>ready</c> event has been
/// acknowledged. Use this hook to register custom protocols or perform other
/// initialization that must occur prior to the <c>ready</c> event.
/// </summary>
public Func<Task> OnBeforeReady { get; set; } = () => Task.CompletedTask;

/// <summary>
/// Gets or sets the callback that is invoked when the Electron <c>ready</c> event is
/// fired. Use this hook to create browser windows or perform post-ready initialization.
/// </summary>
public Func<Task> OnReady { get; set; } = () => Task.CompletedTask;

/// <summary>
/// Gets or sets the callback that is invoked when the Electron process is about to quit.
/// This maps to the <c>will-quit</c> event in Electron and can be used to perform
/// graceful shutdown logic. The default implementation does nothing.
/// </summary>
public Func<Task> OnWillQuit { get; set; } = () => Task.CompletedTask;

/// <summary>
/// Gets or sets the callback that is invoked when the Electron process has fully
/// terminated. This can be used for cleanup tasks. The default implementation does
/// nothing.
/// </summary>
public Func<Task> OnQuit { get; set; } = () => Task.CompletedTask;
}
}
20 changes: 20 additions & 0 deletions src/ElectronNET.API/ElectronNetOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace ElectronNET
{
/// <summary>
/// Provides configuration options for Electron.NET. Consumers can assign
/// an instance of <see cref="ElectronAppLifetimeEvents"/> to the <see cref="Events"/>
/// property to hook into the Electron application lifecycle. Additional
/// configuration properties can be added to this class in future versions without
/// breaking existing consumers.
/// </summary>
public class ElectronNetOptions
{
/// <summary>
/// Gets or sets the collection of lifecycle callbacks. The default value is
/// an instance of <see cref="ElectronAppLifetimeEvents"/> with no-op
/// implementations. Assigning a new instance or modifying individual
/// callbacks allows consumers to customize the startup sequence.
/// </summary>
public ElectronAppLifetimeEvents Events { get; set; } = new ElectronAppLifetimeEvents();
}
}
6 changes: 6 additions & 0 deletions src/ElectronNET.API/ElectronNetRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ static ElectronNetRuntime()

internal static Func<Task> OnAppReadyCallback { get; set; }

/// <summary>
/// Global configuration options for the Electron.NET runtime, including
/// lifecycle events that can be configured from the ASP.NET host builder.
/// </summary>
public static ElectronNetOptions Options { get; set; } = new ElectronNetOptions();

internal static SocketIoFacade GetSocket()
{
return RuntimeControllerCore?.Socket;
Expand Down
33 changes: 32 additions & 1 deletion src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using ElectronNET;
using ElectronNET.AspNet;
using ElectronNET.AspNet.Runtime;
using ElectronNET.Runtime;
Expand Down Expand Up @@ -57,9 +58,39 @@ public static class WebHostBuilderExtensions
/// }
/// </code>
/// </example>
[Obsolete("This method is deprecated. Please use the overload with the configure parameter")]
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<Task> onAppReadyCallback)
{
ElectronNetRuntime.OnAppReadyCallback = onAppReadyCallback;
if (onAppReadyCallback == null)
{
throw new ArgumentNullException(nameof(onAppReadyCallback));
}

// Backwards compatible overload – wraps the single callback into the new options model.
return UseElectron(builder, args, options =>
{
options.Events.OnReady = onAppReadyCallback;
});
}

/// <summary>
/// Adds Electron.NET support to the current ASP.NET Core web host with granular lifecycle
/// configuration. The provided <see cref="ElectronNetOptions"/> allows registration of callbacks
/// for different phases of the Electron runtime.
/// </summary>
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Action<ElectronNetOptions> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}

var options = new ElectronNetOptions();
configure(options);
ElectronNetRuntime.Options = options;

// Preserve behaviour of the original API by mapping OnReady to the legacy callback.
ElectronNetRuntime.OnAppReadyCallback = options.Events?.OnReady;

var webPort = PortHelper.GetFreePort(ElectronNetRuntime.AspNetWebPort ?? ElectronNetRuntime.DefaultWebPort);
ElectronNetRuntime.AspNetWebPort = webPort;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System;
using System.Threading.Tasks;
using ElectronNET;
using ElectronNET.API;
using ElectronNET.Common;
using ElectronNET.Runtime.Controllers;
Expand Down Expand Up @@ -81,9 +82,22 @@ protected void HandleStopped()
(this.aspNetLifetimeAdapter.IsNullOrStopped()))
{
this.TransitionState(LifetimeState.Stopped);

// Everything is fully stopped – fire the OnQuit callback.
Task.Run(this.RunQuitCallback);
}
}

/// <summary>
/// Invoked when ASP.NET lifetime enters Stopping (ApplicationStopping).
/// We only trigger the OnWillQuit callback here; the actual state
/// transition to Stopping is handled in <see cref="HandleStopped"/>.
/// </summary>
protected void HandleStopping()
{
Task.Run(this.RunWillQuitCallback);
}

protected abstract override Task StopCore();

private void SocketBridge_Ready(object sender, EventArgs e)
Expand All @@ -108,10 +122,67 @@ private void AspNetLifetimeAdapter_Stopped(object sender, EventArgs e)

private void AspNetLifetimeAdapter_Stopping(object sender, EventArgs e)
{
this.HandleStopping();
}

private async Task RunWillQuitCallback()
{
var events = ElectronNetRuntime.Options?.Events;
var handler = events?.OnWillQuit;

if (handler == null)
{
return;
}

try
{
await handler().ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("Exception while executing OnWillQuit callback.\n" + ex);
// We are already stopping; no need to call this.Stop() here.
}
}

private async Task RunQuitCallback()
{
var events = ElectronNetRuntime.Options?.Events;
var handler = events?.OnQuit;

if (handler == null)
{
return;
}

try
{
await handler().ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("Exception while executing OnQuit callback.\n" + ex);
}
}

private async Task RunReadyCallback()
{
var events = ElectronNetRuntime.Options?.Events;
if (events?.OnBeforeReady != null)
{
try
{
await events.OnBeforeReady().ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("Exception while executing OnBeforeReady callback. Stopping...\n" + ex);
this.Stop();
return;
}
}

if (ElectronNetRuntime.OnAppReadyCallback == null)
{
Console.WriteLine("Warning: Non OnReadyCallback provided in UseElectron() setup.");
Expand Down
29 changes: 19 additions & 10 deletions src/ElectronNET.WebApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace ElectronNET.WebApp
{
using System;
using System.Threading.Tasks;
using ElectronNET.API.Entities;

Expand All @@ -23,20 +24,28 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args)
.UseStartup<Startup>();
}

public static async Task ElectronBootstrap()
private static void ElectronBootstrap(ElectronNetOptions options)
{
//AddDevelopmentTests();

var browserWindow = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
options.Events = new()
{
Width = 1152,
Height = 940,
Show = false
});
OnBeforeReady = async () =>
{
Console.WriteLine("Firing before ready callback!");
},
OnReady = async () =>
{
var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
{
Width = 1152,
Height = 940,
Show = false
});

await browserWindow.WebContents.Session.ClearCacheAsync();
await window.WebContents.Session.ClearCacheAsync();

browserWindow.OnReadyToShow += () => browserWindow.Show();
window.OnReadyToShow += window.Show;
}
};
}

private static void AddDevelopmentTests()
Expand Down
Loading