Pergunta

Eu tenho um projeto MSI Básico. Eu preciso remover outro produto MSI na instalação que agora está integrado na nossa aplicação principal. Eu tentei usar os cenários de atualização e tratá-lo como um grande upgrade. No entanto, isso não funcionou por causa dos códigos de atualização não corresponde eu acredito.

Em seguida, eu também fez uma ação personalizada que msiexec.exe correu atrás do CostFinalize (acho que isso foi afirmado na ajuda do InstallShield.) Isso funcionou perfeitamente até que eu instalado em um sistema que não tem o instalador eu estava procurando remover. Meu instalador seria um fracasso se o outro produto obsoleto não foi instalado. Eu tentei colocar uma condição para a ação personalizada definida pela busca do sistema, mas parece que a pesquisa do sistema é limitada em termos de funcionalidade. Eu não pode apenas verificar uma chave reg e definir uma propriedade booleana.

Todas as idéias?

Foi útil?

Solução

Algumas coisas a considerar

1) O UpgradeTable (FindRelatedProducts / RemoveExisting produtos) pode ser usado para remover ProductCodes associados UpgradeCode de outro produto.

2) Se a memória serve, MSI não irá remover um produto por usuário durante uma Per-Machine instalar (ou o contrário). O contexto tem que ser o mesmo.

3) O UI Sequence não é executado durante instalações silenciosas.

4) Não é possível executar a sequência de msiexec executar porque existe um sistema de largura de exclusão mútua de apenas uma sequência de executar por máquina.

5) Se você agendar no UI (eu já lhe disse que você não deve, uma vez que não é executado durante instalações silenciosas) há um outro mutex que diz apenas 1 UI por processo.

Se você está indo de per-usuário por usuário ou por máquina por máquina, eu acho que é reasonaable você deve ser capaz de fazer o que quiser usando linhas elementos / tabela de upgrade sem escrever ações personalizadas. Caso contrário, você precisará de um bootstrapper estilo setup.exe para lidar com a desinstalação antes de entrar no mundo msiexec.

Outras dicas

Eu consegui isso no InstallShield 2013 usando personalizado InstallScript. O script é executado por meio de uma ação personalizada na seqüência UI, mas eu colocá-lo depois do diálogo "SetupProgress", ou seja, antes de "Executar Ação" em vez de após a CostFinalize (como a documentação diz). Eu adicionei a condição "não instalado" para a ação. Se você colocar isso na ordem sugerida, ele vai começar a desinstalação assim que o instalador foi inicializado. Se você movê-lo para onde eu fiz, ele não começar até que o usuário clicou no botão de instalação final agora.

A razão para colocar isso na seqüência UI é para contornar a um instalador MSI (ou desinstalação) a um problema de tempo. Isso simplesmente não funciona na sequência de execução por causa dessa restrição.

A fraqueza principal deste método é que, como Christopher declarou, este não irá funcionar em uma instalação silenciosa (que também é encontrada na documentação IS). Que é o meio oficial para alcançar este embora. (Confira: http://helpnet.installshield.com/installshield16helplib/IHelpCustomActionMSIExec.htm ) Se você pode viver com isso (desde instalação silenciosa é geralmente como cenário especial), então isso funciona muito bem.

Como Chris também disse, você não pode iniciar o ui desinstalador enquanto o ui principal está em execução, mas isso não é um problema com o meu script, porque acrescenta uma opção de linha de comando para executar o desinstalador sem a ui (ou seja, em silêncio).

Meu script também evita ter que saber o guid do aplicativo que você deseja desinstalar. Aqui está o script para ligar para a ação personalizada (UninstallPriorVersions é a função de ponto 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top