目录
inno setup从小白到大白
这是我初次使用inno setup制作安装包,花了几日时间搜罗全网中别人的inno经验,制作了一个自己的安装包。这里分享一下我的成果( ̄▽ ̄)"
从新建的向导开始
点击File -> New,开始新建iss文件
取消点击create a new empty script file的勾,否则无法进入向导帮助。点击next
填写软件的名称,版本,公司名,公司网址的信息。点击next
填写软件的安装目录,这里我选择自定义的安装目录并填了C:\Program Files,my_program是安装解压时的目录,并允许用户修改安装目录的位置。这样在安装时显示的安装目录的路径就为C:\Program Files\my_program。点击next
编译一个release的版本,这里选择release下的exe的文件位置,允许用户在安装完成后运行这个exe文件,然后点击add folder…选择文件夹,这个文件夹放置了软件运行需要的文件,即安装包压缩的文件。点击next
在开始菜单创建快捷方式。如果想在开始菜单创建文件的话要取消第一个勾(create a shortcut的那个),然后下面的application start menu folder name会亮起,“我的程序”就是开始菜单的文件名。倒数第二个是在桌面创建快捷方式。点击next
这里输入凭证信息,我的软件没有这些(别期待,只是个输出hello world的exe),所以全部为空。点击next
默认的是仅为当前用户安装,勾选了可以让用户选择安装模式。
之后双击安装包就会弹出选择模式
点击next。
选择安装包显示的语言,因为我事先有中文的配置文件,所以这里有Chinese,这里我先选择English,稍后在iss脚本文件中配置回中文模式(如果有中文的话,大家可以直接选择中文,免折腾)。点击next
这里选择编译后得到的安装包的保存路径和名称。点击next
这里选择使用#define,这样可以简化脚本文件的编写,一旦需要修改可以只改动define中的内容(生成的isl会有软件名,版本号,公司名之类的)。点击next
完成了脚本文件的向导操作,下面就可以生成iss文件了
点击finish后会弹出下面的对话框问你是否立刻编译,选择是会先叫你保存脚本文件,再开始编译生成一个安装包,这里我还需要配置一些信息,所以我先选择否(可以先编译一个版本和后面作比较)
编辑脚本文件
[setup]
根据上面向导中的内容生成出来的iss中的setup部分如下图
选择安装目录
这里添加一行
UsePreviousAppDir=no
不加时,安装包第一次双击打开时可以让用户选择安装目录,但之后再选择安装目录时,就会跳过选择安装目录的这步。加了后每次打开安装包时都可以选择安装目录。
配置图片
WizardImageFile=F:\inno\image\finish.bmp
WizardSmallImageFile=F:\inno\image\small_icon.bmp
WizardSmallImageFile是向导右上角的小图标。所有图片的格式需要为bmp
WizardImageFile是安装向导最后一页左侧的图片,
[message]
上面生成的脚本文件没有message这项,可以自己加上。下面设置了安装时对话框的标题和选择安装模式(当前用户 or 所有用户)的对话框标题,setupapptitle是消息的对话框mesbox标题
#define MyOutputBaseFilename “mysetup”
#define MyDialogTitle “安装”
[Messages]
SetupAppTitle= 消息标题
SetupWindowTitle={#MyDialogTitle}{#MyOutputBaseFilename}
PrivilegesRequiredOverrideTitle = {#MyDialogTitle}{#MyOutputBaseFilename}
[languages]
上面选择语言是选择的是english,所以这里是默认的isl文件。
改成中文需要有中文的isl文件,然后选择中文isl的相对文件位置
[Languages]
Name: “chinesesimp”; MessagesFile: “compiler:Languages\Chinese.isl”
中文isl文件可以在一些中文增强版的inno setup中找到。也可以在这里下载chinese.isl
移进inno安装目录的 Languages文件夹中
[tasks]
上面向导生成的tasks如下
只有创建快捷方式的任务可选,而且默认是不勾选的。这里我改为默认勾选,并且添加创建开始菜单的任务
[Tasks]
Name: “desktopicon”; Description: “{cm:CreateDesktopIcon}”; GroupDescription: “{cm:AdditionalIcons}”; Flags: checkedonce
Name: “startmenuicon”;Description:“创建开始菜单”;GroupDescription:"{cm:AdditionalIcons}"; Flags: checkedonce
删除Flag也可以达到勾选的效果
[files]
这里我没有什么需要改,所以省略了吧
[icons]
这里配置桌面快捷方式的文件和开始菜单的文件,如果软件还需要在菜单在新建一项“软件说明书“,可以在这里配置这项
[Icons]
Name: “{group}{#MyAppName}”; Filename: “{app}{#MyAppExeName}”; Tasks: startmenuicon
Name: “{autodesktop}{#MyAppName}”; Filename: “{app}{#MyAppExeName}”; Tasks: desktopicon
Name: “{group}\使用说明”; Filename: “F:\inno\软件说明书.txt”;Tasks: startmenuicon
第一和第二行是向导默许生成的,第三行是软件说明书一项。{group}是开始菜单的文件名,如向导时快捷方式配置那一页的”我的程序“就是group。这里只是说明一下,后面编译时我只用默认的前二行。
[Run]
安装完成时默认运行exe,这里也没有要改的,省略
[Registry]
添加registry可以在注册表写东西,如下面一行
[Registry]
Root: HKLM; Subkey: “Software\myprogram”; ValueType: string; ValueName: “myprogramVersion”; ValueData: “1.0”; Flags: uninsdeletevalue
表示在注册表中的根为HKEY_LOCAL_MACHINE中的Software项添加myprogram一行,记录我软件的版本号,uninsdeletevalue表示卸载时就删除该项。后面编译时没有这项
后面发现inno setup默认安装时会根据appid在注册表中写入软件的卸载信息,如下
[code]
这里可以添加一些代码段,我在这里加了安装时检测软件是否安装(通过检查注册表中的软件信息实现),如果安装了调用卸载程序,和安装时检查安装程序是否已经打开的代码。下面直接上代码
[Code]
{获取安装信息}
function GetUninstallString():String;
var
ResultStr: String;
begin
ResultStr:='';
{PrivilegesRequired=lowest}
//注册表位置,需要知道AppId在注册表中的位置,可以安装后在注册表中查找AppId
if RegQueryStringValue(HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{1138B0E0-ABC0-424F-A80D-A834D140ED75}_is1', 'UninstallString', ResultStr) then
begin
ResultStr := RemoveQuotes(ResultStr);
{PrivilegesRequired=admin}
//同上
end else if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{1138B0E0-ABC0-424F-A80D-A834D140ED75}_is1', 'UninstallString', ResultStr) then
begin
ResultStr := RemoveQuotes(ResultStr);
end;
Result := ResultStr;
end;
//卸载程序
function UninstallApp(uninstallString: String): Boolean;
var
ResultCode: Integer;
bRes: Boolean;
begin
result := true;
if length(uninstallString) > 0 then
begin
//如果发现程序已经安装过了
if MsgBox('{#MyAppName} 已经安装,是否卸载本程序?', mbConfirmation, MB_YESNO) = IDYES then
begin
uninstallString := RemoveQuotes(uninstallString);
bRes := Exec(uninstallString, '/silent', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
if bRes and (ResultCode = 0) then
begin
result := true;
Exit;
end
else
begin
MsgBox('卸载失败!请手动卸载', mbInformation, MB_OK);
result:= false;
end;
end
else
result := false;
end;
end;
//安装程序是否已启动
function InitializeSetUpOpened(): Boolean;
var
bRes: Boolean;
MainRun: HWND;
ResultCode: Integer;
begin
result := false;
//需要一个固定的窗口标题
MainRun := FindWindowByWindowName('{#MyDialogTitle}{#MyOutputBaseFilename}');
if MainRun <> 0 then
begin
MsgBox('程序已启动!', mbInformation, MB_OK);
result := true;
end;
end;
//选择完安装模式才进入
function InitializeSetup(): Boolean;
var
uninstallString: String;
begin
result := true;
//已安装程序
if Not UninstallApp(GetUninstallString()) then
begin
result := false;
Exit;
end;
//已打开安装程序
if InitializeSetUpOpened() then
begin
result := false;
Exit;
end;
end;
最后可以编译运行了
PS
- isl中的路径都可以改为相对路径
- 选择完安装模式才会进入InitializeSetup
安装时遇到的问题
错误5:拒绝访问
原因:权限不足。
如果选择仅为当前用户安装,则执行时没有管理员权限。而需要打包的工程设置了需要管理员权限,如下图的vs工程的属性页中
需要修改inno的设置。下载reshacker
reshacker安装包下载 http://www.angusj.com/resourcehacker/reshacker_setup.exe
reshacker官网 http://www.angusj.com/resourcehacker/
运行 resource_hacker.exe,打开SetupLdr.e32
再次编译,得到的安装包图标会带有一个盾,即以管理员方式运行
卸载时检测软件是否打开(没有满意的解决方法)
卸载时如何检测程序是否打开?默认的卸载方法是删除没有被打开的程序占用的文件,留下一些无法删除文件让用户自己删除。
- 经过一番实践,发现网上istask.dll和psvince.dll的例子无法检测我的软件是否打开(明明网上一大堆这些例子,不知道为什么我这么惨。而且这些dll无法检测外,双击打开安装程序时还会卡上个5秒,总之很慢);
- 另一个FindWindowByWindowName是通过窗口标题查找程序的,但hyper3d的标题会随着打开的工程文件名变化,貌似也没用;
- 还有一个FindWindowByClassName查找窗口,但不知道哪个是窗口的className( ̄▽ ̄)”;
- 最后还有一个方法是在setup中设置appMutex值,在c++程序中createMutex和releaseMutex,这种方法只支持程序单开(有人讲现在很多程序都是单开的,但我觉得双开的多,如word之类的,难道是手机程序!?不知道手机程序如何打包)。
终于找到一个卸载时强制关闭所有程序的代码段
[Code]
function InitializeUninstall(): Boolean;
var ErrorCode: Integer;
begin
ShellExec(‘open’,‘taskkill.exe’,’/f /im {#MyAppExeName}’,’’,SW_HIDE,ewNoWait,ErrorCode);
ShellExec(‘open’,‘tskill.exe’,’ {#MyAppName}’,’’,SW_HIDE,ewNoWait,ErrorCode);
result := True;
end;
关于上面提到的安装时检测安装程序有冇打开,因为安装程序的windowName固定,所以可以用FindWindowByWindowName。O( ̄▽ ̄)ブ
[1]: reshacker: https://blog.csdn.net/longji/article/details/53523808
[2]: 中文 https://www.cnblogs.com/leochan007/p/8203675.html
[3]: 窗口标题 https://blog.csdn.net/chenlu5201314/article/details/54963255
[4]: 注册表 https://blog.csdn.net/yushanddddfenghailin/article/details/17250871