在开发桌面程序时,往往需要用到打包工具将程序打包为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服务,我这里收藏了一个极好用的脚本。

  1. // Code pasted from the following address, for examples and more visit it:
  2. // http://www.vincenzo.net/isxkb/index.php?title=Service_-_Functions_to_Start%2C_Stop%2C_Install%2C_Remove_a_Service
  3.  
  4. // function IsServiceInstalled(ServiceName: string) : boolean;
  5. // function IsServiceRunning(ServiceName: string) : boolean;
  6. // function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
  7. // function RemoveService(ServiceName: string) : boolean;
  8. // function StartService(ServiceName: string) : boolean;
  9. // function StopService(ServiceName: string) : boolean;
  10. // function SetupService(service, port, comment: string) : boolean;
  11.  
  12. type
  13. SERVICE_STATUS = record
  14. dwServiceType : cardinal;
  15. dwCurrentState : cardinal;
  16. dwControlsAccepted : cardinal;
  17. dwWin32ExitCode : cardinal;
  18. dwServiceSpecificExitCode : cardinal;
  19. dwCheckPoint : cardinal;
  20. dwWaitHint : cardinal;
  21. end;
  22. HANDLE = cardinal;
  23.  
  24. const
  25. SERVICE_QUERY_CONFIG = $1;
  26. SERVICE_CHANGE_CONFIG = $2;
  27. SERVICE_QUERY_STATUS = $4;
  28. SERVICE_START = $10;
  29. SERVICE_STOP = $20;
  30. SERVICE_ALL_ACCESS = $f01ff;
  31. SC_MANAGER_ALL_ACCESS = $f003f;
  32. SERVICE_WIN32_OWN_PROCESS = $10;
  33. SERVICE_WIN32_SHARE_PROCESS = $20;
  34. SERVICE_WIN32 = $30;
  35. SERVICE_INTERACTIVE_PROCESS = $100;
  36. SERVICE_BOOT_START = $0;
  37. SERVICE_SYSTEM_START = $1;
  38. SERVICE_AUTO_START = $2;
  39. SERVICE_DEMAND_START = $3;
  40. SERVICE_DISABLED = $4;
  41. SERVICE_DELETE = $10000;
  42. SERVICE_CONTROL_STOP = $1;
  43. SERVICE_CONTROL_PAUSE = $2;
  44. SERVICE_CONTROL_CONTINUE = $3;
  45. SERVICE_CONTROL_INTERROGATE = $4;
  46. SERVICE_STOPPED = $1;
  47. SERVICE_START_PENDING = $2;
  48. SERVICE_STOP_PENDING = $3;
  49. SERVICE_RUNNING = $4;
  50. SERVICE_CONTINUE_PENDING = $5;
  51. SERVICE_PAUSE_PENDING = $6;
  52. SERVICE_PAUSED = $7;
  53.  
  54. // #######################################################################################
  55. // nt based service utilities
  56. // #######################################################################################
  57. function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
  58. external 'OpenSCManagerA@advapi32.dll stdcall';
  59.  
  60. function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
  61. external 'OpenServiceA@advapi32.dll stdcall';
  62.  
  63. function CloseServiceHandle(hSCObject :HANDLE): boolean;
  64. external 'CloseServiceHandle@advapi32.dll stdcall';
  65.  
  66. function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
  67. external 'CreateServiceA@advapi32.dll stdcall';
  68.  
  69. function DeleteService(hService :HANDLE): boolean;
  70. external 'DeleteService@advapi32.dll stdcall';
  71.  
  72. function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
  73. external 'StartServiceA@advapi32.dll stdcall';
  74.  
  75. function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
  76. external 'ControlService@advapi32.dll stdcall';
  77.  
  78. function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
  79. external 'QueryServiceStatus@advapi32.dll stdcall';
  80.  
  81. function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
  82. external 'QueryServiceStatus@advapi32.dll stdcall';
  83.  
  84. function GetLastError() : cardinal;
  85. external 'GetLastError@kernel32.dll stdcall';
  86.  
  87. function OpenServiceManager() : HANDLE;
  88. begin
  89. if UsingWinNT() = true then begin
  90. Result := OpenSCManager('','',SC_MANAGER_ALL_ACCESS);
  91. if Result = 0 then
  92. MsgBox('the servicemanager is not available', mbError, MB_OK)
  93. end
  94. else begin
  95. MsgBox('only nt based systems support services', mbError, MB_OK)
  96. Result := 0;
  97. end
  98. end;
  99.  
  100. function IsServiceInstalled(ServiceName: string) : boolean;
  101. var
  102. hSCM : HANDLE;
  103. hService: HANDLE;
  104. begin
  105. hSCM := OpenServiceManager();
  106. Result := false;
  107. if hSCM <> 0 then begin
  108. hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_CONFIG);
  109. if hService <> 0 then begin
  110. Result := true;
  111. CloseServiceHandle(hService)
  112. end;
  113. CloseServiceHandle(hSCM)
  114. end
  115. end;
  116.  
  117. function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
  118. var
  119. hSCM : HANDLE;
  120. hService: HANDLE;
  121. begin
  122. hSCM := OpenServiceManager();
  123. Result := false;
  124. if hSCM <> 0 then begin
  125. hService := CreateService(hSCM,ServiceName,DisplayName,SERVICE_ALL_ACCESS,ServiceType,StartType,0,FileName,'',0,'','','');
  126. if hService <> 0 then begin
  127. Result := true;
  128. // Win2K & WinXP supports aditional description text for services
  129. if Description<> '' then
  130. RegWriteStringValue(HKLM,'System\CurrentControlSet\Services\' + ServiceName,'Description',Description);
  131. CloseServiceHandle(hService)
  132. end;
  133. CloseServiceHandle(hSCM)
  134. end
  135. end;
  136.  
  137. function RemoveService(ServiceName: string) : boolean;
  138. var
  139. hSCM : HANDLE;
  140. hService: HANDLE;
  141. begin
  142. hSCM := OpenServiceManager();
  143. Result := false;
  144. if hSCM <> 0 then begin
  145. hService := OpenService(hSCM,ServiceName,SERVICE_DELETE);
  146. if hService <> 0 then begin
  147. Result := DeleteService(hService);
  148. CloseServiceHandle(hService)
  149. end;
  150. CloseServiceHandle(hSCM)
  151. end
  152. end;
  153.  
  154. function StartService(ServiceName: string) : boolean;
  155. var
  156. hSCM : HANDLE;
  157. hService: HANDLE;
  158. begin
  159. hSCM := OpenServiceManager();
  160. Result := false;
  161. if hSCM <> 0 then begin
  162. hService := OpenService(hSCM,ServiceName,SERVICE_START);
  163. if hService <> 0 then begin
  164. Result := StartNTService(hService,0,0);
  165. CloseServiceHandle(hService)
  166. end;
  167. CloseServiceHandle(hSCM)
  168. end;
  169. end;
  170.  
  171. function StopService(ServiceName: string) : boolean;
  172. var
  173. hSCM : HANDLE;
  174. hService: HANDLE;
  175. Status : SERVICE_STATUS;
  176. begin
  177. hSCM := OpenServiceManager();
  178. Result := false;
  179. if hSCM <> 0 then begin
  180. hService := OpenService(hSCM,ServiceName,SERVICE_STOP);
  181. if hService <> 0 then begin
  182. Result := ControlService(hService,SERVICE_CONTROL_STOP,Status);
  183. CloseServiceHandle(hService)
  184. end;
  185. CloseServiceHandle(hSCM)
  186. end;
  187. end;
  188.  
  189. function IsServiceRunning(ServiceName: string) : boolean;
  190. var
  191. hSCM : HANDLE;
  192. hService: HANDLE;
  193. Status : SERVICE_STATUS;
  194. begin
  195. hSCM := OpenServiceManager();
  196. Result := false;
  197. if hSCM <> 0 then begin
  198. hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_STATUS);
  199. if hService <> 0 then begin
  200. if QueryServiceStatus(hService,Status) then begin
  201. Result :=(Status.dwCurrentState = SERVICE_RUNNING)
  202. end;
  203. CloseServiceHandle(hService)
  204. end;
  205. CloseServiceHandle(hSCM)
  206. end
  207. 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 环境加入到打包程序中

  1. [file]
  2. Source: "D:\NetFx20SP2_x86.exe"; DestDir: "{tmp}"; Flags: ignoreversion {#IsExternal}; Check: NeedsFramework
  3. <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..."

  1.  

检查目标系统中是否有.Net 环境(以2.0为例),有则安装,无则不装。

  1. //判断是否需要安装.NET Framework 2.0 SP1(或以上)
  2. function NeedsFramework(): Boolean;
  3. begin
  4. Result := (IsDotNET20Detected = false);
  5. end;
  6.  
  7. function IsDotNET20Detected(): boolean;
  8. var
  9.     success: boolean;
  10.     sp: cardinal;
  11. begin
  12.     
  13.     success := RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727', 'SP', sp);
  14.     Result :=  success and (sp >= 1);
  15. end;

接着,我们要建立互斥变量来限制只能运行一个安装程序。

  1. function InitializeSetup():boolean;
  2. var bResult:boolean;
  3.  
  4. begin
  5. Result := true;
  6.  
  7. //检测是否有另一个安装程序在运行
  8. bResult := CheckForMutexes('MutexBugskySetup');
  9. if bResult = true then
  10. begin
  11. MsgBox('另一安装程序已经在运行,此安装程序将退出。',mbInformation,MB_OK);
  12. Result := false;
  13. Exit;
  14. end else
  15. begin
  16. //没有就创建互斥量
  17. CreateMutex('MutexBugskySetup');
  18. end;
  19. end;

接下来我们要检查安装和卸载时是否程序正在运行,为了提示用户保存数据,所以要关闭程序后再安装。

  1. <pre name="code" class="plain">function InitializeSetup():boolean;
  2.  
  3. var ResultCode:Integer;
  4. var hwnd:HWND;
  5. var CloseNum:integer
  6.  
  7. 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;
  8.  
  9. end;

卸载的时候,只要在CurUninstallStepChanged()方法中做同样的事就行了。

  1.  

好了,就写到这里了,具体的语法和API请参考Inno Setup帮助文档

参考:

http://www.cnblogs.com/xiaogangqq123/archive/2012/03/19/2405730.html

http://stackoverflow.com/questions/2456987/upgrading-windows-service-using-inno-setup

使用Inno SetUp脚本打包Winform程序的更多相关文章

  1. Setup Factory打包winform程序

    摘要 Setup Factory是一款软件安装工具.Setup Factory支持创建一个安装文件或一个单间的setup.exe文件,生成文件可以运行于任意版本的windows中. 步骤 1.安装Se ...

  2. [InnoSetup]Inno Setup软件打包脚本

     脚本由 Inno Setup 脚本向导 生成! ; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档!   #define MyAppName "SFT期货交易系统&quo ...

  3. Inno setup 简单打包教程

    转自:http://blog.csdn.net/ruifangcui7758/article/details/6662646 前段时间关注了VC6.0自带的Installshield打包的使用方法,感 ...

  4. INNO SETUP脚本向导创建的基本脚本

    脚本范例分析:先来看看一段用INNO SETUP脚本向导创建的基本脚本的[Setup]段: [Setup]   AppName=Premiere 6.5 汉化补丁-----------------(程 ...

  5. 以前编写的inno setup脚本,涵盖了自定义安装界面,调用dll等等应用 (转)

    以前编写的inno setup脚本,涵盖了自定义安装界面,调用dll等等应用 (转) ; Script generated by the Inno Setup 脚本向导. ; SEE THE DOCU ...

  6. Inno Setup脚本语法大全

    Inno Setup脚本语法大全 ResourceShare Bruce 11个月前 (10-28) 6136浏览 0评论   Inno Setup 是什么?Inno Setup 是一个免费的 Win ...

  7. inno setup脚本,涵盖了自定义安装界面,调用dll等等应用

    ; Script generated by the Inno Setup 脚本向导. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETU ...

  8. Inno Setup脚本

    某天夜晚一场狂风暴雨,由于办公室座位旁的窗户没关,笔记本电脑泡了一夜水,无法开机,无奈送修,里面的大量资料也不知道会不会丢失. is的脚本只有重新写了,重新研究了一下检测程序是否正在运行的判断方法,另 ...

  9. Inno Setup 5打包exe遇到的坑,做一个学习记录

    ; 脚本由 Inno Setup 脚本向导 生成!; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档! #define MyAppName "人员管理系统"#de ...

随机推荐

  1. BZOJ4487 [Jsoi2015]染色问题

    BZOJ4487 [Jsoi2015]染色问题 题目描述 传送门 题目分析 发现三个限制,大力容斥推出式子是\(\sum_{i=0}^{N}\sum_{j=0}^{M}\sum_{k=0}^{C}(- ...

  2. RQN 273 马棚问题 dp

    PID273 / 马棚问题  2016-07-29 18:21:55 运行耗时:1624 ms 运行内存:16248 KB 题目描述 每天,小明和他的马外出,然后他们一边跑一边玩耍.当他们结束的时候, ...

  3. hdu 1540 Tunnel Warfare 线段数区间合并

    Tunnel Warfare Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) P ...

  4. vmware增加共享文件夹

    增加共享文件夹 VMWare提供共享文件夹功能.前提是在虚拟机中安装VMware tools 1. 安装VMware tools 会自动在虚拟机中的/media/VMware Tools/中有个压缩包 ...

  5. spring3: 对JDBC的支持 之 Spring提供的其它帮助 SimpleJdbcInsert/SimpleJdbcCall/SqlUpdate/JdbcTemplate 生成主键/批量处理

    7.4  Spring提供的其它帮助 7.4.1  SimpleJdbc方式 Spring JDBC抽象框架提供SimpleJdbcInsert和SimpleJdbcCall类,这两个类通过利用JDB ...

  6. Referenced file contains errors (http://www.springframework.org/schema/aop/spring-aop-3.0.xsd). For more information, right click on the message in th

    XML code<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC &q ...

  7. ubuntu定时执行任务——crontab的使用

    先补上几个链接,后续再总结 #参考# http://www.cnblogs.com/kaituorensheng/p/4494321.html http://blogread.cn/it/articl ...

  8. SpringBoot_11_将springboot项目部署到外部tomcat上

    一.前言 二. 三.参考资料 如何将Spring Boot项目打包部署到外部Tomcat 2.SpringBoot 项目如何在tomcat容器中运行

  9. [JS学习笔记]Event对象

    写在前面 学习和总结JS时会伴随性的生成一些dome,其中包含一些动态输出的结果和标注. 之前通过鸡贼的办法实现了在博客中执行JS,但很多时候需要一张干净的页面编写dome,所以尝试通过一些在线的JS ...

  10. React 实现 Table 的思考

    琼玖 1 年前 (写的零零散散, 包括github不怎么样) Table 是最常用展示数据的方式之一,可是一个产品中往往很多非常类似的 Table, 但是我们碰到的情况往往是 Table A 要排序, ...