1. 程式人生 > 實用技巧 >Inno Setup簡介

Inno Setup簡介

Inno Setup用Delphi寫成,其官方網站同時也提供源程式免費下載。它雖不能與Installshield這類恐龍級的安裝製作軟體相比,但也當之無愧算是後起之秀。Inno Setup是一個免費的安裝製作軟體,小巧、簡便、精美是其最大特點,支援pascal指令碼,能快速製作出標準Windows2000風格的安裝介面,足以完成一般安裝任務。

公司目前是使用的Installshield,但是我們專案原先基於innosetup做的打包指令碼,所以我也就接著用InnoSetup來開發了。

一開始以為innoseup想要做好看的介面,像百度360騰訊這些大廠的安裝程式那樣的介面有些不可能,但跟著https://blog.csdn.net/linxinfa/article/details/108995508?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242

這篇文章的指引,發現innosetup真的強大,理論上只要能封裝dll,也許拿QT寫個安裝介面給innosetup用也不是不可能。

下面是我通過網上一些現成的資源改的安裝指令碼的實際執行GIF,為避免誤會,我抹去了原先產品的logo

基礎知識網上有大把教程,我就不再贅述了。在開始innosetup美化工作的前面,得先擴充下一些API介面,單純靠innosetup是做不出好看的介面的,我們得準備一些介面,主要依賴於botva2.dll和innocallback.dll,前者負責按鈕圖示選擇框等美化控制元件的建立,後者則是完善這些控制元件操作後的回撥函式。

