3

我有一个基本的MSI项目。我需要删除现在集成到我们主应用程序中的另一个MSI产品。我试图使用升级方案并将其视为主要升级。但是,由于我认为升级代码不匹配,所以这不起作用。安装时卸载另一个MSI

接下来,我还做了一个自定义操作,在CostFinalize之后运行msiexec.exe(我认为这是在Installshield帮助中说明的)。直到我安装在没有安装程序的系统上时,希望消除。如果未安装其他过时的产品,我的安装程序将失败。我试图在系统搜索设置的自定义操作上添加一个条件,但似乎系统搜索功能有限。我不能只检查一个注册表项并设置一个布尔属性。

任何想法?

回答

5

有几件事情要考虑

1)UpgradeTable(FindRelatedProducts/RemoveExisting产品)可以用来删除与其他产品的相关的UpgradeCode ProductCodes。

2)如果使用内存,MSI不会在每台机器安装期间(或其他方式)移除每个用户的产品。上下文必须相同。

3)在静默安装期间,UI序列不运行。

4)您不能从执行顺序运行msiexec,因为每台计算机只有一个执行顺序的系统互斥量。 5)如果你在UI中进行计划(我已经告诉过你不应该在静默安装期间不运行),还有另一个互斥量,每个进程只有1个UI。

如果你要从每个用户到每个用户或每台机器到每台机器,我认为这是合理的,你应该能够使用升级元素/表行来做你想做的事情,而无需编写自定义操作。否则,在进入msiexec世界之前,您需要一个setup.exe样式的引导程序来处理卸载。

+0

感谢您的信息。按照您的建议,我可以使用升级元素完成所需的工作。 – Web 2010-03-10 13:50:04

-1

我在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;