Installation 安装时卸载另一个MSI
我有一个基本的微星项目。我需要在安装时删除另一个MSI产品,它现在集成到我们的主应用程序中。我尝试使用升级场景,并将其视为主要升级。但是,这不起作用,因为我相信升级代码不匹配 接下来,我还做了一个自定义操作,在CostFinalize完成后运行msiexec.exe(我想这在Installshield帮助中有说明)。在我安装到一个没有要删除的安装程序的系统上之前,这个操作一直运行得很好。如果没有安装其他过时的产品,我的安装程序将失败。我试图对系统搜索设置的自定义操作设置一个条件,但似乎系统搜索功能有限。我不能只检查一个注册表项,然后设置一个布尔属性Installation 安装时卸载另一个MSI,installation,windows-installer,installshield,installshield-2010,Installation,Windows Installer,Installshield,Installshield 2010,我有一个基本的微星项目。我需要在安装时删除另一个MSI产品,它现在集成到我们的主应用程序中。我尝试使用升级场景,并将其视为主要升级。但是,这不起作用,因为我相信升级代码不匹配 接下来,我还做了一个自定义操作,在CostFinalize完成后运行msiexec.exe(我想这在Installshield帮助中有说明)。在我安装到一个没有要删除的安装程序的系统上之前,这个操作一直运行得很好。如果没有安装其他过时的产品,我的安装程序将失败。我试图对系统搜索设置的自定义操作设置一个条件,但似乎系统搜索功
有什么想法吗?需要考虑的一些事情 1) UpgradeTable(FindRelatedProducts/RemoveExisting Products)可用于删除与其他产品的UpgradeCode关联的产品代码 2) 如果内存可用,MSI不会在每台机器安装期间(或以其他方式)删除每用户产品。上下文必须相同 3) UI序列在静默安装期间不运行 4) 您不能从执行序列运行msiexec,因为每台机器只有一个执行序列的系统范围互斥体 5) 如果您在UI中安排时间(我已经告诉过您不应该这样做,因为它不会在静默安装期间运行),那么另一个互斥体表示每个进程只有一个UI
如果您要从每用户到每用户,或者从每台机器到每台机器,我认为您应该能够在不编写自定义操作的情况下使用升级元素/表行来执行您想要的操作。否则,在进入msiexec世界之前,您需要一个setup.exe风格的引导程序来处理卸载。我在InstallShield 2013中使用自定义InstallScript实现了这一点。脚本通过UI序列中的自定义操作执行,但我将其放在“SetupProgress”对话框之后,即“executeaction”之前,而不是CostFinalize之后(如文档所述)。我在操作中添加了条件“未安装”。如果按建议的顺序放置,安装程序初始化后将立即启动卸载。如果你把它移到我做的地方,直到用户点击final install now按钮,它才会启动 将其放入UI序列的原因是为了一次解决一个msi安装程序(或卸载程序)的问题。由于该限制,这在执行序列中根本不起作用 这种方法的主要缺点是,正如Christopher所说的,这种方法在静默安装中不起作用(这也可以在is文档中找到)。但这是实现这一目标的官方手段。(请检查:)如果您可以接受这种情况(因为静默安装通常作为特例场景),那么这种方式就可以了 正如Chris所说,您不能在主ui运行时启动卸载程序ui,但这不是我的脚本的问题,因为它添加了一个命令行开关以在没有ui的情况下运行卸载程序(即静默) 我的脚本还避免了必须知道要卸载的应用程序的guid。以下是绑定到自定义操作的脚本(UninstallPriorVersions是入口点函数):
////////////////////////////////////////////////////////////////////////////////
//
//此模板脚本提供构建入口点所需的代码
//要在InstallScript自定义操作中调用的函数。
//
//
//文件名:Setup.rul
//
//描述:InstallShield脚本
//
////////////////////////////////////////////////////////////////////////////////
//对于Windows的内置InstallScript函数原型,包括Ifx.h
//安装程序API函数原型和常量,并声明
//OnBegin和OnEnd事件。
#包括“ifx.h”
//关键字export将MyFunction()标识为入口点函数。
//它接受的参数必须是安装程序数据库的句柄。
导出原型卸载版本(HWND);
//任务:声明全局变量、定义常量和原型用户-
//在这里定义和DLL函数。
原型编号UninstallApplicationByName(字符串);
原型编号GetUninstallCmdLine(字符串、BOOL、BYREF字符串);
原型字符串GetUninstallKey(字符串);
原型编号RegDBGetSubKeyNameContainingValue(编号、字符串、字符串、字符串、BYREF字符串);
//要执行的操作:为此入口点函数创建自定义操作:
// 1. 右键单击序列/动作视图中的“自定义动作”。
// 2. 从关联菜单中选择“自定义操作向导”。
// 3. 继续执行向导并为自定义操作指定唯一名称。
// 4. 为自定义操作类型选择“运行InstallScript代码”,然后在
//下一个面板选择“MyFunction”(或条目的新名称)-
//点函数),用于源。
// 5. 单击“下一步”,接受默认选择,直到向导启动
//创建自定义操作。
//
//完成自定义操作后,必须在设置中通过
//将其插入序列或使其成为对话框
//控制事件。
///////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
//
// 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;