1. 程式人生 > >使用Inno SetUp指令碼打包Winform程式

使用Inno SetUp指令碼打包Winform程式

在開發桌面程式時,往往需要用到打包工具將程式打包為exe可執行檔案。

之前在專案中用了下 InstallShield Limited Edition for Visual Studio  2015,它的功能很強大,但是需要付費使用,而且有些細粒度的操作比較複雜。

後來,我發現了一款簡單方便的打包工具,即我們今天的主角Inno Setup

Inno Setup 支援pascal指令碼,這樣我們就能通過直接寫指令碼來實現功能了。

下面我們來看看我們打包過程中常見功能需求:

  1. 建立安裝exe檔案
  2. 建立解除安裝exe檔案
  3. 建立各種快捷方式,桌面、快速啟動欄等
  4. 安裝、解除安裝Windows 服務
  5. 添加註冊表資訊
  6. 打包.Net framework 環境
  7. 安裝時不能同時執行兩個安裝程式
  8. 安裝/解除安裝時檢測程式(或服務)是否在執行,如果在執行則先關閉程式。

程式的使用方法就不多做介紹了,開啟程式新建指令碼,會創建出一個基本的框架,然後我們就可以修改指令碼新增功能了。

上面的1、2、3 條功能基本的指令碼已經幫我們實現了。

關於操作windows服務,我這裡收藏了一個極好用的指令碼。

//  Code pasted from the following address, for examples and more visit it:
//  http://www.vincenzo.net/isxkb/index.php?title=Service_-_Functions_to_Start%2C_Stop%2C_Install%2C_Remove_a_Service


// function IsServiceInstalled(ServiceName: string) : boolean;
// function IsServiceRunning(ServiceName: string) : boolean;
// function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
// function RemoveService(ServiceName: string) : boolean;
// function StartService(ServiceName: string) : boolean;
// function StopService(ServiceName: string) : boolean;
// function SetupService(service, port, comment: string) : boolean;

type
    SERVICE_STATUS = record
        dwServiceType               : cardinal;
        dwCurrentState              : cardinal;
        dwControlsAccepted          : cardinal;
        dwWin32ExitCode             : cardinal;
        dwServiceSpecificExitCode   : cardinal;
        dwCheckPoint                : cardinal;
        dwWaitHint                  : cardinal;
    end;
    HANDLE = cardinal;

const
    SERVICE_QUERY_CONFIG        = $1;
    SERVICE_CHANGE_CONFIG       = $2;
    SERVICE_QUERY_STATUS        = $4;
    SERVICE_START               = $10;
    SERVICE_STOP                = $20;
    SERVICE_ALL_ACCESS          = $f01ff;
    SC_MANAGER_ALL_ACCESS       = $f003f;
    SERVICE_WIN32_OWN_PROCESS   = $10;
    SERVICE_WIN32_SHARE_PROCESS = $20;
    SERVICE_WIN32               = $30;
    SERVICE_INTERACTIVE_PROCESS = $100;
    SERVICE_BOOT_START          = $0;
    SERVICE_SYSTEM_START        = $1;
    SERVICE_AUTO_START          = $2;
    SERVICE_DEMAND_START        = $3;
    SERVICE_DISABLED            = $4;
    SERVICE_DELETE              = $10000;
    SERVICE_CONTROL_STOP        = $1;
    SERVICE_CONTROL_PAUSE       = $2;
    SERVICE_CONTROL_CONTINUE    = $3;
    SERVICE_CONTROL_INTERROGATE = $4;
    SERVICE_STOPPED             = $1;
    SERVICE_START_PENDING       = $2;
    SERVICE_STOP_PENDING        = $3;
    SERVICE_RUNNING             = $4;
    SERVICE_CONTINUE_PENDING    = $5;
    SERVICE_PAUSE_PENDING       = $6;
    SERVICE_PAUSED              = $7;

