Electron App 安裝包定製 -- Inno Setup 腳本 Pascal Scripting 初探

在做 Electron 項目時,有個需求是安裝包安裝時要給客戶機上裝上某個軟件

在查看 Inno Setup 官網後發現是通過 .iss 腳本編寫實現自定義安裝過程

可在 .iss 內可以添加腳本爲安裝過程添加邏輯

爲了測試方便我用 vite 新建一個全新的 electron 項目

用的是這個腳手架 https://github.com/electron-vite/electron-vite-vue

用其它腳手架也行,反正我們對 app 內容本身並不關注,只關心製作安裝包

新建 electron 項目

安裝注意事項(都是我踩過的坑..)

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 窗口

關掉項目, 下一步測一下打包功能

編譯打包 app

項目已經幫你整合了 electron-builder

直接運行打包命令

yarn build

大概率會失敗
image

從 github 拉 electron 失敗

需要從鏡像服務器,設置 electron_mirror 到專門鏡像 後再打包

image

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 文件

image

通過 ide 新建 .iss 文件

image

過程如下:

  1. 打開 Inno Setup Compiler

  2. 新建一個 Inno Setup Script Wizard 開始創建 .iss 腳本文件

  3. 按提示一步步往下走,可以選擇填寫 app 名字,版本,公司之類的信息這些都不重要

  4. 直到 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 這一頁

  5. Custom compiler output folder: 選擇打包文件輸出目錄

    我選擇輸出到 first-electron\dist-setup 目錄

  6. 繼續按提示往下走,最後會讓你保存成 .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 編譯該文件

安裝程序最後會顯示這樣的畫面:

image

點擊 “完成” 按鈕就會出現以下畫面:

image

非常簡單!

能不能在安裝開始前就調用某個程序?

上面說的是安裝程序安裝完成後執行另外的一個程序,那麼如果想在安裝程序安裝前執行呢?

[code]節點腳本控制安裝過程

需要 Pascal 腳本了, 開始吧!

說實話作爲長期使用 Javascript 的我來說第一次接觸 Pascal Scripting 我心裏一萬頭xx馬奔騰而過

而且我看 Inno Setup Compiler 官網 <jrsoftware.org> 的大概意思是 Pascal Scripting 不太好弄

官方建議用戶從 Inno Setup 6\Examples 安裝目錄下的 Examples 目錄下參考官方提供的腳本例子

在 setup.iss 文件內添加 [code] 節點

這個節點允許你爲安裝過程添加邏輯,可細化到安裝的每一步

它通過在 [code] 節點下暴露 Event Functions 實現,比如:

  • function InitializeSetup(): Boolean;
  • procedure DeinitializeSetup();
  • function InitializeUninstall(): Boolean;
  • ...

我姑且把它理解爲生命週期吧!

那麼如果想在安裝開始時就調用

使用 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 會輸出日誌,也可以看到東方財富的安裝程序也啓動了

image

注意日誌輸出畫面中的 這兩句:

[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' 

編譯測試結果:

image

果然被改名爲 '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')

編譯測試一下

image

可以看到已經執行調用東方財富的安裝程序

先別急着關掉,我們可以通過 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

image

果然臨時文件夾內文件夾結構如我們所指定的那樣

該路徑會在安裝程序結束時自動刪除

用 [code] 實現安裝完成後執行 "dfcft8.exe" 可執行文件

之前是在 [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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章