From b00adcbd387f1c356d6ce6f52b8f484b19dc5e12 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sun, 7 Dec 2025 22:57:35 +0530 Subject: [PATCH 1/2] feat: Add ElectronHostHook sample application (#967) --- .../Controllers/HomeController.cs | 21 +++++++++++ .../ElectronHostHook/connector.ts | 18 ++++++++++ .../ElectronHostHook/index.ts | 16 +++++++++ .../ElectronHostHook/tsconfig.json | 12 +++++++ ...lectronNET.Samples.ElectronHostHook.csproj | 36 +++++++++++++++++++ .../Program.cs | 35 ++++++++++++++++++ .../Properties/launchSettings.json | 12 +++++++ .../Views/Home/Index.cshtml | 24 +++++++++++++ src/ElectronNET.sln | 11 ++++++ 9 files changed, 185 insertions(+) create mode 100644 src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs create mode 100644 src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts create mode 100644 src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/index.ts create mode 100644 src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json create mode 100644 src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj create mode 100644 src/ElectronNET.Samples.ElectronHostHook/Program.cs create mode 100644 src/ElectronNET.Samples.ElectronHostHook/Properties/launchSettings.json create mode 100644 src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml diff --git a/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs b/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs new file mode 100644 index 00000000..10c10596 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs @@ -0,0 +1,21 @@ +using ElectronNET.API; +using Microsoft.AspNetCore.Mvc; + +namespace ElectronNET.Samples.ElectronHostHook.Controllers +{ + public class HomeController : Controller + { + public async Task Index() + { + string message = "Electron not active"; + if (HybridSupport.IsElectronActive) + { + // Call the HostHook defined in ElectronHostHook/index.ts + var result = await Electron.HostHook.CallAsync("ping", "Hello from C#"); + message = $"Sent 'Hello from C#', Received: '{result}'"; + } + + return View("Index", message); + } + } +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts new file mode 100644 index 00000000..ed776ad3 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts @@ -0,0 +1,18 @@ +import { Socket } from "socket.io"; + +export class Connector { + constructor(private socket: Socket, public app: any) { + } + + on(key: string, javaScriptCode: Function): void { + this.socket.on(key, (...args: any[]) => { + const id: string = args.pop(); + const done = (result: any) => { + this.socket.emit(id, result); + }; + + args = [...args, done]; + javaScriptCode(...args); + }); + } +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/index.ts b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/index.ts new file mode 100644 index 00000000..54b69c97 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/index.ts @@ -0,0 +1,16 @@ +import { Connector } from "./connector"; +import { Socket } from "socket.io"; + +export class HookService extends Connector { + constructor(socket: Socket, public app: any) { + super(socket, app); + } + + onHostReady(): void { + // execute your own JavaScript Host logic here + this.on("ping", (msg, done) => { + console.log("Received ping from C#:", msg); + done("pong: " + msg); + }); + } +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json new file mode 100644 index 00000000..fbf08f2c --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": false, + "noEmitOnError": true, + "removeComments": false, + "sourceMap": true, + "target": "es5", + "lib": ["es2015", "dom"] + }, + "exclude": ["node_modules", "wwwroot"] +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj b/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj new file mode 100644 index 00000000..70eb4366 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj @@ -0,0 +1,36 @@ + + + true + + + + + + net8.0 + OutOfProcess + AspNetCoreModule + false + commonjs + true + ElectronHostHook/tsconfig.json + true + true + + + + + + + + + + + + + + + + + + + diff --git a/src/ElectronNET.Samples.ElectronHostHook/Program.cs b/src/ElectronNET.Samples.ElectronHostHook/Program.cs new file mode 100644 index 00000000..241221e9 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/Program.cs @@ -0,0 +1,35 @@ +using ElectronNET.API; + +namespace ElectronNET.Samples.ElectronHostHook +{ + public class Program + { + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + builder.WebHost.UseElectron(args); + builder.Services.AddElectron(); + builder.Services.AddControllersWithViews(); + + var app = builder.Build(); + + app.UseStaticFiles(); + app.UseRouting(); + + app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + + if (HybridSupport.IsElectronActive) + { + Task.Run(async () => + { + var window = await Electron.WindowManager.CreateWindowAsync(); + }); + } + + app.Run(); + } + } +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/Properties/launchSettings.json b/src/ElectronNET.Samples.ElectronHostHook/Properties/launchSettings.json new file mode 100644 index 00000000..d4c4b1eb --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "ElectronNET.Samples.ElectronHostHook": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml b/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml new file mode 100644 index 00000000..142d015c --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml @@ -0,0 +1,24 @@ +@model string +@{ + Layout = null; +} + + + + + + ElectronHostHook Sample + + + +

ElectronHostHook Sample

+

This sample demonstrates bidirectional communication between C# and the Electron Host process.

+ +
+ Result: @Model +
+ + diff --git a/src/ElectronNET.sln b/src/ElectronNET.sln index 6cadc125..ef64ab18 100644 --- a/src/ElectronNET.sln +++ b/src/ElectronNET.sln @@ -62,6 +62,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronNET.IntegrationTest EndProject Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "ElectronNET.Host", "ElectronNET.Host\ElectronNET.Host.esproj", "{1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElectronNET.Samples.ElectronHostHook", "ElectronNET.Samples.ElectronHostHook\ElectronNET.Samples.ElectronHostHook.csproj", "{B8D65F3A-7E54-4632-9F1C-46679237B312}" + ProjectSection(ProjectDependencies) = postProject + {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6} = {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6} + {8860606D-6847-F22A-5AED-DF4E0984DD24} = {8860606D-6847-F22A-5AED-DF4E0984DD24} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +111,10 @@ Global {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6}.Release|Any CPU.Build.0 = Release|Any CPU {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6}.Release|Any CPU.Deploy.0 = Release|Any CPU + {B8D65F3A-7E54-4632-9F1C-46679237B312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8D65F3A-7E54-4632-9F1C-46679237B312}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8D65F3A-7E54-4632-9F1C-46679237B312}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8D65F3A-7E54-4632-9F1C-46679237B312}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -120,6 +130,7 @@ Global {06CAADC7-DE5B-47B4-AB2A-E9501459A2D1} = {D36CDFFD-3438-42E4-A7FF-88BA19AC4964} {AE877E48-6B44-63C2-8EA0-DB58D096B553} = {75129C45-FC6F-41B0-A485-07F4A7E031ED} {1C5FD66E-A1C6-C436-DF7C-3ECE4FEDDFE6} = {1BB6F634-2831-4496-83A6-BC6761DCEC8D} + {B8D65F3A-7E54-4632-9F1C-46679237B312} = {EDCBFC49-2AEE-4BAF-9368-4409298C52FC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {81A62E71-9E04-4EFE-AD5C-23165375F8EF} From 331c2f548c84647eeff9002cd1e53958025f3020 Mon Sep 17 00:00:00 2001 From: Aditya Date: Sun, 7 Dec 2025 23:08:09 +0530 Subject: [PATCH 2/2] fix: address review comments (protocol, config, startup) --- .../ElectronHostHook/.gitignore | 3 +++ .../ElectronHostHook/connector.ts | 15 +++++++++------ .../ElectronHostHook/package.json | 12 ++++++++++++ .../ElectronHostHook/tsconfig.json | 9 +++------ .../Program.cs | 14 +++++--------- .../Views/Home/Index.cshtml | 3 ++- 6 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/.gitignore create mode 100644 src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/package.json diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/.gitignore b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/.gitignore new file mode 100644 index 00000000..984dc924 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/.gitignore @@ -0,0 +1,3 @@ +node_modules +*.js +*.js.map diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts index ed776ad3..295ba95f 100644 --- a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts @@ -7,12 +7,15 @@ export class Connector { on(key: string, javaScriptCode: Function): void { this.socket.on(key, (...args: any[]) => { const id: string = args.pop(); - const done = (result: any) => { - this.socket.emit(id, result); - }; - - args = [...args, done]; - javaScriptCode(...args); + try { + javaScriptCode(...args, (data) => { + if (data) { + this.socket.emit(`${key}Complete${id}`, data); + } + }); + } catch (error) { + this.socket.emit(`${key}Error${id}`, `Host Hook Exception`, error); + } }); } } diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/package.json b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/package.json new file mode 100644 index 00000000..069cbac2 --- /dev/null +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/package.json @@ -0,0 +1,12 @@ +{ + "name": "electron-host-hook", + "version": "1.0.0", + "description": "Connector for Electron.NET projects.", + "main": "index.js", + "dependencies": { + "socket.io": "^4.8.1" + }, + "devDependencies": { + "typescript": "^5.9.3" + } +} diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json index fbf08f2c..db8ccccd 100644 --- a/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json +++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json @@ -1,12 +1,9 @@ { "compilerOptions": { "module": "commonjs", - "noImplicitAny": false, - "noEmitOnError": true, - "removeComments": false, + "target": "ES2019", "sourceMap": true, - "target": "es5", - "lib": ["es2015", "dom"] + "skipLibCheck": true }, - "exclude": ["node_modules", "wwwroot"] + "exclude": ["node_modules"] } diff --git a/src/ElectronNET.Samples.ElectronHostHook/Program.cs b/src/ElectronNET.Samples.ElectronHostHook/Program.cs index 241221e9..ee8fa812 100644 --- a/src/ElectronNET.Samples.ElectronHostHook/Program.cs +++ b/src/ElectronNET.Samples.ElectronHostHook/Program.cs @@ -8,7 +8,11 @@ public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); - builder.WebHost.UseElectron(args); + builder.WebHost.UseElectron(args, async () => + { + var window = await Electron.WindowManager.CreateWindowAsync(); + }); + builder.Services.AddElectron(); builder.Services.AddControllersWithViews(); @@ -21,14 +25,6 @@ public static void Main(string[] args) name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); - if (HybridSupport.IsElectronActive) - { - Task.Run(async () => - { - var window = await Electron.WindowManager.CreateWindowAsync(); - }); - } - app.Run(); } } diff --git a/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml b/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml index 142d015c..f0f3be97 100644 --- a/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml +++ b/src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml @@ -4,8 +4,9 @@ } - + + ElectronHostHook Sample