在开发桌面程序时,往往需要用到打包工具将程序打包为exe可执行文件。
之前在项目中用了下 InstallShield Limited Edition for Visual Studio 2015,它的功能很强大,但是需要付费使用,而且有些细粒度的操作比较复杂。
后来,我发现了一款简单方便的打包工具,即我们今天的主角Inno Setup
Inno Setup 支持pascal脚本,这样我们就能通过直接写脚本来实现功能了。
下面我们来看看我们打包过程中常见功能需求:
- 创建安装exe文件
- 创建卸载exe文件
- 创建各种快捷方式,桌面、快速启动栏等
- 安装、卸载Windows 服务
- 添加注册表信息
- 打包.Net framework 环境
- 安装时不能同时运行两个安装程序
- 安装/卸载时检测程序(或服务)是否在运行,如果在运行则先关闭程序。
程序的使用方法就不多做介绍了,打开程序新建脚本,会创建出一个基本的框架,然后我们就可以修改脚本添加功能了。
上面的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 'OpenSCManagerA@advapi32.dll stdcall'; function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenServiceA@advapi32.dll stdcall'; function CloseServiceHandle(hSCObject :HANDLE): boolean;
external 'CloseServiceHandle@advapi32.dll stdcall'; function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
external 'CreateServiceA@advapi32.dll stdcall'; function DeleteService(hService :HANDLE): boolean;
external 'DeleteService@advapi32.dll stdcall'; function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external 'StartServiceA@advapi32.dll stdcall'; function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'ControlService@advapi32.dll stdcall'; function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall'; function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall'; function GetLastError() : cardinal;
external 'GetLastError@kernel32.dll 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帮助文档。
参考:
http://www.cnblogs.com/xiaogangqq123/archive/2012/03/19/2405730.html
http://*.com/questions/2456987/upgrading-windows-service-using-inno-setup
使用Inno SetUp脚本打包Winform程序的更多相关文章
-
Setup Factory打包winform程序
摘要 Setup Factory是一款软件安装工具.Setup Factory支持创建一个安装文件或一个单间的setup.exe文件,生成文件可以运行于任意版本的windows中. 步骤 1.安装Se ...
-
[InnoSetup]Inno Setup软件打包脚本
脚本由 Inno Setup 脚本向导 生成! ; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档! #define MyAppName "SFT期货交易系统&quo ...
-
Inno setup 简单打包教程
转自:http://blog.csdn.net/ruifangcui7758/article/details/6662646 前段时间关注了VC6.0自带的Installshield打包的使用方法,感 ...
-
INNO SETUP脚本向导创建的基本脚本
脚本范例分析:先来看看一段用INNO SETUP脚本向导创建的基本脚本的[Setup]段: [Setup] AppName=Premiere 6.5 汉化补丁-----------------(程 ...
-
以前编写的inno setup脚本,涵盖了自定义安装界面,调用dll等等应用 (转)
以前编写的inno setup脚本,涵盖了自定义安装界面,调用dll等等应用 (转) ; Script generated by the Inno Setup 脚本向导. ; SEE THE DOCU ...
-
Inno Setup脚本语法大全
Inno Setup脚本语法大全 ResourceShare Bruce 11个月前 (10-28) 6136浏览 0评论 Inno Setup 是什么?Inno Setup 是一个免费的 Win ...
-
inno setup脚本,涵盖了自定义安装界面,调用dll等等应用
; Script generated by the Inno Setup 脚本向导. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETU ...
-
Inno Setup脚本
某天夜晚一场狂风暴雨,由于办公室座位旁的窗户没关,笔记本电脑泡了一夜水,无法开机,无奈送修,里面的大量资料也不知道会不会丢失. is的脚本只有重新写了,重新研究了一下检测程序是否正在运行的判断方法,另 ...
-
Inno Setup 5打包exe遇到的坑,做一个学习记录
; 脚本由 Inno Setup 脚本向导 生成!; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档! #define MyAppName "人员管理系统"#de ...
随机推荐
-
IT男常用软件网站整理
1. 猎豹免费WiFI. 属于wifi共享软件. 360免费wifi.. 2. 悟空VPN, 免费VPN.http://www.wkdaili.net/ 3. PLSQL. 4. WinSCP, ...
-
Linux+Mono+Asp.net入门:05CentOs安装Mono(上)
http://www.cnblogs.com/jameszou/archive/2013/05/18/3085754.html 准备工作 常见问题 a. Xshell显示中文乱码问题 [文件]–> ...
-
iOS学习之懒加载
懒加载——也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,其实是重写getter方法. 注意:如果是懒加载的话则一定要注意先判断是否已经有了,如果没有那么再去进行实例化 使用懒 ...
-
zabbix3.2源码搭建
首先环境是标准的lnmp 1.创建zabbix组和系统用户来管理 groupadd zabbix useradd -g zabbix zabbix -s /sbin/nologin 2.在mysql ...
-
微信网页授权 通过code获取openid 报错40163 code been used
使用好好的微信功能,突然安卓无法正常使用了,苹果的正常. 安卓报错内容: 40163,code been used. 题外话:微信的东西,为何报英文错误呢,装什么13. 实测结果:安卓获取用户信息时 ...
-
【1】Java中double转BigDecimal的注意事项
项目遇到该问题 先上结论:不要直接用double变量作为构造BigDecimal的参数. 线上有这么一段Java代码逻辑: 1,接口传来一个JSON串,里面有个数字:57.3. 2,解析JSON并把这 ...
-
IntelliJ IDEA src下新建包, 没有层级结构
新建项目后再src先右键点击新建包 com.example , 然后想在com.example 包中包含其他包, 当点击src新建包后,出现如图的情况 解决: 继续在src上右键新建package ...
-
[UE4]子控件Child Widget顶层容器选择
如果父级容器是Canvas,则可以直接设置尺寸.放到其他widget的时候也会保持设定好的尺寸(而不管父容器是什么类型).
-
有关keras(Ubuntu14.04,python2.7)
第一部分:安装 由于我的电脑之前已经已经配置好了caffe,因此有关python的一切相关包都已经安装完成.因此,即使不用Anaconda安装依然很简单. sudo pip install tenso ...
-
Redis Quick Start [遇到问题解决问题版]
http://redis.io/topics/quickstart make test 时的问题: 问题:gcc: Command not found 解决:yum install gcc [root ...