Pregunta

Tengo un proyecto MSI básico.Necesito eliminar otro producto MSI durante la instalación que ahora está integrado en nuestra aplicación principal.Intenté utilizar los escenarios de actualización y tratarlos como una actualización importante.Sin embargo, esto no funcionó debido a que creo que los códigos de actualización no coinciden.

A continuación, también realicé una acción personalizada que ejecutaba msiexec.exe después de CostFinalize (creo que esto se indicó en la ayuda de Installshield). Esto funcionó perfectamente hasta que lo instalé en un sistema que no tenía el instalador que buscaba eliminar.Mi instalador fallaría si no se instalara el otro producto obsoleto.Intenté poner una condición a la acción personalizada establecida por la búsqueda del sistema, pero parece que la búsqueda del sistema tiene una funcionalidad limitada.No puedo simplemente verificar una clave de registro y establecer una propiedad booleana.

¿Algunas ideas?

¿Fue útil?

Solución

Algunas cosas a considerar

1) UpgradeTable ( FindRelatedProducts / RemoveExisting Products ) se puede utilizar para eliminar ProductCodes asociados con el UpgradeCode de otro producto.

2) Si no hay memoria, MSI no eliminará un producto por usuario durante una instalación por máquina (o al revés).El contexto tiene que ser el mismo.

3) La secuencia de interfaz de usuario no se ejecuta durante instalaciones silenciosas.

4) No puede ejecutar msiexec desde la secuencia de ejecución porque hay un mutex en todo el sistema de solo una secuencia de ejecución por máquina.

5) Si programa en la interfaz de usuario (ya le dije que no debería hacerlo, ya que no se ejecuta durante las instalaciones silenciosas), hay otro mutex que dice solo 1 interfaz de usuario por proceso.

Si va de por usuario a por usuario o de por máquina a por máquina, creo que es razonable que pueda hacer lo que quiera utilizando elementos de actualización/filas de tabla sin escribir acciones personalizadas.De lo contrario, necesitará un programa previo estilo setup.exe para manejar la desinstalación antes de ingresar al mundo msiexec.

Otros consejos

he logrado esto en InstallShield 2013 usando encargo InstallScript. El script se ejecuta a través de una acción personalizada en la secuencia de la interfaz de usuario, pero lo colocó tras el diálogo "SetupProgress", es decir, antes de "Ejecutar acción" en lugar de después de la CostFinalize (como la documentación que dice). He añadido la condición "no se instala" a la acción. Si coloca esto en el orden sugerido, se dará inicio a la desinstalación en cuanto el instalador ha inicializado. Si lo mueve a donde hice, no se inicio a hasta que el usuario ha hecho clic en el último botón Instalar ahora.

La razón para poner esto en la secuencia de la interfaz de usuario es moverse por el instalador MSI (o desinstalación) a un problema de tiempo. Esto simplemente no funciona en la secuencia de ejecución debido a que la restricción.

La principal debilidad de este método es que como se ha dicho Christopher, esto no funcionará en una instalación silenciosa (que también se encuentra en la documentación IS). Es decir, los medios oficiales para lograr esto, sin embargo. (Echa un vistazo a: http://helpnet.installshield.com/installshield16helplib/IHelpCustomActionMSIExec.htm ) Si se puede vivir con eso (ya que la instalación silenciosa es generalmente escenario como caso especial), entonces esto funciona muy bien.

Como también se dijo Chris, no se puede iniciar la interfaz de usuario de desinstalación mientras que la interfaz de usuario principal está en marcha, pero eso no es un problema con mi guión, ya que añade un modificador de línea de comandos para ejecutar el programa de desinstalación sin la interfaz de usuario (es decir, en silencio).

Mi script también evita tener que conocer el GUID de la aplicación que desea desinstalar. Aquí está la secuencia de comandos para unirse a la acción personalizada (UninstallPriorVersions es la función de punto de entrada):

////////////////////////////////////////////////////////////////////////////////
    //                                                                            
    //  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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top