Skip to content

Conversation

@adityashirsatrao007
Copy link

Closes #967

Changes

  • Added new sample project \ElectronNET.Samples.ElectronHostHook\ demonstrating bidirectional communication using \ElectronHostHook\ logic.
  • Implemented \ElectronHostHook/index.ts\ to handle 'ping' event and return 'pong'.
  • Implemented HomeController to call the hook from C#.
  • Added the new sample to \ElectronNET.sln.

Details

This addresses the request for a focused 'ElectronHostHook Sample' in the linked issue. The sample is minimal and self-contained, showing only the hook mechanism.

Copilot AI review requested due to automatic review settings December 7, 2025 17:27
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new sample application demonstrating the ElectronHostHook functionality, which enables bidirectional communication between C# and the Electron host process via TypeScript/JavaScript.

Key Changes

  • New sample project showcasing ping/pong communication between C# (HomeController) and TypeScript (ElectronHostHook/index.ts)
  • Implementation of custom Connector class to handle socket-based communication
  • Integration into the solution under "Test Apps" folder alongside existing samples

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/ElectronNET.sln Adds new sample project with proper dependencies and solution folder nesting
src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj Project configuration with TypeScript compilation and ElectronNET references
src/ElectronNET.Samples.ElectronHostHook/Program.cs ASP.NET Core application setup with Electron integration
src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs Controller demonstrating HostHook.CallAsync usage
src/ElectronNET.Samples.ElectronHostHook/Views/Home/Index.cshtml Simple UI displaying communication results
src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/connector.ts Base connector class for socket communication (has critical bug)
src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/index.ts HookService implementation with ping event handler
src/ElectronNET.Samples.ElectronHostHook/ElectronHostHook/tsconfig.json TypeScript compiler configuration
src/ElectronNET.Samples.ElectronHostHook/Properties/launchSettings.json Development launch configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 10 to 15
const done = (result: any) => {
this.socket.emit(id, result);
};

args = [...args, done];
javaScriptCode(...args);
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The done callback implementation doesn't match the protocol expected by Electron.HostHook.CallAsync<T>().

The C# API expects socket events with the format ${eventName}Complete${guid} for successful results and ${eventName}Error${guid} for errors (see HostHook.cs lines 74 and 68). However, this implementation only emits to id directly, which won't be received by the C# side.

Additionally, there's no error handling. The callback should wrap the JavaScript code execution in a try-catch block.

Replace the current implementation with:

on(key: string, javaScriptCode: Function): void {
    this.socket.on(key, (...args: any[]) => {
        const id: string = args.pop();

        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);
        }
    });
}

This matches the pattern used in the existing ElectronNET.WebApp/ElectronHostHook/connector.ts.

Suggested change
const done = (result: any) => {
this.socket.emit(id, result);
};
args = [...args, done];
javaScriptCode(...args);
try {
javaScriptCode(...args, (data: any) => {
if (data) {
this.socket.emit(`${key}Complete${id}`, data);
}
});
} catch (error) {
this.socket.emit(`${key}Error${id}`, `Host Hook Exception`, error);
}

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,18 @@
import { Socket } from "socket.io";
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ElectronHostHook directory is missing a package.json file, which is required to define the socket.io dependency used in both connector.ts and index.ts.

Without this file, the TypeScript compilation will fail and the runtime won't have access to the socket.io library.

Add a package.json file with at least the following content:

{
  "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"
  }
}

Reference: ElectronNET.WebApp/ElectronHostHook/package.json

Copilot uses AI. Check for mistakes.
Comment on lines 11 to 30
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();
});
}
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The window creation uses a fire-and-forget Task.Run pattern without proper error handling or awaiting the result. This can lead to unobserved task exceptions and doesn't follow the recommended pattern for Electron.NET applications.

Use the callback version of UseElectron instead, which provides proper error handling and lifecycle management:

builder.WebHost.UseElectron(args, async () =>
{
    var window = await Electron.WindowManager.CreateWindowAsync();
});

