mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-07-16 21:56:00 +00:00
Add ElectronNET.AspNet project (ASP.Net specific runtime code)
This commit is contained in:
33
src/ElectronNET.AspNet/API/ServiceCollectionExtensions.cs
Normal file
33
src/ElectronNET.AspNet/API/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the <see cref="Electron"/> Members to the Service Collection
|
||||
/// </summary>
|
||||
public static IServiceCollection AddElectron(this IServiceCollection services)
|
||||
=> services
|
||||
// adding in this manner to ensure late binding.
|
||||
.AddSingleton(_ => IpcMain.Instance)
|
||||
.AddSingleton(_ => App.Instance)
|
||||
.AddSingleton(_ => AutoUpdater.Instance)
|
||||
.AddSingleton(_ => WindowManager.Instance)
|
||||
.AddSingleton(_ => Menu.Instance)
|
||||
.AddSingleton(_ => Dialog.Instance)
|
||||
.AddSingleton(_ => Notification.Instance)
|
||||
.AddSingleton(_ => Tray.Instance)
|
||||
.AddSingleton(_ => GlobalShortcut.Instance)
|
||||
.AddSingleton(_ => Shell.Instance)
|
||||
.AddSingleton(_ => Screen.Instance)
|
||||
.AddSingleton(_ => Clipboard.Instance)
|
||||
.AddSingleton(_ => HostHook.Instance)
|
||||
.AddSingleton(_ => PowerMonitor.Instance)
|
||||
.AddSingleton(_ => NativeTheme.Instance)
|
||||
.AddSingleton(_ => Dock.Instance);
|
||||
}
|
||||
}
|
||||
103
src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs
Normal file
103
src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.AspNet;
|
||||
using ElectronNET.AspNet.Runtime;
|
||||
using ElectronNET.Runtime;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Helpers;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="IWebHostBuilder"/> to enable Electron.NET
|
||||
/// integration in ASP.NET Core applications (including Razor Pages) using the WebHost-based hosting model.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Call this extension during web host configuration (for example, inside <c>ConfigureWebHostDefaults</c> in Program.cs)
|
||||
/// to wire up Electron with any command-line arguments and an optional application-ready callback.
|
||||
/// </remarks>
|
||||
public static class WebHostBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Electron.NET support to the current ASP.NET Core web host and registers an application-ready callback.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IWebHostBuilder"/> to extend.</param>
|
||||
/// <param name="args">The command-line arguments passed to the process.</param>
|
||||
/// <param name="onAppReadyCallback">
|
||||
/// An asynchronous callback invoked when the Electron app is ready. Use this to create windows or perform initialization.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The same <see cref="IWebHostBuilder"/> instance to enable fluent configuration.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code language="csharp">
|
||||
/// using Microsoft.AspNetCore.Hosting;
|
||||
/// using Microsoft.Extensions.Hosting;
|
||||
/// using ElectronNET.API;
|
||||
///
|
||||
/// public class Program
|
||||
/// {
|
||||
/// public static void Main(string[] args)
|
||||
/// {
|
||||
/// Host.CreateDefaultBuilder(args)
|
||||
/// .ConfigureWebHostDefaults(webBuilder =>
|
||||
/// {
|
||||
/// webBuilder.UseStartup<Startup>();
|
||||
/// webBuilder.UseElectron(args, async () =>
|
||||
/// {
|
||||
/// // Create the main browser window or perform other startup tasks.
|
||||
/// });
|
||||
/// })
|
||||
/// .Build()
|
||||
/// .Run();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[] args, Func<Task> onAppReadyCallback)
|
||||
{
|
||||
ElectronNetRuntime.OnAppReadyCallback = onAppReadyCallback;
|
||||
|
||||
var webPort = PortHelper.GetFreePort(ElectronNetRuntime.AspNetWebPort ?? ElectronNetRuntime.DefaultWebPort);
|
||||
ElectronNetRuntime.AspNetWebPort = webPort;
|
||||
|
||||
// check for the content folder if its exists in base director otherwise no need to include
|
||||
// It was used before because we are publishing the project which copies everything to bin folder and contentroot wwwroot was folder there.
|
||||
// now we have implemented the live reload if app is run using /watch then we need to use the default project path.
|
||||
if (Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}\\wwwroot"))
|
||||
{
|
||||
builder = builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
|
||||
.UseUrls("http://localhost:" + webPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = builder.UseUrls("http://localhost:" + webPort);
|
||||
}
|
||||
|
||||
builder = builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddTransient<IStartupFilter, ServerReadyStartupFilter>();
|
||||
services.AddSingleton<AspNetLifetimeAdapter>();
|
||||
|
||||
switch (ElectronNetRuntime.StartupMethod)
|
||||
{
|
||||
case StartupMethod.PackagedElectronFirst:
|
||||
case StartupMethod.UnpackedElectronFirst:
|
||||
services.AddSingleton<IElectronNetRuntimeController, RuntimeControllerAspNetElectronFirst>();
|
||||
break;
|
||||
case StartupMethod.PackagedDotnetFirst:
|
||||
case StartupMethod.UnpackedDotnetFirst:
|
||||
services.AddSingleton<IElectronNetRuntimeController, RuntimeControllerAspNetDotnetFirst>();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/ElectronNET.AspNet/ElectronNET.AspNet.csproj
Normal file
44
src/ElectronNET.AspNet/ElectronNET.AspNet.csproj
Normal file
@@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\common.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
|
||||
<PackageId>$(PackageNamePrefix).AspNet</PackageId>
|
||||
<Title>$(PackageId)</Title>
|
||||
<Description>$(DescriptionFirstPart) This package contains the ASP.Net Core integration.</Description>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<IncludeSymbols>True</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<Nullable>disable</Nullable>
|
||||
<RootNamespace>ElectronNET</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net6.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net6.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PackageIcon.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ElectronNET.API\ElectronNET.API.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
BIN
src/ElectronNET.AspNet/PackageIcon.png
Normal file
BIN
src/ElectronNET.AspNet/PackageIcon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,132 @@
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.Common;
|
||||
using ElectronNET.Runtime.Controllers;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Services.SocketBridge;
|
||||
|
||||
internal abstract class RuntimeControllerAspNetBase : RuntimeControllerBase
|
||||
{
|
||||
private readonly AspNetLifetimeAdapter aspNetLifetimeAdapter;
|
||||
private SocketBridgeService socketBridge;
|
||||
|
||||
protected RuntimeControllerAspNetBase(AspNetLifetimeAdapter aspNetLifetimeAdapter)
|
||||
{
|
||||
this.aspNetLifetimeAdapter = aspNetLifetimeAdapter;
|
||||
this.aspNetLifetimeAdapter.Ready += this.AspNetLifetimeAdapter_Ready;
|
||||
this.aspNetLifetimeAdapter.Stopping += this.AspNetLifetimeAdapter_Stopping;
|
||||
this.aspNetLifetimeAdapter.Stopped += this.AspNetLifetimeAdapter_Stopped;
|
||||
|
||||
ElectronNetRuntime.RuntimeControllerCore = this;
|
||||
}
|
||||
|
||||
internal override SocketBridgeService SocketBridge => this.socketBridge;
|
||||
|
||||
internal override SocketIoFacade Socket
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.State == LifetimeState.Ready)
|
||||
{
|
||||
return this.socketBridge.Socket;
|
||||
}
|
||||
|
||||
throw new Exception("Cannot access socket bridge. Runtime is not in 'Ready' state");
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateSocketBridge(int port)
|
||||
{
|
||||
this.socketBridge = new SocketBridgeService(port);
|
||||
this.socketBridge.Ready += this.SocketBridge_Ready;
|
||||
this.socketBridge.Stopped += this.SocketBridge_Stopped;
|
||||
this.socketBridge.Start();
|
||||
}
|
||||
|
||||
protected void HandleReady()
|
||||
{
|
||||
if (this.SocketBridge.IsReady() &&
|
||||
this.ElectronProcess.IsReady() &&
|
||||
this.aspNetLifetimeAdapter.IsReady())
|
||||
{
|
||||
this.TransitionState(LifetimeState.Ready);
|
||||
Task.Run(this.RunReadyCallback);
|
||||
}
|
||||
}
|
||||
|
||||
protected void HandleStopped()
|
||||
{
|
||||
this.TransitionState(LifetimeState.Stopping);
|
||||
|
||||
if (this.SocketBridge.IsNotStopped())
|
||||
{
|
||||
this.SocketBridge.Stop();
|
||||
}
|
||||
|
||||
if (this.ElectronProcess.IsNotStopped())
|
||||
{
|
||||
this.ElectronProcess.Stop();
|
||||
}
|
||||
|
||||
if (this.aspNetLifetimeAdapter.IsNotStopped())
|
||||
{
|
||||
this.aspNetLifetimeAdapter.Stop();
|
||||
}
|
||||
|
||||
if ((this.SocketBridge.IsNullOrStopped()) &&
|
||||
(this.ElectronProcess.IsNullOrStopped()) &&
|
||||
(this.aspNetLifetimeAdapter.IsNullOrStopped()))
|
||||
{
|
||||
this.TransitionState(LifetimeState.Stopped);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract override Task StopCore();
|
||||
|
||||
private void SocketBridge_Ready(object sender, EventArgs e)
|
||||
{
|
||||
this.HandleReady();
|
||||
}
|
||||
|
||||
private void AspNetLifetimeAdapter_Ready(object sender, EventArgs e)
|
||||
{
|
||||
this.HandleReady();
|
||||
}
|
||||
|
||||
private void SocketBridge_Stopped(object sender, EventArgs e)
|
||||
{
|
||||
this.HandleStopped();
|
||||
}
|
||||
|
||||
private void AspNetLifetimeAdapter_Stopped(object sender, EventArgs e)
|
||||
{
|
||||
this.HandleStopped();
|
||||
}
|
||||
|
||||
private void AspNetLifetimeAdapter_Stopping(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private async Task RunReadyCallback()
|
||||
{
|
||||
if (ElectronNetRuntime.OnAppReadyCallback == null)
|
||||
{
|
||||
Console.WriteLine("Warning: Non OnReadyCallback provided in UseElectron() setup.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ElectronNetRuntime.OnAppReadyCallback().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Exception while executing OnAppReadyCallback. Stopping...\n" + ex);
|
||||
this.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.Common;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Helpers;
|
||||
using ElectronNET.Runtime.Services.ElectronProcess;
|
||||
|
||||
internal class RuntimeControllerAspNetDotnetFirst : RuntimeControllerAspNetBase
|
||||
{
|
||||
private ElectronProcessBase electronProcess;
|
||||
private int? port;
|
||||
|
||||
public RuntimeControllerAspNetDotnetFirst(AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(aspNetLifetimeAdapter)
|
||||
{
|
||||
}
|
||||
|
||||
internal override ElectronProcessBase ElectronProcess => this.electronProcess;
|
||||
|
||||
protected override Task StartCore()
|
||||
{
|
||||
var isUnPacked = ElectronNetRuntime.StartupMethod.IsUnpackaged();
|
||||
var electronBinaryName = ElectronNetRuntime.ElectronExecutable;
|
||||
var args = Environment.CommandLine;
|
||||
this.port = ElectronNetRuntime.ElectronSocketPort;
|
||||
|
||||
if (!this.port.HasValue)
|
||||
{
|
||||
this.port = PortHelper.GetFreePort(ElectronNetRuntime.DefaultSocketPort);
|
||||
ElectronNetRuntime.ElectronSocketPort = this.port;
|
||||
}
|
||||
|
||||
this.electronProcess = new ElectronProcessActive(isUnPacked, electronBinaryName, args, this.port.Value);
|
||||
this.electronProcess.Ready += this.ElectronProcess_Ready;
|
||||
this.electronProcess.Stopped += this.ElectronProcess_Stopped;
|
||||
|
||||
return this.electronProcess.Start();
|
||||
}
|
||||
|
||||
protected override Task StopCore()
|
||||
{
|
||||
this.electronProcess.Stop();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ElectronProcess_Ready(object sender, EventArgs e)
|
||||
{
|
||||
this.TransitionState(LifetimeState.Started);
|
||||
this.CreateSocketBridge(this.port!.Value);
|
||||
}
|
||||
|
||||
private void ElectronProcess_Stopped(object sender, EventArgs e)
|
||||
{
|
||||
this.HandleStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Services.ElectronProcess;
|
||||
|
||||
internal class RuntimeControllerAspNetElectronFirst : RuntimeControllerAspNetBase
|
||||
{
|
||||
private ElectronProcessBase electronProcess;
|
||||
private int? port;
|
||||
|
||||
public RuntimeControllerAspNetElectronFirst(AspNetLifetimeAdapter aspNetLifetimeAdapter) : base(aspNetLifetimeAdapter)
|
||||
{
|
||||
}
|
||||
|
||||
internal override ElectronProcessBase ElectronProcess => this.electronProcess;
|
||||
|
||||
protected override Task StartCore()
|
||||
{
|
||||
this.port = ElectronNetRuntime.ElectronSocketPort;
|
||||
|
||||
if (!this.port.HasValue)
|
||||
{
|
||||
throw new Exception("No port has been specified by Electron!");
|
||||
}
|
||||
|
||||
if (!ElectronNetRuntime.ElectronProcessId.HasValue)
|
||||
{
|
||||
throw new Exception("No electronPID has been specified by Electron!");
|
||||
}
|
||||
|
||||
this.CreateSocketBridge(this.port!.Value);
|
||||
|
||||
this.electronProcess = new ElectronProcessPassive(ElectronNetRuntime.ElectronProcessId.Value);
|
||||
this.electronProcess.Stopped += this.ElectronProcess_Stopped;
|
||||
|
||||
this.electronProcess.Start();
|
||||
|
||||
Task.Run(() => this.TransitionState(LifetimeState.Started));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task StopCore()
|
||||
{
|
||||
this.electronProcess.Stop();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ElectronProcess_Stopped(object sender, EventArgs e)
|
||||
{
|
||||
this.HandleStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace ElectronNET.AspNet
|
||||
{
|
||||
using System;
|
||||
using ElectronNET.AspNet.Runtime;
|
||||
using ElectronNET.Runtime;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
internal sealed class ServerReadyStartupFilter : IStartupFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Extends the provided <paramref name="next" /> and returns an <see cref="T:System.Action" /> of the same type.
|
||||
/// </summary>
|
||||
/// <param name="next">The Configure method to extend.</param>
|
||||
/// <returns>A modified <see cref="T:System.Action" />.</returns>
|
||||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||
{
|
||||
return app =>
|
||||
{
|
||||
_ = app.ApplicationServices.GetService<AspNetLifetimeAdapter>();
|
||||
var runtimeController = app.ApplicationServices.GetService<IElectronNetRuntimeController>();
|
||||
|
||||
runtimeController.Start();
|
||||
|
||||
next(app);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace ElectronNET.AspNet.Runtime
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using ElectronNET.Runtime.Data;
|
||||
using ElectronNET.Runtime.Services;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
internal class AspNetLifetimeAdapter : LifetimeServiceBase
|
||||
{
|
||||
private readonly IHostApplicationLifetime lifetimeSercice;
|
||||
|
||||
public AspNetLifetimeAdapter(IHostApplicationLifetime lifetimeSercice)
|
||||
{
|
||||
this.lifetimeSercice = lifetimeSercice;
|
||||
|
||||
this.lifetimeSercice.ApplicationStarted.Register(() => this.TransitionState(LifetimeState.Ready));
|
||||
this.lifetimeSercice.ApplicationStopping.Register(() => this.TransitionState(LifetimeState.Stopping));
|
||||
this.lifetimeSercice.ApplicationStopped.Register(() => this.TransitionState(LifetimeState.Stopped));
|
||||
}
|
||||
|
||||
protected override Task StopCore()
|
||||
{
|
||||
this.lifetimeSercice.StopApplication();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user