Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ae3f755
Fixed center handling
FlorianRappl Dec 22, 2025
809b0a6
Added missing methods
FlorianRappl Dec 22, 2025
682a1c3
Optional
FlorianRappl Dec 23, 2025
b180fc2
Double-up timeout for electron-builder
softworkz Jan 21, 2026
6c95dfd
BrowserWindow: Add OnBoundsChanged event
softworkz Jan 21, 2026
c8f1cdf
Add tests for BrowserWindow.OnBoundsChanged event
softworkz Jan 21, 2026
9f64898
ElectronNET.Host: Revert JS SDK to default value from VS 2026
softworkz Jan 21, 2026
17f761d
Fix FormatException
softworkz Jan 21, 2026
891da14
Relax Migration check for package.json in root
softworkz Jan 21, 2026
04a224a
Merge pull request #1012 from softworkz/submit_migcheck_updates
FlorianRappl Jan 21, 2026
53698d1
Merge pull request #1013 from softworkz/submit_timeout
FlorianRappl Jan 21, 2026
d85a64f
Merge pull request #1014 from softworkz/submit_new_event
FlorianRappl Jan 21, 2026
bff3fff
feat: Add the Handle, HandleOnce, and RemoveHandler events to IpcMain
DYH1319 Jan 24, 2026
092789a
feat: Add overloaded methods in IpcMain that supports listeners with …
DYH1319 Jan 24, 2026
7174118
Merge pull request #1019 from DYH1319/feat/submit_new_event_for_ipc
FlorianRappl Feb 2, 2026
21226e1
Updated deps
FlorianRappl Feb 4, 2026
b3b124b
Updated TS formatting to match prettier
FlorianRappl Feb 4, 2026
8009348
Update transformed files
FlorianRappl Feb 4, 2026
09c735a
SocketIoFacade: Improve disposal, avoid exception on tear-down
softworkz Feb 14, 2026
a451e3a
Fix global.json link in solution
softworkz Feb 14, 2026
1067902
Add missing electron-builder.json in sample project
softworkz Feb 14, 2026
41c5b5a
Relax test timings
softworkz Feb 14, 2026
497e6aa
Add check for existing preload script path before overriding
AeonSake Feb 17, 2026
850d8f6
Add documentation for preload script and warning for Blazor
AeonSake Feb 17, 2026
05fb9e5
Merge pull request #1026 from softworkz/submit_disposed_exceptions
FlorianRappl Feb 17, 2026
77ca79a
Merge pull request #1027 from softworkz/submit_housekeeping
FlorianRappl Feb 17, 2026
d5df307
Update documentation to reflect current approach
AeonSake Feb 17, 2026
3850a71
Revert "Add check for existing preload script path before overriding"
AeonSake Feb 17, 2026
842e870
Add overload for GetAllDisplaysAsync() with timout parameter
softworkz Feb 25, 2026
c22c248
Merge pull request #1031 from AeonSake/1028-preload-override
FlorianRappl Feb 25, 2026
9a935c0
Updated changelog for release
FlorianRappl Feb 25, 2026
e28eadc
Merge branch 'develop' of https://github.com/ElectronNET/Electron.NET…
FlorianRappl Feb 25, 2026
8eead56
removed trailing whitespace
FlorianRappl Feb 25, 2026
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
15 changes: 15 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# 0.4.1

## ElectronNET.Core

