Domanda

Ho un progetto di base MSI. Ho bisogno di togliere un altro prodotto MSI per l'installazione che è ora integrato nella nostra applicazione principale. Ho cercato di utilizzare gli scenari di aggiornamento e trattarlo come un importante aggiornamento. Tuttavia, questo non ha funzionato a causa dei codici di aggiornamento non corrisponde credo.

Poi, ho anche fatto un'azione personalizzata che msiexec.exe corse dietro l'CostFinalize (penso sia stato negli aiuti Installshield.) Questo ha funzionato perfettamente fino a quando ho installato su un sistema che non ha avuto il programma di installazione che cercavo rimuovere. La mia installazione fallirebbe se l'altro prodotto obsoleto non è stato installato. Ho provato a mettere una condizione sull'azione set personalizzato dalla ricerca del sistema, ma sembra la ricerca del sistema è limitata in termini di funzionalità. Non posso controllare una chiave del registro e impostare una proprietà booleana.

Tutte le idee?

È stato utile?

Soluzione

Un paio di cose da considerare

1) L'UpgradeTable (FindRelatedProducts / RemoveExisting Prodotti) può essere usato per rimuovere ProductCodes associati UpgradeCode di un altro prodotto.

2) Se la memoria non serve, MSI non rimuoverà un prodotto per utente nel corso di una macchina Per-installare (o viceversa). Il contesto deve essere la stessa.

3) la sequenza di interfaccia utente non viene eseguito durante le installazioni silenziose.

4) Non è possibile eseguire msiexec dalla sequenza di esecuzione perché non v'è un ampio sistema di mutex di un solo eseguire la sequenza per macchina.

5) Se si pianifica nella UI (ho già detto che non si dovrebbe in quanto non viene eseguito durante le installazioni silenziose) c'è un altro mutex che dice solo 1 UI per processo.

Se avete intenzione da ogni singolo utente per ogni utente o per-macchina per computer, penserei che sia reasonaable si dovrebbe essere in grado di fare ciò che si desidera utilizzare gli elementi / righe della tabella senza scrivere azioni personalizzate di aggiornamento. In caso contrario, avrete bisogno di un programma di avvio automatico stile setup.exe per gestire la disinstallazione prima di entrare nel mondo msiexec.

Altri suggerimenti

ho raggiunto questo in InstallShield 2013 utilizzando personalizzato InstallScript. Lo script viene eseguito tramite un'azione personalizzata nella sequenza di interfaccia utente, ma ho messo dopo la finestra di dialogo "SetupProgress", vale a dire prima "Esegui azione" anziché dopo la CostFinalize (come la documentazione lo dice). Ho aggiunto la condizione "non installato" all'azione. Se si inserisce questo nell'ordine suggerito, si darà il via alla disinstallazione non appena il programma di installazione è stato inizializzato. Se si sposta al punto in cui l'ho fatto, non dare il via fino a quando l'utente ha fatto clic sul pulsante finale installare ora.

Il motivo di inserire nella sequenza UI è muoversi quella installazione MSI (o disinstallazione) ad un problema di tempo. Questo semplicemente non funziona nella sequenza di esecuzione a causa di tale restrizione.

La debolezza principale di questo metodo è che, come ha dichiarato Christopher, questo non funzionerà in un'installazione invisibile (che si trova anche nella documentazione è). Questo è il mezzo ufficiale per il raggiungimento di questo però. (Check-out: http://helpnet.installshield.com/installshield16helplib/IHelpCustomActionMSIExec.htm ) Se si può vivere con quella (dal silenzio installazione è in genere lo scenario come caso speciale), allora questo funziona bene.

Come Chris ha anche detto, non è possibile avviare l'interfaccia utente di disinstallazione, mentre l'interfaccia utente primaria è in esecuzione, ma non è un problema con il mio script perché aggiunge una riga di comando per eseguire il programma di disinstallazione, senza l'interfaccia utente (vale a dire in silenzio).

Il mio script evita anche di dover conoscere il GUID dell'applicazione che si desidera disinstallare. Ecco lo script di legarsi l'azione personalizzata (UninstallPriorVersions è la funzione di punto di ingresso):

////////////////////////////////////////////////////////////////////////////////
    //                                                                            
    //  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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top