生成 Windows EXE 启动器

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

EXE 是可选项,默认不生成。启用后,生成的包里仍然保留 run.shrun.commandrun.bat。两种启动方式使用完全相同的归档结构、类路径、主类和 JVM 启动选项。

1. 适用范围

应用类型Java 应用、Spring Boot(三种布局均可)、Tomcat
目标平台windows-x64windows-x86windows-aarch64
不支持类库加密 —— 单个 .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生成 Windows 应用 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 文件版本:1 到 4 段数字,每段取值 0 到 65535。留空表示 0.0.0.0
--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 应用为例,EXE 与启动脚本一起放在包的根目录下:

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

包内的 README.md 会列出已生成的 EXE 文件名,以及代码签名方面的提示。

6. 运行行为

EXE 所在的目录就是应用的根目录。启动器只接受根目录之内的运行时、归档、类路径和工作目录;指向包外的路径,以及被符号链接或 junction 替换过的路径,都会被拒绝,进程不会启动。因此整个包可以随意移动或改名,但不能把 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 参数参考;GUI 向导流程见 GUI 使用指南