// #######################################################################################
// nt based service utilities
// #######################################################################################
function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
external '
[email protected]
stdcall'; function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE; external '[email protected] stdcall'; function CloseServiceHandle(hSCObject :HANDLE): boolean; external '[email protected] stdcall'; function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal; external '
[email protected]
stdcall'; function DeleteService(hService :HANDLE): boolean; external '[email protected] stdcall'; function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean; external '[email protected] stdcall'; function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean; external '[email protected] stdcall'; function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean; external '[email protected] stdcall'; function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean; external '[email protected] stdcall'; function GetLastError() : cardinal; external '[email protected] stdcall'; function OpenServiceManager() : HANDLE; begin if UsingWinNT() = true then begin Result := OpenSCManager('','',SC_MANAGER_ALL_ACCESS); if Result = 0 then MsgBox('the servicemanager is not available', mbError, MB_OK) end else begin MsgBox('only nt based systems support services', mbError, MB_OK) Result := 0; end end; function IsServiceInstalled(ServiceName: string) : boolean; var hSCM : HANDLE; hService: HANDLE; begin hSCM := OpenServiceManager(); Result := false; if hSCM <> 0 then begin hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_CONFIG); if hService <> 0 then begin Result := true; CloseServiceHandle(hService) end; CloseServiceHandle(hSCM) end end; function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean; var hSCM : HANDLE; hService: HANDLE; begin hSCM := OpenServiceManager(); Result := false; if hSCM <> 0 then begin hService := CreateService(hSCM,ServiceName,DisplayName,SERVICE_ALL_ACCESS,ServiceType,StartType,0,FileName,'',0,'','',''); if hService <> 0 then begin Result := true; // Win2K & WinXP supports aditional description text for services if Description<> '' then RegWriteStringValue(HKLM,'System\CurrentControlSet\Services\' + ServiceName,'Description',Description); CloseServiceHandle(hService) end; CloseServiceHandle(hSCM) end end; function RemoveService(ServiceName: string) : boolean; var hSCM : HANDLE; hService: HANDLE; begin hSCM := OpenServiceManager(); Result := false; if hSCM <> 0 then begin hService := OpenService(hSCM,ServiceName,SERVICE_DELETE); if hService <> 0 then begin Result := DeleteService(hService); CloseServiceHandle(hService) end; CloseServiceHandle(hSCM) end end; function StartService(ServiceName: string) : boolean; var hSCM : HANDLE; hService: HANDLE; begin hSCM := OpenServiceManager(); Result := false; if hSCM <> 0 then begin hService := OpenService(hSCM,ServiceName,SERVICE_START); if hService <> 0 then begin Result := StartNTService(hService,0,0); CloseServiceHandle(hService) end; CloseServiceHandle(hSCM) end; end; function StopService(ServiceName: string) : boolean; var hSCM : HANDLE; hService: HANDLE; Status : SERVICE_STATUS; begin hSCM := OpenServiceManager(); Result := false; if hSCM <> 0 then begin hService := OpenService(hSCM,ServiceName,SERVICE_STOP); if hService <> 0 then begin Result := ControlService(hService,SERVICE_CONTROL_STOP,Status); CloseServiceHandle(hService) end; CloseServiceHandle(hSCM) end; end; function IsServiceRunning(ServiceName: string) : boolean; var hSCM : HANDLE; hService: HANDLE; Status : SERVICE_STATUS; begin hSCM := OpenServiceManager(); Result := false; if hSCM <> 0 then begin hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_STATUS); if hService <> 0 then begin if QueryServiceStatus(hService,Status) then begin Result :=(Status.dwCurrentState = SERVICE_RUNNING) end; CloseServiceHandle(hService) end; CloseServiceHandle(hSCM) end end;

有了這個指令碼,windows 服務怎麼玩都可以了,在[code]  下把這個檔案加上就可以使用其中的方法了。

接下來要操作登錄檔了,在[Registry]下寫指令碼我們就可以操作登錄檔了,格式如下:

[Registry]
Root: HKLM; Subkey: "SOFTWARE\ODBC\ODBC.INI\bsjdw101100";ValueType: string; ValueName: "AutoStop"; ValueData: "Yes"
Root: HKLM; Subkey: "SOFTWARE\ODBC\ODBC.INI\bsjdw101100";ValueType: string; ValueName: "Debug"; ValueData: "No"

修改的東西包括,登錄檔路徑、值型別、key和value。

再往下,我們就要寫打包.Net framework 進安裝包。XP 沒有自帶.Net framework 環境,有的可能會帶 2.0 。win 7 自帶 .Net framework 3.5(包含 .NET 2.0 and 3.0) ,Windows 8 預設安裝了 .NET Framework 4.5 (包含 4.0)。但是.Net 4.0是不向下相容.Net 3.5的,所以即使你裝了4.0 你的程式可能還是用不了。感興趣可以參考Net Framework各個版本區別

下面我們來看看這些指令碼怎麼寫?

首先我們要把.Net 環境加入到打包程式中

