使用Innosetup對軟件進行打包

一:背景

由於我們以SAAS服務方式,爲客戶分發軟件包,每個軟件包中都有客戶自定義的一些特定信息,如:公司名稱、LOGO、快捷方式名稱、安裝路徑指定等,這些信息是在後臺操作界面中指定,如下:

 

目前我們採用的是在客戶下載客戶端時,根據後臺的設置,使用WINRAR生成一個自解壓包,其中將客戶定製的這些信息均會被自動打包到此自解壓包中;在一般情況下,我們的這個方案還是可行的,並且也使用此方案運營了幾年。

但我們逐步發現此方案有如下缺點:

1.    使用WINRAR自解壓方式,沒有請求管理員權限運行,導致在WIN7等系統上安裝不成功的問題

2.    快捷方式創建只能在當前用戶桌面

3.    由於使用的是自解壓運行方式,並且沒有添加數字簽名【動態生成的,爲了安全,沒有在生成後再加簽名,並且有些環境下也籤不了名】,會引起如金山毒霸的報毒

4.    WIN7上運行安裝後,會提示此軟件未正確安裝的問題

 

以上問題,給客戶的正常使用帶來了非常多的困擾,特別是第3點,讓客戶誤以爲我們的安裝包有病毒【我們爲了客戶安裝的便利性,一次性做成了自解壓運行行爲】,在此背景下,我們進行了探索,使用了另外的方式來進行安裝包的生成與安裝。

 

二:方案與工具 

1.       涉及工具:

Inno setup爲打包工具

 

2.       方案

此方案採用InnoSetup來進行客戶端文件的打包,完成後進行簽名,放到服務器上後,即不再對此文件包進行任何變更,以免破壞其數字簽名信息;

SAAS服務器上針對不同客戶進行信息定製時,以不同的文件名來反映出不同的客戶信息。如:ClientSetup_100000001.exe

此文件名用於標識不同的客戶,如:1000000爲客戶標識,後面的01作爲對1000000的校驗串,如果客戶無意修改了文件名,則通過提示信息告知。

啓動安裝時,ClientSetup_100000001.exe先對文件名進行校驗,如果正確,則通過此ID獲取其定製的信息【連接的服務器地址已經包含在安裝包中】,獲取到安裝地址、快捷方式名稱、LOGO等信息後,控制後續的安裝流程進行安裝。

這裏會涉及到安裝過程中的網絡訪問問題,特別是代理的處理,所以需要處理好在安裝時的網絡訪問,目前我們的處理是:

1)  如果直接能訪問網絡,下載信息,則下載,如果失敗,則進入第2步

2)  如果檢測到IE設置了代理,則通過此代理進行訪問,如果需要授權則進入第3步

3)  則要設置代理信息等

見下圖:

 

三:具體實現

 

由於我們對Pascal 腳本不熟悉,所以我更多的是使用C++寫成DLL,通過在innosetup的相關事件及函數中調用的方式來實現。

1.       檢測文件名是否有應該放在安裝包剛啓動時進行檢測,在事件InitializeSetup中檢測,如果文件名校驗不正確,則直接退出安裝:

 

function InitializeSetup(): Boolean;   

begin

  if StringCheck(ExpandConstant('{srcexe}')) = 0 then

  begin

  MsgBox('文件名無效,請重新下載安裝包!', mbConfirmation, MB_OK);

  Result := False;

  end

  else

  begin

  Result := True;

  end

  MyProgChecked := False;

  ShotCutName := '';

end;

 


2.       在文件檢驗檢測通過,則開始複製文件,當複製了輔助安裝的DLL後,即可在其AfterInstall中進行網絡訪問,獲取服務器中定製的信息:

 

Source: "..\dependency\RTSClient.ini"; DestDir: "{app}"; Flags: ignoreversion

Source: "..\dependency\ClientLogo.ico"; DestDir: "{app}"; Flags: ignoreversion; 

Source: "..\ch\ProxyWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion;

Source: "..\ch\SetupWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall:DownLoadCustomizeData()  


3.       對於DLL的引用,需要進行聲明後方可調用:

function StringCheck(lpPath: AnsiString): Integer;

external 'StringCheck@files:SetupWrapper.dll stdcall setuponly';

 

function DownCustomizeData(lpSetupPath:AnsiString): Integer;

external 'DownCustomizeData@files:SetupWrapper.dll stdcall setuponly';

 

function GetSpecificPath(lpchar:PChar; nFlag:Integer): Integer;

external 'GetSpecificPath@files:SetupWrapper.dll stdcall setuponly';

 

function CopyCustomizeData(lpPath: AnsiString): Integer;

external 'CopyCustomizeData@files:SetupWrapper.dll stdcall setuponly';

 


 

4.       在安裝的過程中,要對路徑選擇、快捷方式創建等窗口進行隱藏,如下:

function ShouldSkipPage(PageID: Integer): Boolean;

begin

Result := False;

if PageID <> wpFinished then

begin

Result := True;

end

end;

 

procedure CurPageChanged(CurPageID: Integer);   

begin 

if CurPageID <> wpFinished then

begin

PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONDOWN,0,0);

PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONUP,0,0);

end

end; 


 

5.       在獲取到真正的安裝路徑後,需要將文件複製的路徑定位到真正路徑下[快捷方式的創建也是同樣]:

Source: "..\ch\RTSKeyGenDll.Dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion

Name: "{group}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"


 

這樣才能保證安裝的信息與服務器上配置的一致。

 

6.       對於調用的DLL中的函數,則具體執行文件名檢測、服務器數據下載等事務,在此就不多述了

 

7.       打包的完整代碼腳本如下【部分與公司業務相關代碼未包含】

 

