nResult = SdStartCopy( szTitle, szMsg, listStartCopy );
//*******加入安装过程中用户所选择的主要步骤 |
ListAddString(listStartCopy,"客户信息:",AFTER); |
ListAddString(listStartCopy,"用户名:" + svName,AFTER); |
ListAddString(listStartCopy,"用户单位:" + svCompany,AFTER); |
ListAddString(listStartCopy,"",AFTER); |
ListAddString(listStartCopy,"程序安装路径:" + szDir,AFTER); |
ListAddString(listStartCopy,"",AFTER); |
ListAddString(listStartCopy,"程序文件夹:" + szfolder,AFTER); |
ListAddString(listStartCopy,"",AFTER); |
case TYPICAL : ListAddString(listStartCopy,"安装类型:典型安装",AFTER); |
case COMPACT: ListAddString(listStartCopy,"安装类型:压缩安装",AFTER); |
case CUSTOM: ListAddString(listStartCopy,"安装类型:自定义安装",AFTER); |
其中svName、svCompany、szDir等变量我们可以在事件OnFirstUIBefore开始的变量定义中找到。从变量的名称我们很清楚的知道该变量存放的是用户名,单位,安装目录等。 |
十二、 第一幅背景,第二幅背景——图片12,图片13 |
| | 细心的朋友一定会注意到软件在安装过程中会出现两副不同的背景图片,图12和图13,这就是InstallShield的显示区界面,我们可以称它为布告板。这看上去是不是很像在播放幻灯片?呵呵,我们可以和微软的比一比了(说句笑话)。言归正传,还是来说说是如何实现的吧。 |
布告板,它只有在文件被传输时才被激活。也就是说,当你调用ComponentTransferData函数来解压并拷贝时布告板才被显示,该函数是有系统自动调用,用不着我们来操心。 |
但是,你不能为一个布告板指定显示时间,InstallShield会根据整个程序的安装时间(指文件拷贝时间)自动的为每一个文件平均分配,但至少是2秒。如果你的安装程序仅持续20秒,而你却放置了25副图片,很显然系统只会显示前10副图片。 |
1、 在你的脚本里,首先要确保在文件被传输前调用Enable(BACKGROUND) 和 Enable(FULLWINDOWMODE),我们可以加在事件OnMoving中。形式如下: |
PlaceWindow(STATUSDLG, 400, 10, LOWER_LEFT); |
RegDBSetItem(REGDB_APPPATH, szAppPath); |
RegDBSetItem(REGDB_APPPATH_DEFAULT, szAppPath ^ @PRODUCT_KEY); |
Enable(BACKGROUND) :显示安装主背景窗口 |
Enable(FULLWINDOWMODE):设置主背景窗口为最大化。 |
2、 在设置文件(Setup Files)面板中,在合适的目标语言和平台下放置具有特殊后缀名的布告板文件。默认的命名是:“Bbrd”,然后再跟一个数字,最后再加上后缀“.bmp”或“.wmf”。例如,我们现在要加三副BMP图片,则这三副图片的名称分别应该是Bbrd1.bmp、Bbrd2.bmp、Bbrd3.bmp。 |
好了,运行一下,很不错。我们可以利用这项技术在安装过程中播放类似幻灯片效果,就像安装Windows一样。 |
十三、 复制完成 | ![](http://www.ccw.com.cn/htm/app/aprog/01_8_1_3p.jpg) |
| 在很多情况下,当我们修改了注册表或是在系统中安装了其它驱动程序的时候,为了确保应用程序能够顺利运行,我们还是希望用户在安装结束之后能够重新启动一下系统。这时函数SdFinishReboot就显得十分有用。 |
该函数显示一个重新启动的对话框,它提供给用户两种选择:系统自动启动和用户自己启动。当函数返回WILL_REBOOT表明用户选择了重新启动;当返回NEXT(或1)表明用户选择了自己启动;当返回小于0表明当用户选择了重新启动,但重新启动失败。在缺省的情况下是用户自己启动。 |
一般情况下,我们是在事件OnEnd中调用SdFinishReboot函数。OnEnd顾名思义是在Setup脚本里最后被执行的事件。程序代码如下: |
/*****************重启动代码*******************/ |
STRING szTitle, szMsg1, szMsg2; |
NUMBER nOption, nReserved; |
szTitle = "已经完成全部的拷贝工作,"; |
szMsg1 = "在使用本软件前必须重新启动。"; |
szMsg2 = '请选择一种启动方式,然后单击"确定"完成安装。'; |
if (SdFinishReboot (szTitle, szMsg1, nOption, szMsg2, nReserved) < 0) then |
MessageBox ("重启动失败,请重新启动系统后再使用本软件!", SEVERE); |
/**************** 结束 *******************/ |
其中,szTitle、szMsg1、szMsg2不设置也罢,只是系统会以缺省的方式出现。 |
如果说我们完成了图1到图15全部功能后就可以马上动手制作安装盘的话那我们所做的也只是给应用程序加了一个花边,并没有实际功效。它只是把原先由人工的复制工作交给系统自动去执行,更说不上文章开头所说的实现系统的自动配置。我们还是得在应用程序发行时去用户现场安装、配置一些接口程序。 |
大家都知道,我们开发的程序有百分之九十以上是和数据库打交道。目前,数据库种类的繁多,各软件厂商又各自推出自己的数据库接口程序,这就使得我们要确保开发出来的应用程序最终能够安全顺利的运行就必须携带上自己的数据库接口程序,虽然有一些接口标准,但这远远不够。因此,如何将这些工作交给安装程序去完成就成为能否制作出具有专业水准的安装程序的关键,也是为什么我会把它作为一个标题单独分开。 |
那么如何在制作安装程序的时候实现数据库驱动程序的安装和配置呢?要实现它我们还是先从注册表和ODBC两个基本概念入手,下面进行分别讨论。 |
在早期的Windows系列应用程序中用后缀为.ini的文件保存Windows系统和其他该应用程序的配置信息。后来,Windows95的出现,系统就用一个称做注册表的来代替保存应用程序配置信息的.ini文件,尽管Windows95仍然支持使用.ini文件。用注册表来保留这些信息可以为我们提供一个配置信息的单一源,这不同于那些分布在磁盘里的各种.ini文件的杂烩。同时,注册表也提供了一个简单机制来保存当前用户或当前配置的信息。注册表甚至允许你通过网络管理其他机器的注册表。另外,在NT下,注册表能提供安全级别,这在先前的配置存储机制中是没有的。因此,使得能否合理的使用注册表成为WIN32应用程序的特征之一。 |
注册表使用的是树型体系结构,树中的每个结点称键。每个键也可以包含其他的键或子键。它允许进一步的分支,也即为值,它用来存储有效的数据。在注册表中,注册表用键来组织数据,一个键中的值用它们的名来识别,键名由可以打印的ASCII字符组成的简单字符串。另外,以英文句号(.)开始的键名被系统所保留。键中的值可以用不同的数据来表示,可以是从一个简单的整数到用户定义的二进制对象。在注册表中只有四个基本键会受到应用程序安装的影响,这四个键分别是HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE和HKEY_USERS。而其中对我们配置ODBC有用的只有HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER。 |
在HKEY_LOCAL_MACHINE中保存的是关于本地机器的配置信息,独立于任何特殊的用户都存储在其中。此配置信息又进一步分成几个不同的子键,它们是Config,Enum,Hardware,Network,Security,Software和System。 |
其中,Software子键用于包含用户应用程序的配置信息,这是最有可能被我们的应用程序使用的地方。由于我们的应用程序所要的信息一般来说对其他程序并不需要,同时也为了更好的管理这些信息,因此我们应该在HKEY_LOCAL_MACHINE/SOFTWARE下增加一个子键,键名当然是能代表我们的应用程序。 |
键HKEY_CURRENT_USER通常为我们的应用程序存储和恢复特殊用户相应的特殊配置提供了方便。同样,在HKEY_CURRENT_USER中也存在一些子键,最常用的键是Software,打开Software键我们会发现有一个子键ODBC,顾名思义里面存放的当前机器上的ODBC.INI文件的内容, |
好了,有关注册表的一些知识就简单的说到这里,在开始开始配置我们的ODBC之前,为了便于读者能够顺利的读懂本文所附带的程序代码,现将有关InstallShell注册表函数作一个简单的介绍。 |
该函数为其他的注册表函数设置一个不同的根键。大多数InstallShield注册表函数工作在缺省的以键HKEY_CLASSES_ROOT为根的注册键树上,用这个函数你就可以指定一个特殊的键为根,像键HKEY_LOCAL_MACHINE或 HKEY_CURRENT_USER或HKEY_USERS。 |
该函数为某个在注册表中的键设置键值。如果该键不存在,该函数将为你创建该键。但是,新创建的键不会被安装软件卸载程序所登记,除非它是一个已登记过的键的子键。要想让安装软件卸载程序所登记可以先用RegDBCreateKeyEx创建然后再设置该键值。 |
该函数用来在注册表中创建一个键。当使用该函数来创建一个键或子键时,为了保证能被正确地安装软件卸载程序登记要首先确保它的父键首先已经被成功创建。 |
ODBC,即开放式数据库连接,它是由Microsoft公司提供的应用程序接口(API),一个单独的应用程序通过它可以访问许多个不同类型的数据库及不同格式的文件。 |
虽然继ODBC之后业界已推出了一些像OLE DB,ADO等之类的新技术,但目前,开放式数据库连接API也许是在Windows应用程序中用得最广泛的数据库接口。在每种数据库所用到的专用接口中,除了加入特殊代码外,还需要为ODBC API译码。在ODBC API和用来与数据库交换信息的专用接口之间,特殊的ODBC驱动程序提供了任何一种必需的译码。 |
以下的表列出了 ODBC 桌面数据库驱动程序 4.0 的每一个组件所要求的文件。这些文件安装在 Windows的系统目录/Windows/System或 Windows NT的系统目录/Windows/System32下。如果 ODBC 文件以前被安装在一个不同的目录中,请确定你使用的是在 /Windows/System (或 System32) 目录下的新文件。 |
某些文件是多个部件共同需要的。如果你想要和你的应用程序一起重新分配任一个 ODBC 桌面数据库驱动程序,这些文件也必须被重新分配。建议在分发软件时在你的应用程序目录下建立一个 “/ODBC/”目录,将这些重新拷贝到该目录底下,但一定要确保该路径是个PATH搜索路径。 |
以下的文件对于每一个 ODBC 桌面数据库驱动程序 4.0 是共同的: |
Msrd2x40.dll,Msrd3x40.dll |
Msxbse40.dll,Oddbse32.dll |
Mstext40.dll,Odtext32.dll |
1、 ODBC 桌面数据库驱动程序4.0版本至少需要 16 MB 的随机访问内存 (RAM) |
2、 有关与这些驱动程序一起使用的ODBC版本的信息,参考ODBC程序员参考手册。 |
3、Adaptive Server Anywehre ODBC驱动程序 |
下表列出的是工作在Adaptive Server Anywehre环境下的驱动程序,必须将这些文件拷贝到一个独立的目录下,并且使系统能够搜索到。 |
1、 ODBC翻译程序仅仅在你的应用程序是依赖于ANSI标准的字符串转换。 |
2、 网络接口程序库是专门用于网络通信用,它仅仅是在客户端的程序访问网络服务器上才必须存在。 |
3、 连接对话框程序文件在以下的几种情况下才需要加入: |
为了使用ODBC驱动程序安装程序不仅仅只是将这些驱动程序文件拷贝到硬盘上,它还必须在注册表中设置一组ODBC驱动程序的属性。 |
Adaptive Server Anywhere安装程序会自动在NT和WIN9X的系统注册表中标识和配置ODBC驱动程序。因此,如果你为你的最终用户制作应用程序的安装程序时也必须进行相同的设置。 |
通过查看Windows注册表工具,我们发现Adaptive Server Anywhere的ODBC驱动程序是在下列的键中被系统所标识的。 |
HKEY_LOCAL_MACHINE/Software/ODBC/ODBCINST.INI/Adaptive Server Anywhere 6.0,其中需要设置的下键有 |
除此之外,你还必须在键HKEY_LOCAL_MACHINE/Software/ODBC/ODBCINST.INI/ODBC Drivers中为Adaptive Server Anywhere注册 |
Adaptive Server Anywhere 6.0 |
除了进行ODBC驱动程序注册外,还必须为用户的数据源进行注册。这是因为每一个用户数据源必须被注册表登记才能被系统所识别,这样用户才能使用它。因此,必须在键HKEY_CURRENT_USER/Software/ODBC/ODBC.INI/中进行如下注册。 |
Adaptive Server Anywhere Sample Database |
1、 上面的path除了DatabaseFile指的是客户数据库所在的路径,其他的都是指Adaptive Server Anywhere的安装路径。我们在给客户分发应用软件的时候一般不再另外安装Adaptive Server Anywhere,所以可以将Adaptive Server Anywhere路径下的文件Dbeng6.exe,Dbodbc6.dll,Dblgon6.dll,Dbport6.dll,Dbcon6.dll,Dbodtr6.dll一起分发给客户,并且拷贝至一个单独目录下,然后将路径指向它就可以了。 |
2、 在dbeng6.exe后加上“-q”是为了隐藏任务栏上的SQLAnywhere窗口。 |
另外,必须将数据源加到注册表中的数据源列表中,加入到如下键中 |
HKEY_CURRENT_USER/Software/ODBC/ODBC.INI/ODBC Data Sources/ |
该键中存放的是每一个数据源和ODBC驱动程序的关联。键名是指数据源名字;键值是指ODBC驱动程序的名字。由于本文所叙述的是基于PowerBuilder7.0开发环境下的应用程序的发布,所以键值都是"Adaptive Server Anywhere 6.0"(因为,当我们安装PowerBuilder 7.0时,系统默认的ODBC驱动程序的名字为Adaptive Server Anywhere 6.0),当然,如果你愿意话也可以改变它。
这里有必要提示一下,由于用户数据源的配置可能包含一些敏感的数据库设置,比如说连接数据库的用户ID和口令。这些设置都会以无格式的文本形式存储在注册表中,可以很容易的被Windows注册表编辑程序regedit.exe或regedt32.exe查看,只要有点编程经验的人都能通过获这些实用工具取该ID和口令来修改数据库中数据。因此,你在处理这些问题的时候还要三思一下,你可以选择加密口令或让用户在连接数据库时录入。 |
既然我们知道了如何在注册表中为应用程序进行ODBC配置,那么剩下的问题是,把他们放在什么地方?不用说,大家也猜到了,最好的地方就是当所有文件都已经复制完毕,在我们要求用户重新启动系统之前。事件OnMoved可以做到这一切。在做这些事的同时我们最好能够显示给用户一个消息框,告诉用户我们在干什么,做完这一切之后再关闭它。这就要用到函数SdShowMsg和Delay。见图。 |
该事件是在当所有在目标机器上的组件都被安装或反安装时响应,在该事件中的代码总是会被执行。 |
SdShowMsg:该函数打开或关闭一个非模态的小窗口,该窗口显示指定的消息。 |
Delay:该函数可以用指定的时间(秒)来使安装程序的执行时间延迟。 |
/*********************程序代码********************/ |
//*******当所有数据拷贝完毕后在这里配置ODBC |
STRING szNumName, szNumValue, svNumValue, szTitle; |
NUMBER nType, nSize, nvType, nvSize; |
szMsg = "正在进行系统配置,请等待..."; |
svDB = TARGETDIR + "//db//Demo.db"; |
svASAOdbcDll = TARGETDIR + "//ASA//dbodbc6.dll"; |
svASA = TARGETDIR + "//ASA//dbeng6.exe"; |
RegDBSetDefaultRoot (HKEY_LOCAL_MACHINE); |
szKey = "Software//ODBC//ODBCINST.INI//MyDemo ASA"; |
if (RegDBCreateKeyEx(szKey, szClass) < 0) then |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szNumValue = svASAOdbcDll; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szKey = "Software//ODBC//ODBCINST.INI//ODBC Drivers"; |
szNumName = "MyDemo ASA"; |
szNumValue = "Installed"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
RegDBSetDefaultRoot (HKEY_CURRENT_USER); |
szKey = "Software//ODBC//ODBC.INI//MyDemo"; |
if (RegDBCreateKeyEx(szKey, szClass) < 0) then |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szNumName = "DatabaseFile"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szNumName = "Description"; |
szNumValue = "My Paper's Sample"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szNumValue = svASAOdbcDll; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szNumValue = svASA + " -d -c8m"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
szKey = "Software//ODBC//ODBC.INI//ODBC Data Sources"; |
szNumValue = "MyDemo ASA"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注册表操作失败,安装程序将终止!", SEVERE); |
// UInstalled -- 反安装后删除新建的键 |
if ( FindFile(TARGETDIR + "//db//", "Demo.db", svResult) < 0 ) then |
RegDBSetDefaultRoot (HKEY_LOCAL_MACHINE); |
szKey = "Software//ODBC//ODBCINST.INI//MyDemo ASA"; |
if (RegDBDeleteKey (szKey) < 0) then |
MessageBox ("删除注册表数据失败!", SEVERE); |
szKey = "//Software//ODBC//ODBCINST.INI//ODBC Drivers"; |
if (RegDBDeleteValue (szKey, "MyDemo ASA") < 0) then |
MessageBox ("删除注册表数据失败!", SEVERE); |
RegDBSetDefaultRoot (HKEY_CURRENT_USER); |
szKey = "Software//ODBC//ODBC.INI//MyDemo"; |
if (RegDBDeleteKey (szKey) < 0) then |
MessageBox ("删除注册表数据失败!", SEVERE); |
szKey = "Software//ODBC//ODBC.INI//ODBC Data Sources"; |
if (RegDBDeleteValue (szKey, "MyDemo") < 0) then |
MessageBox ("删除注册表数据失败!", SEVERE); |
SdShowMsg (szMsg, FALSE); |
/********************* 结束 ********************/ |
在进行完成注册表的添加之后还必须做的最后一件事就是为我们的应用程序指定一个搜索路径,因为我们在安装的时候拷贝了一些DLL库文件。显儿易见这就要求我们在Autoexec.bat中文件添加一个搜索路径。 |
* FindFile:在指定的路径下查找指定的文件,当函数返回0时表示文件找到,当返回小于0的任何数时表示没有找到。 |
* CreateFile:创建一个新的文件,如果该文件已经存在,那么CreateFile将覆盖掉原先的。同样,在创建之前要先用OpenFileMode设置文件模式。 |
* OpenFileMode:在你想要打开一个已存在的文件或建立一个新文件设置文件的模式。文件的模式根据文件类型的不同会有如下几种: |
* OpenFile:打开一个已经存在的文本文件或二进制文件。但打开之前必须先要用OpenFileMode设置文件打开的模式。 |
* WriteLine:该函数在一个以添加模式(append mode)打开或建立的文本文件中写上一行文本。 |
* CloseFile:当你完成用GetLine读文件或用WriteLine写文件的操作后必须用CloseFile函数将使用的文件关闭。 |
* LongPathToQuote:在长文件名上放置或去掉双引号。因为如果是没有加引号的长文件名PAHT命令是不会认的。 |
/**********************程序代码******************/ |
OpenFileMode(FILE_MODE_APPEND); |
if (FindFile("c://", "autoexec.bat", svResult) < 0) then //没有,需要create一个 |
CreateFile(nvFileHandle, "c://","Autoexec.bat" ); |
OpenFile ( nvFileHandle , "C://" , "Autoexec.bat" ); |
svDir = TARGETDIR + "//DLL"; // 将路径指向DLL目录 |
LongPathToQuote (svDir , TRUE ); |
WriteLine( nvFileHandle," "); |
WriteLine( nvFileHandle ,"Set PATH=" +svDir +";%PATH%"); |
WriteLine( nvFileHandle," "); |
/********************** 结束 ******************/ |
到目前为止,我们已经完成了制作一个应用程序的安装实例所需要的全部工作。剩下的也只是一些锦上添花的事了。让我们最后再看看还有什么要做的?对了,还应该像其他专业软件那样在开始菜单中或者是在桌面上或者是在程序文件夹中添加快捷方式,这样用户在安装完毕之后可以很方便的找到它。在这里我们将会用到函数AddFolderIcon。 |
/******************程序代码******************/ |
STRING szProgramFolder, szItemName, szCommandLine, szWorkingDir, szIconPath; |
STRING szShortCutKey, szProgram, szParam; |
//SzProgramFolder = FOLDER_STARTMENU; |
// Set up parameters for call to AddFolderIcon. |
szProgram = FOLDER_STARTMENU; |
szParam = TARGETDIR + "//Demo.exe"; |
LongPathToQuote (szProgram, TRUE); |
LongPathToShortPath (szParam); |
szWorkingDir = TARGETDIR + "//DLL"; // 加上应用程序的工作路径 |
szProgramFolder = FOLDER_STARTMENU; |
szItemName = "My Demo in StartMenu"; |
if (AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir, |
szIconPath, nIcon, szShortCutKey, REPLACE) < 0) then |
MessageBox ("安装程序在进行开始菜单中添加快捷方式失败,稍侯请自己添加!.", SEVERE); |
szProgramFolder = FOLDER_PROGRAMS; |
szItemName = "My Demo in Programs"; |
if (AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir, |
szIconPath, nIcon, szShortCutKey, REPLACE) < 0) then |
MessageBox ("安装程序在进行程序文件中添加快捷方式失败,稍侯请自己添加!.", SEVERE); |
szProgramFolder = FOLDER_DESKTOP; |
szItemName = "My Demo in Desktop"; |
if (AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir, |
szIconPath, nIcon, szShortCutKey, REPLACE) < 0) then |
MessageBox ("安装程序在进行桌面上添加快捷方式失败,稍侯请自己添加!.", SEVERE); |
/****************** 结束 ******************/ |
当然了,我们还得在卸载程序中加上删除这些快捷方式的代码,否则的话当用户删除了我们的应用程序后会对我们的程序怒发冲冠的,我当然不希望这样。 |
/***************程序代码***************/ |
if (DeleteFolderIcon (FOLDER_DESKTOP, "My Demo in Desktop") < 0) then |
MessageBox ("安装程序在删除桌面上的快捷方式的时候出错,稍侯请自己删除!.", SEVERE); |
if (DeleteFolderIcon (FOLDER_STARTMENU, "My Demo in StartMenu") < 0) then |
MessageBox ("安装程序在删除开始菜单中的快捷方式的时候出错,稍侯请自己删除!.", SEVERE); |
if (DeleteFolderIcon (FOLDER_PROGRAMS, "My Demo in Programs") < 0) then |
MessageBox ("安装程序在删除程序文件夹中的快捷方式的时候出错,稍侯请自己删除!.", SEVERE); |
/*************** 结束 ***************/ |
好了,我们可以拿着这个安装程序向老板领赏了。还不赶快制作一个具有专业级水平的应用程序安装盘来分发给我们的客户。相信他们一定会瞪大眼睛惊叹我们的软件开发水平是一流的。废话不说了,现在就开始行动! |
至此,所有图例的有关技术已经全部讲述完毕。总之,InstallShield博大精深,我所叙述的只是一些皮毛而已,如果大家对她想更深入的了解还是请看她自带的帮助文档。 |
不过,最后我还是要多说一句,千万不要忘了编写一个详细,完整的用户手册。这可是用户最需要的,但同时也是我们最懒得去做的一件事。 | | |