偶遇csdnerA君, 需要解决领导给他的打包需求.
需求要求:
* 二次安装时,需要检查是否有旧版安装.
* 如果有旧版安装,提示用户是否继续安装.
* 如果不继续安装,退出安装程序.
* 如果继续安装,实现覆盖安装,不让用户在UI上选择( 修改,修复,卸载).
我在2010年时,经常会用到InstallShield. 现在的打包程序是自己写的, 界面效果好, 安装程序控制灵活.
我开始建议A君自己去写安装程序. 但是A君说领导指定必须用InstallShield来打包, 晕倒啊~
A君在用InstallShield的过程中, 不知道是哪里的细节搞的不对, 不能实现覆盖安装, 也不能完全实现提示用户后的安装控制.
我今天下午和晚上就贡献给他, 为他来做这个实验. 对自己来说, 也是对技术点的复习和提高. 和他讨论的过程中, 也学到了一些他的心得.
实验做完了, 解决了他的问题, 实验结果很完美~
实验环境 : InstallShield2010
test-installShield-2015-0516-2232.rar
#include "ifx.h"
prototype IsProductWasInstalled();
prototype GetUnInstallExePathName(BYREF STRING);
prototype RunUnInstallExe();
BOOL g_bSetupNow; ///< 如果旧版程序已经安装, 记录用户选择(是否覆盖安装)
function OnBegin()
begin
// TODO: Perform custom initialization steps, check requirements, etc.
g_bSetupNow = TRUE;
if IsProductWasInstalled() then
if (AskYesNo("已经发现本软件已经被安装!\r\n" + "是否继续安装?", YES) = NO) then
g_bSetupNow = FALSE; ///< 用户选择不安装
// RunUnInstallExe();
endif;
endif;
end;
function IsProductWasInstalled()
BOOL bRc;
begin
if (MAINTENANCE != 0) then
// 产品已经被安装过
bRc = TRUE;
else
// 产品首次被安装(还没有被安装,本次安装是首次)
bRc = FALSE;
endif;
return bRc;
end;
function GetUnInstallExePathName(strPathName)
BOOL bRc;
begin
if (RegDBGetItem(REGDB_UNINSTALL_MODIFYPATH, strPathName) < 0) then
bRc = FALSE;
else
bRc = TRUE;
endif;
return bRc;
end;
function RunUnInstallExe()
STRING strUnInstallPathName;
begin
/**
if (GetUnInstallExePathName(strUnInstallPathName)) then
LaunchAppAndWait(strUnInstallPathName, "", WAIT);
endif;
*/
ComponentRemoveAll();
end;
//---------------------------------------------------------------------------
// OnFirstUIBefore
//
// First Install UI Sequence - Before Move Data
//
// The OnFirstUIBefore event is called by OnShowUI when the setup is
// running in first install mode. By default this event displays UI allowing
// the end user to specify installation parameters.
//
// Note: This event will not be called automatically in a
// program...endprogram style setup.
//---------------------------------------------------------------------------
function OnFirstUIBefore()
number nResult, nLevel, nSize, nSetupType;
string szTitle, szMsg, szOpt1, szOpt2, szLicenseFile;
string szName, szCompany, szTargetPath, szDir, szFeatures;
BOOL bLicenseAccepted;
begin
/// 不安装时的处理 : 退出安装程序
if (g_bSetupNow == FALSE) then
abort;
endif;
// Added in InstallShield 15 - Show an appropriate error message if
// -removeonly is specified and the product is not installed.
if( REMOVEONLY ) then
Disable( DIALOGCACHE );
szMsg = SdLoadString( IDS_IFX_ERROR_PRODUCT_NOT_INSTALLED_UNINST );
SdSubstituteProductInfo( szMsg );
MessageBox( szMsg, SEVERE );
abort;
endif;
nSetupType = COMPLETE;
szDir = TARGETDIR;
szName = "";
szCompany = "";
bLicenseAccepted = FALSE;
// Beginning of UI Sequence
Dlg_Start:
nResult = 0;
Dlg_SdWelcome:
szTitle = "";
szMsg = "";
//{{IS_SCRIPT_TAG(Dlg_SdWelcome)
nResult = SdWelcome( szTitle, szMsg );
//}}IS_SCRIPT_TAG(Dlg_SdWelcome)
if (nResult = BACK) goto Dlg_Start;
Dlg_SdLicense2:
szTitle = "";
szOpt1 = "";
szOpt2 = "";
//{{IS_SCRIPT_TAG(License_File_Path)
szLicenseFile = SUPPORTDIR ^ "License.rtf";
//}}IS_SCRIPT_TAG(License_File_Path)
//{{IS_SCRIPT_TAG(Dlg_SdLicense2)
nResult = SdLicense2Ex( szTitle, szOpt1, szOpt2, szLicenseFile, bLicenseAccepted, TRUE );
//}}IS_SCRIPT_TAG(Dlg_SdLicense2)
if (nResult = BACK) then
goto Dlg_SdWelcome;
else
bLicenseAccepted = TRUE;
endif;
Dlg_SdRegisterUser:
szMsg = "";
szTitle = "";
//{{IS_SCRIPT_TAG(Dlg_SdRegisterUser)
nResult = SdRegisterUser( szTitle, szMsg, szName, szCompany );
//}}IS_SCRIPT_TAG(Dlg_SdRegisterUser)
if (nResult = BACK) goto Dlg_SdLicense2;
Dlg_SetupType2:
szTitle = "";
szMsg = "";
nResult = CUSTOM;
//{{IS_SCRIPT_TAG(Dlg_SetupType2)
nResult = SetupType2( szTitle, szMsg, "", nSetupType, 0 );
//}}IS_SCRIPT_TAG(Dlg_SetupType2)
if (nResult = BACK) then
goto Dlg_SdRegisterUser;
else
nSetupType = nResult;
if (nSetupType != CUSTOM) then
szTargetPath = TARGETDIR;
nSize = 0;
FeatureCompareSizeRequired( MEDIA, szTargetPath, nSize );
if (nSize != 0) then
MessageBox( szSdStr_NotEnoughSpace, WARNING );
goto Dlg_SetupType2;
endif;
endif;
endif;
Dlg_SdAskDestPath2:
if ((nResult = BACK) && (nSetupType != CUSTOM)) goto Dlg_SetupType2;
szTitle = "";
szMsg = "";
if (nSetupType = CUSTOM) then
//{{IS_SCRIPT_TAG(Dlg_SdAskDestPath2)
nResult = SdAskDestPath2( szTitle, szMsg, szDir );
//}}IS_SCRIPT_TAG(Dlg_SdAskDestPath2)
TARGETDIR = szDir;
endif;
if (nResult = BACK) goto Dlg_SetupType2;
Dlg_SdFeatureTree:
if ((nResult = BACK) && (nSetupType != CUSTOM)) goto Dlg_SdAskDestPath2;
szTitle = "";
szMsg = "";
szFeatures = "";
nLevel = 2;
if (nSetupType = CUSTOM) then
//{{IS_SCRIPT_TAG(Dlg_SdFeatureTree)
nResult = SdFeatureTree( szTitle, szMsg, TARGETDIR, szFeatures, nLevel );
//}}IS_SCRIPT_TAG(Dlg_SdFeatureTree)
if (nResult = BACK) goto Dlg_SdAskDestPath2;
endif;
Dlg_SQLServer:
nResult = OnSQLServerInitialize( nResult );
if( nResult = BACK ) goto Dlg_SdFeatureTree;
Dlg_ObjDialogs:
nResult = ShowObjWizardPages( nResult );
if (nResult = BACK) goto Dlg_SQLServer;
Dlg_SdStartCopy2:
szTitle = "";
szMsg = "";
//{{IS_SCRIPT_TAG(Dlg_SdStartCopy2)
nResult = SdStartCopy2( szTitle, szMsg );
//}}IS_SCRIPT_TAG(Dlg_SdStartCopy2)
if (nResult = BACK) goto Dlg_ObjDialogs;
// Added in 11.0 - Set appropriate StatusEx static text.
SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_FIRSTUI ) );
return 0;
end;
//---------------------------------------------------------------------------
// OnMaintUIBefore
//
// Maintenance UI Sequence - Before Move Data
//
// The OnMaintUIBefore event is called by OnShowUI when the setup is
// running in maintenance mode. By default this event displays UI that
// allows the end user to add or remove features, repair currently
// installed features or uninstall the application.
//
// Note: This event will not be called automatically in a
// program...endprogram style setup.
//---------------------------------------------------------------------------
function OnMaintUIBefore()
number nResult, nType;
string szTitle, szMsg;
begin
/// 不安装时的处理 : 退出安装程序
if (g_bSetupNow == FALSE) then
abort;
endif;
// nType defaults to MODIFY.
nType = REPAIR; ///< 只有选修复, 才能实现覆盖安装
//Initialize SQL
OnSQLServerInitializeMaint();
// Beginning of UI Sequence
Dlg_Start:
// Added in Version 9.5 - Support for REMOVEONLY option.
if( !REMOVEONLY ) then
// In standard mode show maintenance dialog
Disable( BACKBUTTON );
/// 不让用户选择, 直接进行"修复", 实现覆盖安装
// nType = SdWelcomeMaint( szTitle, szMsg, nType );
Enable( BACKBUTTON );
nResult = NEXT;
else
// Hide the initial progress dialog as otherwise the user can
// click on it, and hide the MessageBox.
Disable( DIALOGCACHE );
// In RemoveOnly mode, set to remove.
nType = REMOVEALL;
endif;
// Show Uninstall Confirmation Dialog
if ( nType = REMOVEALL ) then
nResult = MessageBox( SdLoadString( IFX_MAINTUI_MSG ), MB_YESNO );
if (nResult != IDYES ) then
if( REMOVEONLY ) then
// In REMOVEONLY mode, abort the setup.
abort;
else
// In non-REMOVEONLY mode, redisplay the previous dialog.
goto Dlg_Start;
endif;
endif;
endif;
Dlg_SdFeatureTree:
if ( nType = MODIFY ) then
szTitle = "";
szMsg = SdLoadString( SD_STR_COMPONENT_MAINT_MSG );
nResult = SdFeatureTree( szTitle, szMsg, TARGETDIR, "", -1 );
if ( nResult = BACK ) goto Dlg_Start;
endif;
Dlg_ObjDialogs:
nResult = ShowObjWizardPages( nResult );
if ( ( nResult = BACK ) && ( nType != MODIFY ) ) goto Dlg_Start;
if ( ( nResult = BACK ) && ( nType = MODIFY ) ) goto Dlg_SdFeatureTree;
switch(nType)
case REMOVEALL:
// Ensure that all previously installed features are removed.
FeatureRemoveAllInMediaAndLog();
// Added in 11.0 - Set appropriate StatusEx static text.
SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_MAINTUI_REMOVEALL ) );
case REPAIR:
// Changed for DevStudio 9, Disk1 files are now always updated when installed
// so when running from ADDREMOVE we need to prevent these files from being
// updated since this will result in files being updated that are locked by the setup.
// Updating these files when running from ADDREMOVE should not be needed since updates
// are not run directly from Add/Remove.
if( ADDREMOVE ) then
// Reinstall all previously installed features, except
// disk1 features.
FeatureUpdate( "" );
else
// Reinstall all previously installed features.
FeatureReinstall();
endif;
// Added in 11.0 - Set appropriate StatusEx static text.
SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_MAINTUI_REPAIR ) );
case MODIFY:
// Added in 11.0 - Set appropriate StatusEx static text.
SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_MAINTUI_MODIFY ) );
endswitch;
end;
//---------------------------------------------------------------------------
// OnMoveData
//
// The OnMoveData event is called by OnShowUI to initiate the file
// transfer of the setup.
//
// Note: This event will not be called automatically in a
// program...endprogram style setup.
//---------------------------------------------------------------------------
function OnMoveData()
number nResult, nMediaFlags;
begin
// Don't install the DISK1COMPONENT if MAINT_OPTION_NONE was specified.
if( MAINT_OPTION = MAINT_OPTION_NONE ) then
FeatureSelectItem( MEDIA, DISK1COMPONENT, FALSE );
endif;
// Updated in 11.5, disable the cancel button during file transfer unless
// this is non-maintenance mode or repair mode.
if( MAINTENANCE && ( !REINSTALLMODE || UPDATEMODE ) ) then
Disable( CANCELBUTTON );
endif;
// Show Status
// Note: Start status window at 1 in case CreateInstallationInfo call
// is lengthy.
SetStatusWindow( 1, "" );
Enable( STATUSEX );
StatusUpdate( ON, 100 );
// Create the uninstall infomation (after displaying the progress dialog)
// Don't create uninstall information if MAINT_OPTION_NONE was specified.
if( MAINT_OPTION != MAINT_OPTION_NONE ) then
CreateInstallationInfo();
endif;
// Move Data
nResult = FeatureTransferData( MEDIA );
// Moved in 11.0, Check for failure before creating uninstall key.
// Handle move data error and abort if error occured.
if( nResult < ISERR_SUCCESS ) then
OnComponentError();
abort;
endif;
// Create uninstall key, if DISK1COMPONENT was installed.
if( IFX_DISK1INSTALLED ) then
// Store text-subs for maintenance mode later, only do this when
// disk 1 is installed. Note that any text-subs that are updated after
// this call will not be remembered during maintenance mode.
FeatureSaveTarget("");
// Write uninstall information.
MaintenanceStart();
// Customize Uninstall Information
OnCustomizeUninstInfo();
endif;
// Disable Status
Disable( STATUSEX );
end;