生成 Windows EXE 启动器

打包时可以为受保护的应用生成一个原生 Windows 启动程序。它是纯 Win32 可执行文件,双击即可运行应用,不需要用户先安装 Java,也不需要用户看到 run.bat

EXE 是可选项,默认不生成。启用后,生成的包里同时保留原有的 run.sh / run.command / run.bat,两种启动方式使用完全相同的归档、类路径、主类和 JVM 参数。

1. 适用范围

项目支持情况
应用类型Java Application、Spring Boot(三种布局均可)、Tomcat
目标平台windows-x64windows-x86windows-aarch64
不支持Library Encryption(单个 .p4jx 归档没有可启动的应用目录)

必须至少选择一个 Windows 目标平台。同时选择 Linux 或 macOS 平台不会导致任务失败:这些平台的子包照常生成,只是没有 EXE,仍使用原有启动脚本。所有目标平台都不是 Windows 时,任务会直接报错。

2. GUI 操作

  1. 在选择输入文件和目标平台的页面上勾选 生成 Windows 应用 EXE(x64/x86/ARM64),并至少选择一个 Windows 目标平台。
  2. 这个开关位于简单模式和高级模式的分叉之前,因此两种模式都可以生成 EXE。
  3. 勾选后会多出一个 Windows EXE 启动器 页面,用于填写文件名、启动器模式、图标和 Windows 版本信息。不勾选则直接进入最终确认页。
  4. 启用 EXE 后,JVM 启动参数 输入框只出现在这个启动器页面上,避免同一项参数出现两个入口。填写的参数同时写入 EXE 和启动脚本。
  5. 最终确认页会列出实际会生成 EXE 的平台,以及填写过的启动器字段。

3. CLI 示例

最简形式,只需 --windows-exe

p4j --target-platform windows-x64 javaapp app.jar dist --windows-exe

指定文件名和窗口模式:

p4j --target-platform windows-x64 javaapp app.jar dist \
  --windows-exe \
  --exe-name MyApp.exe \
  --exe-mode gui \
  --exe-icon assets/app.ico

一次生成三种 Windows 架构,并填写完整的版本资源:

p4j --target-platform windows-x64,windows-x86,windows-aarch64 \
  springboot app.jar dist \
  --windows-exe \
  --exe-name MyService \
  --exe-file-version 1.4.2.0 \
  --exe-product-version 1.4.2.0 \
  --exe-company 'Example Inc.' \
  --exe-product 'Example Service' \
  --exe-description 'Example background service' \
  --exe-copyright 'Copyright (C) 2026 Example Inc.'

Tomcat 包同样支持,生成的 EXE 以前台方式启动内嵌 Tomcat:

p4j --target-platform windows-x64 tomcat app.war dist --context /demo --windows-exe

4. 参数说明

选项说明
--windows-exe启用 EXE 生成
--exe-name <name>EXE 文件名;留空则使用输入文件名,Tomcat 使用 tomcat
--exe-mode <mode>console(默认)或 gui
--exe-icon <ico>Windows 图标文件,.ico 格式,可选
--exe-file-version <a.b.c.d>PE 文件版本
--exe-product-version <a.b.c.d>PE 产品版本
--exe-company <text>公司名称
--exe-product <text>产品名称
--exe-description <text>文件说明,显示在任务管理器和文件属性中
--exe-copyright <text>版权声明

任意一个 --exe-* 选项都会自动启用 EXE 生成,不必额外再写 --windows-exe

文件名只能是文件名,不能包含 /\ 或目录部分;没有 .exe 后缀时自动补上。

两个版本号字段的规则相同:1 到 4 段用英文句点分隔的数字,每段取值 0 到 65535,例如 1.0.0.1。Windows 把每段存成 16 位无符号整数,所以不接受 v1.01.0-beta 这类带字母或空格的写法。留空表示 0.0.0.0。这两个字段在打包开始前校验,不会等到打包中途才失败。

5. 输出结构

以 Java Application 为例,EXE 与启动脚本并列在包根目录:

dist/
├── MyApp.exe             # 新增的 Windows 启动器
├── app.p4jx
├── vlxjre/
├── lib/
├── run.sh
├── run.command
├── run.bat
└── README.md

包内的 README.md 会列出生成的 EXE 文件名,并附上代码签名提示。

6. 运行行为

EXE 所在目录就是应用根目录。启动器只接受根目录以内的运行时、归档、类路径和工作目录;指向包外的路径、以及用符号链接或联接点替换过的路径都会被拒绝,进程不会启动。这意味着可以整包移动或改名,但不能把 EXE 单独复制出来使用。

行为说明
运行时固定使用包内 vlxjre\bin\java.exe,不使用系统 Java,也不读取 JAVA_HOME
命令行参数传给 EXE 的参数原样追加到应用参数之后
控制台模式继承当前控制台的标准输入输出,等待应用结束并返回应用的退出码
GUI 模式不创建控制台窗口,适合桌面应用
环境变量启动前清除 JAVA_TOOL_OPTIONS_JAVA_OPTIONSJDK_JAVA_OPTIONSCLASSPATH,避免外部注入改变启动参数

EXE 的 JVM 参数在打包时固化并随参数块一起签名,运行时校验,不能在部署后修改。APP_JAVA_OPTS 只对 run.bat 有效,EXE 不读取它。需要改动 EXE 的 JVM 参数时,请重新打包。

出于同样的原因,-javaagent-agentlib-agentpath-Xbootclasspath--patch-module 不允许写入 EXE 的启动参数。用 --jvm-option 传入这些参数时,打包会直接失败。

7. 代码签名

Protector4J 不为生成的 EXE 附加发布者签名,也不接触任何 Authenticode 凭据。图标、版本资源和启动参数等所有 PE 修改都在打包阶段完成,因此签名步骤应放在最后:

  1. 完成打包,确认 EXE 能正常启动应用。
  2. 用你自己的证书做 Authenticode 签名,并加 RFC3161 时间戳。
  3. 用 Windows 的 /pa 策略验签。

签名完成后不要再修改这个 PE 文件,任何改动都会使签名失效。需要更换图标或版本号时,请重新打包并重新签名。

8. 任务文件字段

导出的 p4j-task.yml 使用版本 2,启用 EXE 时保存以下字段。版本 1 的任务文件仍可正常读取。

windowsExe: true
exeName: MyApp.exe
exeMode: console
exeIcon: assets/app.ico
exeFileVersion: 1.4.2.0
exeProductVersion: 1.4.2.0
exeCompany: Example Inc.
exeProduct: Example Service
exeDescription: Example background service
exeCopyright: Copyright (C) 2026 Example Inc.

9. 常见问题

现象原因与处理
提示需要 Windows 目标平台启用了 EXE 但没有选择任何 windows-x64windows-x86windows-aarch64 目标
提示版本号格式不正确版本字段含字母、空格或超过 4 段,或某段超出 0 到 65535
提示 EXE 名称只能是文件名文件名里带了路径分隔符,改成不含目录的名字
提示找不到图标文件--exe-icon 指向的 .ico 不存在,检查路径
双击后闪退且没有窗口用的是 GUI 模式而应用启动失败,改用控制台模式重新打包即可看到错误输出
单独复制出来的 EXE 无法运行启动器要求运行时和归档位于同一个包目录内,请复制整个输出目录

完整的 CLI 选项见 CLI 参数参考,GUI 向导流程见 GUI 使用指南