在做 Electron 專案時,有個需求是安裝包安裝時要給客戶機上裝上某個軟體
在檢視 Inno Setup 官網後發現是通過 .iss 指令碼編寫實現自定義安裝過程
可在 .iss 內可以新增指令碼為安裝過程新增邏輯
為了測試方便我用 vite 新建一個全新的 electron 專案
用的是這個腳手架 https://github.com/electron-vite/electron-vite-vue
用其它腳手架也行,反正我們對 app 內容本身並不關注,只關心製作安裝包
安裝注意事項(都是我踩過的坑..)
yarn config get registry
我用的是這個 https://registry.npm.taobao.org/
yarn create electron-vite
專案名 first-electron
模板我選了 vue ,其實無所謂我們只是跑個electron demo 目的是打包而不是開發
Project name: first-electron
Project template: Vue
成功後
cd first-electron
yarn
yarn dev
專案應該跑起來了可以看到 electron 視窗
關掉專案, 下一步測一下打包功能
專案已經幫你整合了 electron-builder
直接執行打包命令
yarn build
大概率會失敗
從 github 拉 electron 失敗
需要從映象伺服器,設定 electron_mirror 到專門映象 後再打包
yarn config set electron_mirror https://npm.taobao.org/mirrors/electron/
yarn build
成功後在專案 first-electron\release 目錄下就是編譯完成的 electron app 了
我的是在 first-electron\release\0.0.0 目錄下,0.0.0 一看就是軟體版本號了
專案根目錄的 electron-builder.json5
就是 electron-builder 組態檔
組態檔內的 "output": "release/${version}" 就是定義編譯後的檔案目錄,一般不會去改
first-electron_0.0.0.exe
這個檔案就是單獨檔案的安裝版,雙擊安裝時會有安裝過程
win-unpacked
就是綠色版, 安裝包的製作就是把這個資料夾內的檔案進行打包
如果想對安裝包安裝過程進行自定義
那麼首先電腦上先安裝 Inno Setup Compiler, 再建立 setup.iss 檔案
通過 ide 新建 .iss 檔案
過程如下:
開啟 Inno Setup Compiler
新建一個 Inno Setup Script Wizard 開始建立 .iss 指令碼檔案
按提示一步步往下走,可以選擇填寫 app 名字,版本,公司之類的資訊這些都不重要
直到 Application Files
Application main executable file 原預設的 Myprog.exe 主執行檔案變更到我們自己 first-electron 的主執行檔案,如下:
first-electron\release\0.0.0\win-unpacked\first-electron.exe
另外 Other application files: 這一項點選 Add folder... 按鈕
把 win-unpacked 整個資料夾也新增進來
繼續按提示往下走,直到 Compiler Settings 這一頁
Custom compiler output folder: 選擇打包檔案輸出目錄
我選擇輸出到 first-electron\dist-setup 目錄
繼續按提示往下走,最後會讓你儲存成 .iss 檔案,這個 .iss 檔案就是打包指令碼了
我在這個例子中輸出檔案儲存為了 setup.iss
先不動這個指令碼。直接編譯,完成後可以在 first-electron\dist-setup 看到 windows 安裝包了
比如主程式安裝完後想自動執行安裝東方財富的的安裝檔案 「dfcft8.exe」
(別問為什麼是 「東方財富」 這個安裝包,我的電腦的下載資料夾內剛好看到有這個.exe 執行檔案就用這個來測試吧)
為什麼要做這麼流氓的事? 這只是舉個例子而已!
其實,真實專案中有可能是客戶機器上缺少某種環境或檔案之類的東西,那麼你可以利用此方法幫使用者安裝上
讓我們開始吧。 非常的簡單!
修改之前儲存的 setup.iss 檔案
在 setup.iss 檔案中找到 [Run] 節點新增如下程式碼:
[Run]
Filename: "{app}\resources\bin\dfcft8.exe"; Description: ""; Flags: nowait postinstall skipifsilent
使用 Inno setup compiler 編譯該檔案
安裝程式最後會顯示這樣的畫面:
點選 「完成」 按鈕就會出現以下畫面:
非常簡單!
上面說的是安裝程式安裝完成後執行另外的一個程式,那麼如果想在安裝程式安裝前執行呢?
需要 Pascal 指令碼了, 開始吧!
說實話作為長期使用 Javascript 的我來說第一次接觸 Pascal Scripting 我心裡一萬頭xx馬奔騰而過
而且我看 Inno Setup Compiler 官網 <jrsoftware.org> 的大概意思是 Pascal Scripting 不太好弄
官方建議使用者從
Inno Setup 6\Examples
安裝目錄下的 Examples 目錄下參考官方提供的指令碼例子
在 setup.iss 檔案內新增 [code] 節點
這個節點允許你為安裝過程新增邏輯,可細化到安裝的每一步
它通過在 [code] 節點下暴露 Event Functions 實現,比如:
我姑且把它理解為生命週期吧!
那麼如果想在安裝開始時就呼叫
使用 InitializeSetup 並在其內用 Exec 方法呼叫 'dfcft8.exe'
下面實現安裝過程開始之前前呼叫
[code]
function InitializeSetup(): Boolean;
var
ResultCode: Integer;
begin
ExtractTemporaryFile('dfcft8.exe');
Exec(ExpandConstant('{tmp}\dfcft8.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode)
Log('InitializeSetup called');
Result := true
end;
為 setup.iss 新增上面程式碼後用 Inno Setup Compiler Ide 按 F9 或 Run 選單 Run選項 "編譯並啟動"
安裝包啟動後點選"確定"按鈕,Ide 會輸出紀錄檔,也可以看到東方財富的安裝程式也啟動了
注意紀錄檔輸出畫面中的 這兩句:
[15:19:26.074] Created temporary directory: C:\Users\ADMINI~1\AppData\Local\Temp\is-8P52P.tmp
[15:19:26.082] Extracting temporary file: C:\Users\ADMINI~1\AppData\Local\Temp\is-8P52P.tmp\dfcft8.exe
意思是先建個臨時資料夾,再從安裝包中先解壓出 "dfctt8.exe" 這個檔案放到臨時資料夾內
此時安裝目錄內是取不到檔案的, 因為我們的 first-electron 程式還沒有安裝成功,也不會在客戶電腦上有安裝目錄
只能從安裝包這個壓縮檔案內解壓獲取臨時檔案
這是 setup.iss 現在的程式碼
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "first-electron"
#define MyAppVersion "1.5"
#define MyAppPublisher "My Company, Inc."
#define MyAppURL "https://www.example.com/"
#define MyAppExeName "first-electron.exe"
#define MyAppAssocName MyAppName + " File"
#define MyAppAssocExt ".myp"
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{72C0FB3C-108B-4530-8D27-5D31B5349C3C}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
ChangesAssociations=yes
DisableProgramGroupPage=yes
; Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
OutputDir=C:\Users\Administrator\Desktop\first-electron\dist-setup
OutputBaseFilename=first-electron-setup
Compression=lzma
SolidCompression=yes
WizardStyle=modern
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "C:\Users\Administrator\Desktop\first-electron\release\0.0.0\win-unpacked\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\Administrator\Desktop\first-electron\release\0.0.0\win-unpacked\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Registry]
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".myp"; ValueData: ""
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
; Filename: "{app}\resources\bin\dfcft8.exe"; Description: ""; Flags: nowait postinstall skipifsilent
[code]
function InitializeSetup(): Boolean;
var
ResultCode: Integer;
begin
ExtractTemporaryFile('dfcft8.exe');
Exec(ExpandConstant('{tmp}\dfcft8.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
Log('InitializeSetup called');
Result := true
end;
這裡有個疑問,ExtractTemporaryFile('dfcft8.exe')
路徑
這裡可沒指定過 'dfcft8.exe' 檔案位置,而且我明明是把它放在了 resources\bin\dfcft8.exe
下,難道會自動搜尋?
測試一下猜想
為了測試我把用另一個 "electron-fiddle.exe" 安裝包放在 resources
資料夾並且名稱改為同樣的 'dfcft8.exe' ,這樣就有兩個同名檔案在不同資料夾下
resources\bin\'dfcft8.exe'
resources\'dfcft8.exe'
編譯測試結果:
果然被改名為 'dfcft8.exe' 的 'electron-fiddle.exe' 被啟動代替了東方財富的安裝程式
官網我找不到資料,但測試後得到的結論是 ExtractTemporaryFile 會返回搜尋到的第一個匹配檔案
如果非要解壓指定資料夾下的指定檔案呢?
那麼需要換種方法,像下面這樣改:
[Files]
Source: "C:\Users\Administrator\Desktop\first-electron\release\0.0.0\win-unpacked\resources\bin\dfcft8.exe"; DestDir: "{tmp}\resources\bin"
[code]
function InitializeSetup(): Boolean;
var
ResultCode: Integer;
begin
ExtractTemporaryFiles('{tmp}\resources\bin\dfcft8.exe')
Exec(ExpandConstant('{tmp}\resources\bin\dfcft8.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
Log('InitializeSetup called');
Result := true
end;
[Files] 節點指定 DestDir: "{tmp}\resources\bin"
[code] 節點內使用 ExtractTemporaryFiles('{tmp}\resources\bin\dfcft8.exe') 解壓
並且 Exec 方法呼叫時也要傳路徑 ExpandConstant('{tmp}\resources\bin\dfcft8.exe')
編譯測試一下
可以看到已經執行呼叫東方財富的安裝程式
先別急著關掉,我們可以通過 log 輸出的臨時路徑資訊,找到對應的資料夾開啟檢視 C:\Users\ADMINI~1\AppData\Local\Temp\is-0467N.tmp
[17:15:15.953] Created temporary directory: C:\Users\ADMINI~1\AppData\Local\Temp\is-0467N.tmp
果然臨時資料夾內資料夾結構如我們所指定的那樣
該路徑會在安裝程式結束時自動刪除
之前是在 [Run] 節點下實現,其實也可以用 procedure DeinitializeSetup()
實現類似的功能
改動如下:
[code]
function InstallDfcf: Boolean;
var
ResultCode: Integer;
begin
if not Exec(ExpandConstant('{app}\resources\bin\dfcft8.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
Log('安裝東方財富失敗. Error code: ' + IntToStr(ResultCode));
end;
end;
procedure DeinitializeSetup();
begin
InstallDfcf;
end;
函數 DeinitializeSetup
就是安裝結束時呼叫自定義函數 InstallDfcf
InstallDfcf 函數內部的 Exec 呼叫了 'dfcft8.exe'
Inno Setup 其實可客製化化的功能還有很多,官網檔案就仁者見仁智者見智了
全面詳細學習 Inno Setup 沒啥意義,用哪個功能直接瞭解這一塊兒就行,就是學習各種設定
現在有 chatgpt,還記啥設定呢,不明白可以直接問它,chatgpt 真是事半功倍啊,反正我就是這麼做的
我們該把時間用於更有意義的地方,比如摸魚!
部落格園: http://cnblogs.com/willian/
github: https://github.com/willian12345/