Then remove the Task.Run block (lines 24-30) entirely. The callback will be invoked automatically when the Electron app is ready.

Reference: ElectronNET.AspNet/API/WebApplicationBuilderExtensions.cs for the recommended pattern.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,16 @@
import { Connector } from "./connector";
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ElectronHostHook directory is missing a .gitignore file to exclude generated files and dependencies from version control.

Add a .gitignore file in the ElectronHostHook directory with content similar to the other samples to ignore:

  • node_modules/ directory
  • Generated JavaScript files (*.js, *.js.map)
  • npm/yarn logs and lock files
  • Other Node.js artifacts

For consistency with existing patterns, you can copy the .gitignore from src/ElectronNET.WebApp/ElectronHostHook/.gitignore or src/ElectronNET.Host/ElectronHostHook/.gitignore.

Copilot uses AI. Check for mistakes.
}

<!DOCTYPE html>
<html>
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <html> tag is missing the lang attribute, which is important for accessibility (screen readers) and SEO.

Add the lang attribute:

<html lang="en">
Suggested change
<html>
<html lang="en">

Copilot uses AI. Check for mistakes.

<!DOCTYPE html>
<html>
<head>
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <head> section is missing the character encoding declaration, which should be specified early in the document for proper text rendering.

Add the charset meta tag as the first element in <head>:

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width" />
    <title>ElectronHostHook Sample</title>
    ...
Suggested change
<head>
<head>
<meta charset="utf-8">

Copilot uses AI. Check for mistakes.
Comment on lines 4 to 11
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5",
"lib": ["es2015", "dom"]
},
"exclude": ["node_modules", "wwwroot"]
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The TypeScript target is set to "es5" while the lib includes "es2015". This inconsistency could cause issues since ES2015 features may not transpile correctly to ES5.

For consistency with the existing ElectronNET.WebApp/ElectronHostHook/tsconfig.json, consider updating to:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2019",
    "sourceMap": true,
    "skipLibCheck": true
  },
  "exclude": [
    "node_modules"
  ]
}

This matches the configuration used in the main WebApp sample and is more appropriate for the Node.js environment in Electron.

Suggested change
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5",
"lib": ["es2015", "dom"]
},
"exclude": ["node_modules", "wwwroot"]
"target": "ES2019",
"sourceMap": true,
"skipLibCheck": true
},
"exclude": [
"node_modules"
]

Copilot uses AI. Check for mistakes.
{
Task.Run(async () =>
{
var window = await Electron.WindowManager.CreateWindowAsync();
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to window is useless, since its value is never read.

Suggested change
var window = await Electron.WindowManager.CreateWindowAsync();
await Electron.WindowManager.CreateWindowAsync();

Copilot uses AI. Check for mistakes.
@softworkz
Copy link
Collaborator

@adityashirsatrao007 - Wow, cool! That was fast (after I posted the issue at least)..

I think we need to determine a number of conventions for how to include the samples in the repo.

The existing test apps are meant for development. They have project files that allow them to consume all of Electron.NET directly, bypassing nuget packages (unless modified).
The actual samples should not be like that. They should be minimal und use the nuget packages just like everybody is using them. As such, they also shouldn't be part of the development solution and they shouldn't be in the src folder but rather in their own folder - obviously ./samples - I'd say.

The folder name should be the subject without prefix, i.e. in this case ElectronHostHook.

As a 3-part root namespace is a bit ugly, I'd suggest that all samples should have two parts

  1. ElectronNETSample or ElectronNetSample
  2. The subject - e.g. ElectronHostHook

So, the full paths would be:

samples/ElectronHostHook/ElectronNetSample.ElectronHostHook.sln
samples/ElectronHostHook/ElectronNetSample.ElectronHostHook.csproj

But please don't take my word for it and don't make any changes yet.
We want to see what @FlorianRappl is thinking about it first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ideas for Sample Applications

2 participants