; 腳本由 Inno Setup 腳本嚮導 生成!
; 有關創建 Inno Setup 腳本文件的詳細資料請查閱幫助文檔!

#define MyAppName "遠程在線系統客戶端"
#define MyAppVersion "3.1"
#define MyAppPublisher "測試公司名稱"
#define MyAppURL "http://www.test.com/"
#define MyAppExeName "Test.exe"

[Setup]
; 注: AppId的值爲單獨標識該應用程序。
; 不要爲其他安裝程序使用相同的AppId值。
; (生成新的GUID,點擊 工具|在IDE中生成GUID。)
AppId={{2C5393DC-8D35-4D13-B7AB-B389AE5E4111}
;AppName={code:testtest|a}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
VersionInfoDescription={#MyAppName}
VersionInfoProductTextVersion={#MyAppVersion}
VersionInfoVersion={#MyAppVersion}
DefaultDirName={pf}\Test
DefaultGroupName={#MyAppName}
OutputDir=.\..\package
OutputBaseFilename=Test
Compression=lzma
SolidCompression=yes

[Languages]
Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"

[Files]   
Source: "..\dependency\RTSClient.ini"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\dependency\ClientLogo.ico"; DestDir: "{app}"; Flags: ignoreversion; 
Source: "..\ch\ProxyWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion;
Source: "..\ch\SetupWrapper.dll"; DestDir: "{app}"; Flags: ignoreversion; AfterInstall:DownLoadCustomizeData()  
;完成LOGO後,需要將Rtsclient.ini與logo複製過去            
Source: "..\ch\AESEncryption.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion; AfterInstall:CopyLogo() 
Source: "..\ch\ChatLib.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion
Source: "..\ch\FileTransferLib.dll"; DestDir: "{code:GetRealSetupPath}"; Flags: ignoreversion

[Icons]
Name: "{group}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"
Name: "{group}\{cm:UninstallProgram,{code:GetShotCutName}}"; Filename: "{uninstallexe}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"
Name: "{commondesktop}\{code:GetShotCutName}"; Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; WorkingDir:"{code:GetRealSetupPath}"; IconFilename: "{code:GetRealSetupPath}\ClientLogo.ico"

[Run]
Filename: "{code:GetRealSetupPath}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{code:GetShotCutName}}"; Flags: nowait postinstall skipifsilent

[Code]
const
  WM_LBUTTONDOWN = 513;
  WM_LBUTTONUP = 514;   

var
  MyProgChecked: Boolean;
  RealSetupPath: String;
  ShotCutName : String;
function StringCheck(lpPath: AnsiString): Integer;
external 'StringCheck@files:SetupWrapper.dll stdcall setuponly';

function DownCustomizeData(lpSetupPath:AnsiString): Integer;
external 'DownCustomizeData@files:SetupWrapper.dll stdcall setuponly';

function GetSpecificPath(lpchar:PChar; nFlag:Integer): Integer;
external 'GetSpecificPath@files:SetupWrapper.dll stdcall setuponly';

function CopyCustomizeData(lpPath: AnsiString): Integer;
external 'CopyCustomizeData@files:SetupWrapper.dll stdcall setuponly';

function InitializeSetup(): Boolean;   
begin
  if StringCheck(ExpandConstant('{srcexe}')) = 0 then
  begin
  MsgBox('文件名無效,請重新下載安裝包!', mbConfirmation, MB_OK);
  Result := False;
  end
  else
  begin
  Result := True;
  end
  MyProgChecked := False;
  ShotCutName := '';
end;

procedure CancelButtonClick ( CurPageID : Integer; var Cancel, Confirm: Boolean);
begin
  Cancel := True;
  Confirm := False;
end;

function GetShotCutName(Param: String) : String;
var
  ShotName: String;
  ReturnLength: Integer;
begin
if Length(ShotCutName) = 0 then
begin
  SetLength(ShotName, 270);
  ReturnLength := GetSpecificPath(ShotName, 2);
  ShotCutName := Copy(ShotName, 0, ReturnLength);
  //MsgBox(ShotCutName, mbConfirmation, MB_OK);
end
   Result :=  ShotCutName;
end;
procedure CopyLogo();
begin
CopyCustomizeData('');
end;
  
procedure DownLoadCustomizeData();

var
  RealPath: String;
  PathLength: Integer;
  ReturnValue: Integer;
begin
//在此處去獲取服務器配置等信息
ReturnValue := DownCustomizeData(ExpandConstant('{app}'));
if ReturnValue < 0 then
begin
MsgBox(Format('連接服務器失敗[錯誤碼:%d],將按默認設置進行安裝!', [ReturnValue]), mbConfirmation, MB_OK);
//PostMessage(WizardForm.CancelButton.Handle,WM_LBUTTONDOWN,0,0);
//PostMessage(WizardForm.CancelButton.Handle,WM_LBUTTONUP,0,0);
end
//else 
//begin
//設置好真正的安裝目錄
//設置好是否換了LOGO
SetLength(RealPath, 270);
PathLength := GetSpecificPath(RealPath, 1);
RealSetupPath  := Copy(RealPath, 0, PathLength);
//MsgBox(RealSetupPath, mbConfirmation, MB_OK);
//end
end;

function GetRealSetupPath(Param: String) : String;
begin
Result := RealSetupPath;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID <> wpFinished then
begin
  Result := True;
end
end;

procedure CurPageChanged(CurPageID: Integer);   
begin 
if CurPageID <> wpFinished then
begin
PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONDOWN,0,0);
PostMessage(WizardForm.NextButton.Handle,WM_LBUTTONUP,0,0);
end
end;  


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