//*****************************************************************************************************************************************************************************************************************************
//系統api介面 function SetLayeredWindowAttributes(hwnd:HWND; crKey:Longint; bAlpha:byte; dwFlags:longint ):longint;external 'SetLayeredWindowAttributes@user32 stdcall'; //函式宣告 function SetWindowLong(Wnd: HWnd; Index: Integer; NewLong: Longint): Longint; external '[email protected] stdcall'; function
CallWindowProc(lpPrevWndFunc: Longint; hWnd: HWND; Msg: UINT; wParam, lParam: Longint): Longint; external '[email protected] stdcall'; function SetTimer(hWnd: LongWord; nIDEvent, uElapse: LongWord; lpTimerFunc: LongWord): LongWord; external '[email protected] stdcall'; function KillTimer(hWnd: LongWord; nIDEvent: LongWord): LongWord; external '[email protected] stdcall'; function GetWindowLong(Wnd: HWnd; Index: Integer): Longint; external '[email protected] stdcall'; function ReleaseCapture(): Longint; external '[email protected] stdcall'; function CreateRoundRectRgn(p1, p2, p3, p4, p5, p6: Integer): THandle; external 'CreateRoundRectRgn@gdi32 stdcall'; function SetWindowRgn(hWnd: HWND; hRgn: THandle; bRedraw: Boolean): Integer; external 'SetWindowRgn@user32 stdcall'; function LoadCursorFromFile(FileName: String): Cardinal; external '[email protected] stdcall'; function DeleteObject(p1: Longword): BOOL; external '[email protected] stdcall'; function GetPM(nIndex:Integer):Integer; external '[email protected] stdcall'; function GetSystemMetrics(nIndex: Integer): Integer; external '[email protected] stdcall'; //***************************************************************************************************************************************************************************************************************************** //botva2.dll function ImgLoad(Wnd :HWND; FileName :PAnsiChar; Left, Top, Width, Height :integer; Stretch, IsBkg :boolean) :Longint; external 'ImgLoad@{tmp}\botva2.dll stdcall delayload'; procedure ImgSetVisibility(img :Longint; Visible :boolean); external 'ImgSetVisibility@{tmp}\botva2.dll stdcall delayload'; procedure ImgApplyChanges(h:HWND); external 'ImgApplyChanges@{tmp}\botva2.dll stdcall delayload'; procedure ImgSetPosition(img :Longint; NewLeft, NewTop, NewWidth, NewHeight :integer); external 'ImgSetPosition@files:botva2.dll stdcall'; procedure ImgRelease(img :Longint); external 'ImgRelease@{tmp}\botva2.dll stdcall delayload'; procedure gdipShutdown; external 'gdipShutdown@{tmp}\botva2.dll stdcall delayload'; function BtnCreate(hParent:HWND; Left,Top,Width,Height:integer; FileName:PAnsiChar; ShadowWidth:integer; IsCheckBtn:boolean):HWND; external 'BtnCreate@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetText(h:HWND; Text:PAnsiChar); external 'BtnSetText@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetVisibility(h:HWND; Value:boolean); external 'BtnSetVisibility@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetFont(h:HWND; Font:Cardinal); external 'BtnSetFont@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetFontColor(h:HWND; NormalFontColor, FocusedFontColor, PressedFontColor, DisabledFontColor: Cardinal); external 'BtnSetFontColor@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetEvent(h:HWND; EventID:integer; Event:Longword); external 'BtnSetEvent@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetCursor(h:HWND; hCur:Cardinal); external 'BtnSetCursor@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetEnabled(h:HWND; Value:boolean); external 'BtnSetEnabled@{tmp}\botva2.dll stdcall delayload'; function GetSysCursorHandle(id:integer):Cardinal; external 'GetSysCursorHandle@{tmp}\botva2.dll stdcall delayload'; function BtnGetChecked(h:HWND):boolean; external 'BtnGetChecked@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetChecked(h:HWND; Value:boolean); external 'BtnSetChecked@{tmp}\botva2.dll stdcall delayload'; procedure CreateFormFromImage(h:HWND; FileName:PAnsiChar); external 'CreateFormFromImage@{tmp}\botva2.dll stdcall delayload'; procedure BtnSetPosition(h:HWND; NewLeft, NewTop, NewWidth, NewHeight: integer); external 'BtnSetPosition@{tmp}\botva2.dll stdcall delayload'; procedure ImgSetVisiblePart(img:Longint; NewLeft, NewTop, NewWidth, NewHeight : integer); external 'ImgSetVisiblePart@files:botva2.dll stdcall'; //***************************************************************************************************************************************************************************************************************************** //innocallback.dll type TBtnEventProc = procedure (h:HWND); TPBProc = function(h:hWnd;Msg,wParam,lParam:Longint):Longint; //百分比 TTimerProc = procedure(h:longword; msg:longword; idevent:longword; dwTime:longword); function CallBack_Button(Callback: TBtnEventProc; ParamCount: Integer): Longword; external 'wrapcallback@{tmp}\innocallback.dll stdcall delayload'; function CallBack_ProgressBar(P:TPBProc;ParamCount:integer):LongWord; external 'wrapcallback@files:innocallback.dll stdcall'; function CallBack_Timer(callback: TTimerProc; Paramcount: Integer): Longword; external 'wrapcallback@files:innocallback.dll stdcall'; //****************************************************************************************************************************************************************************************************************************** //退出當前安裝或者解除安裝程序 procedure ExitProcess(exitCode:integer);external '[email protected] stdcall'; //******************************************************************************************************************************************************************************************************************************

按我的理解,innosetup分成兩條線,一條線就是顯示給我們使用者看的介面,還有一條線就是他的後臺處理邏輯,他後臺不變的就是安裝和解除安裝的前期準備工作,還有就是安裝和解除安裝的執行過程,這個過程我們是基本動不了什麼的,innosetup會把file欄位裡提供的檔案解壓到我們的安裝目錄,解除安裝的時候會把安裝的檔案再刪除。最後一個過程就是我們安裝解除安裝完成後的那段時間,使用者不點選完成,程式不回結束。像下面圖一樣,畫的有些難看,湊合看下就行了,安裝的三個大部分都是單向的,一旦進入到下一步了就不能回到上一步了,但是每個大過程中的每個介面是雙向的,可以回到上一步,這應該是好理解的

後臺邏輯這條線什麼時候跳轉到下一步,完全是由innosetupde的介面控制著。比如下面這段程式碼,是立即安裝按鈕的按下的回撥函式,這時候我們會呼叫WizardForm.NextButton.OnClick(WizardForm);這一句話等同於按下安裝介面的下一步按鈕,讓後臺邏輯進入到安裝的執行過程,就是那個有進度條的介面。

