前一篇嘗試Office 2003 VSTO的開發、部署有提到用VS開發一個簡單的VSTO程序。打包C/S程序,我首先想到的是VS里自帶的Setup Project。很遺憾,VS2012及后面的版本都剔除了Setup Project,改用InstallShield Limited Edition。
Setup Project配置起來N麻煩,如:配置完成了之后,一旦修改了項目的東西然后重新生成,只有移除原來定義好的快捷方式、主輸出,然后重新設置才能應用修改。而ISLE,對於一些簡單的打包,基本上都是滿足了的,而且整個操作過程都有界面,動動鼠標就行了;里面有部分功能是可以寫代碼定制的,不過要授權收費!這是我個人的使用心得,安裝打包用得不多,有誤之處,還請多多指教。
無意中發現了Inno這個小東西(官網地址),然后深深地被它吸引了。麻雀雖小,五臟俱全,可以完全免費使用,最主要的是它允許自己使用Pascal語言編寫定制腳本。
網上挺多資料的,但很多都是雷同,且測試時部分有bug。下面貼上自己的一個代碼栗子,參考了網上的幾篇文章,然后修改整理的。
需要注意的:
1、打包指定文件(夾)時,該文件(夾)必須存在文件,不然編譯時提示錯誤。路徑(可使用相對路徑)CheckOfficeConsole\DotNetFramework必須要存在文件,可以是任何類型的。
Source: "CheckOfficeConsole\DotNetFramework\*"; DestDir: "{tmp}"; Flags: ignoreversion
2、檢測.net framework,如果不存在則從指定網址下載安裝包時,需要用到isxdl.dll。這個dll需要下載並安裝ISTool。
3、.net framework 的下載地址,我找到了除v3.0、v1版本以外的安裝包地址,測試時都可用。未來微軟網站也許會對這些資源包地址更新,所以不保證一直可用。
4、安裝過程中,可通過Inno的函數讀取指定目錄的.ini文件,並獲得文件里的參數值。這個太贊了有木有!一來可以減少在Inno腳本里硬編碼,二來可以在安裝過程中初始化相關數據。如:Output目錄有一個setup.ini文件,里面的內容為:
[Custom] dotNetVersion = v4.5
Inno的讀取代碼:
function GetCustomConfig(key:string):string; var myValue:string; begin myValue:=ExpandConstant('{ini:{src}\Setup.ini,Custom,'+key+'}') result := myValue; end; //使用 //..... GetCustomConfig('dotNetVersion'); //.....
完整版:
; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! ; Ivan 2015-1-30 #define MyAppName "VSTO Excel by Ivan" #define MyAppVersion "1.0" #define MyAppPublisher "My Company, Inc." #define MyAppURL "http://www.IvanBy.com/" #define MyAppExeName "CheckOfficeConsole.exe" [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={{6DFC5AE2-4844-4440-A6B3-284E15A14AEA} AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={pf}\{#MyAppName} DefaultGroupName={#MyAppName} OutputBaseFilename=setup Compression=lzma SolidCompression=yes [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: C:\Program Files (x86)\ISTool\isxdl.dll; Flags: dontcopy; Source: "CheckOfficeConsole\bin\Release\CheckOfficeConsole.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "CheckOfficeConsole\DotNetFramework\*"; DestDir: "{tmp}"; Flags: ignoreversion Source: "Excel2010Setup\Excel2010Setup\Express\DVD-5\DiskImages\*"; DestDir: "{app}\2010"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" ;更改顯示在程序中顯示的消息文本 [Messages] BeveledLabel=Ivan Code [Run] ;Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent Filename: "{app}\{#MyAppExeName}" [Code] var dotNetDownloadNeeded: boolean; dotNetLocalPath:string; procedure isxdl_AddFile(URL, Filename: PAnsiChar); external 'isxdl_AddFile@files:isxdl.dll stdcall'; function isxdl_DownloadFiles(hWnd: Integer): Integer; external 'isxdl_DownloadFiles@files:isxdl.dll stdcall'; function isxdl_SetOption(Option, Value: PAnsiChar): Integer; external 'isxdl_SetOption@files:isxdl.dll stdcall'; //檢測是否存在特定版本的.net framework function IsDotNetDetected(version: string; service:cardinal): boolean; // Indicates whether the specified version and service pack of the .NET Framework is installed. // // version -- Specify one of these strings for the required .NET Framework version: // 'v1.1.4322' .NET Framework 1.1 // 'v2.0.50727' .NET Framework 2.0 // 'v3.0' .NET Framework 3.0 // 'v3.5' .NET Framework 3.5 // 'v4\Client' .NET Framework 4.0 Client Profile // 'v4\Full' .NET Framework 4.0 Full Installation // 'v4.5' .NET Framework 4.5 // // service -- Specify any non-negative integer for the required service pack level: // 0 No service packs required // 1, 2, etc. Service pack 1, 2, etc. required var key: string; install, release, serviceCount: cardinal; check45, success: boolean; begin // .NET 4.5 installs as update to .NET 4.0 Full if version = 'v4.5' then begin version := 'v4\Full'; check45 := true; end else check45 := false; // installation key group for all .NET versions key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version; // .NET 3.0 uses value InstallSuccess in subkey Setup if Pos('v3.0', version) = 1 then begin success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install); end else begin success := RegQueryDWordValue(HKLM, key, 'Install', install); end; // .NET 4.0/4.5 uses value Servicing instead of SP if Pos('v4', version) = 1 then begin success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount); end else begin success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount); end; // .NET 4.5 uses additional value Release if check45 then begin success := success and RegQueryDWordValue(HKLM, key, 'Release', release); success := success and (release >= 378389); end; result := success and (install = 1) and (serviceCount >= service); end; //准備安裝.net framework需要的條件(本地還是聯網) function PreInstallDotNet(dotNetName:string;dotNetDownloadUrl:string):boolean; begin if (not IsAdminLoggedOn()) then begin MsgBox('您電腦安裝 Microsoft .NET Framework 需要管理員權限', mbInformation, MB_OK); Result := false; end else begin dotNetLocalPath := ExpandConstant('{src}') + '\'+dotNetName; if not FileExists(dotNetLocalPath) then begin dotNetLocalPath := ExpandConstant('{tmp}') + '\'+dotNetName; if not FileExists(dotNetLocalPath) then begin isxdl_AddFile(dotNetDownloadUrl, dotNetLocalPath); dotNetDownloadNeeded := true; end; end; SetIniString('install', 'dotnetRedist', dotNetLocalPath, ExpandConstant('{tmp}\dep.ini')); end; end; //執行安裝.net framework function DoInstallDotNet():boolean; var hWnd: Integer; ResultCode: Integer; begin result := true; hWnd := StrToInt(ExpandConstant('{wizardhwnd}')); // don’t try to init isxdl if it’s not needed because it will error on < ie 3 if dotNetDownloadNeeded then begin isxdl_SetOption('label', '正在下載 Microsoft .NET Framework'); isxdl_SetOption('des-c-r-i-p-tion', '您還未安裝Microsoft .NET Framework. 請您耐心等待幾分鍾,下載完成后會安裝到您的的計算機中。'); if isxdl_DownloadFiles(hWnd) = 0 then result := false; end; if result = true then begin if Exec(ExpandConstant(dotNetLocalPath), '/qb', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin // handle success if necessary; ResultCode contains the exit code if not (ResultCode = 0) then begin result := false; end; end else begin // handle failure if necessary; ResultCode contains the error code result := false; end; end; end; //檢測是否安裝了等於大於指定版本的.net framework function IsIncludeFramework(version: string): boolean; var isInclued:boolean; begin //最高版本的 if IsDotNetDetected('v4.5',0) then begin isInclued := true; end else if version = 'v4.5' then begin PreInstallDotNet('dotNetFx45_Full_setup.exe','http://download.microsoft.com/download/B/A/4/BA4A7E71-2906-4B2D-A0E1-80CF16844F5F/dotNetFx45_Full_setup.exe'); end else if IsDotNetDetected('v4\Full',0) then begin isInclued := true; end else if version = 'v4\Full' then begin PreInstallDotNet('dotNetFx40_Full_x86_x64.exe','http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe'); end else if IsDotNetDetected('v4\Client',0) then begin isInclued := true; end else if version = 'v4\Client' then begin PreInstallDotNet('dotNetFx40_Client_x86_x64.exe','http://download.microsoft.com/download/5/6/2/562A10F9-C9F4-4313-A044-9C94E0A8FAC8/dotNetFx40_Client_x86_x64.exe'); end else if IsDotNetDetected('v3.5',0) then begin isInclued := true; end else if Pos('v3.5',version) = 1 then begin PreInstallDotNet('dotNetFx35setup.exe','http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe'); end else if IsDotNetDetected('v3.0',0) then begin isInclued := true; end else if Pos('v3.0',version) = 1 then begin PreInstallDotNet('dotNetFx35setup.exe','http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe'); end else if IsDotNetDetected('v2.0.50727',0) then begin isInclued := true; end else if Pos('v2',version) = 1 then begin PreInstallDotNet('dotnetfx.exe','http://download.microsoft.com/download/5/6/7/567758a3-759e-473e-bf8f-52154438565a/dotnetfx.exe'); end else if IsDotNetDetected('v1.1.4322',0) then begin isInclued:= true; end else if Pos('v1',version)=1 then begin PreInstallDotNet('dotNetFx35setup.exe','http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe'); end; result := isInclued; end; //取得自定義的配置 // Setup.ini // [Custom] // dotNetVersion = v4.5 function GetCustomConfig(key:string):string; var myValue:string; begin myValue:=ExpandConstant('{ini:{src}\Setup.ini,Custom,'+key+'}') result := myValue; end; function InitializeSetup(): Boolean; begin //do something result:= true; end; function NextButtonClick(CurPage: Integer): Boolean; var dotNetVersion:string; begin Result := true; if (CurPage = wpReady) then begin dotNetVersion := GetCustomConfig('dotNetVersion'); if Length(dotNetVersion) = 0 then begin dotNetVersion := 'v4.0'; end else if not (Pos('v',dotNetVersion) = 1) then begin dotNetVersion := 'v'+dotNetVersion; end; if not IsIncludeFramework(dotNetVersion) then begin if not DoInstallDotNet() then begin MsgBox('當前操作需要安裝.NET Framework ' + dotNetVersion + '或以上版本。'#13#13 '在嘗試自動安裝期間,似乎出現一些小問題(或用戶取消了安裝),'#13 '請重試嘗試安裝。', mbInformation, MB_OK); result:= false; end; end; end; end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); var ErrorCode: Integer; begin case CurUninstallStep of usUninstall: begin // 正在卸載 end; usPostUninstall: begin //卸載完成 ShellExec('open', 'http://www.IvanBy.com', '', '', SW_SHOW, ewNoWait, ErrorCode) end; end; end;