Compare commits

..

60 Commits

Author SHA1 Message Date
Florian Rappl
9cb210040d Fixed #1105 2026-09-03 16:25:47 +02:00
Florian Rappl
7e8f45b16d Updated changelog 2026-09-03 16:17:19 +02:00
Florian Rappl
76dc687269 Merged 2026-09-03 11:28:37 +02:00
Florian Rappl
dcf4585510 Merge pull request #1108 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/humanfs/node-0.16.8
Bump @humanfs/node from 0.16.7 to 0.16.8 in /src/ElectronNET.WebApp/ElectronHostHook
2026-09-03 08:54:37 +02:00
dependabot[bot]
a6d7434adf Bump @humanfs/node in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [@humanfs/node](https://github.com/humanwhocodes/humanfs/tree/HEAD/packages/node) from 0.16.7 to 0.16.8.
- [Release notes](https://github.com/humanwhocodes/humanfs/releases)
- [Changelog](https://github.com/humanwhocodes/humanfs/blob/main/packages/node/CHANGELOG.md)
- [Commits](https://github.com/humanwhocodes/humanfs/commits/node-v0.16.8/packages/node)

---
updated-dependencies:
- dependency-name: "@humanfs/node"
  dependency-version: 0.16.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-03 00:19:56 +00:00
Florian Rappl
76ff44f67e Merge pull request #1104 from epsitec-sa/feat/#1095
chore(build): allow customization of target framwork version in dev mode (#1095)
2026-08-14 13:07:48 +02:00
Samuel Nussbaum
20ab82b16d chore(build): fix error when props file is missing 2026-08-14 11:25:19 +02:00
Samuel Nussbaum
b27f705a6a chore(build): Change import barriere 2026-08-14 10:52:10 +02:00
Samuel Nussbaum
8697c4de44 chore(build): passthrough props barriere 2026-08-14 10:07:58 +02:00
Florian Rappl
376a791e48 Merge pull request #1099 from epsitec-sa/bug/#1096
chore(build): fix #1096 by depends on resolve unrelated target
2026-08-11 09:45:16 +02:00
Florian Rappl
195a29052e Merge pull request #1100 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/js-yaml-4.3.1
Bump js-yaml from 4.3.0 to 4.3.1 in /src/ElectronNET.WebApp/ElectronHostHook
2026-08-11 09:31:59 +02:00
Florian Rappl
f577a53227 Merge pull request #1102 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.Host/electron-39.8.10
Bump electron from 30.5.1 to 39.8.10 in /src/ElectronNET.Host
2026-08-11 08:52:04 +02:00
Florian Rappl
382d544468 Merge pull request #1101 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/brace-expansion-1.1.18
Bump brace-expansion from 1.1.14 to 1.1.18 in /src/ElectronNET.WebApp/ElectronHostHook
2026-08-11 08:51:42 +02:00
dependabot[bot]
18f08f9aff Bump electron from 30.5.1 to 39.8.10 in /src/ElectronNET.Host
Bumps [electron](https://github.com/electron/electron) from 30.5.1 to 39.8.10.
- [Release notes](https://github.com/electron/electron/releases)
- [Commits](https://github.com/electron/electron/compare/v30.5.1...v39.8.10)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 39.8.10
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 23:17:12 +00:00
dependabot[bot]
def289ab54 Bump brace-expansion in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.14 to 1.1.18.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.14...v1.1.18)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.18
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 23:17:02 +00:00
dependabot[bot]
4011f3d30b Bump js-yaml in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 4.3.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.3.0...4.3.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 23:17:01 +00:00
Samuel Nussbaum
26b9d86ee4 chore(build): target frameworks customization 2026-08-10 10:33:00 +02:00
Florian Rappl
3b57de976a Merge pull request #1098 from epsitec-sa/bug/#1097
fix(packaging): Restore cross-compile on same platform and packaging dir name behavior
2026-08-10 10:19:45 +02:00
Samuel Nussbaum
d9e9b3d2e6 fix(packaging): Architecture must be defined in electron-builder compile args in order to allow cross-compile on same platform. Enforce default-arch to be the default arch in order to avoid arch in package directory name. 2026-08-10 09:22:21 +02:00
Samuel Nussbaum
1ec1fde3c6 chore(build): fix #1096 by depends on not resolve related target 2026-08-07 14:37:13 +02:00
Florian Rappl
e5ccf75c9f Merge pull request #1094 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.Host/ElectronHostHook/socket.io-parser-4.2.7
Bump socket.io-parser from 4.2.6 to 4.2.7 in /src/ElectronNET.Host/ElectronHostHook
2026-08-05 09:08:03 +02:00
dependabot[bot]
819e643ab8 Bump socket.io-parser in /src/ElectronNET.Host/ElectronHostHook
Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.6 to 4.2.7.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.6...socket.io-parser@4.2.7)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-version: 4.2.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-05 05:07:46 +00:00
Florian Rappl
7fe3f9b5b6 Merge pull request #1093 from ElectronNET/develop
Release 0.5.2
2026-08-03 14:53:48 +02:00
Florian Rappl
16cbc5f2a4 Improved platform agnostic for test 2026-08-03 14:36:20 +02:00
Florian Rappl
3a2c997e6a Ensure windows are not retained after being destroyed #1008 2026-08-03 14:25:47 +02:00
Florian Rappl
f88f5f8b18 Added tests, improved icon implementation 2026-08-03 14:19:25 +02:00
Florian Rappl
4e39f5f377 Added support for MacOS modern app icon #1047 2026-08-03 14:12:09 +02:00
Florian Rappl
7fa5ec5b0b Fixed #1050 2026-08-03 14:09:01 +02:00
Florian Rappl
2f8f717dbb Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-03 11:13:32 +02:00
Florian Rappl
525d6185fb Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-03 11:13:08 +02:00
Florian Rappl
73703819be Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-03 11:12:56 +02:00
Florian Rappl
09249f74c6 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-03 11:12:41 +02:00
Florian Rappl
cfd8be36f3 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-03 11:12:19 +02:00
Florian Rappl
6055635eb5 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-03 11:11:15 +02:00
Florian Rappl
74a734ae58 Merge pull request #1092 from epsitec-sa/bug/#1091
Bug/#1091 - Fix CA1416 on AspNet project
2026-07-29 11:05:29 +02:00
Samuel Nussbaum
d4402d1040 chore(build): Apply same NoWarn regardless of current runtime, platform or architecture in order to avoid compile issue when used in development mode 2026-07-29 09:47:00 +02:00
Florian Rappl
4b65ed26a4 Merge pull request #1089 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/flatted-3.4.3
Bump flatted from 3.3.3 to 3.4.3 in /src/ElectronNET.WebApp/ElectronHostHook
2026-07-28 09:07:22 +02:00
Florian Rappl
e12145004e Merge pull request #1090 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.Host/socket.io-parser-4.2.7
Bump socket.io-parser from 4.2.5 to 4.2.7 in /src/ElectronNET.Host
2026-07-28 09:07:11 +02:00
Florian Rappl
3e8ebde05b Merge pull request #1088 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.Host/multi-32449639ce
Bump builder-util-runtime and electron-updater in /src/ElectronNET.Host
2026-07-28 09:06:58 +02:00
Florian Rappl
820d7bbcec Merge pull request #1087 from epsitec-sa/bug/#1084
Bug/#1084 - Visual studio loading issue
2026-07-28 09:05:52 +02:00
Samuel Nussbaum
5eeab1bd24 fix(warning): remove warining CS4014 2026-07-28 06:54:48 +02:00
Roger Vuistiner
1fff688098 chore: consolidate MSBuild configuration barriers
- Moved Directory.Build.* barriers from nuke to root folder to cover all projects.
- Added a Directory.Packages.props barrier to stop central package management.
- Removed obsolete 'ManagePackageVersionsCentrally' properties.
2026-07-28 06:54:43 +02:00
dependabot[bot]
9b701f5258 Bump socket.io-parser from 4.2.5 to 4.2.7 in /src/ElectronNET.Host
Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.5 to 4.2.7.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.7)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-version: 4.2.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-28 03:41:41 +00:00
dependabot[bot]
b41c7e51b1 Bump flatted in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.3.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.3)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-28 03:41:32 +00:00
dependabot[bot]
1d24067f4b Bump builder-util-runtime and electron-updater in /src/ElectronNET.Host
Bumps [builder-util-runtime](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/builder-util-runtime) to 9.7.0 and updates ancestor dependency [electron-updater](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-updater). These dependencies need to be updated together.


Updates `builder-util-runtime` from 9.5.1 to 9.7.0
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/builder-util-runtime/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/HEAD/packages/builder-util-runtime)

Updates `electron-updater` from 6.8.3 to 6.8.9
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/electron-updater@6.8.9/packages/electron-updater)

---
updated-dependencies:
- dependency-name: builder-util-runtime
  dependency-version: 9.7.0
  dependency-type: indirect
- dependency-name: electron-updater
  dependency-version: 6.8.9
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-28 03:41:30 +00:00
Samuel Nussbaum
44e5c4e230 chore(build): runtime identifier is not required anymore 2026-07-27 16:45:52 +02:00
Samuel Nussbaum
0b3053477d chore(dx): visual studio load regression, let electron choose the required runtime. 2026-07-27 16:45:52 +02:00
Samuel Nussbaum
3fecc0ee1b fix: Visual studio load issue 2026-07-27 16:45:51 +02:00
Florian Rappl
95089e1721 Merge pull request #1085 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/js-yaml-4.3.0
Bump js-yaml from 4.1.1 to 4.3.0 in /src/ElectronNET.WebApp/ElectronHostHook
2026-07-22 07:12:18 +02:00
Florian Rappl
cec178cf50 Merge pull request #1086 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.Host/brace-expansion-1.1.16
Bump brace-expansion from 1.1.12 to 1.1.16 in /src/ElectronNET.Host
2026-07-22 07:12:07 +02:00
dependabot[bot]
20af8a692c Bump brace-expansion from 1.1.12 to 1.1.16 in /src/ElectronNET.Host
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 1.1.16.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.12...v1.1.16)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.16
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-22 02:19:22 +00:00
dependabot[bot]
4e69bcf030 Bump js-yaml in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.3.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.1...4.3.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-22 02:19:18 +00:00
Florian Rappl
5ae5bf0df7 Merge pull request #1083 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.Host/js-yaml-4.3.0
Bump js-yaml from 4.1.1 to 4.3.0 in /src/ElectronNET.Host
2026-07-15 07:12:13 +02:00
dependabot[bot]
5da5d1c618 Bump js-yaml from 4.1.1 to 4.3.0 in /src/ElectronNET.Host
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.3.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.1...4.3.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 22:27:36 +00:00
Florian Rappl
b6e496b2c6 Merge pull request #1082 from ElectronNET/dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/js-yaml-4.3.0
Bump js-yaml from 4.1.1 to 4.3.0 in /src/ElectronNET.WebApp/ElectronHostHook
2026-07-09 15:33:25 +02:00
Florian Rappl
5974cb9e43 Merge branch 'develop' into dependabot/npm_and_yarn/src/ElectronNET.WebApp/ElectronHostHook/js-yaml-4.3.0 2026-07-09 10:42:03 +02:00
dependabot[bot]
d4d4e9dddd Bump js-yaml in /src/ElectronNET.WebApp/ElectronHostHook
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.3.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.1.1...4.3.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-08 23:02:16 +00:00
Florian Rappl
3b5f21c724 Merge pull request #1081 from epsitec-sa/bug/default-runtime
chore: use default sdk runtime identifier
2026-07-06 15:17:02 +02:00
Samuel Nussbaum
5aebe807c3 change to less brittle solution 2026-07-06 11:02:48 +02:00
Samuel Nussbaum
bdee008cc8 use default sdk runtime identifier 2026-07-06 10:44:05 +02:00
28 changed files with 510 additions and 166 deletions

View File

@@ -1,9 +1,25 @@
# 0.6.0
## ElectronNET.Core
- Updated dependencies
- Fixed socket bridge connection when a system proxy is configured (#1105)
- Fixed cross-compilation behavior on same platform (#1098) @epsnm
- Fixed resolution of unrelated target (#1099) @epsnm
- Added target framework customization (#1095) @epsnm
# 0.5.2
## ElectronNET.Core
- Fixed token param being appended to external URLs (#1075)
- Fixed startup mode discriminator in case of existing `wwwroot` (#1050)
- Fixed CA1416 on ASP.NET project (#1091) @epsnm
- Fixed loading issue in plain Visual Studio (#1084) @epsnm
- Fixed bloated ASAR file (#1080) @epsnm
- Improved selection of runtime identifier (#1081) @epsnm
- Added more variants for `UseElectron` (#1076) @AeonSake
- Added support for modern MacOS app icon (#1047)
# 0.5.1

15
Directory.Build.props Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This file prevents unintended imports of unrelated MSBuild files -->
<!-- Uncomment to include parent Directory.Build.props file -->
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />-->
<PropertyGroup>
<ElectronNetBuildProps>$([MSBuild]::GetPathOfFileAbove('ElectronNet.Build.props', '$(MSBuildThisFileDirectory)../'))</ElectronNetBuildProps>
</PropertyGroup>
<!-- Only import specific project related MSBuild file -->
<Import Project="$(ElectronNetBuildProps)" Condition=" '$(ElectronNetBuildProps)' != '' " />
</Project>

View File

@@ -2,7 +2,7 @@
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This file prevents unintended imports of unrelated MSBuild files -->
<!-- Uncomment to include parent Directory.Build.props file -->
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />-->
<!-- Uncomment to include parent Directory.Packages.props file -->
<!--<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Packages.props', '$(MSBuildThisFileDirectory)../'))" />-->
</Project>

View File

@@ -54,7 +54,7 @@ Add the Electron.NET configuration to your `.csproj` file:
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ElectronNET.Core" Version="0.5.1" />
<PackageReference Include="ElectronNET.Core" Version="0.6.0" />
</ItemGroup>
```

View File

@@ -70,6 +70,12 @@ Since electron builder still expects a `package.json` file to exist, ElectronNET
}
```
### App Icon Path
The `ElectronIcon` property supports classic icon files (such as `.ico` and `.icns`) and modern macOS `.icon` app icon packages.
For `.icon`, provide the folder path (for example `Assets/MyApp.icon`). During build/publish, Electron.NET copies the full directory into the Electron output so `electron-builder.json` can reference it (for example `"mac": { "icon": "MyApp.icon" }`).
### Node.js Integration
Electron.NET requires Node.js integration to be enabled for IPC to function. If you are not using the IPC functionality you can disable Node.js integration like so:

View File

@@ -19,6 +19,7 @@ namespace ElectronNET.API
internal WindowManager()
{
EnsureBrowserWindowClosedSubscription();
}
internal static WindowManager Instance
@@ -106,17 +107,6 @@ namespace ElectronNET.API
tcs.SetResult(browserWindow);
});
BridgeConnector.Socket.Once<int[]>("BrowserWindowClosed", (ids) =>
{
for (int index = 0; index < _browserWindows.Count; index++)
{
if (!ids.Contains(_browserWindows[index].Id))
{
_browserWindows.RemoveAt(index);
}
}
});
if (loadUrl.Equals("http://localhost", StringComparison.OrdinalIgnoreCase) && ElectronNetRuntime.AspNetWebPort.HasValue)
{
loadUrl = $"{loadUrl}:{ElectronNetRuntime.AspNetWebPort}";
@@ -149,6 +139,40 @@ namespace ElectronNET.API
return await tcs.Task.ConfigureAwait(false);
}
private readonly object _browserWindowSubscriptionSync = new();
private bool _browserWindowClosedSubscribed;
private void EnsureBrowserWindowClosedSubscription()
{
if (_browserWindowClosedSubscribed)
{
return;
}
lock (_browserWindowSubscriptionSync)
{
if (_browserWindowClosedSubscribed)
{
return;
}
BridgeConnector.Socket.On<int[]>("BrowserWindowClosed", HandleBrowserWindowClosed);
_browserWindowClosedSubscribed = true;
}
}
private void HandleBrowserWindowClosed(int[] ids)
{
if (ids == null || ids.Length == 0)
{
_browserWindows.Clear();
return;
}
var existingIds = ids.ToHashSet();
_browserWindows.RemoveAll(window => !existingIds.Contains(window.Id));
}
private bool IsWindows10()
{
return RuntimeInformation.OSDescription.Contains("Windows 10");

View File

@@ -4,6 +4,7 @@ namespace ElectronNET.API;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using ElectronNET.API.Serialization;
using SocketIO.Serializer.SystemTextJson;
@@ -18,13 +19,21 @@ internal class SocketIOConnection : ISocketConnection
public SocketIOConnection(string uri, string authorization)
{
var opts = string.IsNullOrEmpty(authorization) ? new SocketIOOptions() : new SocketIOOptions
var opts = new SocketIOOptions
{
ExtraHeaders = new Dictionary<string, string>
// The bridge is a loopback IPC channel, so it must never go through a
// (system) proxy - otherwise the handshake never reaches the socket server.
Proxy = DirectProxy.Instance,
};
if (!string.IsNullOrEmpty(authorization))
{
opts.ExtraHeaders = new Dictionary<string, string>
{
["authorization"] = authorization
},
};
};
}
_socket = new SocketIO(uri, opts);
_socket.Serializer = new SystemTextJsonSerializer(ElectronJson.Options);
// Use default System.Text.Json serializer from SocketIOClient.
@@ -152,4 +161,15 @@ internal class SocketIOConnection : ISocketConnection
throw new ObjectDisposedException(nameof(SocketIOConnection));
}
}
private sealed class DirectProxy : IWebProxy
{
public static readonly DirectProxy Instance = new DirectProxy();
public ICredentials Credentials { get; set; }
public Uri GetProxy(Uri destination) => destination;
public bool IsBypassed(Uri host) => true;
}
}

View File

@@ -3,7 +3,7 @@
<Import Project="..\common.props" />
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
<PackageId>$(PackageNamePrefix).API</PackageId>
<Title>$(PackageId)</Title>
@@ -29,7 +29,7 @@
</PackageReference>
<PackageReference Include="SocketIOClient" Version="3.1.2" />
<PackageReference Include="System.Drawing.Common" Version="8.0.22" />
<PackageReference Include="System.Text.Json" Version="8.0.6" />
<PackageReference Include="System.Text.Json" Version="8.0.6" Condition=" '$(TargetFramework)' == 'net6.0' " />
</ItemGroup>
<ItemGroup>

View File

@@ -216,14 +216,16 @@
var webPort = ElectronNetRuntime.AspNetWebPort ?? 0;
// 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.
// In packaged mode, static content is deployed alongside the app binaries, so we must
// point content root to the process base directory. In unpackaged/watch scenarios we
// keep the default project content root to preserve live reload behavior.
var isPackagedStartup = ElectronNetRuntime.StartupMethod == StartupMethod.PackagedElectronFirst ||
ElectronNetRuntime.StartupMethod == StartupMethod.PackagedDotnetFirst;
// For port 0 (dynamic port assignment), Kestrel requires binding to specific IP (127.0.0.1) not localhost
var host = webPort == 0 ? "127.0.0.1" : "localhost";
if (Directory.Exists($"{AppDomain.CurrentDomain.BaseDirectory}\\wwwroot"))
if (isPackagedStartup)
{
builder = builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
.UseUrls($"http://{host}:{webPort}");

View File

@@ -3,7 +3,7 @@
<Import Project="..\common.props" />
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
<PackageId>$(PackageNamePrefix).AspNet</PackageId>
<Title>$(PackageId)</Title>
@@ -13,23 +13,6 @@
<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)'=='Debug|net10.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>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net10.0|AnyCPU'">
<NoWarn>1701;1702;4014;CS4014;CA1416;CS1591</NoWarn>
</PropertyGroup>
<ItemGroup>

View File

@@ -145,7 +145,7 @@
catch (Exception ex)
{
Console.WriteLine("Exception while executing OnAppReadyCallback. Stopping...\n" + ex);
this.Stop();
_ = this.Stop();
}
}
}

View File

@@ -70,7 +70,7 @@
<ProjectReference Include="..\ElectronNET.API\ElectronNET.API.csproj" Condition="$(ElectronNetDevMode)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ElectronNET.Core" Version="0.5.1" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
</ItemGroup>
<Import Project="..\ElectronNET\build\ElectronNET.Core.targets" Condition="$(ElectronNetDevMode)" />

View File

@@ -247,9 +247,9 @@
}
},
"node_modules/socket.io-parser": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz",
"integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==",
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
"integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",

View File

@@ -230,7 +230,10 @@ module.exports = (socket, app) => {
window = app["mainWindow"];
if (window) {
window.reload();
windows.push(window);
synchronizeWindowRegistry();
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
windows.push(window);
}
electronSocket.emit("BrowserWindowCreated", window.id);
return;
}
@@ -253,21 +256,9 @@ module.exports = (socket, app) => {
}
});
lastOptions = options;
window.on("closed", (sender) => {
for (let index = 0; index < windows.length; index++) {
const windowItem = windows[index];
try {
windowItem.id;
}
catch (error) {
if (error.message === "Object has been destroyed") {
windows.splice(index, 1);
const ids = [];
windows.forEach((x) => ids.push(x.id));
electronSocket.emit("BrowserWindowClosed", ids);
}
}
}
window.on("closed", () => {
synchronizeWindowRegistry();
emitBrowserWindowClosed();
});
app.on("activate", () => {
// On macOS it's common to re-create a window in the app when the
@@ -703,12 +694,47 @@ module.exports = (socket, app) => {
getWindowById(id).setBrowserView((0, browserView_1.browserViewMediateService)(browserViewId));
});
function getWindowById(id) {
const runtimeWindow = electron_1.BrowserWindow.fromId(id);
if (runtimeWindow) {
return runtimeWindow;
}
synchronizeWindowRegistry();
for (let index = 0; index < windows.length; index++) {
const element = windows[index];
if (element.id === id) {
if (tryGetWindowId(element) === id) {
return element;
}
}
throw new Error(`BrowserWindow with id '${id}' was not found.`);
}
function tryGetWindowId(element) {
try {
return element.id;
}
catch {
return null;
}
}
function synchronizeWindowRegistry() {
const runtimeWindows = electron_1.BrowserWindow.getAllWindows();
const runtimeWindowIds = new Set(runtimeWindows.map((entry) => entry.id));
for (let index = windows.length - 1; index >= 0; index--) {
const windowId = tryGetWindowId(windows[index]);
if (windowId === null || !runtimeWindowIds.has(windowId)) {
windows.splice(index, 1);
}
}
readyToShowWindowsIds = readyToShowWindowsIds.filter((entryId) => runtimeWindowIds.has(entryId));
}
function emitBrowserWindowClosed() {
const ids = [];
for (const entry of windows) {
const windowId = tryGetWindowId(entry);
if (windowId !== null) {
ids.push(windowId);
}
}
electronSocket.emit("BrowserWindowClosed", ids);
}
};
//# sourceMappingURL=browserWindows.js.map

View File

@@ -255,7 +255,10 @@ export = (socket: Socket, app: Electron.App) => {
window = app["mainWindow"];
if (window) {
window.reload();
windows.push(window);
synchronizeWindowRegistry();
if (!windows.some((entry) => tryGetWindowId(entry) === window.id)) {
windows.push(window);
}
electronSocket.emit("BrowserWindowCreated", window.id);
return;
}
@@ -283,21 +286,9 @@ export = (socket: Socket, app: Electron.App) => {
lastOptions = options;
window.on("closed", (sender) => {
for (let index = 0; index < windows.length; index++) {
const windowItem = windows[index];
try {
windowItem.id;
} catch (error) {
if (error.message === "Object has been destroyed") {
windows.splice(index, 1);
const ids = [];
windows.forEach((x) => ids.push(x.id));
electronSocket.emit("BrowserWindowClosed", ids);
}
}
}
window.on("closed", () => {
synchronizeWindowRegistry();
emitBrowserWindowClosed();
});
app.on("activate", () => {
@@ -907,11 +898,57 @@ export = (socket: Socket, app: Electron.App) => {
});
function getWindowById(id: number): Electron.BrowserWindow {
const runtimeWindow = BrowserWindow.fromId(id);
if (runtimeWindow) {
return runtimeWindow;
}
synchronizeWindowRegistry();
for (let index = 0; index < windows.length; index++) {
const element = windows[index];
if (element.id === id) {
if (tryGetWindowId(element) === id) {
return element;
}
}
throw new Error(`BrowserWindow with id '${id}' was not found.`);
}
function tryGetWindowId(element: Electron.BrowserWindow): number | null {
try {
return element.id;
} catch {
return null;
}
}
function synchronizeWindowRegistry(): void {
const runtimeWindows = BrowserWindow.getAllWindows();
const runtimeWindowIds = new Set(runtimeWindows.map((entry) => entry.id));
for (let index = windows.length - 1; index >= 0; index--) {
const windowId = tryGetWindowId(windows[index]);
if (windowId === null || !runtimeWindowIds.has(windowId)) {
windows.splice(index, 1);
}
}
readyToShowWindowsIds = readyToShowWindowsIds.filter((entryId) =>
runtimeWindowIds.has(entryId),
);
}
function emitBrowserWindowClosed(): void {
const ids: number[] = [];
for (const entry of windows) {
const windowId = tryGetWindowId(entry);
if (windowId !== null) {
ids.push(windowId);
}
}
electronSocket.emit("BrowserWindowClosed", ids);
}
};

View File

@@ -11,13 +11,13 @@
"dependencies": {
"dasherize": "^2.0.0",
"electron-host-hook": "file:./ElectronHostHook",
"electron-updater": "^6.6.2",
"electron-updater": "^6.8.9",
"image-size": "^1.2.1",
"socket.io": "^4.8.1"
},
"devDependencies": {
"@types/node": "^22.18",
"electron": "^30.0.3",
"electron": "^39.8.10",
"eslint": "^9.39.1",
"typescript": "^5.9.3"
}
@@ -476,9 +476,9 @@
"optional": true
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -497,9 +497,9 @@
}
},
"node_modules/builder-util-runtime": {
"version": "9.5.1",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz",
"integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==",
"version": "9.7.0",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz",
"integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.4",
@@ -762,15 +762,15 @@
"optional": true
},
"node_modules/electron": {
"version": "30.5.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-30.5.1.tgz",
"integrity": "sha512-AhL7+mZ8Lg14iaNfoYTkXQ2qee8mmsQyllKdqxlpv/zrKgfxz6jNVtcRRbQtLxtF8yzcImWdfTQROpYiPumdbw==",
"version": "39.8.10",
"resolved": "https://registry.npmjs.org/electron/-/electron-39.8.10.tgz",
"integrity": "sha512-zbYtGPYUI7PzqLAzkk21Rk6j67WN0hxn0Mq/njErZo1d0HSf33is4f8ICI5fMLy5vYe0JtCtM5sYunNOaochSQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^20.9.0",
"@types/node": "^22.7.7",
"extract-zip": "^2.0.1"
},
"bin": {
@@ -785,12 +785,12 @@
"link": true
},
"node_modules/electron-updater": {
"version": "6.8.3",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.3.tgz",
"integrity": "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==",
"version": "6.8.9",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.9.tgz",
"integrity": "sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig==",
"license": "MIT",
"dependencies": {
"builder-util-runtime": "9.5.1",
"builder-util-runtime": "9.7.0",
"fs-extra": "^10.1.0",
"js-yaml": "^4.1.0",
"lazy-val": "^1.0.5",
@@ -847,16 +847,6 @@
"node": ">= 10.0.0"
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "20.19.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
"integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
@@ -1495,9 +1485,19 @@
"license": "ISC"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -1957,9 +1957,9 @@
}
},
"node_modules/sax": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz",
"integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
"integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=11.0.0"
@@ -2052,9 +2052,9 @@
}
},
"node_modules/socket.io-parser": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz",
"integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==",
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
"integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",

View File

@@ -19,12 +19,12 @@
"dasherize": "^2.0.0",
"electron-host-hook": "file:./ElectronHostHook",
"image-size": "^1.2.1",
"electron-updater": "^6.6.2",
"electron-updater": "^6.8.9",
"socket.io": "^4.8.1"
},
"devDependencies": {
"@types/node": "^22.18",
"electron": "^30.0.3",
"electron": "^39.8.10",
"eslint": "^9.39.1",
"typescript": "^5.9.3"
}

View File

@@ -0,0 +1,133 @@
using System.Diagnostics;
namespace ElectronNET.IntegrationTests.Tests;
/// <summary>
/// Tests for ElectronNET.Core.targets icon copy behavior.
/// Covers GitHub issue #1047: modern macOS .icon app-icon packages are folders
/// and must be copied recursively, not treated as a single file.
/// </summary>
public class ElectronIconTargetsTests
{
private static readonly string CorePropsPath = FindBuildFile("src/ElectronNET/build/ElectronNET.Core.props", "ElectronNET/build/ElectronNET.Core.props");
private static readonly string CoreTargetsPath = FindBuildFile("src/ElectronNET/build/ElectronNET.Core.targets", "ElectronNET/build/ElectronNET.Core.targets");
[Fact]
public async Task ElectronCoreTargets_ElectronIconDirectory_ShouldBeMappedIntoElectronOutput()
{
var tempDir = CreateTempProjectDirectory();
try
{
var iconPackageDir = Path.Combine(tempDir, "Assets", "MyApp.icon");
Directory.CreateDirectory(Path.Combine(iconPackageDir, "layers"));
await File.WriteAllTextAsync(Path.Combine(iconPackageDir, "manifest.json"), "{}");
await File.WriteAllTextAsync(Path.Combine(iconPackageDir, "layers", "foreground.png"), "not-a-real-png");
await WriteMinimalCsprojAsync(tempDir);
var (exitCode, output) = await RunDotnetMsBuildAsync(tempDir, "DumpElectronIconCopyItems");
var normalizedOutput = NormalizePathSeparators(output);
exitCode.Should().Be(0,
$"MSBuild target evaluation must succeed. Full output:\n{output}");
normalizedOutput.Should().Contain(
".electron/MyApp.icon/manifest.json",
$"the icon package root file must be mapped into .electron/MyApp.icon. Full output:\n{output}");
normalizedOutput.Should().Contain(
".electron/MyApp.icon/layers/foreground.png",
$"nested files in .icon package must preserve structure in .electron output. Full output:\n{output}");
}
finally
{
Directory.Delete(tempDir, recursive: true);
}
}
private static string FindBuildFile(string relativeFromRepoRoot, string relativeFromSrc)
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir != null)
{
var fromRepoRoot = Path.Combine(dir.FullName, relativeFromRepoRoot);
if (File.Exists(fromRepoRoot))
{
return Path.GetFullPath(fromRepoRoot);
}
var fromSrc = Path.Combine(dir.FullName, relativeFromSrc);
if (File.Exists(fromSrc))
{
return Path.GetFullPath(fromSrc);
}
dir = dir.Parent;
}
throw new FileNotFoundException(
$"Could not locate '{relativeFromRepoRoot}' by walking up from '{AppContext.BaseDirectory}'.");
}
private static string CreateTempProjectDirectory()
{
var tempDir = Path.Combine(Path.GetTempPath(), $"electron-net-icon-test-{Guid.NewGuid():N}");
Directory.CreateDirectory(tempDir);
Directory.CreateDirectory(Path.Combine(tempDir, "Properties"));
return tempDir;
}
private static Task WriteMinimalCsprojAsync(string tempDir)
{
var propsPathEscaped = CorePropsPath.Replace("'", "&apos;");
var targetsPathEscaped = CoreTargetsPath.Replace("'", "&apos;");
return File.WriteAllTextAsync(
Path.Combine(tempDir, "TestApp.csproj"),
$$"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<Import Project="{{propsPathEscaped}}" />
<PropertyGroup Label="ElectronNetCommon">
<ElectronIcon>Assets/MyApp.icon</ElectronIcon>
</PropertyGroup>
<Import Project="{{targetsPathEscaped}}" />
<Target Name="DumpElectronIconCopyItems"
DependsOnTargets="ElectronResolvePaths;ElectronGetCopyToOutputDirectoryItems">
<Message Importance="High"
Text="ELECTRON_COPY_ITEMS: @(_ElectronFilesToCopyWithTargetPath->'%(TargetPath)')" />
</Target>
</Project>
""");
}
private static async Task<(int ExitCode, string Output)> RunDotnetMsBuildAsync(string workingDirectory, string target)
{
var psi = new ProcessStartInfo("dotnet", $"msbuild TestApp.csproj --nologo -v:minimal /restore /t:{target}")
{
WorkingDirectory = workingDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
};
using var process = Process.Start(psi)!;
var stdOut = await process.StandardOutput.ReadToEndAsync();
var stdErr = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
return (process.ExitCode, stdOut + stdErr);
}
private static string NormalizePathSeparators(string value)
{
return value.Replace('\\', '/');
}
}

View File

@@ -0,0 +1,55 @@
namespace ElectronNET.IntegrationTests.Tests;
/// <summary>
/// Regression checks for BrowserWindow lifecycle cleanup in WindowManager.
/// Covers GitHub issue #1008.
/// </summary>
public class WindowManagerLifecycleTests
{
private static readonly string WindowManagerFilePath = FindWindowManagerFile();
[Fact]
public void WindowManager_ShouldUsePersistentBrowserWindowClosedSubscription()
{
File.Exists(WindowManagerFilePath).Should().BeTrue(
$"WindowManager source must exist at '{WindowManagerFilePath}'.");
var content = File.ReadAllText(WindowManagerFilePath);
content.Should().Contain(
"Socket.On<int[]>(\"BrowserWindowClosed\"",
"closed-window cleanup must be wired for every close event, not just the first one.");
content.Should().NotContain(
"Socket.Once<int[]>(\"BrowserWindowClosed\"",
"a one-shot subscription causes stale BrowserWindow references after subsequent closes (issue #1008).");
}
private static string FindWindowManagerFile()
{
const string RelativeFromRepoRoot = "src/ElectronNET.API/API/WindowManager.cs";
const string RelativeFromSrc = "ElectronNET.API/API/WindowManager.cs";
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir != null)
{
var fromRepoRoot = Path.Combine(dir.FullName, RelativeFromRepoRoot);
if (File.Exists(fromRepoRoot))
{
return Path.GetFullPath(fromRepoRoot);
}
var fromSrc = Path.Combine(dir.FullName, RelativeFromSrc);
if (File.Exists(fromSrc))
{
return Path.GetFullPath(fromSrc);
}
dir = dir.Parent;
}
throw new FileNotFoundException(
"Could not locate WindowManager.cs by walking up from " +
$"'{AppContext.BaseDirectory}'.");
}
}

View File

@@ -27,8 +27,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ElectronNET.Core" Version="0.5.1" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.5.1" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.9.3" />
</ItemGroup>

View File

@@ -204,29 +204,43 @@
"license": "MIT"
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/types": "^0.15.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanfs/core": "^0.19.2",
"@humanfs/types": "^0.15.0",
"@humanwhocodes/retry": "^0.4.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/types": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -526,9 +540,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
@@ -1123,9 +1137,9 @@
}
},
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz",
"integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==",
"dev": true,
"license": "ISC"
},
@@ -1337,9 +1351,9 @@
"license": "ISC"
},
"node_modules/js-yaml": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{
@@ -1797,9 +1811,9 @@
}
},
"node_modules/readdir-glob/node_modules/brace-expansion": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"

View File

@@ -34,7 +34,6 @@
<ElectronSplashScreen>wwwroot\assets\img\about@2x.png</ElectronSplashScreen>
<License>MIT</License>
<ElectronSingleInstance>false</ElectronSingleInstance>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ElectronBuilderVersion>26.0</ElectronBuilderVersion>
</PropertyGroup>
<PropertyGroup>
@@ -76,8 +75,8 @@
<ProjectReference Include="..\ElectronNET.AspNet\ElectronNET.AspNet.csproj" Condition="$(ElectronNetDevMode)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ElectronNET.Core" Version="0.5.1" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.5.1" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="ElectronNET.Core.AspNet" Version="0.6.0" Condition="'$(ElectronNetDevMode)' != 'true'" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.9.3" />
</ItemGroup>

View File

@@ -3,7 +3,7 @@
<Import Project="..\common.props" />
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
<TargetFrameworks>$(ElectronNetTargetFrameworks)</TargetFrameworks>
<PackageOutputPath>..\..\artifacts</PackageOutputPath>
<PackageId>$(PackageNamePrefix)</PackageId>
<Title>$(PackageId)</Title>

View File

@@ -4,7 +4,12 @@
<PropertyGroup Label="ElectronNetCommon">
<ElectronVersion>30.4.0</ElectronVersion>
<ElectronBuilderVersion>26.0</ElectronBuilderVersion>
<RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == ''">win-x64</RuntimeIdentifier>
<!-- Allow ElectronRuntimeIdentifier to be overridden (explicitly or via RuntimeIdentifier) -->
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</ElectronRuntimeIdentifier>
<!-- When using the dotnet CLI (Core MSBuild), infer the current RID as a default -->
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(MSBuildRuntimeType)' == 'Core'">$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)</ElectronRuntimeIdentifier>
<!-- Otherwise, Full MSBuild runs only on Windows, so infer a Windows RID from OSArchitecture -->
<ElectronRuntimeIdentifier Condition="'$(ElectronRuntimeIdentifier)' == '' AND '$(MSBuildRuntimeType)' == 'Full'">win-$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLower())</ElectronRuntimeIdentifier>
<ElectronSingleInstance>true</ElectronSingleInstance>
<ElectronSplashScreen></ElectronSplashScreen>
<ElectronIcon></ElectronIcon>

View File

@@ -52,7 +52,7 @@
<!-- Targets -->
<Target Name="ElectronBeforeClean" AfterTargets="BeforeClean" DependsOnTargets="ElectronResolvePaths">
<Target Name="ElectronBeforeClean" AfterTargets="BeforeClean" DependsOnTargets="ElectronSetPaths">
<ItemGroup>
<Clean Include="$(ElectronOutDir)**" />
</ItemGroup>
@@ -60,7 +60,7 @@
<!--<Message Text="ElectronBeforeClean - Clean Files: @(Clean)" Importance="High" />-->
</Target>
<Target Name="ElectronClean" AfterTargets="CoreClean" DependsOnTargets="ElectronResolvePaths">
<Target Name="ElectronClean" AfterTargets="CoreClean" DependsOnTargets="ElectronSetPaths">
<RemoveDir Directories="$(ElectronOutDir)" />
@@ -121,7 +121,7 @@
<!-- Electron Builder uses title for the installation dir on Linux, but it should not have spaces -->
<LinuxPrefix>linux</LinuxPrefix>
<ElectronTitle>$(Title)</ElectronTitle>
<ElectronTitle Condition="'$(RuntimeIdentifier.StartsWith($(LinuxPrefix)))' == 'true'">$(Title.Replace(' ', '-'))</ElectronTitle>
<ElectronTitle Condition="'$(ElectronRuntimeIdentifier.StartsWith($(LinuxPrefix)))' == 'true'">$(Title.Replace(' ', '-'))</ElectronTitle>
</PropertyGroup>
<ItemGroup>
@@ -269,7 +269,7 @@
node_modules is missing. Skipped in committed-JS mode and at design time. -->
<Target Name="ElectronRestoreHostHookDependencies"
BeforeTargets="CompileTypeScriptWithTSConfig;CompileTypeScript"
Condition="'@(ElectronCustomHookTsFiles->Count())' &gt; 1 AND Exists('$(MSBuildProjectDirectory)\ElectronHostHook\package.json') AND '$(TypeScriptCompileBlocked)' != 'true' AND '$(DesignTimeBuild)' != 'true'"
Condition="'@(ElectronCustomHookTsFiles->Count())' &gt; 0 AND Exists('$(MSBuildProjectDirectory)\ElectronHostHook\package.json') AND '$(TypeScriptCompileBlocked)' != 'true' AND '$(DesignTimeBuild)' != 'true'"
Inputs="$(MSBuildProjectDirectory)\ElectronHostHook\package.json"
Outputs="$(MSBuildProjectDirectory)\ElectronHostHook\node_modules\.package-lock.json">
@@ -313,7 +313,10 @@
<ItemGroup>
<!--<_ElectronFiles Include="$(ElectronIntermediatePackageJson)" />-->
<_ElectronFiles Include="$(ElectronSplashScreen)" Condition="'$(ElectronSplashScreen)'!=''" />
<_ElectronFiles Include="$(ElectronIcon)" Condition="'$(ElectronIcon)'!=''" />
<_ElectronFiles Include="$(ElectronIcon)"
Condition="'$(ElectronIcon)'!='' AND ( !Exists('$(ElectronIcon)') OR !$([System.IO.Directory]::Exists('$(ElectronIcon)') ) )" />
<_ElectronIconDirectoryFiles Include="$(ElectronIcon)\**\*"
Condition="'$(ElectronIcon)'!='' AND Exists('$(ElectronIcon)') AND $([System.IO.Directory]::Exists('$(ElectronIcon)') )" />
</ItemGroup>
<ItemGroup>
@@ -329,11 +332,16 @@
</ItemGroup>
<Message Importance="High" Text="_ElectronFilesToCopy: @(_ElectronFilesToCopy)" />
<Message Importance="High" Text="_ElectronIconDirectoryFiles: @(_ElectronIconDirectoryFiles)" />
<ItemGroup>
<_ElectronFilesToCopyWithTargetPath Include="@(_ElectronFilesToCopy)">
<TargetPath>$(ElectronDirName)\%(FileName)%(Extension)</TargetPath>
</_ElectronFilesToCopyWithTargetPath>
<_ElectronFilesToCopyWithTargetPath Include="@(_ElectronIconDirectoryFiles)">
<TargetPath>$(ElectronDirName)\$(ElectronIconFileName)\%(RecursiveDir)%(FileName)%(Extension)</TargetPath>
</_ElectronFilesToCopyWithTargetPath>
</ItemGroup>
<Message Text="_ElectronFilesToCopyWithTargetPath: @(_ElectronFilesToCopyWithTargetPath)" />
@@ -365,18 +373,18 @@
<Target Name="ElectronCheckVersionMismatch">
<PropertyGroup>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'win-x64'">x64</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'win-x86'">ia32</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'win-arm64'">arm64</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'linux-x64'">x64</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'linux-arm'">armv7l</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">arm64</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'osx-x64'">x64</ElectronArch>
<ElectronArch Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">arm64</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'win-x64'">x64</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'win-x86'">ia32</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'win-arm64'">arm64</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'linux-x64'">x64</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'linux-arm'">armv7l</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'linux-arm64'">arm64</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'osx-x64'">x64</ElectronArch>
<ElectronArch Condition="'$(ElectronRuntimeIdentifier)' == 'osx-arm64'">arm64</ElectronArch>
<ElectronPlatform Condition="'$(RuntimeIdentifier)' == 'win-x64' OR '$(RuntimeIdentifier)' == 'win-x86' OR '$(RuntimeIdentifier)' == 'win-arm64'">win</ElectronPlatform>
<ElectronPlatform Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm' OR '$(RuntimeIdentifier)' == 'linux-arm64'">linux</ElectronPlatform>
<ElectronPlatform Condition="'$(RuntimeIdentifier)' == 'osx-x64' OR '$(RuntimeIdentifier)' == 'osx-arm64'">mac</ElectronPlatform>
<ElectronPlatform Condition="'$(ElectronRuntimeIdentifier)' == 'win-x64' OR '$(ElectronRuntimeIdentifier)' == 'win-x86' OR '$(ElectronRuntimeIdentifier)' == 'win-arm64'">win</ElectronPlatform>
<ElectronPlatform Condition="'$(ElectronRuntimeIdentifier)' == 'linux-x64' OR '$(ElectronRuntimeIdentifier)' == 'linux-arm' OR '$(ElectronRuntimeIdentifier)' == 'linux-arm64'">linux</ElectronPlatform>
<ElectronPlatform Condition="'$(ElectronRuntimeIdentifier)' == 'osx-x64' OR '$(ElectronRuntimeIdentifier)' == 'osx-arm64'">mac</ElectronPlatform>
<!-- npm uses different OS names than Electron -->
<NpmOs Condition="'$(ElectronPlatform)' == 'win'">win32</NpmOs>
@@ -385,7 +393,7 @@
<!-- npm CPU is same as ElectronArch except for linux-arm -->
<NpmCpu>$(ElectronArch)</NpmCpu>
<NpmCpu Condition="'$(RuntimeIdentifier)' == 'linux-arm'">arm</NpmCpu>
<NpmCpu Condition="'$(ElectronRuntimeIdentifier)' == 'linux-arm'">arm</NpmCpu>
<_CurrentOSPlatform Condition="$([MSBuild]::IsOSPlatform('Windows'))">win</_CurrentOSPlatform>
<_CurrentOSPlatform Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux</_CurrentOSPlatform>
@@ -522,7 +530,7 @@
<Error Condition="'$(_IsPlatformMismatch)' == 'true'"
Code="ELECTRON100"
Text="The target RuntimeIdentifier '$(RuntimeIdentifier)' (platform: $(ElectronPlatform)) does not match the current operating system ($(_CurrentOSPlatform)).
Text="The target RuntimeIdentifier '$(ElectronRuntimeIdentifier)' (platform: $(ElectronPlatform)) does not match the current operating system ($(_CurrentOSPlatform)).
Electron applications must be built on the target operating system:
- Windows targets (win-x64, win-x86, win-arm64) must be built on Windows
@@ -645,7 +653,7 @@ For more information, see: https://github.com/ElectronNET/Electron.NET/wiki/Migr
</PropertyGroup>
<PropertyGroup>
<_NpxCmd>npx electron-builder --config=./$(ElectronBuilderJson) --$(ElectronPlatform) --$(ElectronArch) -c.electronVersion=$(ElectronVersion) -c.directories.output &quot;$(ElectronPublishUrlFullPath)&quot; $(ElectronPaParams)</_NpxCmd>
<_NpxCmd>npx electron-builder -c.$(ElectronPlatform).defaultArch=$(ElectronArch) --config=./$(ElectronBuilderJson) -c.electronVersion=$(ElectronVersion) --$(ElectronPlatform) --$(ElectronArch) -c.directories.output &quot;$(ElectronPublishUrlFullPath)&quot; $(ElectronPaParams)</_NpxCmd>
<_NpxCmd Condition="'$(IsLinuxWsl)' == 'true'">wsl bash -ic '$(_NpxCmd)'</_NpxCmd>
</PropertyGroup>

View File

@@ -572,7 +572,7 @@
<StringProperty Name="ElectronIcon"
DisplayName="App Icon"
Description="Choose a ICO file to be used as application icon"
Description="Choose an icon path for the app (e.g. .ico, .icns, or modern macOS .icon folder)"
Subtype="File"
Category="General">
<StringProperty.DataSource>
@@ -581,7 +581,7 @@
<StringProperty.ValueEditors>
<ValueEditor EditorType="FilePath">
<ValueEditor.Metadata>
<NameValuePair Name="FileTypeFilter" Value="Icon files (*.ico)|*.ico|All files (*.*)|*.*" />
<NameValuePair Name="FileTypeFilter" Value="Icon files (*.ico,*.icns)|*.ico;*.icns|All files (*.*)|*.*" />
</ValueEditor.Metadata>
</ValueEditor>
</StringProperty.ValueEditors>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>0.5.2</Version>
<Version>0.6.0</Version>
<PackageNamePrefix>ElectronNET.Core</PackageNamePrefix>
<Authors>Gregor Biswanger, Florian Rappl, softworkz</Authors>
<Product>Electron.NET</Product>
@@ -18,6 +18,7 @@
<FileVersion>$(Version)</FileVersion>
<Version>$(Version)$(VersionPostFix)</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<ElectronNetTargetFrameworks Condition="'$(ElectronNetTargetFrameworks)' == ''">net6.0;net8.0;net10.0</ElectronNetTargetFrameworks>
</PropertyGroup>
</Project>