//點選立即安裝按鈕
procedure btn_InstallNowOnClick(hBtn:HWND);
begin
  if BtnGetChecked(chk_License) then
  begin
    if edit_Path.Text = '' then
    begin
      SuppressibleMsgBox('路徑名不能為空!', mbConfirmation, MB_OK, IDOK);
    end
    else
    begin
      WizardForm.NextButton.OnClick(WizardForm);
    end;
  end
  else
  begin
    SuppressibleMsgBox('請先閱讀並同意使用者協議!', mbConfirmation, MB_OK, IDOK);
  end;

為了做好看的介面,innosetup提供給我們的頁面基本可以都隱藏掉,當然你也可以選擇魔改原來的介面,這都是一樣的,看你選擇,然後剩下來就是我們的介面美化工作,我們只需要在我們的介面上的每個按鈕響應事件裡控制著innosetup後面那條處理邏輯線的執行就行了,安裝程式開始就是選擇安裝目錄介面或者一鍵安裝介面,這時候沒什麼處理邏輯,就是例項化一個頁面,然後把我們的圖片元素都放到對應的位置,沒什麼技術含量,可以參考程式碼。

//**********************************************************************************************************************
//安裝程式主體部分
//使用這個事件函式啟動時改變嚮導或嚮導頁。你不能在它觸發之後使用 InitializeSetup 事件函式,嚮導窗體不退出。
procedure InitializeWizard();
begin
  //設定頁面屬性
  WizardForm.OuterNotebook.hide;
  WizardForm.Bevel.Hide;
  WizardForm.BorderStyle:=bsnone;
  WizardForm.Position:=poScreenCenter;
  WizardForm.Width:=650;
  WizardForm.Height:=508;
  WizardForm.Color:=clWhite ;
  WizardForm.OnMouseDown := @WizardMouseDown;
  //從[Files]段提取指定的檔案到臨時目錄中
  ExtractTemporaryFile('btn_n.png');
  ExtractTemporaryFile('btn_complete.png');
  ExtractTemporaryFile('btn_setup.png');
  ExtractTemporaryFile('xy.png');
  ExtractTemporaryFile('bigbg.png');
  ExtractTemporaryFile('btn_Browser.png');
  ExtractTemporaryFile('editbox_bkg.png');
  ExtractTemporaryFile('loadingbk.png');
  ExtractTemporaryFile('loading.png');
  ExtractTemporaryFile('license.png');
  ExtractTemporaryFile('loading_pic1.png');
  ExtractTemporaryFile('loading_pic2.png');
  ExtractTemporaryFile('loading_pic3.png');
  ExtractTemporaryFile('loading_pic4.png'); 
  ExtractTemporaryFile('checkbox.png');
  ExtractTemporaryFile('checkboxdeep.png'); 
  ExtractTemporaryFile('loading_pic.png');
  ExtractTemporaryFile('finish_bg.png');
  ExtractTemporaryFile('btn_close.png');
  ExtractTemporaryFile('btn_min.png');
  //ExtractTemporaryFile('vcredist_x64.exe');
  //ExtractTemporaryFile('vcredist_x86.exe');
  //ExtractTemporaryFile('ConsoleApplication12.exe');
  //初始化背景頁面
  img_Background:=ImgLoad(WizardForm.Handle,ExpandConstant('{tmp}\xy.png'),0,0,650,450,false,true);
  bool_custom:=true;
  //初始化編輯框背景
  img_EditBox:= ImgLoad(WizardForm.Handle,ExpandConstant('{tmp}\editbox_bkg.png'),60,380,436,21,false,false);
  ImgSetVisibility(img_EditBox,false)
  //初始化關閉按鈕
  btn_Close:=BtnCreate(WizardForm.Handle,627,8,17,15,ExpandConstant('{tmp}\btn_close.png'),1,False)
  BtnSetEvent(btn_Close,BtnClickEventID,CallBack_Button(@btn_CloseOnClick,1));
  //初始化最小化按鈕
  btn_Min:=BtnCreate(WizardForm.Handle,607,8,17,15,ExpandConstant('{tmp}\btn_min.png'),1,False)
  BtnSetEvent(btn_Min,BtnClickEventID,CallBack_Button(@btn_MinOnClick,1));
  //初始化立即安裝按鈕
  btn_InstallNow:=BtnCreate(WizardForm.Handle,225,308,199,58,ExpandConstant('{tmp}\btn_setup.png'),1,False)
  BtnSetEvent(btn_InstallNow,BtnClickEventID,CallBack_Button(@btn_InstallNowOnClick,1));
  //初始化瀏覽按鈕
  btn_Browser:=BtnCreate(WizardForm.Handle,520,375,82,32,ExpandConstant('{tmp}\btn_Browser.png'),1,False)
  BtnSetEvent(btn_Browser,BtnClickEventID,CallBack_Button(@btn_BrowserOnClick,1));
  BtnSetVisibility(btn_Browser,false)
  //初始化自定義安裝按鈕
  btn_CustomInstall:=BtnCreate(WizardForm.Handle,560,421,75,15,ExpandConstant('{tmp}\btn_n.png'),1,False)
  BtnSetEvent(btn_CustomInstall,BtnClickEventID,CallBack_Button(@btn_CustomInstallOnClick,1));
  //初始化使用者協議按鈕和選擇框
  btn_License:=BtnCreate(WizardForm.Handle,100,421,54,15,ExpandConstant('{tmp}\license.png'),1,False)
  BtnSetEvent(btn_License,BtnClickEventID,CallBack_Button(@btn_LicenseOnClick,1));
  chk_License:=BtnCreate(WizardForm.Handle,22,421,15,15,ExpandConstant('{tmp}\checkboxdeep.png'),4,TRUE)
  //設定安裝進度條監聽
  PBOldProc:=SetWindowLong(WizardForm.ProgressGauge.Handle,-4,CallBack_ProgressBar(@PBProc,4));
  //初始化定時器
  js1:=0
  js2:=650
  timer_Page := TTimer.Create(WizardForm);
  timer_Page.OnTimer := @func_Timer;
  //初始化安裝路徑編輯框
  edit_Path:= TEdit.Create(WizardForm);
  with edit_Path do
  begin
    Parent := WizardForm;
    text :=WizardForm.DirEdit.text;
    Font.Name:='宋體'
    BorderStyle:=bsNone;
    SetBounds(60,385,436,15)
    OnChange:=@edit_PathChange;
    Color := $00FFE2D0
    TabStop :=false;
  end;
  edit_Path.Hide;
  ShapeForm(WizardForm, WINDDOW_RADIUS);
  //應用圖片變化
  ImgApplyChanges(WizardForm.Handle)
end;

這裡面有些需要注意的點,你要使用的圖片資源,要像下面這樣設計:

按鈕從上到下要有四張,分別對應按鈕什麼都不做的樣子,滑鼠懸浮在按鈕上方的樣子,按鈕被按下的樣子,還有按鈕被禁用的樣子。

選擇框則對應八張,上面四張是選擇框沒被選中時候對應滑鼠什麼都沒做的樣子,滑鼠懸浮在選擇框上方的樣子,滑鼠按下選擇框的樣子,選擇框被禁用的樣子。然後下面四張是選擇框選中時對應的四種樣子。

設計是這樣設計的,具體為什麼這樣設計,就是botva2.dll這個dll的介面要求,我也沒深究過。

然後還有一個計數器的概念,這個東西很關鍵,控制著innosetup的動畫部分,後面我們會專門介紹下安裝過程中的那個圖片滾動動畫是怎麼做的,那是我們介面美化的最難的部分,需要專門開一個貼講下邏輯。

注意每次設定完介面都要呼叫下ImgApplyChanges(WizardForm.Handle),這個會通知頁面重新整理,不然按鈕的建立移位等效果都不會生效。