3

У меня есть проект MSI Basic. Мне нужно удалить другой продукт MSI при установке, который теперь интегрирован в наше основное приложение. Я попытался использовать сценарии обновления и рассматривать его как серьезное обновление. Тем не менее, это не сработало из-за кодов обновления, не соответствующих мне.Удалите другую MSI при установке

Далее я также сделал специальное действие, которое запускало msiexec.exe после CostFinalize (я думаю, это было указано в справке Installshield.) Это сработало отлично, пока я не установил систему, у которой не было установщика, в котором я был чтобы удалить. Мой установщик потерпит неудачу, если другой устаревший продукт не будет установлен. Я попытался установить условие на пользовательские действия, установленные системным поиском, но, похоже, системный поиск ограничен функциональностью. Я не могу просто проверить ключ реестра и установить логическое свойство.

Любые идеи?

ответ

5

Несколько вещей, чтобы рассмотреть

1) UpgradeTable (FindRelatedProducts/RemoveExisting продукты) могут быть использованы для удаления ProductCodes, связанных с UpgradeCode другого продукта.

2) Если память используется, MSI не удаляет продукт Per-User во время установки на машине (или наоборот). Контекст должен быть одним и тем же.

3) Последовательность пользовательского интерфейса не запускается во время бесшумной установки.

4) Вы не можете запустить msiexec из последовательности выполнения, потому что есть системный мьютекс только одной последовательности выполнения для каждой машины.

5) Если вы планируете в пользовательском интерфейсе (я уже говорил вам, что вы не должны этого делать, поскольку он не запускается во время бесшумной установки) есть еще один мьютекс, который говорит только 1 пользовательский интерфейс для каждого процесса.

Если вы переходите от пользователя к пользователю или к машине для каждого компьютера, я думаю, что разумно, что вы сможете делать то, что хотите, с помощью элементов Upgrade/table rows без написания пользовательских действий. В противном случае вам понадобится bootstrapper стиля setup.exe для обработки удаления до входа в мир msiexec.

+0

Спасибо за информацию. Я смог выполнить то, что мне нужно, используя элементы Upgrade, как вы предложили. – Web

-1

Я достиг этого в InstallShield 2013 с использованием пользовательского InstallScript. Сценарий выполняется с помощью пользовательского действия в последовательности пользовательского интерфейса, но я поместил его после диалога «SetupProgress», то есть перед «Execute Action» вместо «CostFinalize» (как говорится в документации). Я добавил условие «НЕ установлен» в действие. Если вы разместите его в предложенном порядке, он начнет удаление, как только инициализируется установщик. Если вы переместите его туда, где я это сделал, он не начнется, пока пользователь не нажмет кнопку окончательной установки сейчас.

Причина, по которой это делается в последовательности пользовательского интерфейса, заключается в том, чтобы обойти один установочный установщик (или деинсталлятор) MSI в задаче времени. Это просто не работает в последовательности выполнения из-за этого ограничения.

Основной недостаток этого метода заключается в том, что, как заявил Кристофер, это не будет работать при бесшумной установке (что также содержится в документации IS). Это официальное средство для достижения этого. (проверьте: http://helpnet.installshield.com/installshield16helplib/IHelpCustomActionMSIExec.htm) Если вы можете жить с этим (так как тихая установка, как правило, является специальным сценарием), то это работает просто отлично.

Как сказал Крис, вы не можете запустить деинсталлятор ui во время работы основного ui, но это не проблема с моим скриптом, потому что он добавляет ключ командной строки для запуска деинсталлятора без ui (т.е. молча) ,

Мой скрипт также позволяет избежать указания руководства приложения, которое вы хотите удалить. Вот скрипт для привязки к пользовательскому действию (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; 
Смежные вопросы