產生 Windows EXE 啟動器
打包時可以為受保護的應用程式產生一個原生 Windows 啟動程式。它是純 Win32 執行檔,按兩下即可執行,你的使用者不需要先安裝 Java,也不必接觸 run.bat。
EXE 是選用項目,預設不會產生。啟用後,產生的包裡仍然保留 run.sh、run.command 與 run.bat。兩種啟動方式使用完全相同的歸檔結構、類別路徑、主類與 JVM 啟動選項。
1. 適用範圍
| 應用程式類型 | Java 應用、Spring Boot(三種佈局皆可)、Tomcat |
| 目標平臺 | windows-x64、windows-x86、windows-aarch64 |
| 不支援 | 類庫加密 —— 單一 .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 | 產生 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.0、1.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_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 無法執行 | 啟動器要求執行時與歸檔位於同一個包目錄中,請複製整個輸出目錄。 |