[file]
Source: "D:\NetFx20SP2_x86.exe"; DestDir: "{tmp}"; Flags: ignoreversion {#IsExternal}; Check: NeedsFramework
<pre name="code" class="plain">//新增執行操作
[Run]
Filename: {tmp}\NetFx20SP2_x86.exe; Parameters: "/q:a /c:""install /l /q"""; WorkingDir: {tmp}; Flags: skipifdoesntexist; StatusMsg: "Installing .NET Framework if needed"
Filename: {win}\Microsoft.NET\Framework\v2.0.50727\CasPol.exe; Parameters: "-q -machine -remgroup ""{#MyAppName}"""; WorkingDir: {tmp}; Flags: skipifdoesntexist runhidden; StatusMsg: "Setting Program Access Permissions..."
Filename: {win}\Microsoft.NET\Framework\v2.0.50727\CasPol.exe; Parameters: "-q -machine -addgroup 1.2 -url ""file://{app}/*"" FullTrust -name ""{#MyAppName}"""; WorkingDir: {tmp}; Flags: skipifdoesntexist runhidden; StatusMsg: "Setting Program Access Permissions..."

檢查目標系統中是否有.Net 環境(以2.0為例),有則安裝,無則不裝。

//判斷是否需要安裝.NET Framework 2.0 SP1(或以上)
function NeedsFramework(): Boolean;
begin
  Result := (IsDotNET20Detected = false);
end;

function IsDotNET20Detected(): boolean;
var
    success: boolean;
    sp: cardinal;
begin
    
    success := RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727', 'SP', sp);
    Result :=  success and (sp >= 1);
end;

接著,我們要建立互斥變數來限制只能執行一個安裝程式。

function InitializeSetup():boolean;
var bResult:boolean;

begin
    Result := true;
    
    //檢測是否有另一個安裝程式在執行
    bResult :=  CheckForMutexes('MutexBugskySetup');
    if  bResult = true then
    begin
       MsgBox('另一安裝程式已經在執行,此安裝程式將退出。',mbInformation,MB_OK);
       Result := false;
       Exit;
    end else
    begin
        //沒有就建立互斥量
        CreateMutex('MutexBugskySetup');
    end;
end;

接下來我們要檢查安裝和解除安裝時是否程式正在執行,為了提示使用者儲存資料,所以要關閉程式後再安裝。
<pre name="code" class="plain">function InitializeSetup():boolean;

var ResultCode:Integer;
var hwnd:HWND;
var CloseNum:integer

begin //先檢測程序中程式是否在執行 hwnd := FindWindowByWindowName('{#MyAppName}'); CloseNum := 0; if hwnd <> 0 then begin ResultCode := MsgBox('檢測程式正在執行,是否關閉程式繼續安裝?'#13 #10'選擇“是”繼續安裝;選擇“否”,退出安裝。',mbInformation,MB_YESNO); //繼續安裝 if ResultCode = IDYES then begin Result := true; end else begin Result := false; Exit; end; end; while ((hwnd <> 0) and (CloseNum < 5)) do begin //關閉服務程式,嘗試5次。 PostMessage(hwnd,18,0,0); Sleep(100); CloseNum := CloseNum + 1; hwnd := FindWindowByWindowName('{#MyAppName}'); end; if IsServiceRunning('{#MyAppServiceName}') then begin StopService('{#MyAppServiceName}'); end end;

end;

解除安裝的時候,只要在CurUninstallStepChanged()方法中做同樣的事就行了。

好了,就寫到這裡了,具體的語法和API請參考Inno Setup幫助文件

參考:

相關推薦

使用Inno SetUp指令碼打包Winform程式

在開發桌面程式時,往往需要用到打包工具將程式打包為exe可執行檔案。 之前在專案中用了下 InstallShield Limited Edition for Visual Studio  2015,它的功能很強大,但是需要付費使用,而且有些細粒度的操作比較複雜。 後來,我發

Inno Setup Compiler打包需要管理員許可權的程式

若在程式安裝完成後,勾選執行程式,如下圖所示: 會出現如下圖所示異常: 解決方法如下: 找到Inno Setup Compiler安裝目錄下的SetupLdr.e32檔案,用ResHacker來開啟SetupLdr.e32,修改Manifest檔案: 將&

Inno Setup 指令碼

給你個我用的例子: Delphi/Pascal code   ? 1 2

Advanced Installer 打包Winform程式

Winform程式打包方式: 1.ClickOnce部署方式可以完成Winform程式的打包,並根據程式的升級進行自動更新,但是不能修改安裝路徑 2.Visual Studio Installer部署Winform程式,可以有安裝介面和修改安裝路徑,但是不能設定

Inno Setup】設定安裝程式的壓縮方法和分割方案

[Setup] ;true (採用分割) ;false (取消分割) DiskSpanning=true ;[262144, 2100000000] DiskSliceSize=210000000

Inno Setup指令碼語法大全

 Bruce  2年前 (2014-10-28)  11189瀏覽  0評論 來源: http://www.uol123.com/2014/10/28/inno-setup%E8%84%9A%E6%9C%AC%E8%AF%AD%E6%B3%95%E5%

Inno setup 簡單打包教程

前段時間關注了VC6.0自帶的Installshield打包的使用方法,感覺繁瑣,現在找到一個比較簡單實用的打包小工具Inno setup,使用方法如下。 (1)將編寫好的程式生成Release版本,開啟Inno setup 5小軟體,彈出如下介面: 點選選單欄“檔案”—

python+pyside+py2exe+inno setup 開發圖形介面程式

pyside-uic E:\user\PycharmProjects\XXX\XXX.ui -o XXX_ui.py 我們把XXX_ui.py放在專案裡,然後編輯py指令碼。例如: fromPySide.QtGuiimport*fromXXX_uiimportUi_FormclassLoginForm(

Inno Setup打包帶有MSI檔案的程式

1 [Files] 2 Source: "C:\Documents and Settings\Administrator\桌面\4\abc.exe"; DestDir: "{app}"; Flags: ignoreversion 3 Source: "C:\Documents and Settin

第一次使用Inno Setup打包程式初記

在平時的工作中,需要將自己寫的程式碼或者完成的程式打包成安裝包供使用者安裝使用。所以首先我將自己對打包和安裝這倆個過程進行簡單,粗略的理解。 打包: 1,壓縮程式執行所需要的檔案 2,編寫安裝程式整個過程的指令碼 安裝 1,解壓縮 2,執行前面編寫的

inno setup打包工具指令碼

; 指令碼由 Inno Setup 指令碼嚮導 生成! ; 有關建立 Inno Setup 指令碼檔案的詳細資料請查閱幫助文件! [Setup] ; 注: AppId的值為單獨標識該應用程式。 ; 不要為其他安裝程式使用相同的AppId值。 ; (生成新的GUID,點選 工

Inno Setup打包的安裝程式在Vista/Win7上自動提示需要管理員許可權的方法

首先,在 [Setup]段  PrivilegesRequired=admin  然後找到INNO安裝目錄下的SetupLdr.e32檔案(其實就是一個exe程式),使用管理員許可權執行用ResHacker修改: 將程式中的Manifest內的: <reque

Inno Setup 打包exe設定程式開機自動啟動

第一種方法:不修改登錄檔 原理是將程式的快捷方式新增到計算機"啟動"資料夾,win7可以 win10沒試過 [Tasks] Name: "startupicon"; Description: "開機啟動"; GroupDescription: "{cm:AdditionalI

INNO SETUP 打包程式 在WIN7下 報CREATE PROCESS 錯誤的許可權問題解決方法

首先,在 [Setup]段  PrivilegesRequired=admin  然後找到INNO安裝目錄下的SetupLdr.e32檔案(其實就是一個exe程式),將程式中的Manifest內的 <requestedExecutionLevel level="a

inno setup打包安裝程式中讓“是否建立快捷方式”預設為“打鉤”的方法

用inno setup的嚮導建立一個安裝檔案,在選擇“是否建立快捷方式”時,系統預設是“不打鉤”的,如何讓它是打勾的呢?很簡單,修改指令碼下面這段: [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIc

inno Setup做應用程式安裝包的示例指令碼(.iss檔案)

{程式安裝前判斷主程式是否在執行} function InitializeSetup(): Boolean; var   ResultCode: Integer; begin   if RegGetSubkeyNames(HKEY_LOCAL_MACHINE,'SOFTWARE\Microsoft\Windo

Winform程式打包

週末閒暇,閒來無事,想起以前在校做的小專案,於是想打包成exe安裝包,今天和各位碼農分享一下 。 首先開啟自己專案,在工具欄中找到"工具"選擇"擴充套件和更新"   然後選擇”聯機“在右側搜尋框輸入”Visual studio Installer“點選安裝 Visual studio

Inno Setup 安裝前解除安裝原程式

很多時候我們需要在安裝檔案之前解除安裝原有的程式而不是覆蓋安裝,本文的code就是實現了這樣的功能。實現原理是:從註冊表'UninstallString'項中讀取解除安裝資訊,用Exec進行靜默解除安裝。下面code中APP_NAME為你的程式名,可以去註冊表中確認。function InitializeSe

MONO 如何打包 .NET程式獨立執行(winform篇)

.NET程式獨立執行是指執行.NET的電腦上,不需要安裝.NET框架。 .NET程式集“獨立執行”並非真正的獨立,它是執行在mono執行時基礎之上的。由於這個執行時可以獨立存在,所以,我們不需要在目標機上安裝.NET框架。 如上圖: 一,嵌入或未嵌入.NET程式集

使用 pyinstaller 把python指令碼打包成 windows exe 可執行程式

首先,需要明確的一點是,必須在 windows 安裝 python,並且用 windows 下的 python 來執行打包的操作,才可以得到 exe 可執行程式(可以先在 linux 下完成除錯)。 因為無論是 py2exe 還是 pyinstaller,都是