Gemfield在发布SYSZUXdesk程序(http://civilnet.cn/syszux/syszuxdesk)时遇到了一个难题, SYSZUXdesk程序以release方式生成.exe后,和相关的dll文件一起拷贝到另一台电脑上,点击运行程序,程序界面闪一下后就消失了。然后屏幕右下角的程序图标出现一会儿(是灰显,鼠标放上去就消失了)。整个过程没有报任何错误。
但作为对比,在gemfield本机(安装有Qt环境)上运行却没有错误。这样的对比说明了一个问题,发行的程序缺少某种必要的库。
奇怪的事情就在这里,我仔细检查了发行包中的dll库,好像都没有落下的。直到某此意外发现,原来发行包中的sql库版本不对导致。
Gemfield下面就总结一下发布Qt应用程序的步骤以及一些概念(仅讨论windows平台下,linux平台的道理一样):
Static发行方式:就是将Qt的应用程序和库编译在一起,库就包含在exe文件中。这样做的前提是,Qt库本身也是static编译的,否则的话你就得重新编译Qt库(这一点参考“如何静态编译Qt库”)。这种发行方式会导致.exe变得极为庞大。以SYSZUXdesk为例,没有static编译时,exe文件大小为4.3MB,而包含了库的static编译后的exe文件为13MB。
动态编译方式:exe文件里不包含库,在程序启动的时候再加载对应的dll文件。采用这种方式发布程序的话,Qt程序应该以release方式发布(也即不要使用debug方式)。在QtSdk的目录下,dll有两种,比如Qtcore4.dll和QtCored4.dll,后者就是debug编译时使用的库,前者是release编译时使用的库。
SYSZUXdesk最终采用的是下面这种方式,步骤如下(编译平台Qt 4.6.2):
1、 编译出SYSZUXdesk.exe程序。
2、 在脱离Qt安装路径的另外一个盘里(比如F:)新建文件夹,名字为SYSZUX,将SYSZUXdesk.exe放在SYSZUX文件下。此时双击运行exe的话一定会提示错误的。
3、 将必须的库放到SYSZUX文件下。必须的库有libgcc_s_dw2-1.dll、mingwm10.dll、QtGui4.dll、QtCore4.dll。此外,因为SYSZUXdesk还添加了其他模块,所以也要把对应的库放入SYSZUX文件夹。他们是:phonon4.dll(phonon模块)、QtNetwork4.dll(网络模块)、QtScript4.dll(script模块)、QtSql4.dll(数据库模块)等。
4、 在上一步中添加的dll文件在Qt sdk中有两处、分别是/bin下和/qt/bin下,而且它们的大小也不一样,肯定有所区别。这是因为这两个路径下的dll所依赖的更底一层的库是不一样的。其中/qt/bin下的dll是依赖mingw的,所以gemfield采用这个路径下的dll文件。
5、 添加插件库。像SYSZUXdesk这样的应用程序采用了sqlite数据库,所以需要包含qsqlite4.dll插件库。这个插件库也有两处,同上面所述的类似,gemfield采用的是/qt/plugins/sqldrivers下的qsqlite4.dll插件库。此外,SYSZUXdesk还采用了/qt/plugins/imageformats下的各种图形插件。
6、 暂停一下。
************************************************
Windows遵循下面的搜索顺序来定位DLL:
1. 包含EXE文件的目录,
2. 进程的当前工作目录,
3. Windows系统目录,
4. Windows目录,
5. 列在Path环境变量中的一系列目录。
************************************************
7、 从上面可以知道,我们把SYSZUXdesk.exe和所有的dll库放在SYSZUX文件夹下,这样exe文件可以找到dll,也就不用设置什么环境变量了。
8、 但是,对于插件库不是这样。应用程序要找到插件库有三种方法。
第一、 应用程序源代码里使用addLibraryPath()函数添加库路径。
第二、 使用qt.conf文件的协助。
第三、 最简单的方法,将插件库放在默认位置。比如,SYSZUXdesk.exe的路径为/SYSZUX/SYSZUXdesk.exe,那么图片插件库的路径为/SYSZUX/imageformats/qgif4.dll等,数据库插件的路径为/SYSZUX/sqldrivers/qsqlite4.dll等。其实,插件库的路径为/SYSZUX/plugins/imageformats/qgif4.dll……也可以,因为这个也属于QtCore4.dll的默认查找路径。
9、 其实,插件库的路径是在QtCore4.dll中调用的。SYSZUXdesk采用的是上述第三种最简单的方法。
10、将整个SYSZUX文件夹复制到另一台PC上(没有安装过Qt)测试一下。
11、使用一种程序打包工具来打包程序。SYSZUXdesk使用的是NSIS打包工具,你可以从http://civilnet.cn/bbs/browse.php?topicno=4246处下载。
最后,gemfield要重申一下,打包前一定要验证第10步。这是因为,虽然你在第2步中已经脱离Qt目录了,但是你使用的是本机上的QtCore4.dll,他仍能依据配置文件查找到插件库的路径所在。所以你即使缺少插件库,在这种情况下也测试不出来。
那么有没有一个工具可以辅助测试exe程序到底依赖哪些库呢?有的,那就是Dependency Walker,你可以从http://civilnet.cn/bbs/browse.php?topicno=4354处下载。该程序用来检测分析一个windows平台上的应用程序对库的依赖关系。还可以以debug形式启动待测的应用程序,观察程序的启动过程中对各种库的调用顺序(也即使用其中的start profiling功能)。有些库是程序执行的时候才开始知道从哪调用的,这样的话start pfofiling功能就非常有用。SYSZUXdesk的sqlite插件版本不对就是靠这种方式检测出来的。
【备注】:本文属于gemfield的CivilNet博客(www.civilnet.cn/gemfield)【Qt乐园】版块;bug提交至[email protected];资料发布及讨论区:www.civilnet.cn/qt;转载此文时,请保证包括【备注】在内的文章的完整性。