- 桌面应用
- 跨平台
【免费下载链接】Electron.NET
:electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor).
Electron.NET 为 ASP.NET Core(Razor Pages、MVC、Blazor)应用提供了灵活的多启动方式支持,通过「已打包/未打包」「控制台/ASP.NET」「.NET 先行/Electron 先行」三种维度的自由组合,覆盖开发调试与生产部署的完整场景。本文以 docs/Using/Startup-Methods.md 为核心,结合仓库源码(StartupManager.cs、StartupMethod.cs、RuntimeControllerDotNetFirst.cs 等)深入拆解每种模式的启动标志、进程流程与底层检测机制,读完即可为你的项目选型正确的启动模式,并掌握launchSettings.json与发布脚本的完整配置方案。
启动方式全景:8 种组合
Electron.NET 支持8 种不同的启动场景,覆盖以下三个维度的全部组合:
| 维度 | 取值 | 说明 |
|---|---|---|
| 部署形态 | Packaged / Unpackaged | 是否已通过 electron-builder 等工具打包成独立应用 |
| 应用类型 | Console / ASP.NET | .NET 侧是控制台宿主还是 ASP.NET Core Web 宿主 |
| 初始化顺序 | Dotnet-first / Electron-first | 启动时先拉起哪个进程,谁掌握生命周期控制权 |
2(部署)× 2(应用类型)× 2(初始化顺序)= 8 种场景。从源码看,枚举 StartupMethod.cs 定义了四种核心模式,其中 Unpackaged 与 Packaged 各含 Electron-first 与 Dotnet-first 两个变体;而 Console / ASP.NET 的区别则体现在运行时控制器的创建路径上——ASP.NET 应用通过 WebApplicationBuilderExtensions.cs 的UseElectron挂载,控制台应用则直接使用ElectronNetRuntime.RuntimeController(见 ElectronNetRuntime.cs)。
框架在启动时自动检测当前应采用的模式,无需手工指定完整配置,依据的是命令行标志与运行环境。
命令行标志速查
-unpackedelectron:Electron 先行调试(未打包)
直接从编译输出目录启动 Electron,由 Electron 反过来拉起 .NET 进程,适合调试 Electron 主进程与 Node.js 代码:
# 先启动 Electron,再由它启动 .NET node node_modules/electron/cli.js main.js -unpackedelectron在StartupMethod.cs的注释中,该模式被描述为"类似于传统 Electron.NET 调试,但不打包(更快),且允许选择调试适配器;除非目标是调试 Node.js,否则很少用到"。其特点是从./bin/*编译输出目录直接运行,无需中间打包环节。
-unpackeddotnet:.NET 先行调试(未打包)
.NET 应用先启动,再由它拉起 Electron。这是"超快启动、就地调试 + Hot Reload(编辑并继续)"的新方式,即使在 WSL 下也能全程在 Visual Studio 内完成调试(见StartupMethod.cs中UnpackedDotnetFirst的注释):
# 先启动 .NET,再由它启动 Electron dotnet run -unpackeddotnet-dotnetpacked:.NET 先行的已打包执行
发布产物中由 .NET 可执行文件先行启动,再加载打包文件中的 Electron:
# 以 .NET 先行的方式运行已打包应用 MyApp.exe -dotnetpacked从 ElectronProcessActive.cs 可以看到,该模式下 .NET 实际向 Electron 传递的命令行参数为-dotnetpacked -electronforcedport={socketPort} {extraArguments},其中-electronforcedport用于强制指定 Socket 桥接端口。
无标志:Electron 先行的已打包执行(默认)
已打包应用直接运行可执行文件,走传统 Electron 行为,由 Electron 先行启动:
# 运行已打包应用(无需任何特殊标志) MyApp.exe四种启动模式详解
模式 1:Unpackaged + Electron-First(开发)
- 适用场景:调试 Electron 主进程与 Node.js 代码
- 命令:
-unpackedelectron标志 - 进程流程:
- Electron 先启动;
- Electron 拉起 .NET 进程;
- .NET 通过 Socket 反向连接回 Electron;
- 应用运行,由 Electron 掌握控制权。
此时 .NET 侧对应UnpackedElectronFirst模式,运行时控制器为 RuntimeControllerElectronFirst.cs:它不主动创建 Electron 进程,而是通过ElectronProcessPassive绑定 Electron 传入的进程 ID(electronPID),并利用SocketBridgeService(port, token)建立通信,port 与 token 均由 Electron 通过命令行参数提供。
模式 2:Unpackaged + .NET-First(开发)
- 适用场景:调试 ASP.NET/C# 代码,享受 Hot Reload
- 命令:
-unpackeddotnet标志 - 进程流程:
- .NET 应用先启动;
- .NET 拉起 Electron 进程;
- Electron 连接回 .NET;
- 应用运行,由 .NET 掌握控制权。
对应UnpackedDotnetFirst模式与 RuntimeControllerDotNetFirst.cs。ElectronProcessActive会从编译输出目录下的.electron文件夹定位 Electron 可执行文件,并携带main.js -unpackeddotnet --trace-warnings -electronforcedport={socketPort}参数启动;在非 Windows 平台上还会先执行chmod -R +x为 Electron dist 目录添加可执行权限(见 ElectronProcessActive.cs)。
模式 3:Packaged + .NET-First(生产)
- 适用场景:已部署应用,由 .NET 控制生命周期
- 命令:
-dotnetpacked标志 - 进程流程:
- .NET 可执行文件先启动;
- .NET 从打包文件中拉起 Electron;
- Electron 从
app.asar或解包目录加载; - .NET 维持进程控制权。
对应PackagedDotnetFirst模式。在StartupMethod.cs的注释中,这一模式提供了"更好的整体应用生命周期管理方式"。此时ElectronProcessActive通过ElectronRootDirResolver解析打包后的 Electron 根目录,找到ElectronExecutable(Windows 下会追加.exe后缀)并携带-dotnetpacked -electronforcedport={socketPort}参数启动(参见 ElectronProcessActive.cs 与 StartupManager.cs 中的SetElectronExecutable)。
模式 4:Packaged + Electron-First(生产)
- 适用场景:传统 Electron 应用行为
- 命令:无需特殊标志
- 进程流程:
- Electron 可执行文件先启动;
- Electron 从打包文件中拉起 .NET;
- .NET 在 Electron 的进程上下文中运行;
- Electron 维持 UI 控制权。
对应PackagedElectronFirst模式,被StartupMethod.cs称为"Electron.NET 的经典启动方式"。运行时控制器同样是RuntimeControllerElectronFirst——从 StartupManager.cs 的CreateRuntimeController可以看到,PackagedElectronFirst与UnpackedElectronFirst共用同一控制器,二者的差异只体现在 Electron 传入的 PID、端口与令牌等参数上。
源码解读:框架如何自动检测启动模式
模式自动检测的逻辑集中在 StartupManager.cs 的Initialize()中,启动时依次完成:收集构建信息(GatherBuildInfo)、收集进程参数(CollectProcessData)、设置 Electron 可执行文件路径(SetElectronExecutable)、最终调用DetectAppTypeAndStartup判定模式,并输出一行Evaluated StartupMethod: ...便于确认。
决策逻辑:谁先启动
// StartupManager.DetectAppTypeAndStartup(简化示意) var isLaunchedByDotNet = LaunchOrderDetector.CheckIsLaunchedByDotNet(); var isUnPackaged = UnpackagedDetector.CheckIsUnpackaged(); if (isLaunchedByDotNet) { return isUnPackaged ? StartupMethod.UnpackedDotnetFirst : StartupMethod.PackagedDotnetFirst; } return isUnPackaged ? StartupMethod.UnpackedElectronFirst : StartupMethod.PackagedElectronFirst;LaunchOrderDetector:判断启动发起方
LaunchOrderDetector.cs 通过三个探针投票决定"是否由 .NET 发起":
| 探针 | 判定 | 说明 |
|---|---|---|
是否存在electronPort参数 | 有 → Electron 先行 | Electron 先行启动时会把端口传给 .NET |
是否存在electronPID参数 | 有 → Electron 先行 | Electron 先行时把自己的进程 ID 传给 .NET |
| 调试器是否已附加 | 附加 → .NET 先行 | 开发调试中默认视为 .NET 发起 |
最终scoreDotNet > scoreElectron时判定为 Dotnet-first。这些参数名定义在 ElectronNetRuntime.cs(electronPort、electronHost、electronPID、electronAuthToken)。
UnpackagedDetector:判断是否已打包
UnpackagedDetector.cs 用五个探针(其中一个计双份)打分:
- 构建配置为
Debug→ 未打包;Release→ 已打包; - 基目录位于
resources/bin→ 已打包;基目录存在.electron目录 → 未打包; - 在
ElectronRootDir下能找到 Electron 可执行文件 → 已打包; - 调试器附加 → 未打包;
- 命令行参数包含
unpacked→ 未打包;包含dotnetpacked→ 已打包。
最终按scoreUnpackaged > scorePackaged判定。这也是"无需显式指定打包状态"的实现基础——框架通过环境探测而非硬编码开关完成决策。
配置示例:ASP.NET 与控制台应用
ASP.NET 应用启动
在Program.cs中通过WebApplicationBuilder的UseElectron扩展方法接入。仓库提供了四种重载,支持回调携带进程参数或IServiceProvider(见 WebApplicationBuilderExtensions.cs):
// Program.cs var builder = WebApplication.CreateBuilder(args); // 为不同启动模式统一配置 builder.WebHost.UseElectron(args, async () => { var browserWindow = await Electron.WindowManager.CreateWindowAsync( new BrowserWindowOptions { Show = false }); await browserWindow.WebContents.LoadURLAsync("http://localhost:8001"); browserWindow.OnReadyToShow += () => browserWindow.Show(); }); var app = builder.Build(); app.Run();说明:http://localhost:8001对应仓库默认的 Web 端口常量DefaultWebPort = 8001(ElectronNetRuntime.cs),而 Socket 桥接默认端口为DefaultSocketPort = 8000。
控制台应用启动
控制台宿主不依赖 ASP.NET 宿主,直接操作RuntimeController完成生命周期管理(可对照 ElectronNET.ConsoleApp/Program.cs 的真实写法):
// Program.cs public static async Task Main(string[] args) { var runtimeController = ElectronNetRuntime.RuntimeController; await runtimeController.Start(); await runtimeController.WaitReadyTask; await InitializeApplication(); // 例如创建主窗口 await runtimeController.WaitStoppedTask; }RuntimeController属性与ElectronNetRuntime的公开状态均在 ElectronNetRuntime.cs 中定义。
launchSettings.json 双模式调试
仓库示例 src/ElectronNET.ConsoleApp/Properties/launchSettings.json 展示了同时配置 .NET 先行与 Electron 先行两种调试入口的方式:
// launchSettings.json { "profiles": { "DotNet (unpackaged)": { "commandName": "Project", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "Electron (unpackaged)": { "commandName": "Executable", "executablePath": "node", "commandLineArgs": "node_modules/electron/cli.js main.js -unpackedelectron", "workingDirectory": "$(TargetDir).electron", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "WSL": { "commandName": "WSL2", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_URLS": "http://localhost:8001/" } } } }注意 Electron 先行配置中的workingDirectory指向$(TargetDir).electron,这是未打包调试时 Electron 二进制的存放位置,与 ElectronProcessActive.cs 中Path.Combine(dir.FullName, ".electron")的定位逻辑一一对应。
启动流程示意
上图(startup_modes.png)展示了部署类型、应用类型与初始化顺序的组合如何影响进程生命周期:横向区分已打包/未打包,纵向区分 .NET 先行/Electron 先行,两者交汇处即为对应进程的启停顺序。
开发工作流
调试工作流
ASP.NET 先行调试(推荐)——在launchSettings.json中配置-unpackeddotnet,即可在 Visual Studio 中直接按 F5 运行并享受 Hot Reload:
// launchSettings.json { "ASP.Net (unpackaged)": { "commandName": "Project", "commandLineArgs": "-unpackeddotnet" } }Electron 先行调试——通过Executable配置直接以 Node 启动 Electron:
// launchSettings.json { "Electron (unpackaged)": { "commandName": "Executable", "executablePath": "node", "commandLineArgs": "node_modules/electron/cli.js main.js -unpackedelectron" } }两种入口可以共存于同一份launchSettings.json,按需切换。
生产部署
Dotnet-first 部署——先dotnet publish生成 RID 特定产物,再进入发布目录安装依赖并执行 electron-builder 打包,最后以-dotnetpacked运行:
# 构建并打包 dotnet publish -c Release -r win-x64 cd publish\Release\net8.0\win-x64 npm install npx electron-builder # 以 dotnet-first 方式运行 MyApp.exe -dotnetpackedElectron-first 部署(默认)——无需任何特殊标志:
# 直接运行已打包应用 MyApp.exe进程生命周期管理
自动清理
Electron.NET 会自动管理进程生命周期(见 RuntimeControllerBase.cs 及其两个子类):
- 主窗口关闭时优雅关闭(graceful shutdown);
- 正确清理子进程——
ElectronProcessActive.StopCore调用ProcessRunner.Cancel终止 Electron,RuntimeControllerDotNetFirst.HandleStopped会在 Electron 或 Socket 桥任一停止时级联停止另一端; - 进程失败的错误处理——
ElectronProcessActive.StartInternal内置 2 分钟超时保护,超时自动 Cancel 进程并越过等待屏障; - 跨平台兼容的进程管理——非 Windows 平台自动处理
chmod可执行权限,并通过CheckRuntimeIdentifier校验构建 RID 与当前平台是否匹配(ElectronProcessActive.cs)。
手动控制
通过ElectronNetRuntime.RuntimeController访问运行时控制器,可手动等待就绪、停止运行时。所有等待任务(WaitStartedTask/WaitReadyTask/WaitStoppingTask/WaitStoppedTask)与状态转换(Uninitialized → Starting → Started → Ready → Stopping → Stopped)统一由 LifetimeServiceBase.cs 提供,非法状态回退会抛出异常以保证状态机严格单调递增:
var runtime = ElectronNetRuntime.RuntimeController; // 等待 Electron 就绪 await runtime.WaitReadyTask; // 停止 Electron 运行时 await runtime.Stop(); await runtime.WaitStoppedTask;通信桥接细节
无论哪种模式,.NET 与 Electron 之间最终都通过SocketBridgeService(port, token)建立 Socket.IO 连接。Electron-first 模式下,Electron 会打印一行Electron Socket: listening on port ... at ... using <token>,由 ElectronProcessActive.cs 中的正则^Electron Socket: listening on port (\d+) at (\S+) using ([a-f0-9]+)$解析出端口、主机与认证令牌,从而完成握手。
故障排查
常见启动问题
"Electron process not found"
- 确保 Node.js 22.x 已安装;
- 检查 .NET 构建是否成功;
- 确认
RuntimeIdentifier设置正确(发布时通过-r win-x64等参数指定,运行时的 RID 校验逻辑见 ElectronProcessActive.cs 的CheckRuntimeIdentifier)。
"Port conflicts"(端口冲突)
- 为不同启动模式使用不同端口;
- 检查是否有其他实例占用默认端口(Socket 默认
8000,Web 默认8001,见 ElectronNetRuntime.cs); - 核实防火墙设置。
"Process won't terminate"(进程无法终止)
- 改用 dotnet-first 模式以获得更可靠的清理(
RuntimeControllerDotNetFirst的HandleStopped会级联停止 Socket 桥与 Electron 进程); - 检查是否存在未处理异常;
- 确认所有窗口均已正确关闭。
最佳实践
选择合适的模式
| 场景 | 推荐模式 | 理由 |
|---|---|---|
| 开发(调试 C#) | .NET-first(-unpackeddotnet) | 支持 Hot Reload,启动更快 |
| 开发(调试 Node.js) | Electron-first(-unpackedelectron) | 可直接调试 Electron 主进程 |
| 生产 | .NET-first(-dotnetpacked) | 更好的进程控制与生命周期管理 |
| 生产(传统行为) | Electron-first(无标志) | 保持传统 Electron 行为 |
| 跨平台 | .NET-first | 各平台行为一致 |
环境配置
如需固定运行环境,可在.csproj中设置:
<!-- .csproj --> <PropertyGroup> <ElectronNETCoreEnvironment>Production</ElectronNETCoreEnvironment> </PropertyGroup>结合 Configuration.md 可进一步控制构建属性与 Electron 版本等信息。
下一步阅读
- 调试不同启动模式
- 针对不同部署场景打包
- 将既有应用迁移到新启动方式
- ASP.NET 应用接入 与 控制台应用接入
总结
Electron.NET 的启动系统通过「已打包/未打包 × 控制台/ASP.NET × .NET 先行/Electron 先行」三个维度组织出 8 种启动场景,并以命令行标志 + 环境探测(LaunchOrderDetector.cs、UnpackagedDetector.cs)自动选择最合适的模式。无论你是需要 Hot Reload 的 .NET 开发者、需要直接调试 Node.js 的 Electron 开发者,还是追求进程可控性的生产部署团队,都能在四类StartupMethod中找到对应方案——这就是 Electron.NET 为 .NET 开发者提供理想调试与部署体验的基础。
- 桌面应用
- 跨平台
【免费下载链接】Electron.NET
:electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor).
相关推荐
ngx-loading-bar版本迁移指南:从Angular 13到16的平滑升级
ngx loading bar版本迁移指南:从Angular 13到16的平滑升级 ngx loading bar是一款为Angular应用提供自动页面加载进度
Electron.NET WindowManager 完全指南:窗口创建、生命周期管理与 BrowserView 集成
Electron.NET WindowManager 完全指南:窗口创建、生命周期管理与 BrowserView 集成 Electron.WindowManag
桌面应用跨平台Electron.NET 应用生命周期管理:Electron.App API 完整实战指南
Electron.NET 应用生命周期管理:Electron.App API 完整实战指南 导读 Electron.App 是 Electron.NET 中控制
桌面应用跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考