- Updated documentation for preload scripts (#1031) @AeonSake
- Updated timeout for electron-builder (#1013) @softworkz
- Updated disposal avoiding exceptions on teardown (#1026) @softworkz
- Updated migration guide (#1015) @hilin
- Fixed handling of `Center` property for windows (#1001)
- Fixed false alarm for `ELECTRON001`, `ELECTRON008`, and `ELECTRON009` (#1012) @softworkz
- Added missing methods on `Cookies` (#1000)
- Added overload for `GetAllDisplaysAsync` with timeout (#1033) @softworkz
- Added `OnBoundsChanged` event (#1014) @softworkz
- Added new events for `ipcMain` (#1019) @DYH1319

# 0.4.0

## ElectronNET.Core
Expand Down
35 changes: 28 additions & 7 deletions docs/Core/Migration-Checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ When you build an Electron.NET project, the following validation checks are perf

| Code | Check | Description |
|------|-------|-------------|
| [ELECTRON001](#1-packagejson-not-allowed) | package.json not allowed | Ensures no package.json exists outside ElectronHostHook |
| [ELECTRON001](#1-packagejson-rules) | package.json location rules | Ensures `package.json`/`package-lock.json` aren’t present in unsupported locations (root `package.json` handled separately) |
| [ELECTRON008](#1-packagejson-rules) | root package.json contains electron | Warns when root `package.json` contains the word `electron` (case-insensitive) |
| [ELECTRON009](#1-packagejson-rules) | root package.json copied to output | Warns when root `package.json` is configured to be copied to output/publish |
| [ELECTRON002](#2-electron-manifestjson-not-allowed) | electron-manifest.json not allowed | Detects deprecated manifest files |
| [ELECTRON003](#3-electron-builderjson-location) | electron-builder.json location | Verifies electron-builder.json exists in Properties folder |
| [ELECTRON004](#3-electron-builderjson-location) | electron-builder.json wrong location | Warns if electron-builder.json is found in incorrect locations |
Expand All @@ -18,24 +20,38 @@ When you build an Electron.NET project, the following validation checks are perf

---

## 1. package.json not allowed
## 1. package.json rules

**Warning Code:** `ELECTRON001`
**Warning Codes:** `ELECTRON001`, `ELECTRON008`, `ELECTRON009`

### What is checked

The build system scans for `package.json` and `package-lock.json` files in your project directory. These files should not exist in the project root or subdirectories (with one exception).
The build system scans for `package.json` and `package-lock.json` files in your project directory.

Rules:

- **ELECTRON001**: `package.json` / `package-lock.json` must not exist in the project directory or subdirectories
- Exception: `ElectronHostHook` folder is allowed
- Note: a **root** `package.json` is **excluded** from `ELECTRON001` and validated by `ELECTRON008` / `ELECTRON009`

- **ELECTRON008**: If a root `package.json` exists, it must **not** contain electron-related dependencies or configuration.

- **ELECTRON009**: If a root `package.json` exists, it must **not** be configured to be copied to output/publish (for example via `CopyToOutputDirectory` / `CopyToPublishDirectory` metadata)

### Why this matters

In previous versions of Electron.NET, a `package.json` file was required in the project. The new version generates this file automatically from MSBuild properties defined in your `.csproj` file.
Electron.NET generates its Electron-related `package.json` during publishing based on MSBuild properties. A user-maintained Electron-related `package.json` can conflict with that process.

Also, ensuring the root `package.json` is not copied prevents accidentally shipping it with the published app.

### Exception

A `package.json` file **is allowed** in the `ElectronHostHook` folder if you're using custom host hooks. This is the only valid location for a manually maintained package.json.
A `package.json` / `package-lock.json` file **is allowed** in the `ElectronHostHook` folder if you're using custom host hooks.

### How to fix

If you have an Electron-related `package.json` from older Electron.NET versions:

1. **Open your project's `.csproj` file**
2. **Add the required properties** to a PropertyGroup with the label `ElectronNetCommon`:

Expand All @@ -51,7 +67,12 @@ A `package.json` file **is allowed** in the `ElectronHostHook` folder if you're
</PropertyGroup>
```

3. **Delete the old `package.json`** file from your project root
3. **Delete** Electron-related `package.json` / `package-lock.json` files (except those under `ElectronHostHook` if applicable)

If you keep a root `package.json` for non-Electron reasons:

- Ensure it does **not** contain electron dependencies or configuration (fixes `ELECTRON008`)
- Ensure it is **not** copied to output/publish (fixes `ELECTRON009`)

> **See also:** [Migration Guide](Migration-Guide.md) for complete migration instructions.

Expand Down
2 changes: 1 addition & 1 deletion docs/GettingStarted/Console-App.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Add the Electron.NET configuration to your `.csproj` file:
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ElectronNET.Core" Version="0.4.0" />
<PackageReference Include="ElectronNET.Core" Version="0.4.1" />
</ItemGroup>
```

Expand Down
20 changes: 20 additions & 0 deletions docs/Using/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ BrowserWindowOptions browserWindowOptions = new BrowserWindowOptions
```


### Preload Script

If you require the use of a [preload script](https://www.electronjs.org/docs/latest/tutorial/tutorial-preload), you can specify the file path of the script when creating a new window like so:

```csharp
WebPreferences wp = new WebPreferences();
wp.Preload = "path/to/preload.js";
BrowserWindowOptions browserWindowOptions = new BrowserWindowOptions
{
WebPreferences = wp
};
```

> [!IMPORTANT]
> When using a preload script _AND_ running a Blazor app, `IsRunningBlazor` must be set to `false` (or removed) and the following lines must be added to the preload script:
> ```js
> global.process = undefined;
> global.module = undefined;
> ```


## 🚀 Next Steps

Expand Down
4 changes: 3 additions & 1 deletion src/ElectronNET.API/API/ApiBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,9 @@ public Invocator(ApiBase apiBase, string callerName, TimeSpan timeout, object ar
{
if (this.tcs != null)
{
var ex = new TimeoutException($"No response after {timeout:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
var ex = new TimeoutException(
$"No response after {(long)timeout.TotalMilliseconds}ms trying to retrieve value {apiBase.objectName}.{callerName}()"
);
this.tcs.TrySetException(ex);
this.tcs = null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/ElectronNET.API/API/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ public void Exit(int exitCode = 0)

public void DisposeSocket()
{
BridgeConnector.Socket.DisposeSocket();
BridgeConnector.Socket.Dispose();
}

/// <summary>
Expand Down
13 changes: 13 additions & 0 deletions src/ElectronNET.API/API/BrowserWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,19 @@ public event Action OnMove
remove => RemoveEvent(value, Id);
}

/// <summary>
/// Emitted when the window is moved or resized.
/// </summary>
/// <remarks>
/// While not being an original Electron event, this one includes the bounds values,
/// saving the additional roundtrip for calling <see cref="GetBoundsAsync"/>.
/// </remarks>
public event Action<Rectangle> OnBoundsChanged
{
add => AddEvent(value, Id);
remove => RemoveEvent(value, Id);
}

/// <summary>
/// macOS: Emitted once when the window is moved to a new position.
/// </summary>
Expand Down
70 changes: 70 additions & 0 deletions src/ElectronNET.API/API/Cookies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ElectronNET.API.Serialization;
using System;
using System.Text.Json;
using System.Threading.Tasks;

namespace ElectronNET.API
{
Expand Down Expand Up @@ -54,10 +55,79 @@ public event Action<Cookie, CookieChangedCause, bool> OnChanged
_changed -= value;

if (_changed == null)
{
BridgeConnector.Socket.Off("webContents-session-cookies-changed" + Id);
}
}
}

private event Action<Cookie, CookieChangedCause, bool> _changed;



/// <summary>
/// Sends a request to get all cookies matching filter, and resolves a callack with the response.
/// </summary>
/// <param name="filter">
/// </param>
/// <returns>A task which resolves an array of cookie objects.</returns>
public Task<Cookie[]> GetAsync(CookieFilter filter)
{
var tcs = new TaskCompletionSource<Cookie[]>();
var guid = Guid.NewGuid().ToString();

BridgeConnector.Socket.Once<Cookie[]>("webContents-session-cookies-get-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-get", Id, filter, guid);

return tcs.Task;
}

/// <summary>
///
/// </summary>
/// <param name="details"></param>
/// <returns></returns>
public Task SetAsync(CookieDetails details)
{
var tcs = new TaskCompletionSource<object>();
var guid = Guid.NewGuid().ToString();

BridgeConnector.Socket.Once<object>("webContents-session-cookies-set-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-set", Id, details, guid);

return tcs.Task;
}

/// <summary>
/// Removes the cookies matching url and name
/// </summary>
/// <param name="url">The URL associated with the cookie.</param>
/// <param name="name">The name of cookie to remove.</param>
/// <returns>A task which resolves when the cookie has been removed</returns>
public Task RemoveAsync(string url, string name)
{
var tcs = new TaskCompletionSource<object>();
var guid = Guid.NewGuid().ToString();

BridgeConnector.Socket.Once<object>("webContents-session-cookies-remove-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-remove", Id, url, name, guid);

return tcs.Task;
}

/// <summary>
/// Writes any unwritten cookies data to disk.
/// </summary>
/// <returns>A task which resolves when the cookie store has been flushed</returns>
public Task FlushStoreAsync()
{
var tcs = new TaskCompletionSource<object>();
var guid = Guid.NewGuid().ToString();

BridgeConnector.Socket.Once<object>("webContents-session-cookies-flushStore-completed" + guid, tcs.SetResult);
BridgeConnector.Socket.Emit("webContents-session-cookies-flushStore", Id, guid);

return tcs.Task;
}
}
}
4 changes: 2 additions & 2 deletions src/ElectronNET.API/API/Entities/BrowserWindowOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ public class BrowserWindowOptions
/// ( if y is used) Window's left offset from screen. Default is to center the
/// window.
/// </summary>
public int X { get; set; } = -1;
public int? X { get; set; }

/// <summary>
/// ( if x is used) Window's top offset from screen. Default is to center the
/// window.
/// </summary>
public int Y { get; set; } = -1;
public int? Y { get; set; }

/// <summary>
/// The width and height would be used as web page's size, which means the actual
Expand Down
10 changes: 5 additions & 5 deletions src/ElectronNET.API/API/Entities/Cookie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class Cookie
/// <summary>
/// Gets or sets a value indicating whether the cookie is a host-only cookie; this will only be true if no domain was passed.
/// </summary>
public bool HostOnly { get; set; }
public bool? HostOnly { get; set; }

/// <summary>
/// Gets or sets the path of the cookie.
Expand All @@ -34,22 +34,22 @@ public class Cookie
/// <summary>
/// Gets or sets a value indicating whether the cookie is marked as secure.
/// </summary>
public bool Secure { get; set; }
public bool? Secure { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the cookie is marked as HTTP only.
/// </summary>
public bool HttpOnly { get; set; }
public bool? HttpOnly { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the cookie is a session cookie or a persistent cookie with an expiration date.
/// </summary>
public bool Session { get; set; }
public bool? Session { get; set; }

/// <summary>
/// Gets or sets the expiration date of the cookie as the number of seconds since the UNIX epoch. Not provided for session cookies.
/// </summary>
public double ExpirationDate { get; set; }
public double? ExpirationDate { get; set; }

/// <summary>
/// Gets or sets the SameSite policy applied to this cookie. Can be "unspecified", "no_restriction", "lax" or "strict".
Expand Down
6 changes: 3 additions & 3 deletions src/ElectronNET.API/API/Entities/CookieFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ public class CookieFilter
/// <summary>
/// (optional) - Filters cookies by their Secure property.
/// </summary>
public bool Secure { get; set; }
public bool? Secure { get; set; }

/// <summary>
/// (optional) - Filters out session or persistent cookies.
/// </summary>
public bool Session { get; set; }
public bool? Session { get; set; }

/// <summary>
/// (optional) - Filters cookies by httpOnly.
/// </summary>
public bool HttpOnly { get; set; }
public bool? HttpOnly { get; set; }
}
}
Loading