生成 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 使用指南