我在InstallShield 2013中使用自定义InstallScript实现了这一点。该脚本通过用户界面序列中的自定义操作执行,但我将它放在“SetupProgress”对话框之后,即在“执行操作”之前,而不是在CostFinalize之后(正如文档所述)。我添加了条件“未安装”的行动。如果按照建议的顺序放置它,只要安装程序初始化就会立即启动卸载。如果您将它移动到我所做的位置,它将不会启动,直到用户点击最终安装即时按钮。
把它放在UI序列中的原因是为了解决一个msi安装程序(或卸载程序)的问题。由于这种限制,这在执行顺序中不起作用。
这种方法的主要弱点是,正如克里斯托弗所言,这不会在无提示安装中工作(IS文档中也有这种安装方式)。这是实现这一点的官方手段。 (检查出:http://helpnet.installshield.com/installshield16helplib/IHelpCustomActionMSIExec.htm)如果你能忍受这一点(因为无声安装通常是特殊情况),那么这工作得很好。
正如Chris所说,在主用户界面运行时,您不能启动卸载程序用户界面,但这对我的脚本没有问题,因为它添加了一个命令行开关,用于在没有用户界面的情况下运行卸载程序(即静默) 。
我的脚本还避免必须知道要卸载的应用程序的guid。这里是绑定到自定义动作的脚本(UninstallPriorVersions是入口点函数):
////////////////////////////////////////////////////////////////////////////////
//
// This template script provides the code necessary to build an entry-point
// function to be called in an InstallScript custom action.
//
//
// File Name: Setup.rul
//
// Description: InstallShield script
//
////////////////////////////////////////////////////////////////////////////////
// Include Ifx.h for built-in InstallScript function prototypes, for Windows
// Installer API function prototypes and constants, and to declare code for
// the OnBegin and OnEnd events.
#include "ifx.h"
// The keyword export identifies MyFunction() as an entry-point function.
// The argument it accepts must be a handle to the Installer database.
export prototype UninstallPriorVersions(HWND);
// To Do: Declare global variables, define constants, and prototype user-
// defined and DLL functions here.
prototype NUMBER UninstallApplicationByName(STRING);
prototype NUMBER GetUninstallCmdLine(STRING, BOOL, BYREF STRING);
prototype STRING GetUninstallKey(STRING);
prototype NUMBER RegDBGetSubKeyNameContainingValue(NUMBER, STRING, STRING, STRING, BYREF STRING);
// To Do: Create a custom action for this entry-point function:
// 1. Right-click on "Custom Actions" in the Sequences/Actions view.
// 2. Select "Custom Action Wizard" from the context menu.
// 3. Proceed through the wizard and give the custom action a unique name.
// 4. Select "Run InstallScript code" for the custom action type, and in
// the next panel select "MyFunction" (or the new name of the entry-
// point function) for the source.
// 5. Click Next, accepting the default selections until the wizard
// creates the custom action.
//
// Once you have made a custom action, you must execute it in your setup by
// inserting it into a sequence or making it the result of a dialog's
// control event.
///////////////////////////////////////////////////////////////////////////////
//
// Function: UninstallPriorVersions
//
// Purpose: Uninstall prior versions of this application
//
///////////////////////////////////////////////////////////////////////////////
function UninstallPriorVersions(hMSI)
begin
UninstallApplicationByName("The Name Of Some App");
end;
///////////////////////////////////////////////////////////////////////////////
//
// Function: UninstallApplicationByName
//
// Purpose: Uninstall an application (without knowing the guid)
//
// Returns: (UninstCmdLine is assigned a value by referrence)
// >= ISERR_SUCCESS The function successfully got the command line.
// < ISERR_SUCCESS The function failed to get the command line.
//
///////////////////////////////////////////////////////////////////////////////
function NUMBER UninstallApplicationByName(AppName)
NUMBER nReturn;
STRING UninstCmdLine;
begin
nReturn = GetUninstallCmdLine(AppName, TRUE, UninstCmdLine);
if(nReturn < ISERR_SUCCESS) then
return nReturn;
endif;
if(LaunchAppAndWait("", UninstCmdLine, LAAW_OPTION_WAIT) = 0) then
return ISERR_SUCCESS;
else
return ISERR_SUCCESS-1;
endif;
end;
///////////////////////////////////////////////////////////////////////////////
//
// Function: GetUninstallCmdLine
//
// Purpose: Get the command line statement to uninstall an application
//
// Returns: (UninstCmdLine is assigned a value by referrence)
// >= ISERR_SUCCESS The function successfully got the command line.
// < ISERR_SUCCESS The function failed to get the command line.
//
///////////////////////////////////////////////////////////////////////////////
function NUMBER GetUninstallCmdLine(AppName, Silent, UninstCmdLine)
NUMBER nReturn;
begin
nReturn = RegDBGetUninstCmdLine (GetUninstallKey(AppName), UninstCmdLine);
if(nReturn < ISERR_SUCCESS) then
return nReturn;
endif;
if(Silent && StrFind(UninstCmdLine, "MsiExec.exe") >= 0)then
UninstCmdLine = UninstCmdLine + " /qn";
endif;
return nReturn;
end;
///////////////////////////////////////////////////////////////////////////////
//
// Function: GetUninstallKey
//
// Purpose: Find the uninstall key in the registry for an application looked up by name
//
// Returns: The uninstall key (i.e. the guid or a fall back value)
//
///////////////////////////////////////////////////////////////////////////////
function STRING GetUninstallKey(AppName)
STRING guid;
STRING Key64, Key32, ValueName;
begin
Key64 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
Key32 = "SOFTWARE\\Wow6432Node\\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
ValueName = "DisplayName";
if(RegDBGetSubKeyNameContainingValue(HKEY_LOCAL_MACHINE, Key64, ValueName, AppName, guid) = 0) then
return guid; // return 64 bit GUID
endif;
if(RegDBGetSubKeyNameContainingValue(HKEY_LOCAL_MACHINE, Key32, ValueName, AppName, guid) = 0) then
return guid; // return 32 bit GUID
endif;
return AppName; // return old style uninstall key (fall back value)
end;
///////////////////////////////////////////////////////////////////////////////
//
// Function: RegDBGetSubKeyNameContainingValue
//
// Purpose: Find a registry sub key containing a given value.
// Return the NAME of the subkey (NOT the entire key path)
//
// Returns: (SubKeyName is assigned a value by referrence)
// = 0 A sub key name was found with a matching value
// != 0 Failed to find a sub key with a matching value
//
///////////////////////////////////////////////////////////////////////////////
function NUMBER RegDBGetSubKeyNameContainingValue(nRootKey, Key, ValueName, Value, SubKeyName)
STRING SearchSubKey, SubKey, svValue;
NUMBER nResult, nType, nvSize;
LIST listSubKeys;
begin
SubKeyName = "";
listSubKeys = ListCreate(STRINGLIST);
if (listSubKeys = LIST_NULL) then
MessageBox ("Unable to create necessary list.", SEVERE);
abort;
endif;
RegDBSetDefaultRoot(nRootKey);
if (RegDBQueryKey(Key, REGDB_KEYS, listSubKeys) = 0) then
nResult = ListGetFirstString (listSubKeys, SubKey);
while (nResult != END_OF_LIST)
SearchSubKey = Key + "\\" + SubKey;
nType = REGDB_STRING;
if (RegDBGetKeyValueEx (SearchSubKey, ValueName, nType, svValue, nvSize) = 0) then
if(svValue = Value) then
SubKeyName = SubKey;
nResult = END_OF_LIST;
endif;
endif;
if(nResult != END_OF_LIST) then
nResult = ListGetNextString (listSubKeys, SubKey);
endif;
endwhile;
endif;
ListDestroy (listSubKeys);
if (SubKeyName = "") then
return 1;
else
return 0;
endif;
end;
感谢您的信息。按照您的建议,我可以使用升级元素完成所需的工作。 – Web 2010-03-10 13:50:04