Удалите другой MSI при установке - PullRequest
3 голосов
/ 09 марта 2010

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

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

Есть идеи?

Ответы [ 2 ]

5 голосов
/ 10 марта 2010

Несколько вещей для рассмотрения

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

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

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

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

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

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

0 голосов
/ 14 февраля 2015

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

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

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

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

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