# 20.9 应用程序安装
如果你的应用程序可以很顺利的安装到用户的电脑上,这无疑在用户开始使用你的程序之前就给用户一个很不错的第一印象.在这一节里,我们将依次介绍在Windows,Linux和OsX平台上怎样制作安装程序,其中涉及到的一些第三方工具可以在附录E中找到.
在Windows系统上安装你的程序
在windows平台上,我们尤其需要一个安装程序,这不只是因为用户期待这样,而且安装程序还需要作一些类似文件类型绑定和创建快捷方式这样的动作.
不够这实在和wxWidgets所关注的邻域差别太大,因此wxWidgets并不准备自己提供这样的工具.一些另外的工具可以用来创建安装程序,比如 NSIS和InstallShield;另外一个广受好评的软件是Inno Setup,它是一个非常强大的,免费的安装程序制作工具,它可以通过脚本来定制安装文件选项,通过Pascal语言来对其现有功能进行扩展.它的网站上也列举了一些用来创建安装脚本的图形界面工具.
如果你需要很频繁的发布新的版本,你可能想要通过一个脚本自动创建安装程序.随书光盘的 examples/chap20/install目录中提供了一个用于创建这样的脚本的例子,你可以按你的需要进行更改.因为它们是Unix风格的 Shell脚本,需要你有MingW或者MSys的环境,这些环境也有在随书光盘中提供.你需要提供的包括一个放置文件的目录,makeinno.sh脚本将会创建Inno Setup的脚本中枚举子目录和文件的部分.而安装脚本的头和尾部那些需要你自己按照自己软件的情况提供的部分将不会被自动创建.你可以使用下面的命令来创建安装文件:
```
sh makeinno.sh c:/temp/imagedir innotop.txt innobott.txt myapps.iss
```
这将会基于文件夹c:/temp/imagedir中的文件创建Inno Setup的脚本文件myapp.iss.
你可以调整makesetup.sh脚本来创建你需要的安装程序,这个文件首先将需要的文件拷贝到一个"images"文件夹(就是前面 makeinno.sh脚本需要的那个文件夹),然后创建setup.exe.这个脚本使用了定义在setup.var中的变量.你可以按照你自己的情况增加新的功能,比如编译你的应用程序或者使用Curl工具拷贝文件到你的FTP站点等.
当你发布应用程序的时候,别忘了增加一个WindowsXp的"manifest"文件.这个文件是一个Xml格式的文件,用来告诉 WindowsXp应该给这个程序应用什么风格.你可以通过在你的程序的资源文件(.rc)中增加wxWidgets标准资源文件的方式来增加这个文件. 如下所示:
```
aardvarkpro ICON aardvarkpro.ico
#include "wx/msw/wx.rc"
```
这将包含一个标准的manifest文件,如果你希望使用自己定义的manifest文件,在包含wx.rc之前,你需要定义wxUSE_NO_MANIFEST宏,然后再指定你自己的manifest文件,如下所示:
```
aardvarkpro ICON aardvarkpro.ico
#define wxUSE_NO_MANIFEST 1
#include "wx/msw/wx.rc"
1 24 "aardvark.manifest"
```
你也可以直接将manifest文件放在你的应用程序目录中,详情可参考wxWidgets发行版自带的docs/msw/winxp.txt文件.
在Linux系统上制作安装程序
在Linux系统,你可以选用图形界面的安装程序,定制的shell脚本或者某个特定发行版的软件包,比如RPM格式(基于Red Hat发行版)和Debian发布包(基于Debian发行版),你甚至可以直接将所有需要的文件压缩成一个包含路径的压缩文件(.tar.gz或者. tar,bz2),安装的时候只需要保持路径解压这个文件就可以了.
Linux环境下的图形界面安装程序包括Loki Setup(免费),Zero G公司的InstallAnywhere和InstallShield等.
基于GTK+的wxWidgets图形界面应用程序是桌面不可感知的:它不依赖于GNOME或者KDE,因此无论在哪种桌面环境下它都可以运行.大多数KDE桌面的发行版都会包括GTK+的库文件.然而,因为它们使用不同的桌面风格,GTK+程序在KDE桌面上看上去可能会有些不适应,某些控件可能超出边界,这种情况下你可以建议你的用户安装一个KDE下的GTK风格的皮肤,比如GTK-Qt(不过,在你把它介绍给你的用户之前,最好自己先测试一下).
你可能会希望在桌面上安装一个图标,以便你的用户可以直接使用它来启动你的程序.要在KDE桌面环境中增加一个图标,你需要拷贝一个合适的APP.desktop文件到PREFIX/share/applications文件夹,其中APP代表你的应用程序,PREFIX则通常代表 /usr,/usr/local或者其它定义在KDEDIR环境变量中的路径.下面演示了一个叫做Acme的Desktop文件,其中架设Acme被安装在/opt/Acme中.
```
[Desktop Entry]
BinaryPattern=Acme;
MimeType=
Name=Acme
Exec=/opt/Acme/acme
Icon=/opt/Acme/acme32x32.png
Type=Application
Terminal=0
```
而要在GNOME桌面上增加一个图标,语法和KDE中相似不过放置的位置应该是~/.gnome-desktop(只对单个用户有效). 更多关于GNOME和KDE桌面文件的定义可以在下面的网址看到:http: //www.freedesktop.org/wiki/Standards_2fdesktop_2dentry_2dspec.
如何制作RPM包的信息可以在http://www.rpm.org找到,那里还包含一个免费的在线电子书.而创建Debian包的信息可以在http://www.debian.org找到.这俩中方法创建的安装包可以允许系统进行依赖性检查,也使得用户可以很容易的浏览软件包的内容和安装软件包.如果需要创建RPM,.deb或者其它格式发行包的软件,可以试一下EPM.
关于使用shell脚本创建Linux的安装文件的方法,随书光盘的examples/chap20/install目录中包含了一个用来安装Acme的示例文件installacme.这个脚本作的事情包括安装整个程序并且创建一个叫做acme脚本,这个脚本在运行实际的可执行文件之前会先设置当前位置环境变量.这样作的好处在于你既不需要将软件所在的目录的路径增加到PATH环境变量中,也不需要将可执行文件直接拷贝到系统路径下就可以执行.所有的数据文件保持在可执行文件所在的目录中.这使得卸载软件变得容易.你当然也可以选择让安装脚本将数据放置到Linux的标准数据目录中.
在examples/chap20/install目录中还包含一个脚本叫做maketarball.sh,它演示了怎样创建一个用户发行的tar格式的压缩包,installacme脚本将包含在这个压缩包内以及另外一个包含所有数据文件的压缩包.你可以修改maketarball.sh以满足你自己的需要.
Linux环境上的动态链接库的问题
因为Linux系统上并没有标准的GUI库,各个发行版按照自己的喜好来添加它喜欢的库和程序,因此你可能会发现在某些系统上,你的应用程序不能运行,提示的原因是无法找到动态链接库.因此,静态链接所有需要的库文件实在是一个很诱人的想法.但是这样又会导致别的一些问题.虽然你不应该静态链接GTK+的那些库文件,但是静态链接wxWidgets的库是可行的,你可以在运行configure脚本的时候选择开关--disable- shared以达到这个目的.你也可以考虑将wxWdigets提供的那些动态链接库以及所需要的GTK+相关的库和你的应用程序打包在一起发布.
另外就是不要在太老的linux发行版(太老的那些动态链接库在新版上已经不提供了)或者太新的Linux发行版(需要一些老的发行版上还没有的库)上编译你的软件.同时考虑在给你的链接器增加-lsupc++ 选项,以便你的程序可以静态链接一些基本的C++的库,而不是需要完全的依赖动态链接库,这样作可能会解决一些潜在的问题(不过,请注意静态链接GPL库时候的版权问题).
最后,如果你想要针对各个发行版发布不同的软件包,如果你不想老是重新启动电脑以切换到不同的Linux,你可以考虑使用一个工具比如伟大的VMware,它可以让你同时在你的机器上运行多个linux的发行版.
在Mac OSX上安装程序
在Mac OSX上,你真正需要作的就是确认你的软件的目录结构是正确的,然后将它作成一个合适的磁盘镜像文件,我们将简单的介绍一下.Mac OSX上没有安装程序制作工具这种东西,你只需要将你的文件夹拖到磁盘的合适的位置就可以了.
下面我们大概来介绍一下Mac的软件包结构,你可以在苹果公司的网站 http://developer.apple.com/documentation/MacOSX/Conceptual/SystemOverview/Bundles/chapter_4_section_3.html 找到更多的信息.
一个软件包包含一个标准的目录结构和一个Info.plist文件,这个文件用来描述软件包的某些属性.
一个小的软件包结构如下所示:
```
DialogBlocks.app/ ; top-level directory
Contents/
Info.plist ; the property list file
MacOS/
DialogBlocks ; the executable
Resources/
dialogblocks-app.icns ; the app icon
dialogblocks-doc.icns ; the document icon(s)
```
下面是一个用于DialogBlocks软禁的Info.plist文件的例子:
```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleIdentifier</key>
<string>uk.co.anthemion.dialogblocks</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>pjd</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>dialogblocks-doc.icns</string>
<key>CFBundleTypeName</key>
<string>pjdfile</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>DialogBlocks</string>
<key>CFBundleIconFile</key>
<string>dialogblocks-app.icns</string>
<key>CFBundleName</key>
<string>DialogBlocks</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>PJDA</string>
<key>CFBundleVersion</key>
<string>1.50</string>
<key>CFBundleShortVersionString</key>
<string>1.50</string>
<key>CFBundleGetInfoString</key>
<string>DialogBlocks version 1.50, (c) 2004 Anthemion Software Ltd.</string>
<key>CFBundleLongVersionString</key>
<string>DialogBlocks version 1.50, (c) 2004 Anthemion Software Ltd.</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright 2004 Anthemion Software Ltd.</string>
<key>LSRequiresCarbon</key>
<true/>
<key>CSResourcesFileMapped</key>
<true/>
</dict>
</plist>
```
程序使用的图标和支持的文档类型是通过CFBundleIconFile和CFBundleTypeIconFile属性指定的.正如我们在第10章,"使用图片编程"中介绍的那样,如果你主要实在Windows或Linux下编程,你可以创建各种不同大小的 (16x16,32x32,48x48和128x128)的图标文件.将其保存为透明的PNG文件,然后拷贝到Mac平台上,在Finder中打开这些文件,将其拷贝和粘贴到苹果公司的图标编辑器中,然后就可以另存为icns文件了.
前面我们介绍过的maketarball.sh脚本也可以用来创建Mac OSX上的磁盘镜像文件.比如AcmeApp-1.50.dmg.它将已经准备好的AcmeApp.app包中的目录结构拷贝到新的目录结构,然后拷贝用于Mac OSX的可执行文件和数据文件,然后再使用下面的代码创建一个可以直接用于Internet安装的磁盘镜像文件:
```
echo Making a disk image...
hdiutil create AcmeApp-$VERSION.dmg -volname AcmeApp-$VERSION -type UDIF -megabytes 50 -fs HFS+
echo Mounting the disk image...
MYDEV=`hdiutil attach AcmeApp-$VERSION.dmg | tail -n 1 | awk '{print $1'}`
echo Device is $MYDEV
echo Copying AcmeApp to the disk image...
ditto --rsrc AcmeApp-$VERSION /Volumes/AcmeApp-$VERSION/AcmeApp-$VERSION
echo Unmounting the disk image...
hdiutil detach $MYDEV
echo Compressing the disk image...
hdiutil convert AcmeApp-$VERSION.dmg -format UDZO -o AcmeApp-$VERSION-compressed.dmg
echo Internet enabling the disk image...
hdiutil internet-enable AcmeApp-$VERSION-compressed.dmg
echo Renaming compressed image...
rm -f AcmeApp-$VERSION.dmg
mv AcmeApp-$VERSION-compressed.dmg AcmeApp-$VERSION.dmg
```
之后,新创建的磁盘镜像文件就可以拷贝到你的FTP站点或者CD-ROM站点.当你的用户在一个浏览器中点击这个文件的时候,文件就会被自动下载,解包,加载成一个虚拟的磁盘,所有这些过程都不需要用户的干预,然后就等着用户把整个软件包拖拽到磁盘的合适的位置,就可以完成软件的安装了.
- 第一章 介绍
- 1.1 为什么要使用wxWidgets?
- 1.2 wxWidgets的历史
- 1.3 wxWidgets社区
- 1.4 wxWidgets和面向对象编程
- 1.5 wxWidgets的体系结构
- 1.6 许可协议
- 第一章小结
- 第二章 开始使用
- 2.1 一个小例子
- 2.2 应用程序类
- 2.3 Frame窗口类
- 2.4 事件处理函数
- 2.5 Frame窗口的构造函数
- 2.6 完整的例子
- 2.7 wxWidgets程序一般执行过程
- 2.8 编译和运行程序
- 第二章小结
- 第三章 事件处理
- 3.1 事件驱动编程
- 3.2 事件表和事件处理过程
- 3.3 过滤某个事件
- 3.4 挂载事件表
- 3.5 动态事件处理方法
- 3.6 窗口标识符
- 3.7 自定义事件
- 第三章小结
- 第四章 窗口的基础知识
- 4.1 窗口解析
- 4.2 窗口类概览
- 4.3 基础窗口类
- 4.4 顶层窗口
- 4.5 容器窗口
- 4.6 非静态控件
- 4.7 静态控件
- 4.8 菜单
- 4.9 控制条
- 第四章小结
- 第五章绘画和打印
- 5.1 理解设备上下文
- 5.2 绘画工具
- 5.3 设备上下文中的绘画函数
- 5.4 使用打印框架
- 5.5 使用wxGLCanvas绘制三维图形
- 第五章小节
- 第六章处理用户输入
- 6.1 鼠标输入
- 6.2 处理键盘事件
- 6.3 处理游戏手柄事件
- 第六章小结
- 第七章使用布局控件进行窗口布局
- 7.1 窗口布局基础
- 7.2 窗口布局控件
- 7.3 使用布局控件进行编程
- 7.4 更多关于布局的话题
- 第七章小结
- 第八章使用标准对话框
- 8.1信息对话框
- 8.2 文件和目录对话框
- 8.3 选择和选项对话框
- 8.4 输入对话框
- 8.5 打印对话框
- 第八章小结
- 第九章创建定制的对话框
- 9.1 创建定制对话框的步骤
- 9.2 一个例子:PersonalRecordDialog
- 9.3 在小型设备上调整你的对话框
- 9.4 一些更深入的话题
- 9.5 使用wxWidgets资源文件
- 第九章小结
- 第十章使用图像编程
- 10.1 wxWidgets中图片相关的类
- 10.2 使用wxBitmap编程
- 10.3 使用wxIcon编程
- 10.4 使用wxCursor编程
- 10.5 使用wxImage编程
- 10.6 图片列表和图标集
- 10.7 自定义wxWidgets提供的小图片
- 第十章小结
- 第十一章剪贴板和拖放操作
- 11.1 数据对象
- 11.2 使用剪贴板
- 11.3 实现拖放操作
- 第十一章小结
- 第十二章高级窗口控件
- 12.1 wxTreeCtrl
- 12.2 wxListCtrl
- 12.3 wxWizard
- 12.4 wxHtmlWindow
- 12.5 wxGrid
- 12.6 wxTaskBarIcon
- 12.7 编写自定义的控件
- 第十二章小结
- 第十三章数据结构类
- 13.1 为什么没有使用STL?
- 13.2 字符串类型
- 13.3 wxArray
- 13.4 wxList和wxNode
- 13.5 wxHashMap
- 13.6 存储和使用日期和时间
- 13.7 其它常用的数据类型
- 第十三章小结
- 第十四章文件和流操作
- 14.1 文件类和函数
- 14.2 流操作相关类
- 第十四章小结
- 第十五章内存管理,调试和错误处理
- 15.1 内存管理基础
- 15.2 检测内存泄漏和其它错误
- 15.3 构建自防御的程序
- 15.4 错误报告
- 15.5 提供运行期类型信息
- 15.6 使用wxModule
- 15.7 加载动态链接库
- 15.8 异常处理
- 15.9 调试提示
- 第十五章小结
- 第十六章编写国际化程序
- 16.1 国际化介绍
- 16.2 从翻译说起
- 16.3 字符编码和Unicode
- 16.4 数字和日期
- 16.5 其它媒介
- 16.6 一个小例子
- 第十六章小结
- 第十七章编写多线程程序
- 17.1 什么时候使用多线程,什么时候不要使用
- 17.2 使用wxThread
- 17.3 用于线程同步的对象
- 17.4 多线程的替代方案
- 第十七章小结
- 第十八章使用wxSocket编程
- 18.1 Socket类和功能概览
- 18.2 Socket及其基本处理介绍
- 18.3 Socket标记
- 18.4 使用Socket流
- 18.5 替代wxSocket
- 第十八章小结
- 第十九章使用文档/视图框架
- 19.1 文档/视图基础
- 19.2 文档/视图框架的其它能力
- 19.3 实现Undo/Redo的策略
- 第十九章小结
- 第二十章完善你的应用程序
- 20.1 单个实例和多个实例
- 20.2 更改事件处理机制
- 20.3 降低闪烁
- 20.4 实现联机帮助
- 20.5 解析命令行参数
- 20.6 存储应用程序资源
- 20.7 调用别的应用程序
- 20.8 管理应用程序设置
- 20.9 应用程序安装
- 20.10 遵循用户界面设计规范
- 20.11 全书小结