生成 Windows EXE 启动器
打包时可以为受保护的应用生成一个原生 Windows 启动程序。它是纯 Win32 可执行文件,双击即可运行应用,不需要用户先安装 Java,也不需要用户看到 run.bat。
EXE 是可选项,默认不生成。启用后,生成的包里同时保留原有的 run.sh / run.command / run.bat,两种启动方式使用完全相同的归档、类路径、主类和 JVM 参数。
1. 适用范围
| 项目 | 支持情况 |
|---|---|
| 应用类型 | Java Application、Spring Boot(三种布局均可)、Tomcat |
| 目标平台 | windows-x64、windows-x86、windows-aarch64 |
| 不支持 | Library Encryption(单个 .p4jx 归档没有可启动的应用目录) |
必须至少选择一个 Windows 目标平台。同时选择 Linux 或 macOS 平台不会导致任务失败:这些平台的子包照常生成,只是没有 EXE,仍使用原有启动脚本。所有目标平台都不是 Windows 时,任务会直接报错。
2. GUI 操作
- 在选择输入文件和目标平台的页面上勾选 生成 Windows 应用 EXE(x64/x86/ARM64),并至少选择一个 Windows 目标平台。
- 这个开关位于简单模式和高级模式的分叉之前,因此两种模式都可以生成 EXE。
- 勾选后会多出一个 Windows EXE 启动器 页面,用于填写文件名、启动器模式、图标和 Windows 版本信息。不勾选则直接进入最终确认页。
- 启用 EXE 后,JVM 启动参数 输入框只出现在这个启动器页面上,避免同一项参数出现两个入口。填写的参数同时写入 EXE 和启动脚本。
- 最终确认页会列出实际会生成 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.0、1.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_OPTIONS、JDK_JAVA_OPTIONS 和 CLASSPATH,避免外部注入改变启动参数 |
EXE 的 JVM 参数在打包时固化并随参数块一起签名,运行时校验,不能在部署后修改。APP_JAVA_OPTS 只对 run.bat 有效,EXE 不读取它。需要改动 EXE 的 JVM 参数时,请重新打包。
出于同样的原因,-javaagent、-agentlib、-agentpath、-Xbootclasspath 和 --patch-module 不允许写入 EXE 的启动参数。用 --jvm-option 传入这些参数时,打包会直接失败。
7. 代码签名
Protector4J 不为生成的 EXE 附加发布者签名,也不接触任何 Authenticode 凭据。图标、版本资源和启动参数等所有 PE 修改都在打包阶段完成,因此签名步骤应放在最后:
- 完成打包,确认 EXE 能正常启动应用。
- 用你自己的证书做 Authenticode 签名,并加 RFC3161 时间戳。
- 用 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-x64、windows-x86 或 windows-aarch64 目标 |
| 提示版本号格式不正确 | 版本字段含字母、空格或超过 4 段,或某段超出 0 到 65535 |
| 提示 EXE 名称只能是文件名 | 文件名里带了路径分隔符,改成不含目录的名字 |
| 提示找不到图标文件 | --exe-icon 指向的 .ico 不存在,检查路径 |
| 双击后闪退且没有窗口 | 用的是 GUI 模式而应用启动失败,改用控制台模式重新打包即可看到错误输出 |
| 单独复制出来的 EXE 无法运行 | 启动器要求运行时和归档位于同一个包目录内,请复制整个输出目录 |